diff --git a/parser/parser.go b/parser/parser.go index ce1553b..9f5988d 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -5,6 +5,7 @@ package parser import ( "fmt" "strings" + "time" "git.max-richter.dev/max/marka/parser/decoders" "git.max-richter.dev/max/marka/parser/matcher" @@ -61,29 +62,73 @@ func MatchBlocks(markdownContent string) ([]matcher.Block, error) { } func ParseFile(markdownContent string) (any, error) { + timings := make(map[string]int64) + + startDetectType := time.Now() markdownContent = strings.TrimSuffix(markdownContent, "\n") contentType, err := DetectType(markdownContent) if err != nil { return nil, fmt.Errorf("could not detect type: %w", err) } + timings["detect_type"] = time.Since(startDetectType).Milliseconds() + startGetTemplate := time.Now() templateContent, err := registry.GetTemplate(contentType) if err != nil { return nil, fmt.Errorf("could not get schema: %w", err) } + timings["get_template"] = time.Since(startGetTemplate).Milliseconds() + startTemplate := time.Now() tpl, err := template.CompileTemplate(templateContent) if err != nil { return nil, fmt.Errorf("failed to compile template: %w", err) } + timings["template_compilation"] = time.Since(startTemplate).Milliseconds() + startMarkdown := time.Now() blocks := matcher.MatchBlocksFuzzy(markdownContent, tpl, 0.3) result, err := decoders.Parse(blocks) if err != nil { return nil, fmt.Errorf("failed to parse blocks: %w", err) } + timings["markdown_parsing"] = time.Since(startMarkdown).Milliseconds() - return result, nil + response := map[string]any{ + "data": result, + "timings": timings, + } + + return response, nil +} + +func ParseFileWithTemplate(markdownContent string, templateContent string) (any, error) { + timings := make(map[string]int64) + + startTemplate := time.Now() + markdownContent = strings.TrimSuffix(markdownContent, "\n") + + tpl, err := template.CompileTemplate(templateContent) + if err != nil { + return nil, fmt.Errorf("failed to compile template: %w", err) + } + timings["template_compilation"] = time.Since(startTemplate).Milliseconds() + + startMarkdown := time.Now() + blocks := matcher.MatchBlocksFuzzy(markdownContent, tpl, 0.3) + + result, err := decoders.Parse(blocks) + if err != nil { + return nil, fmt.Errorf("failed to parse blocks: %w", err) + } + timings["markdown_parsing"] = time.Since(startMarkdown).Milliseconds() + + response := map[string]any{ + "data": result, + "timings": timings, + } + + return response, nil } diff --git a/playground/eslint.config.js b/playground/eslint.config.js index 2c49fa6..29b1aca 100644 --- a/playground/eslint.config.js +++ b/playground/eslint.config.js @@ -12,6 +12,7 @@ const gitignorePath = fileURLToPath(new URL('./.gitignore', import.meta.url)); export default defineConfig( includeIgnoreFile(gitignorePath), + { ignores: ['static/wasm_exec.js'] }, js.configs.recommended, ...ts.configs.recommended, ...svelte.configs.recommended, diff --git a/playground/src/app.d.ts b/playground/src/app.d.ts index 5db8a6a..da08e6d 100644 --- a/playground/src/app.d.ts +++ b/playground/src/app.d.ts @@ -1,22 +1,13 @@ // See https://svelte.dev/docs/kit/types#app.d.ts // for information about these interfaces declare global { - namespace App { - // interface Error {} - // interface Locals {} - // interface PageData {} - // interface PageState {} - // interface Platform {} - } - - export interface Window { - Go: { - new(): { - run: (inst: WebAssembly.Instance) => Promise; - importObject: WebAssembly.Imports; - }; - }; - } + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } } -export { }; +export {}; diff --git a/playground/src/app.html b/playground/src/app.html index 83c25bb..9445e7b 100644 --- a/playground/src/app.html +++ b/playground/src/app.html @@ -3,7 +3,7 @@ - + %sveltekit.head% diff --git a/playground/src/lib/components/EditorPanel.svelte b/playground/src/lib/components/EditorPanel.svelte index b736bf6..8775835 100644 --- a/playground/src/lib/components/EditorPanel.svelte +++ b/playground/src/lib/components/EditorPanel.svelte @@ -1,5 +1,5 @@ -
-
-

{title}

+
+
+ {#if status === 'success'} + + + + {:else if status === 'error'} + + + + {/if} +
+

{title}

+ {#if subtitle} +

{subtitle}

+ {/if} +
+ {#if headerActions} +
+ {@render headerActions()} +
+ {/if} + {#if timing !== undefined} +
{timing}ms
+ {/if}
-
+
{#if readonly} -
-
{value}
+
+
{value}
{:else} - + {/if} -
+
{#if children} diff --git a/playground/src/lib/components/Playground.svelte b/playground/src/lib/components/Playground.svelte index 47f1f64..a5eab0c 100644 --- a/playground/src/lib/components/Playground.svelte +++ b/playground/src/lib/components/Playground.svelte @@ -1,24 +1,9 @@
@@ -64,21 +70,37 @@ My favourite baguette recipe title="Template" bind:value={templateValue} placeholder="Enter your Marka template here..." - /> + {status} + timing={timings?.template_compilation} + subtitle="Define your mapping schema" + > + {#snippet headerActions()} + + {/snippet} + - - {#snippet children()} -
-
- Auto-generated JSON -
-
- {/snippet} -
+
diff --git a/playground/src/lib/wasm.ts b/playground/src/lib/wasm.ts new file mode 100644 index 0000000..d537842 --- /dev/null +++ b/playground/src/lib/wasm.ts @@ -0,0 +1,74 @@ +import { readable } from 'svelte/store'; + +declare global { + interface Window { + Go: { + new (): { + run: (inst: WebAssembly.Instance) => Promise; + importObject: WebAssembly.Imports; + }; + }; + markaParseFile: (input: string) => string; + markaParseFileWithTemplate: (markdown: string, template: string) => string; + markaListTemplates: () => string; + markaGetTemplate: (name: string) => string; + } +} + +export const wasmReady = readable(false, (set) => { + if (typeof window === 'undefined') { + return; + } + + const loadWasm = async () => { + const go = new window.Go(); + try { + const result = await WebAssembly.instantiateStreaming(fetch('/main.wasm'), go.importObject); + go.run(result.instance); + set(true); + } catch (error) { + console.error('Error loading wasm module:', error); + } + }; + + if (document.readyState === 'complete') { + loadWasm(); + } else { + window.addEventListener('load', loadWasm); + } +}); + +export interface ParseResult { + data: unknown; + timings: { [key: string]: number }; +} +export function parseMarkdown(markdown: string): ParseResult { + if (typeof window.markaParseFile !== 'function') { + throw new Error('Wasm module not ready'); + } + const result = window.markaParseFile(markdown); + return JSON.parse(result); +} + +export function parseMarkdownWithTemplate(markdown: string, template: string): ParseResult { + if (typeof window.markaParseFileWithTemplate !== 'function') { + throw new Error('Wasm module not ready'); + } + const result = window.markaParseFileWithTemplate(markdown, template); + return JSON.parse(result); +} + +export function listTemplates(): string[] { + if (typeof window.markaListTemplates !== 'function') { + throw new Error('Wasm module not ready'); + } + const result = window.markaListTemplates(); + return JSON.parse(result); +} + +export function getTemplate(name: string): string { + if (typeof window.markaGetTemplate !== 'function') { + throw new Error('Wasm module not ready'); + } + return window.markaGetTemplate(name); +} diff --git a/playground/src/routes/+layout.svelte b/playground/src/routes/+layout.svelte index 8c56a3c..7b3d426 100644 --- a/playground/src/routes/+layout.svelte +++ b/playground/src/routes/+layout.svelte @@ -7,6 +7,7 @@ + {@render children?.()} diff --git a/playground/src/routes/+page.svelte b/playground/src/routes/+page.svelte index 21aaa30..f536497 100644 --- a/playground/src/routes/+page.svelte +++ b/playground/src/routes/+page.svelte @@ -1,13 +1,11 @@
-