@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
@@ -14,11 +14,11 @@
14
14
  * @module
15
15
  */
16
16
 
17
- import type {ImportDeclaration, VariableDeclaration} from 'estree';
18
- import {parse, type PreprocessorGroup, type AST} from 'svelte/compiler';
17
+ import type { ImportDeclaration, VariableDeclaration } from 'estree';
18
+ import { parse, type PreprocessorGroup, type AST } from 'svelte/compiler';
19
19
  import MagicString from 'magic-string';
20
- import {walk} from 'zimmerframe';
21
- import {should_exclude_path} from '@fuzdev/fuz_util/path.ts';
20
+ import { walk } from 'zimmerframe';
21
+ import { should_exclude_path } from '@fuzdev/fuz_util/path.ts';
22
22
  import {
23
23
  find_attribute,
24
24
  extract_static_string,
@@ -33,11 +33,11 @@ import {
33
33
  remove_import_specifier,
34
34
  handle_preprocess_error,
35
35
  type PreprocessImportInfo,
36
- type ResolvedComponentImport,
36
+ type ResolvedComponentImport
37
37
  } from '@fuzdev/fuz_util/svelte_preprocess_helpers.ts';
38
38
 
39
- import {mdz_parse} from './mdz.ts';
40
- import {mdz_to_svelte} from './mdz_to_svelte.ts';
39
+ import { mdz_parse } from './mdz.ts';
40
+ import { mdz_to_svelte, type MdzToSvelteResult } from './mdz_to_svelte.ts';
41
41
 
42
42
  /**
43
43
  * An estree `ImportDeclaration` augmented with Svelte's position data.
@@ -56,7 +56,7 @@ export interface SveltePreprocessMdzOptions {
56
56
  /**
57
57
  * Component import mapping for mdz content.
58
58
  * Key: component name as used in mdz (e.g., 'Alert').
59
- * Value: import path (e.g., '$lib/Alert.svelte').
59
+ * Value: import path (e.g., '#lib/Alert.svelte').
60
60
  *
61
61
  * If mdz content references a component not in this map,
62
62
  * that `Mdz` usage is skipped (left as runtime).
@@ -116,10 +116,10 @@ const PRECOMPILED_NAME = 'MdzPrecompiled';
116
116
  * Creates a Svelte preprocessor that compiles static `Mdz` content at build time.
117
117
  *
118
118
  * @param options - configuration for component/element resolution and file filtering
119
- * @returns a Svelte `PreprocessorGroup` for use in `svelte.config.js`
119
+ * @returns a Svelte `PreprocessorGroup` for a `preprocess` option, like `sveltekit({preprocess})` in `vite.config.ts`
120
120
  */
121
121
  export const svelte_preprocess_mdz = (
122
- options: SveltePreprocessMdzOptions = {},
122
+ options: SveltePreprocessMdzOptions = {}
123
123
  ): PreprocessorGroup => {
124
124
  const {
125
125
  exclude = [],
@@ -129,52 +129,56 @@ export const svelte_preprocess_mdz = (
129
129
  compiled_component_import = '@fuzdev/mdz/MdzPrecompiled.svelte',
130
130
  code_component_import,
131
131
  codeblock_component_import,
132
- on_error = process.env.CI === 'true' ? 'throw' : 'log',
132
+ on_error = process.env.CI === 'true' ? 'throw' : 'log'
133
133
  } = options;
134
134
 
135
135
  return {
136
136
  name: 'fuz-mdz',
137
137
 
138
- markup: ({content, filename}) => {
138
+ markup: ({ content, filename }) => {
139
139
  if (should_exclude_path(filename, exclude)) {
140
- return {code: content};
140
+ return { code: content };
141
141
  }
142
142
 
143
143
  // Quick bail: does file mention any known Mdz import source?
144
144
  if (!mdz_component_imports.some((source) => content.includes(source))) {
145
- return {code: content};
145
+ return { code: content };
146
146
  }
147
147
 
148
- const ast = parse(content, {filename, modern: true});
148
+ const ast = parse(content, { filename, modern: true });
149
149
 
150
150
  // Resolve which local names map to the Mdz component
151
151
  const mdz_names = resolve_component_names(ast, mdz_component_imports);
152
152
  if (mdz_names.size === 0) {
153
- return {code: content};
153
+ return { code: content };
154
154
  }
155
155
 
156
156
  // Check for MdzPrecompiled name collision
157
157
  if (has_name_collision(ast, compiled_component_import)) {
158
- return {code: content};
158
+ return { code: content };
159
159
  }
160
160
 
161
161
  const s = new MagicString(content);
162
162
  const bindings = build_static_bindings(ast);
163
163
 
164
164
  // Find and transform Mdz usages with static content
165
- const {transformations, total_usages, transformed_usages} = find_mdz_usages(ast, mdz_names, {
166
- components,
167
- elements,
168
- filename,
169
- source: content,
170
- bindings,
171
- on_error,
172
- code_component_import,
173
- codeblock_component_import,
174
- });
165
+ const { transformations, total_usages, transformed_usages } = find_mdz_usages(
166
+ ast,
167
+ mdz_names,
168
+ {
169
+ components,
170
+ elements,
171
+ filename,
172
+ source: content,
173
+ bindings,
174
+ on_error,
175
+ code_component_import,
176
+ codeblock_component_import
177
+ }
178
+ );
175
179
 
176
180
  if (transformations.length === 0) {
177
- return {code: content};
181
+ return { code: content };
178
182
  }
179
183
 
180
184
  // Apply transformations
@@ -190,7 +194,7 @@ export const svelte_preprocess_mdz = (
190
194
  ast,
191
195
  mdz_names,
192
196
  total_usages,
193
- transformed_usages,
197
+ transformed_usages
194
198
  );
195
199
 
196
200
  // Add required imports and remove unused Mdz imports
@@ -200,14 +204,14 @@ export const svelte_preprocess_mdz = (
200
204
  transformations,
201
205
  removable_imports,
202
206
  compiled_component_import,
203
- content,
207
+ content
204
208
  );
205
209
 
206
210
  return {
207
211
  code: s.toString(),
208
- map: s.generateMap({hires: true}),
212
+ map: s.generateMap({ hires: true })
209
213
  };
210
- },
214
+ }
211
215
  };
212
216
  };
213
217
 
@@ -273,11 +277,11 @@ const has_name_collision = (ast: AST.Root, compiled_component_import: string): b
273
277
  */
274
278
  const collect_consumed_bindings = (
275
279
  value: AST.Attribute['value'],
276
- bindings: ReadonlyMap<string, string>,
280
+ bindings: ReadonlyMap<string, string>
277
281
  ): Set<string> => {
278
282
  const consumed: Set<string> = new Set();
279
283
  if (value === true || Array.isArray(value)) return consumed;
280
- const collect_from_expr = (expr: {type: string; [key: string]: any}): void => {
284
+ const collect_from_expr = (expr: { type: string; [key: string]: any }): void => {
281
285
  if (expr.type === 'Identifier' && bindings.has(expr.name)) {
282
286
  consumed.add(expr.name);
283
287
  } else if (expr.type === 'BinaryExpression' && expr.operator === '+') {
@@ -296,6 +300,33 @@ const collect_consumed_bindings = (
296
300
  return consumed;
297
301
  };
298
302
 
303
+ /**
304
+ * Parse a static mdz string and render it to Svelte markup. Returns `null` to
305
+ * leave the usage for the runtime — either the content raised a parse error
306
+ * (reported via `on_error`) or it contains unconfigured tags (which the
307
+ * precompiler emits nothing for; the runtime renders the fallback chrome).
308
+ */
309
+ const render_static_mdz = (
310
+ content: string,
311
+ base: string | undefined,
312
+ context: FindMdzUsagesContext
313
+ ): MdzToSvelteResult | null => {
314
+ let result: MdzToSvelteResult;
315
+ try {
316
+ result = mdz_to_svelte(mdz_parse(content), {
317
+ components: context.components,
318
+ elements: context.elements,
319
+ base,
320
+ code_component_import: context.code_component_import,
321
+ codeblock_component_import: context.codeblock_component_import
322
+ });
323
+ } catch (error) {
324
+ handle_preprocess_error(error, '[fuz-mdz]', context.filename, context.on_error);
325
+ return null;
326
+ }
327
+ return result.has_unconfigured_tags ? null : result;
328
+ };
329
+
299
330
  /**
300
331
  * Walks the AST to find `Mdz` component usages with static `content` props
301
332
  * and generates transformations to replace them with `MdzPrecompiled` children.
@@ -303,7 +334,7 @@ const collect_consumed_bindings = (
303
334
  const find_mdz_usages = (
304
335
  ast: AST.Root,
305
336
  mdz_names: Map<string, ResolvedComponentImport>,
306
- context: FindMdzUsagesContext,
337
+ context: FindMdzUsagesContext
307
338
  ): FindMdzUsagesResult => {
308
339
  const transformations: Array<MdzTransformation> = [];
309
340
  const total_usages: Map<string, number> = new Map();
@@ -312,7 +343,7 @@ const find_mdz_usages = (
312
343
  // zimmerframe types visitors against {type: string}, requiring explicit annotations
313
344
  // on the callback parameters for Svelte-specific AST types like AST.Component
314
345
  walk(ast.fragment as any, null, {
315
- Component(node: AST.Component, ctx: {next: () => void}) {
346
+ Component(node: AST.Component, ctx: { next: () => void }) {
316
347
  // Always recurse into children so nested Mdz components are found
317
348
  ctx.next();
318
349
 
@@ -346,24 +377,8 @@ const find_mdz_usages = (
346
377
  // Extract static string value
347
378
  const content_value = extract_static_string(content_attr.value, context.bindings);
348
379
  if (content_value !== null) {
349
- // Parse mdz content and render to Svelte markup
350
- let result;
351
- try {
352
- const nodes = mdz_parse(content_value);
353
- result = mdz_to_svelte(nodes, {
354
- components: context.components,
355
- elements: context.elements,
356
- base,
357
- code_component_import: context.code_component_import,
358
- codeblock_component_import: context.codeblock_component_import,
359
- });
360
- } catch (error) {
361
- handle_preprocess_error(error, '[fuz-mdz]', context.filename, context.on_error);
362
- return;
363
- }
364
-
365
- // If content has unconfigured tags, skip this usage (fall back to runtime)
366
- if (result.has_unconfigured_tags) return;
380
+ const result = render_static_mdz(content_value, base, context);
381
+ if (result === null) return; // parse error or unconfigured tags — leave for runtime
367
382
 
368
383
  const consumed = collect_consumed_bindings(content_attr.value, context.bindings);
369
384
  const replacement = build_replacement(node, exclude_attrs, result.markup, context.source);
@@ -374,7 +389,7 @@ const find_mdz_usages = (
374
389
  replacement,
375
390
  required_imports: result.imports,
376
391
  consumed_bindings: consumed,
377
- component_node: node,
392
+ component_node: node
378
393
  });
379
394
  return;
380
395
  }
@@ -383,29 +398,17 @@ const find_mdz_usages = (
383
398
  const chain = try_extract_conditional_chain(
384
399
  content_attr.value,
385
400
  context.source,
386
- context.bindings,
401
+ context.bindings
387
402
  );
388
403
  if (chain === null) return;
389
404
 
390
405
  // Parse and render each branch
391
- const branch_results: Array<{markup: string; imports: Map<string, PreprocessImportInfo>}> =
406
+ const branch_results: Array<{ markup: string; imports: Map<string, PreprocessImportInfo> }> =
392
407
  [];
393
- try {
394
- for (const branch of chain) {
395
- const nodes = mdz_parse(branch.value);
396
- const result = mdz_to_svelte(nodes, {
397
- components: context.components,
398
- elements: context.elements,
399
- base,
400
- code_component_import: context.code_component_import,
401
- codeblock_component_import: context.codeblock_component_import,
402
- });
403
- if (result.has_unconfigured_tags) return;
404
- branch_results.push({markup: result.markup, imports: result.imports});
405
- }
406
- } catch (error) {
407
- handle_preprocess_error(error, '[fuz-mdz]', context.filename, context.on_error);
408
- return;
408
+ for (const branch of chain) {
409
+ const result = render_static_mdz(branch.value, base, context);
410
+ if (result === null) return; // parse error or unconfigured tags — leave for runtime
411
+ branch_results.push({ markup: result.markup, imports: result.imports });
409
412
  }
410
413
 
411
414
  // Build {#if}/{:else if}/{:else} markup
@@ -441,12 +444,12 @@ const find_mdz_usages = (
441
444
  replacement,
442
445
  required_imports: merged_imports,
443
446
  consumed_bindings: consumed,
444
- component_node: node,
447
+ component_node: node
445
448
  });
446
- },
449
+ }
447
450
  });
448
451
 
449
- return {transformations, total_usages, transformed_usages};
452
+ return { transformations, total_usages, transformed_usages };
450
453
  };
451
454
 
452
455
  /**
@@ -460,7 +463,7 @@ const remove_dead_const_bindings = (
460
463
  s: MagicString,
461
464
  ast: AST.Root,
462
465
  transformations: Array<MdzTransformation>,
463
- source: string,
466
+ source: string
464
467
  ): void => {
465
468
  // Collect all consumed binding names across transformations
466
469
  const all_consumed: Set<string> = new Set();
@@ -484,12 +487,12 @@ const remove_dead_const_bindings = (
484
487
 
485
488
  for (const name of all_consumed) {
486
489
  // Find the VariableDeclaration containing this binding in instance script
487
- let declaration_node: (VariableDeclaration & {start: number; end: number}) | null = null;
490
+ let declaration_node: (VariableDeclaration & { start: number; end: number }) | null = null;
488
491
  for (const node of instance.content.body) {
489
492
  if (node.type !== 'VariableDeclaration' || node.kind !== 'const') continue;
490
493
  for (const decl of node.declarations) {
491
494
  if (decl.id.type === 'Identifier' && decl.id.name === name) {
492
- declaration_node = node as VariableDeclaration & {start: number; end: number};
495
+ declaration_node = node as VariableDeclaration & { start: number; end: number };
493
496
  break;
494
497
  }
495
498
  }
@@ -530,13 +533,13 @@ const build_replacement = (
530
533
  node: AST.Component,
531
534
  exclude_attrs: ReadonlySet<AST.Attribute>,
532
535
  children_markup: string,
533
- source: string,
536
+ source: string
534
537
  ): string => {
535
538
  // Collect source ranges of all attributes except excluded ones (content, base when resolved)
536
- const other_attr_ranges: Array<{start: number; end: number}> = [];
539
+ const other_attr_ranges: Array<{ start: number; end: number }> = [];
537
540
  for (const attr of node.attributes) {
538
541
  if (exclude_attrs.has(attr as AST.Attribute)) continue;
539
- other_attr_ranges.push({start: attr.start, end: attr.end});
542
+ other_attr_ranges.push({ start: attr.start, end: attr.end });
540
543
  }
541
544
 
542
545
  // Build opening tag with MdzPrecompiled name
@@ -573,11 +576,11 @@ const find_removable_mdz_imports = (
573
576
  ast: AST.Root,
574
577
  mdz_names: Map<string, ResolvedComponentImport>,
575
578
  total_usages: Map<string, number>,
576
- transformed_usages: Map<string, number>,
579
+ transformed_usages: Map<string, number>
577
580
  ): Map<string, ImportRemovalAction> => {
578
581
  const removable: Map<string, ImportRemovalAction> = new Map();
579
582
 
580
- for (const [name, {import_node, specifier}] of mdz_names) {
583
+ for (const [name, { import_node, specifier }] of mdz_names) {
581
584
  const total = total_usages.get(name) ?? 0;
582
585
  const transformed = transformed_usages.get(name) ?? 0;
583
586
 
@@ -604,12 +607,12 @@ const find_removable_mdz_imports = (
604
607
 
605
608
  const positioned_node = import_node as PositionedImportDeclaration;
606
609
  if (import_node.specifiers.length === 1) {
607
- removable.set(name, {node: positioned_node, kind: 'full'});
610
+ removable.set(name, { node: positioned_node, kind: 'full' });
608
611
  } else {
609
612
  removable.set(name, {
610
613
  node: positioned_node,
611
614
  kind: 'partial',
612
- specifier_to_remove: specifier,
615
+ specifier_to_remove: specifier
613
616
  });
614
617
  }
615
618
  }
@@ -637,7 +640,7 @@ const manage_imports = (
637
640
  transformations: Array<MdzTransformation>,
638
641
  removable_imports: Map<string, ImportRemovalAction>,
639
642
  compiled_component_import: string,
640
- source: string,
643
+ source: string
641
644
  ): void => {
642
645
  // Collect all required imports across transformations
643
646
  const required: Map<string, PreprocessImportInfo> = new Map();
@@ -648,7 +651,7 @@ const manage_imports = (
648
651
  }
649
652
 
650
653
  // Always need MdzPrecompiled when transformations occur
651
- required.set(PRECOMPILED_NAME, {path: compiled_component_import, kind: 'default'});
654
+ required.set(PRECOMPILED_NAME, { path: compiled_component_import, kind: 'default' });
652
655
 
653
656
  const script = ast.instance;
654
657
 
@@ -771,7 +774,7 @@ const manage_imports = (
771
774
  action.node,
772
775
  action.specifier_to_remove!,
773
776
  source,
774
- action === partial_carrier ? carrier_lines : '',
777
+ action === partial_carrier ? carrier_lines : ''
775
778
  );
776
779
  }
777
780
  };
@@ -8,7 +8,7 @@
8
8
  * @module
9
9
  */
10
10
 
11
- import {mdz_is_url} from './mdz_helpers.ts';
11
+ import { mdz_is_url } from './mdz_helpers.ts';
12
12
 
13
13
  /** Format a reference as mdz: URLs pass through, identifiers get backticks. */
14
14
  const format_reference = (ref: string): string => (mdz_is_url(ref) ? ref : `\`${ref}\``);