@fuzdev/mdz 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +4 -0
  2. package/dist/Mdz.svelte +18 -9
  3. package/dist/Mdz.svelte.d.ts +9 -2
  4. package/dist/Mdz.svelte.d.ts.map +1 -1
  5. package/dist/MdzNodeView.svelte +129 -115
  6. package/dist/MdzNodeView.svelte.d.ts +4 -1
  7. package/dist/MdzNodeView.svelte.d.ts.map +1 -1
  8. package/dist/MdzPrecompiled.svelte +3 -3
  9. package/dist/MdzPrecompiled.svelte.d.ts.map +1 -1
  10. package/dist/MdzRoot.svelte +3 -3
  11. package/dist/MdzRoot.svelte.d.ts.map +1 -1
  12. package/dist/MdzStream.svelte +6 -6
  13. package/dist/MdzStream.svelte.d.ts.map +1 -1
  14. package/dist/MdzStreamNodeView.svelte +129 -118
  15. package/dist/MdzStreamNodeView.svelte.d.ts +4 -1
  16. package/dist/MdzStreamNodeView.svelte.d.ts.map +1 -1
  17. package/dist/context_helpers.js +1 -1
  18. package/dist/mdz.d.ts +6 -0
  19. package/dist/mdz.d.ts.map +1 -1
  20. package/dist/mdz.js +8 -0
  21. package/dist/mdz_contexts.d.ts.map +1 -1
  22. package/dist/mdz_debug_work.d.ts +38 -0
  23. package/dist/mdz_debug_work.d.ts.map +1 -0
  24. package/dist/mdz_debug_work.js +37 -0
  25. package/dist/mdz_helpers.d.ts +61 -0
  26. package/dist/mdz_helpers.d.ts.map +1 -1
  27. package/dist/mdz_helpers.js +74 -2
  28. package/dist/mdz_lexer.d.ts +12 -0
  29. package/dist/mdz_lexer.d.ts.map +1 -1
  30. package/dist/mdz_lexer.js +190 -40
  31. package/dist/mdz_opcodes.d.ts.map +1 -1
  32. package/dist/mdz_opcodes_to_nodes.d.ts.map +1 -1
  33. package/dist/mdz_opcodes_to_nodes.js +22 -17
  34. package/dist/mdz_stream_parser.d.ts.map +1 -1
  35. package/dist/mdz_stream_parser.js +8 -8
  36. package/dist/mdz_stream_parser_block.js +5 -5
  37. package/dist/mdz_stream_parser_inline.d.ts.map +1 -1
  38. package/dist/mdz_stream_parser_inline.js +22 -20
  39. package/dist/mdz_stream_parser_link.d.ts.map +1 -1
  40. package/dist/mdz_stream_parser_link.js +88 -56
  41. package/dist/mdz_stream_parser_list.d.ts.map +1 -1
  42. package/dist/mdz_stream_parser_list.js +23 -9
  43. package/dist/mdz_stream_parser_state.d.ts +29 -0
  44. package/dist/mdz_stream_parser_state.d.ts.map +1 -1
  45. package/dist/mdz_stream_parser_state.js +59 -10
  46. package/dist/mdz_stream_parser_table.d.ts +3 -2
  47. package/dist/mdz_stream_parser_table.d.ts.map +1 -1
  48. package/dist/mdz_stream_parser_table.js +24 -13
  49. package/dist/mdz_stream_parser_text.d.ts.map +1 -1
  50. package/dist/mdz_stream_parser_text.js +36 -25
  51. package/dist/mdz_stream_parser_url.js +7 -7
  52. package/dist/mdz_stream_state.svelte.d.ts.map +1 -1
  53. package/dist/mdz_to_svelte.d.ts +1 -1
  54. package/dist/mdz_to_svelte.d.ts.map +1 -1
  55. package/dist/mdz_to_svelte.js +28 -31
  56. package/dist/mdz_token_parser.d.ts.map +1 -1
  57. package/dist/mdz_token_parser.js +24 -25
  58. package/dist/svelte_preprocess_mdz.d.ts +2 -2
  59. package/dist/svelte_preprocess_mdz.d.ts.map +1 -1
  60. package/dist/svelte_preprocess_mdz.js +41 -47
  61. package/package.json +28 -40
  62. package/src/lib/context_helpers.ts +2 -2
  63. package/src/lib/mdz.ts +11 -2
  64. package/src/lib/mdz_contexts.ts +5 -5
  65. package/src/lib/mdz_debug_work.ts +38 -0
  66. package/src/lib/mdz_helpers.ts +126 -23
  67. package/src/lib/mdz_lexer.ts +243 -93
  68. package/src/lib/mdz_opcodes.ts +1 -1
  69. package/src/lib/mdz_opcodes_to_nodes.ts +35 -30
  70. package/src/lib/mdz_stream_parser.ts +17 -17
  71. package/src/lib/mdz_stream_parser_block.ts +14 -14
  72. package/src/lib/mdz_stream_parser_inline.ts +38 -34
  73. package/src/lib/mdz_stream_parser_link.ts +99 -66
  74. package/src/lib/mdz_stream_parser_list.ts +41 -29
  75. package/src/lib/mdz_stream_parser_state.ts +100 -28
  76. package/src/lib/mdz_stream_parser_table.ts +47 -36
  77. package/src/lib/mdz_stream_parser_text.ts +40 -39
  78. package/src/lib/mdz_stream_parser_url.ts +11 -11
  79. package/src/lib/mdz_stream_state.svelte.ts +8 -8
  80. package/src/lib/mdz_to_svelte.ts +38 -46
  81. package/src/lib/mdz_token_parser.ts +55 -58
  82. package/src/lib/svelte_preprocess_mdz.ts +92 -89
  83. package/src/lib/tsdoc_mdz.ts +1 -1
@@ -6,8 +6,21 @@
6
6
  *
7
7
  * @module
8
8
  */
9
- import { has_non_whitespace, mdz_extract_single_tag, mdz_heading_id, mdz_merge_adjacent_text, mdz_push_merging_text, } from './mdz_helpers.js';
9
+ import { has_non_whitespace, mdz_extract_single_tag, mdz_heading_id, mdz_merge_adjacent_text, mdz_push_merging_text } from './mdz_helpers.js';
10
10
  import {} from './mdz_lexer.js';
11
+ /**
12
+ * Whether a token type ends the current inline context — the block-boundary
13
+ * set shared by the delimiter-pair and link children loops (an unclosed
14
+ * inline never swallows block structure).
15
+ */
16
+ const token_is_block_boundary = (type) => type === 'paragraph_break' ||
17
+ type === 'heading_start' ||
18
+ type === 'hr' ||
19
+ type === 'codeblock' ||
20
+ type === 'list_open' ||
21
+ type === 'list_item_close' ||
22
+ type === 'blockquote_open' ||
23
+ type === 'table_open';
11
24
  /**
12
25
  * Builds an `MdzNode[]` tree from a lexed `MdzToken[]` stream.
13
26
  * Used by `mdz_parse`, which should be preferred for simple usage.
@@ -88,7 +101,7 @@ export class MdzTokenParser {
88
101
  lang: token.lang,
89
102
  content: token.content,
90
103
  start: token.start,
91
- end: token.end,
104
+ end: token.end
92
105
  });
93
106
  this.#index++;
94
107
  continue;
@@ -243,7 +256,7 @@ export class MdzTokenParser {
243
256
  start_number: children[0]?.number ?? 1,
244
257
  children,
245
258
  start: open.start,
246
- end,
259
+ end
247
260
  };
248
261
  }
249
262
  return { type: 'List', ordered: false, children, start: open.start, end };
@@ -293,7 +306,7 @@ export class MdzTokenParser {
293
306
  lang: t.lang,
294
307
  content: t.content,
295
308
  start: t.start,
296
- end: t.end,
309
+ end: t.end
297
310
  });
298
311
  this.#index++;
299
312
  continue;
@@ -365,7 +378,7 @@ export class MdzTokenParser {
365
378
  type: 'Text',
366
379
  content: `(${token.reference})`,
367
380
  start: token.start,
368
- end: token.end,
381
+ end: token.end
369
382
  };
370
383
  case 'tag_close':
371
384
  this.#index++;
@@ -373,7 +386,7 @@ export class MdzTokenParser {
373
386
  type: 'Text',
374
387
  content: `</${token.name}>`,
375
388
  start: token.start,
376
- end: token.end,
389
+ end: token.end
377
390
  };
378
391
  default:
379
392
  this.#index++;
@@ -398,14 +411,7 @@ export class MdzTokenParser {
398
411
  this.#index++;
399
412
  return { type: node_type, children, start, end: t.end };
400
413
  }
401
- if (t.type === 'paragraph_break' ||
402
- t.type === 'heading_start' ||
403
- t.type === 'hr' ||
404
- t.type === 'codeblock' ||
405
- t.type === 'list_open' ||
406
- t.type === 'list_item_close' ||
407
- t.type === 'blockquote_open' ||
408
- t.type === 'table_open') {
414
+ if (token_is_block_boundary(t.type)) {
409
415
  break;
410
416
  }
411
417
  const node = this.#parse_inline();
@@ -434,20 +440,13 @@ export class MdzTokenParser {
434
440
  children,
435
441
  link_type: ref_token.link_type,
436
442
  start,
437
- end: ref_token.end,
443
+ end: ref_token.end
438
444
  };
439
445
  }
440
446
  // No link_ref - treat as text
441
447
  return { type: 'Text', content: '[', start, end: start + 1 };
442
448
  }
443
- if (t.type === 'paragraph_break' ||
444
- t.type === 'heading_start' ||
445
- t.type === 'hr' ||
446
- t.type === 'codeblock' ||
447
- t.type === 'list_open' ||
448
- t.type === 'list_item_close' ||
449
- t.type === 'blockquote_open' ||
450
- t.type === 'table_open') {
449
+ if (token_is_block_boundary(t.type)) {
451
450
  break;
452
451
  }
453
452
  const node = this.#parse_inline();
@@ -465,7 +464,7 @@ export class MdzTokenParser {
465
464
  children: [{ type: 'Text', content: token.reference, start: token.start, end: token.end }],
466
465
  link_type: token.link_type,
467
466
  start: token.start,
468
- end: token.end,
467
+ end: token.end
469
468
  };
470
469
  }
471
470
  #parse_tag_node() {
@@ -542,7 +541,7 @@ export class MdzTokenParser {
542
541
  type: 'Paragraph',
543
542
  children: merged,
544
543
  start: merged[0].start,
545
- end: merged[merged.length - 1].end,
544
+ end: merged[merged.length - 1].end
546
545
  };
547
546
  }
548
547
  }
@@ -23,7 +23,7 @@ export interface SveltePreprocessMdzOptions {
23
23
  /**
24
24
  * Component import mapping for mdz content.
25
25
  * Key: component name as used in mdz (e.g., 'Alert').
26
- * Value: import path (e.g., '$lib/Alert.svelte').
26
+ * Value: import path (e.g., '#lib/Alert.svelte').
27
27
  *
28
28
  * If mdz content references a component not in this map,
29
29
  * that `Mdz` usage is skipped (left as runtime).
@@ -74,7 +74,7 @@ export interface SveltePreprocessMdzOptions {
74
74
  * Creates a Svelte preprocessor that compiles static `Mdz` content at build time.
75
75
  *
76
76
  * @param options - configuration for component/element resolution and file filtering
77
- * @returns a Svelte `PreprocessorGroup` for use in `svelte.config.js`
77
+ * @returns a Svelte `PreprocessorGroup` for a `preprocess` option, like `sveltekit({preprocess})` in `vite.config.ts`
78
78
  */
79
79
  export declare const svelte_preprocess_mdz: (options?: SveltePreprocessMdzOptions) => PreprocessorGroup;
80
80
  //# sourceMappingURL=svelte_preprocess_mdz.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"svelte_preprocess_mdz.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/svelte_preprocess_mdz.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,EAAQ,KAAK,iBAAiB,EAAW,MAAM,iBAAiB,CAAC;AA+BxE;;GAEG;AACH,MAAM,WAAW,0BAA0B;IAC1C,gCAAgC;IAChC,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IAEjC;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEpC;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEnC;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAEtC;;;;OAIG;IACH,yBAAyB,CAAC,EAAE,MAAM,CAAC;IAEnC;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAE/B;;;;OAIG;IACH,0BAA0B,CAAC,EAAE,MAAM,CAAC;IAEpC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC;CAC3B;AAID;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GACjC,UAAS,0BAA+B,KACtC,iBAyFF,CAAC"}
1
+ {"version":3,"file":"svelte_preprocess_mdz.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/svelte_preprocess_mdz.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,EAAS,KAAK,iBAAiB,EAAY,MAAM,iBAAiB,CAAC;AA+B1E;;GAEG;AACH,MAAM,WAAW,0BAA0B;IAC1C,gCAAgC;IAChC,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IAEjC;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEpC;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEnC;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAEtC;;;;OAIG;IACH,yBAAyB,CAAC,EAAE,MAAM,CAAC;IAEnC;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAE/B;;;;OAIG;IACH,0BAA0B,CAAC,EAAE,MAAM,CAAC;IAEpC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC;CAC3B;AAID;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GACjC,UAAS,0BAA+B,KACtC,iBA6FF,CAAC"}
@@ -17,7 +17,7 @@ import { parse } from 'svelte/compiler';
17
17
  import MagicString from 'magic-string';
18
18
  import { walk } from 'zimmerframe';
19
19
  import { should_exclude_path } from '@fuzdev/fuz_util/path.ts';
20
- import { find_attribute, extract_static_string, try_extract_conditional_chain, build_static_bindings, resolve_component_names, has_identifier_in_tree, find_import_insert_position, generate_import_lines, remove_variable_declaration, remove_import_declaration, remove_import_specifier, handle_preprocess_error, } from '@fuzdev/fuz_util/svelte_preprocess_helpers.ts';
20
+ import { find_attribute, extract_static_string, try_extract_conditional_chain, build_static_bindings, resolve_component_names, has_identifier_in_tree, find_import_insert_position, generate_import_lines, remove_variable_declaration, remove_import_declaration, remove_import_specifier, handle_preprocess_error } from '@fuzdev/fuz_util/svelte_preprocess_helpers.ts';
21
21
  import { mdz_parse } from './mdz.js';
22
22
  import { mdz_to_svelte } from './mdz_to_svelte.js';
23
23
  const PRECOMPILED_NAME = 'MdzPrecompiled';
@@ -25,10 +25,10 @@ const PRECOMPILED_NAME = 'MdzPrecompiled';
25
25
  * Creates a Svelte preprocessor that compiles static `Mdz` content at build time.
26
26
  *
27
27
  * @param options - configuration for component/element resolution and file filtering
28
- * @returns a Svelte `PreprocessorGroup` for use in `svelte.config.js`
28
+ * @returns a Svelte `PreprocessorGroup` for a `preprocess` option, like `sveltekit({preprocess})` in `vite.config.ts`
29
29
  */
30
30
  export const svelte_preprocess_mdz = (options = {}) => {
31
- const { exclude = [], components = {}, elements = {}, mdz_component_imports = ['@fuzdev/mdz/Mdz.svelte'], compiled_component_import = '@fuzdev/mdz/MdzPrecompiled.svelte', code_component_import, codeblock_component_import, on_error = process.env.CI === 'true' ? 'throw' : 'log', } = options;
31
+ const { exclude = [], components = {}, elements = {}, mdz_component_imports = ['@fuzdev/mdz/Mdz.svelte'], compiled_component_import = '@fuzdev/mdz/MdzPrecompiled.svelte', code_component_import, codeblock_component_import, on_error = process.env.CI === 'true' ? 'throw' : 'log' } = options;
32
32
  return {
33
33
  name: 'fuz-mdz',
34
34
  markup: ({ content, filename }) => {
@@ -60,7 +60,7 @@ export const svelte_preprocess_mdz = (options = {}) => {
60
60
  bindings,
61
61
  on_error,
62
62
  code_component_import,
63
- codeblock_component_import,
63
+ codeblock_component_import
64
64
  });
65
65
  if (transformations.length === 0) {
66
66
  return { code: content };
@@ -77,9 +77,9 @@ export const svelte_preprocess_mdz = (options = {}) => {
77
77
  manage_imports(s, ast, transformations, removable_imports, compiled_component_import, content);
78
78
  return {
79
79
  code: s.toString(),
80
- map: s.generateMap({ hires: true }),
80
+ map: s.generateMap({ hires: true })
81
81
  };
82
- },
82
+ }
83
83
  };
84
84
  };
85
85
  /**
@@ -138,6 +138,29 @@ const collect_consumed_bindings = (value, bindings) => {
138
138
  collect_from_expr(value.expression);
139
139
  return consumed;
140
140
  };
141
+ /**
142
+ * Parse a static mdz string and render it to Svelte markup. Returns `null` to
143
+ * leave the usage for the runtime — either the content raised a parse error
144
+ * (reported via `on_error`) or it contains unconfigured tags (which the
145
+ * precompiler emits nothing for; the runtime renders the fallback chrome).
146
+ */
147
+ const render_static_mdz = (content, base, context) => {
148
+ let result;
149
+ try {
150
+ result = mdz_to_svelte(mdz_parse(content), {
151
+ components: context.components,
152
+ elements: context.elements,
153
+ base,
154
+ code_component_import: context.code_component_import,
155
+ codeblock_component_import: context.codeblock_component_import
156
+ });
157
+ }
158
+ catch (error) {
159
+ handle_preprocess_error(error, '[fuz-mdz]', context.filename, context.on_error);
160
+ return null;
161
+ }
162
+ return result.has_unconfigured_tags ? null : result;
163
+ };
141
164
  /**
142
165
  * Walks the AST to find `Mdz` component usages with static `content` props
143
166
  * and generates transformations to replace them with `MdzPrecompiled` children.
@@ -181,25 +204,9 @@ const find_mdz_usages = (ast, mdz_names, context) => {
181
204
  // Extract static string value
182
205
  const content_value = extract_static_string(content_attr.value, context.bindings);
183
206
  if (content_value !== null) {
184
- // Parse mdz content and render to Svelte markup
185
- let result;
186
- try {
187
- const nodes = mdz_parse(content_value);
188
- result = mdz_to_svelte(nodes, {
189
- components: context.components,
190
- elements: context.elements,
191
- base,
192
- code_component_import: context.code_component_import,
193
- codeblock_component_import: context.codeblock_component_import,
194
- });
195
- }
196
- catch (error) {
197
- handle_preprocess_error(error, '[fuz-mdz]', context.filename, context.on_error);
198
- return;
199
- }
200
- // If content has unconfigured tags, skip this usage (fall back to runtime)
201
- if (result.has_unconfigured_tags)
202
- return;
207
+ const result = render_static_mdz(content_value, base, context);
208
+ if (result === null)
209
+ return; // parse error or unconfigured tags — leave for runtime
203
210
  const consumed = collect_consumed_bindings(content_attr.value, context.bindings);
204
211
  const replacement = build_replacement(node, exclude_attrs, result.markup, context.source);
205
212
  transformed_usages.set(node.name, (transformed_usages.get(node.name) ?? 0) + 1);
@@ -209,7 +216,7 @@ const find_mdz_usages = (ast, mdz_names, context) => {
209
216
  replacement,
210
217
  required_imports: result.imports,
211
218
  consumed_bindings: consumed,
212
- component_node: node,
219
+ component_node: node
213
220
  });
214
221
  return;
215
222
  }
@@ -219,24 +226,11 @@ const find_mdz_usages = (ast, mdz_names, context) => {
219
226
  return;
220
227
  // Parse and render each branch
221
228
  const branch_results = [];
222
- try {
223
- for (const branch of chain) {
224
- const nodes = mdz_parse(branch.value);
225
- const result = mdz_to_svelte(nodes, {
226
- components: context.components,
227
- elements: context.elements,
228
- base,
229
- code_component_import: context.code_component_import,
230
- codeblock_component_import: context.codeblock_component_import,
231
- });
232
- if (result.has_unconfigured_tags)
233
- return;
234
- branch_results.push({ markup: result.markup, imports: result.imports });
235
- }
236
- }
237
- catch (error) {
238
- handle_preprocess_error(error, '[fuz-mdz]', context.filename, context.on_error);
239
- return;
229
+ for (const branch of chain) {
230
+ const result = render_static_mdz(branch.value, base, context);
231
+ if (result === null)
232
+ return; // parse error or unconfigured tags — leave for runtime
233
+ branch_results.push({ markup: result.markup, imports: result.imports });
240
234
  }
241
235
  // Build {#if}/{:else if}/{:else} markup
242
236
  let children_markup = '';
@@ -270,9 +264,9 @@ const find_mdz_usages = (ast, mdz_names, context) => {
270
264
  replacement,
271
265
  required_imports: merged_imports,
272
266
  consumed_bindings: consumed,
273
- component_node: node,
267
+ component_node: node
274
268
  });
275
- },
269
+ }
276
270
  });
277
271
  return { transformations, total_usages, transformed_usages };
278
272
  };
@@ -402,7 +396,7 @@ const find_removable_mdz_imports = (ast, mdz_names, total_usages, transformed_us
402
396
  removable.set(name, {
403
397
  node: positioned_node,
404
398
  kind: 'partial',
405
- specifier_to_remove: specifier,
399
+ specifier_to_remove: specifier
406
400
  });
407
401
  }
408
402
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fuzdev/mdz",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "a strict markdown dialect built for streaming, Svelte authoring, docs websites, and untrusted content",
5
5
  "tagline": "markdown for fuz",
6
6
  "glyph": "📝",
@@ -30,15 +30,22 @@
30
30
  "deploy": "gro deploy",
31
31
  "benchmark": "gro run src/benchmarks/mdz.benchmark.ts",
32
32
  "benchmark:save": "gro run src/benchmarks/mdz.benchmark.ts --save",
33
- "benchmark:clean": "rm -f src/benchmarks/baseline.json"
33
+ "benchmark:clean": "rm -f src/benchmarks/baseline.json",
34
+ "benchmark:render": "BENCHMARK_RENDER=1 gro test render.benchmark",
35
+ "benchmark:render:save": "BENCHMARK_RENDER=save gro test render.benchmark",
36
+ "benchmark:render:clean": "rm -f src/benchmarks/render/baseline.json"
34
37
  },
35
38
  "type": "module",
39
+ "imports": {
40
+ "#lib/*": "./src/lib/*",
41
+ "#routes/*": "./src/routes/*"
42
+ },
36
43
  "engines": {
37
44
  "node": ">=24.14"
38
45
  },
39
46
  "peerDependencies": {
40
47
  "@fuzdev/fuz_util": ">=0.65.1",
41
- "@sveltejs/kit": "^2.47.3",
48
+ "@sveltejs/kit": "^3",
42
49
  "@types/estree": "^1",
43
50
  "esm-env": "^1",
44
51
  "svelte": "^5"
@@ -56,52 +63,33 @@
56
63
  "zimmerframe": "^1.1.4"
57
64
  },
58
65
  "devDependencies": {
59
- "@fuzdev/blake3_wasm": "^0.1.1",
60
- "@fuzdev/fuz_code": "^0.46.1",
61
- "@fuzdev/fuz_css": "^0.63.2",
62
- "@fuzdev/fuz_ui": "^0.205.1",
63
- "@fuzdev/fuz_util": "^0.65.1",
64
- "@fuzdev/gro": "^0.205.1",
65
- "@ryanatkn/eslint-config": "^0.12.1",
66
+ "@fuzdev/blake3-wasm": "^0.2.0",
67
+ "@fuzdev/fuz_code": "^0.50.0",
68
+ "@fuzdev/fuz_css": "^0.66.1",
69
+ "@fuzdev/fuz_ui": "^0.212.0",
70
+ "@fuzdev/fuz_util": "^0.72.0",
71
+ "@fuzdev/gro": "^0.214.0",
72
+ "@ryanatkn/eslint-config": "^0.12.2",
66
73
  "@sveltejs/acorn-typescript": "^1.0.10",
67
- "@sveltejs/adapter-static": "^3.0.10",
68
- "@sveltejs/kit": "2.64.0",
69
- "@sveltejs/package": "^2.5.7",
70
- "@sveltejs/vite-plugin-svelte": "^7.1.2",
74
+ "@sveltejs/adapter-static": "^4.0.0",
75
+ "@sveltejs/kit": "^3.0.1",
76
+ "@sveltejs/package": "^3.0.0",
77
+ "@sveltejs/vite-plugin-svelte": "^7.3.1",
71
78
  "@types/estree": "^1.0.8",
72
79
  "@types/node": "^24.12.4",
73
80
  "@webref/css": "^8.2.0",
74
81
  "eslint": "^9.39.1",
75
82
  "eslint-plugin-svelte": "^3.13.1",
76
83
  "esm-env": "^1.2.2",
77
- "prettier": "^3.7.4",
78
- "prettier-plugin-svelte": "^3.5.1",
79
- "svelte": "^5.56.3",
80
- "svelte-check": "^4.6.0",
81
- "svelte-docinfo": "^0.5.3",
84
+ "svelte": "^5.57.2",
85
+ "svelte-check": "^4.7.6",
86
+ "svelte-docinfo": "^0.8.0",
82
87
  "svelte2tsx": "^0.7.56",
83
88
  "tslib": "^2.8.1",
84
- "typescript": "^5.9.3",
85
- "typescript-eslint": "^8.48.1",
86
- "vite": "^8.0.16",
87
- "vitest": "^4.0.15"
88
- },
89
- "prettier": {
90
- "plugins": [
91
- "prettier-plugin-svelte"
92
- ],
93
- "useTabs": true,
94
- "printWidth": 100,
95
- "singleQuote": true,
96
- "bracketSpacing": false,
97
- "overrides": [
98
- {
99
- "files": "package.json",
100
- "options": {
101
- "useTabs": false
102
- }
103
- }
104
- ]
89
+ "typescript": "^6.0.3",
90
+ "typescript-eslint": "^8.71.1",
91
+ "vite": "^8.3.4",
92
+ "vitest": "^5.0.0"
105
93
  },
106
94
  "sideEffects": [
107
95
  "**/*.css"
@@ -1,4 +1,4 @@
1
- import {getContext, setContext} from 'svelte';
1
+ import { getContext, setContext } from 'svelte';
2
2
 
3
3
  // This uses a function instead of a class because of the overloaded type signatures.
4
4
  // It could be implemented internally with a class like `SvelteContext`
@@ -47,6 +47,6 @@ export function create_context<T>(fallback?: () => T): {
47
47
  },
48
48
  get_maybe,
49
49
  // this is typesafe, so no runtime check:
50
- set: (value: T | undefined = fallback?.()) => setContext(key, value)!,
50
+ set: (value: T | undefined = fallback?.()) => setContext(key, value)!
51
51
  };
52
52
  }
package/src/lib/mdz.ts CHANGED
@@ -50,8 +50,8 @@
50
50
  * @module
51
51
  */
52
52
 
53
- import {MdzLexer} from './mdz_lexer.ts';
54
- import {MdzTokenParser} from './mdz_token_parser.ts';
53
+ import { MdzLexer } from './mdz_lexer.ts';
54
+ import { MdzTokenParser } from './mdz_token_parser.ts';
55
55
 
56
56
  // TODO design incremental parsing or some system that preserves Svelte components across re-renders when possible
57
57
 
@@ -89,6 +89,15 @@ export class MdzTableCellParser {
89
89
  this.#lexer = new MdzLexer(text);
90
90
  }
91
91
 
92
+ /**
93
+ * Rebind to a new buffer so one instance serves a whole table — the
94
+ * streaming path rebinds per row (the buffer string changes across feeds;
95
+ * the lexer's search memo survives when it doesn't).
96
+ */
97
+ rebind(text: string): void {
98
+ this.#lexer.rebind(text);
99
+ }
100
+
92
101
  parse(cell_start: number, cell_end: number): Array<MdzNode> {
93
102
  this.#parser.reset(this.#lexer.lex_table_cell(cell_start, cell_end));
94
103
  return this.#parser.parse_inline_run();
@@ -1,6 +1,6 @@
1
- import type {Component} from 'svelte';
1
+ import type { Component } from 'svelte';
2
2
 
3
- import {create_context} from './context_helpers.ts';
3
+ import { create_context } from './context_helpers.ts';
4
4
 
5
5
  /**
6
6
  * Component registry for custom Svelte components that can be used in mdz content.
@@ -56,7 +56,7 @@ export const mdz_base_context = create_context<() => string | undefined>();
56
56
  * content as `reference` — the prop name matches `fuz_ui`'s `DocsLink`, so it
57
57
  * can be injected directly to auto-link backticked API identifiers.
58
58
  */
59
- export type MdzCodeComponent = Component<{reference: string}>;
59
+ export type MdzCodeComponent = Component<{ reference: string }>;
60
60
 
61
61
  /**
62
62
  * Context for overriding how inline `` `code` `` spans render, via a getter
@@ -69,7 +69,7 @@ export const mdz_code_context = create_context<() => MdzCodeComponent | undefine
69
69
  * matching `@fuzdev/fuz_code`'s `Code`, so it can be injected directly for
70
70
  * syntax highlighting.
71
71
  */
72
- export type MdzCodeblockComponent = Component<{lang: string | null; content: string}>;
72
+ export type MdzCodeblockComponent = Component<{ lang: string | null; content: string }>;
73
73
 
74
74
  /**
75
75
  * Context for overriding how fenced code blocks render, via a getter function.
@@ -92,7 +92,7 @@ interface MdzGetterContext<T> {
92
92
  */
93
93
  export const mdz_set_context_with_fallback = <T>(
94
94
  context: MdzGetterContext<T>,
95
- value_fn: () => T | undefined,
95
+ value_fn: () => T | undefined
96
96
  ): void => {
97
97
  const ancestor = context.get_maybe();
98
98
  context.set(() => value_fn() ?? ancestor?.());
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Deterministic scan-work counter for the complexity-ratio guards
3
+ * (`mdz_scaling_ratio.test.ts`). Off by default; when `enabled`, the parser hot
4
+ * paths accumulate the number of characters scanned (and tag-stack frames
5
+ * walked) into `total`. Unlike wall-clock timing this is a pure function of
6
+ * input + algorithm — invariant across machines, runs, and thermal state — so a
7
+ * super-linear regression shows up as a work ratio > ~linear regardless of the
8
+ * ~2× machine variance that makes absolute timing unusable here. Its sole
9
+ * consumer is `mdz_scaling_ratio.test.ts`.
10
+ *
11
+ * Coverage is the dominant re-scan choke points — `#index_of` + `#tokenize_text`
12
+ * (sync) and the streaming memo scans, `consume_text_run`, and `try_close_tag`'s
13
+ * stack walk — not every scan. A super-linear regression isolated to an
14
+ * *uncounted* probe (e.g. `#has_paragraph_break_between`, the tag-name collection
15
+ * loop, the URL/path probes) wouldn't move the ratio; extend the instrumentation
16
+ * if hot cost ever migrates there.
17
+ *
18
+ * Zero store when disabled: each instrumented site guards on `enabled` (a single
19
+ * branch-predicted property load, the same category as the shipped `DEV`
20
+ * checks). It is deliberately NOT the `esm-env` `DEV` flag — `DEV` is true under
21
+ * every `gro test`/`gro dev` run, which would leave the counter live in ordinary
22
+ * development; this flag is flipped true only by the ratio test.
23
+ *
24
+ * @nodocs
25
+ */
26
+ export const mdz_debug_work: { enabled: boolean; total: number } = { enabled: false, total: 0 };
27
+
28
+ /**
29
+ * Reset the counter and set whether the hot paths accumulate into it. Tests call
30
+ * `mdz_debug_work_reset()` immediately before a parse and read
31
+ * `mdz_debug_work.total` immediately after; pass `false` to disable and zero it.
32
+ *
33
+ * @nodocs
34
+ */
35
+ export const mdz_debug_work_reset = (enabled = true): void => {
36
+ mdz_debug_work.enabled = enabled;
37
+ mdz_debug_work.total = 0;
38
+ };