@fuzdev/mdz 0.3.0 → 0.5.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 (81) hide show
  1. package/LICENSE +10 -17
  2. package/README.md +6 -3
  3. package/dist/Mdz.svelte +8 -7
  4. package/dist/Mdz.svelte.d.ts.map +1 -1
  5. package/dist/MdzNodeView.svelte +74 -30
  6. package/dist/MdzNodeView.svelte.d.ts +1 -2
  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 +3 -3
  13. package/dist/MdzStream.svelte.d.ts.map +1 -1
  14. package/dist/MdzStreamNodeView.svelte +79 -31
  15. package/dist/MdzStreamNodeView.svelte.d.ts +1 -2
  16. package/dist/MdzStreamNodeView.svelte.d.ts.map +1 -1
  17. package/dist/context_helpers.js +1 -1
  18. package/dist/mdz.d.ts +12 -1
  19. package/dist/mdz.d.ts.map +1 -1
  20. package/dist/mdz.js +1 -1
  21. package/dist/mdz_contexts.d.ts +33 -6
  22. package/dist/mdz_contexts.d.ts.map +1 -1
  23. package/dist/mdz_debug_work.d.ts.map +1 -1
  24. package/dist/mdz_helpers.d.ts +31 -3
  25. package/dist/mdz_helpers.d.ts.map +1 -1
  26. package/dist/mdz_helpers.js +104 -12
  27. package/dist/mdz_lexer.d.ts +3 -1
  28. package/dist/mdz_lexer.d.ts.map +1 -1
  29. package/dist/mdz_lexer.js +115 -31
  30. package/dist/mdz_opcodes.d.ts +3 -1
  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 +25 -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.js +4 -4
  38. package/dist/mdz_stream_parser_link.d.ts.map +1 -1
  39. package/dist/mdz_stream_parser_link.js +83 -11
  40. package/dist/mdz_stream_parser_list.d.ts.map +1 -1
  41. package/dist/mdz_stream_parser_list.js +13 -7
  42. package/dist/mdz_stream_parser_state.d.ts.map +1 -1
  43. package/dist/mdz_stream_parser_state.js +7 -7
  44. package/dist/mdz_stream_parser_table.d.ts.map +1 -1
  45. package/dist/mdz_stream_parser_table.js +16 -9
  46. package/dist/mdz_stream_parser_text.js +2 -2
  47. package/dist/mdz_stream_parser_url.js +7 -7
  48. package/dist/mdz_stream_state.svelte.d.ts +3 -1
  49. package/dist/mdz_stream_state.svelte.d.ts.map +1 -1
  50. package/dist/mdz_stream_state.svelte.js +4 -0
  51. package/dist/mdz_to_svelte.d.ts +17 -4
  52. package/dist/mdz_to_svelte.d.ts.map +1 -1
  53. package/dist/mdz_to_svelte.js +22 -9
  54. package/dist/mdz_token_parser.d.ts.map +1 -1
  55. package/dist/mdz_token_parser.js +25 -11
  56. package/dist/svelte_preprocess_mdz.d.ts +6 -3
  57. package/dist/svelte_preprocess_mdz.d.ts.map +1 -1
  58. package/dist/svelte_preprocess_mdz.js +11 -11
  59. package/package.json +25 -40
  60. package/src/lib/context_helpers.ts +2 -2
  61. package/src/lib/mdz.ts +15 -3
  62. package/src/lib/mdz_contexts.ts +41 -11
  63. package/src/lib/mdz_debug_work.ts +1 -1
  64. package/src/lib/mdz_helpers.ts +164 -43
  65. package/src/lib/mdz_lexer.ts +173 -80
  66. package/src/lib/mdz_opcodes.ts +3 -1
  67. package/src/lib/mdz_opcodes_to_nodes.ts +40 -30
  68. package/src/lib/mdz_stream_parser.ts +17 -17
  69. package/src/lib/mdz_stream_parser_block.ts +14 -14
  70. package/src/lib/mdz_stream_parser_inline.ts +18 -18
  71. package/src/lib/mdz_stream_parser_link.ts +88 -18
  72. package/src/lib/mdz_stream_parser_list.ts +31 -25
  73. package/src/lib/mdz_stream_parser_state.ts +27 -27
  74. package/src/lib/mdz_stream_parser_table.ts +38 -31
  75. package/src/lib/mdz_stream_parser_text.ts +3 -3
  76. package/src/lib/mdz_stream_parser_url.ts +11 -11
  77. package/src/lib/mdz_stream_state.svelte.ts +11 -8
  78. package/src/lib/mdz_to_svelte.ts +71 -27
  79. package/src/lib/mdz_token_parser.ts +52 -38
  80. package/src/lib/svelte_preprocess_mdz.ts +66 -60
  81. package/src/lib/tsdoc_mdz.ts +1 -1
@@ -6,7 +6,7 @@
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
11
  /**
12
12
  * Whether a token type ends the current inline context — the block-boundary
@@ -101,7 +101,7 @@ export class MdzTokenParser {
101
101
  lang: token.lang,
102
102
  content: token.content,
103
103
  start: token.start,
104
- end: token.end,
104
+ end: token.end
105
105
  });
106
106
  this.#index++;
107
107
  continue;
@@ -256,7 +256,7 @@ export class MdzTokenParser {
256
256
  start_number: children[0]?.number ?? 1,
257
257
  children,
258
258
  start: open.start,
259
- end,
259
+ end
260
260
  };
261
261
  }
262
262
  return { type: 'List', ordered: false, children, start: open.start, end };
@@ -306,7 +306,7 @@ export class MdzTokenParser {
306
306
  lang: t.lang,
307
307
  content: t.content,
308
308
  start: t.start,
309
- end: t.end,
309
+ end: t.end
310
310
  });
311
311
  this.#index++;
312
312
  continue;
@@ -378,7 +378,7 @@ export class MdzTokenParser {
378
378
  type: 'Text',
379
379
  content: `(${token.reference})`,
380
380
  start: token.start,
381
- end: token.end,
381
+ end: token.end
382
382
  };
383
383
  case 'tag_close':
384
384
  this.#index++;
@@ -386,7 +386,7 @@ export class MdzTokenParser {
386
386
  type: 'Text',
387
387
  content: `</${token.name}>`,
388
388
  start: token.start,
389
- end: token.end,
389
+ end: token.end
390
390
  };
391
391
  default:
392
392
  this.#index++;
@@ -440,7 +440,7 @@ export class MdzTokenParser {
440
440
  children,
441
441
  link_type: ref_token.link_type,
442
442
  start,
443
- end: ref_token.end,
443
+ end: ref_token.end
444
444
  };
445
445
  }
446
446
  // No link_ref - treat as text
@@ -464,7 +464,7 @@ export class MdzTokenParser {
464
464
  children: [{ type: 'Text', content: token.reference, start: token.start, end: token.end }],
465
465
  link_type: token.link_type,
466
466
  start: token.start,
467
- end: token.end,
467
+ end: token.end
468
468
  };
469
469
  }
470
470
  #parse_tag_node() {
@@ -478,7 +478,14 @@ export class MdzTokenParser {
478
478
  const t = this.#tokens[this.#index];
479
479
  if (t.type === 'tag_close' && t.name === tag_name) {
480
480
  this.#index++;
481
- return { type: node_type, name: tag_name, children, start, end: t.end };
481
+ return {
482
+ type: node_type,
483
+ name: tag_name,
484
+ attributes: open_token.attributes,
485
+ children,
486
+ start,
487
+ end: t.end
488
+ };
482
489
  }
483
490
  const node = this.#parse_inline();
484
491
  if (node)
@@ -491,7 +498,14 @@ export class MdzTokenParser {
491
498
  const token = this.#tokens[this.#index];
492
499
  const node_type = token.is_component ? 'Component' : 'Element';
493
500
  this.#index++;
494
- return { type: node_type, name: token.name, children: [], start: token.start, end: token.end };
501
+ return {
502
+ type: node_type,
503
+ name: token.name,
504
+ attributes: token.attributes,
505
+ children: [],
506
+ start: token.start,
507
+ end: token.end
508
+ };
495
509
  }
496
510
  // -- Paragraph flushing --
497
511
  #flush_paragraph(paragraph_children, trim_trailing = false) {
@@ -541,7 +555,7 @@ export class MdzTokenParser {
541
555
  type: 'Paragraph',
542
556
  children: merged,
543
557
  start: merged[0].start,
544
- end: merged[merged.length - 1].end,
558
+ end: merged[merged.length - 1].end
545
559
  };
546
560
  }
547
561
  }
@@ -14,6 +14,7 @@
14
14
  * @module
15
15
  */
16
16
  import { type PreprocessorGroup } from 'svelte/compiler';
17
+ import { type MdzComponentImport } from './mdz_to_svelte.js';
17
18
  /**
18
19
  * Options for `svelte_preprocess_mdz`.
19
20
  */
@@ -23,12 +24,14 @@ export interface SveltePreprocessMdzOptions {
23
24
  /**
24
25
  * Component import mapping for mdz content.
25
26
  * Key: component name as used in mdz (e.g., 'Alert').
26
- * Value: import path (e.g., '$lib/Alert.svelte').
27
+ * Value: import path (e.g., '#lib/Alert.svelte'), which receives no attributes,
28
+ * or `{path, props}` naming the attributes passed as props
29
+ * (e.g., `{path: '#lib/Alert.svelte', props: ['status']}`, or `props: 'all'`).
27
30
  *
28
31
  * If mdz content references a component not in this map,
29
32
  * that `Mdz` usage is skipped (left as runtime).
30
33
  */
31
- components?: Record<string, string>;
34
+ components?: Record<string, MdzComponentImport>;
32
35
  /**
33
36
  * Allowed HTML elements in mdz content (e.g., `{aside: true, details: true}`).
34
37
  * Values are boolean placeholders for future per-element configuration,
@@ -74,7 +77,7 @@ export interface SveltePreprocessMdzOptions {
74
77
  * Creates a Svelte preprocessor that compiles static `Mdz` content at build time.
75
78
  *
76
79
  * @param options - configuration for component/element resolution and file filtering
77
- * @returns a Svelte `PreprocessorGroup` for use in `svelte.config.js`
80
+ * @returns a Svelte `PreprocessorGroup` for a `preprocess` option, like `sveltekit({preprocess})` in `vite.config.ts`
78
81
  */
79
82
  export declare const svelte_preprocess_mdz: (options?: SveltePreprocessMdzOptions) => PreprocessorGroup;
80
83
  //# 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;AAsB1E,OAAO,EAAiB,KAAK,kBAAkB,EAA0B,MAAM,oBAAoB,CAAC;AASpG;;GAEG;AACH,MAAM,WAAW,0BAA0B;IAC1C,gCAAgC;IAChC,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IAEjC;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAEhD;;;;;;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
  /**
@@ -152,7 +152,7 @@ const render_static_mdz = (content, base, context) => {
152
152
  elements: context.elements,
153
153
  base,
154
154
  code_component_import: context.code_component_import,
155
- codeblock_component_import: context.codeblock_component_import,
155
+ codeblock_component_import: context.codeblock_component_import
156
156
  });
157
157
  }
158
158
  catch (error) {
@@ -216,7 +216,7 @@ const find_mdz_usages = (ast, mdz_names, context) => {
216
216
  replacement,
217
217
  required_imports: result.imports,
218
218
  consumed_bindings: consumed,
219
- component_node: node,
219
+ component_node: node
220
220
  });
221
221
  return;
222
222
  }
@@ -264,9 +264,9 @@ const find_mdz_usages = (ast, mdz_names, context) => {
264
264
  replacement,
265
265
  required_imports: merged_imports,
266
266
  consumed_bindings: consumed,
267
- component_node: node,
267
+ component_node: node
268
268
  });
269
- },
269
+ }
270
270
  });
271
271
  return { transformations, total_usages, transformed_usages };
272
272
  };
@@ -396,7 +396,7 @@ const find_removable_mdz_imports = (ast, mdz_names, total_usages, transformed_us
396
396
  removable.set(name, {
397
397
  node: positioned_node,
398
398
  kind: 'partial',
399
- specifier_to_remove: specifier,
399
+ specifier_to_remove: specifier
400
400
  });
401
401
  }
402
402
  }
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@fuzdev/mdz",
3
- "version": "0.3.0",
3
+ "version": "0.5.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": "📝",
7
7
  "logo": "logo.svg",
8
8
  "logo_alt": "a friendly pixelated spider facing you",
9
- "license": "MIT",
9
+ "license": "0BSD",
10
10
  "homepage": "https://mdz.fuz.dev/",
11
11
  "author": {
12
12
  "name": "Ryan Atkinson",
@@ -36,12 +36,16 @@
36
36
  "benchmark:render:clean": "rm -f src/benchmarks/render/baseline.json"
37
37
  },
38
38
  "type": "module",
39
+ "imports": {
40
+ "#lib/*": "./src/lib/*",
41
+ "#routes/*": "./src/routes/*"
42
+ },
39
43
  "engines": {
40
44
  "node": ">=24.14"
41
45
  },
42
46
  "peerDependencies": {
43
47
  "@fuzdev/fuz_util": ">=0.65.1",
44
- "@sveltejs/kit": "^2.47.3",
48
+ "@sveltejs/kit": "^3",
45
49
  "@types/estree": "^1",
46
50
  "esm-env": "^1",
47
51
  "svelte": "^5"
@@ -59,52 +63,33 @@
59
63
  "zimmerframe": "^1.1.4"
60
64
  },
61
65
  "devDependencies": {
62
- "@fuzdev/blake3_wasm": "^0.1.1",
63
- "@fuzdev/fuz_code": "^0.46.1",
64
- "@fuzdev/fuz_css": "^0.63.2",
65
- "@fuzdev/fuz_ui": "^0.205.1",
66
- "@fuzdev/fuz_util": "^0.65.1",
67
- "@fuzdev/gro": "^0.205.1",
68
- "@ryanatkn/eslint-config": "^0.12.1",
66
+ "@fuzdev/blake3-wasm": "^0.2.0",
67
+ "@fuzdev/fuz_code": "^0.52.1",
68
+ "@fuzdev/fuz_css": "^0.66.1",
69
+ "@fuzdev/fuz_ui": "^0.213.0",
70
+ "@fuzdev/fuz_util": "^0.72.0",
71
+ "@fuzdev/gro": "^0.216.0",
72
+ "@ryanatkn/eslint-config": "^0.13.0",
69
73
  "@sveltejs/acorn-typescript": "^1.0.10",
70
- "@sveltejs/adapter-static": "^3.0.10",
71
- "@sveltejs/kit": "2.64.0",
72
- "@sveltejs/package": "^2.5.7",
73
- "@sveltejs/vite-plugin-svelte": "^6.2.4",
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",
74
78
  "@types/estree": "^1.0.8",
75
79
  "@types/node": "^24.12.4",
76
80
  "@webref/css": "^8.2.0",
77
81
  "eslint": "^9.39.1",
78
82
  "eslint-plugin-svelte": "^3.13.1",
79
83
  "esm-env": "^1.2.2",
80
- "prettier": "^3.7.4",
81
- "prettier-plugin-svelte": "^3.5.1",
82
- "svelte": "^5.56.3",
83
- "svelte-check": "^4.6.0",
84
- "svelte-docinfo": "^0.5.3",
84
+ "svelte": "^5.57.2",
85
+ "svelte-check": "^4.7.6",
86
+ "svelte-docinfo": "^0.9.0",
85
87
  "svelte2tsx": "^0.7.56",
86
88
  "tslib": "^2.8.1",
87
- "typescript": "^5.9.3",
88
- "typescript-eslint": "^8.48.1",
89
- "vite": "^7.3.6",
90
- "vitest": "^4.0.15"
91
- },
92
- "prettier": {
93
- "plugins": [
94
- "prettier-plugin-svelte"
95
- ],
96
- "useTabs": true,
97
- "printWidth": 100,
98
- "singleQuote": true,
99
- "bracketSpacing": false,
100
- "overrides": [
101
- {
102
- "files": "package.json",
103
- "options": {
104
- "useTabs": false
105
- }
106
- }
107
- ]
89
+ "typescript": "^6.0.3",
90
+ "typescript-eslint": "^8.71.1",
91
+ "vite": "^8.3.4",
92
+ "vitest": "^5.0.0"
108
93
  },
109
94
  "sideEffects": [
110
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
@@ -45,13 +45,13 @@
45
45
  * ## Status
46
46
  *
47
47
  * Pre-stable — breaking changes are expected as the dialect matures.
48
- * Next: attributes, editing.
48
+ * Next: editing.
49
49
  *
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
 
@@ -251,10 +251,20 @@ export interface MdzNodeTableCell extends MdzNodeBase {
251
251
  children: Array<MdzNode>;
252
252
  }
253
253
 
254
+ export interface MdzAttribute {
255
+ name: string;
256
+ /** `true` for bare boolean shorthand (`disabled` ≡ `disabled={true}`). */
257
+ value: string | true;
258
+ start: number;
259
+ end: number;
260
+ }
261
+
254
262
  export interface MdzNodeElement extends MdzNodeBase {
255
263
  type: 'Element';
256
264
  /** HTML element name (e.g. `div`, `span`, `aside`). */
257
265
  name: string;
266
+ /** Parsed tag attributes; always present, possibly empty. */
267
+ attributes: Array<MdzAttribute>;
258
268
  children: Array<MdzNode>;
259
269
  }
260
270
 
@@ -262,5 +272,7 @@ export interface MdzNodeComponent extends MdzNodeBase {
262
272
  type: 'Component';
263
273
  /** Svelte component name (e.g. `Alert`, `Card`). */
264
274
  name: string;
275
+ /** Parsed tag attributes; always present, possibly empty. */
276
+ attributes: Array<MdzAttribute>;
265
277
  children: Array<MdzNode>;
266
278
  }
@@ -1,22 +1,52 @@
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
+
5
+ /**
6
+ * The attributes a registered component receives as props: a list of allowed
7
+ * names, or `'all'` to pass every attribute through. Attributes outside the
8
+ * list are dropped (DEV-warned).
9
+ */
10
+ export type MdzComponentProps = ReadonlyArray<string> | 'all';
11
+
12
+ /**
13
+ * A component registry entry that allows attributes through as props.
14
+ */
15
+ export interface MdzComponentConfig {
16
+ component: Component<any, any>;
17
+ /** Attribute names passed as props, or `'all'`; omitted passes none. */
18
+ props?: MdzComponentProps;
19
+ }
4
20
 
5
21
  /**
6
22
  * Component registry for custom Svelte components that can be used in mdz content.
7
23
  *
8
- * For example, registering 'Alert' allows using `<Alert>...</Alert>` in mdz content.
24
+ * A bare component entry renders the tag with no props — its attributes are
25
+ * dropped. A `MdzComponentConfig` entry names the attributes that pass through
26
+ * as props, so a component only receives what it's prepared for:
9
27
  *
10
- * The Map values are Svelte components.
28
+ * ```ts
29
+ * new Map([
30
+ * ['Alert', {component: Alert, props: ['status']}], // `<Alert status="error">`
31
+ * ['Note', Note], // `<Note>`, attributes dropped
32
+ * ]);
33
+ * ```
34
+ *
35
+ * Attribute values are author-controlled strings (or `true` for bare names),
36
+ * so with untrusted content, allow only props the component treats safely.
37
+ * `props: 'all'` passes everything — a component that spreads rest props onto
38
+ * an element then forwards `style`, `href`, and the like.
11
39
  */
12
- export type MdzComponents = Map<string, Component<any, any>>; // TODO support params
40
+ // props pass through untyped (`string | true`); a typed registry is a separate concern
41
+ export type MdzComponents = Map<string, Component<any, any> | MdzComponentConfig>;
13
42
 
14
43
  /**
15
44
  * Element registry for HTML elements that can be used in mdz content.
16
45
  *
17
- * For example, registering 'div' allows using `<div>...</div>` in mdz content.
18
- *
19
- * The Map values are boolean placeholders for future configuration options.
46
+ * Register an element by mapping its name to `true` (e.g. `['aside', true]`),
47
+ * which allows `<aside>...</aside>` in mdz content. Only `true` enables an
48
+ * element — the renderers treat any other value (including `false`) as
49
+ * unregistered, so the tag falls back to literal text.
20
50
  */
21
51
  export type MdzElements = Map<string, boolean>;
22
52
 
@@ -56,7 +86,7 @@ export const mdz_base_context = create_context<() => string | undefined>();
56
86
  * content as `reference` — the prop name matches `fuz_ui`'s `DocsLink`, so it
57
87
  * can be injected directly to auto-link backticked API identifiers.
58
88
  */
59
- export type MdzCodeComponent = Component<{reference: string}>;
89
+ export type MdzCodeComponent = Component<{ reference: string }>;
60
90
 
61
91
  /**
62
92
  * Context for overriding how inline `` `code` `` spans render, via a getter
@@ -69,7 +99,7 @@ export const mdz_code_context = create_context<() => MdzCodeComponent | undefine
69
99
  * matching `@fuzdev/fuz_code`'s `Code`, so it can be injected directly for
70
100
  * syntax highlighting.
71
101
  */
72
- export type MdzCodeblockComponent = Component<{lang: string | null; content: string}>;
102
+ export type MdzCodeblockComponent = Component<{ lang: string | null; content: string }>;
73
103
 
74
104
  /**
75
105
  * Context for overriding how fenced code blocks render, via a getter function.
@@ -92,7 +122,7 @@ interface MdzGetterContext<T> {
92
122
  */
93
123
  export const mdz_set_context_with_fallback = <T>(
94
124
  context: MdzGetterContext<T>,
95
- value_fn: () => T | undefined,
125
+ value_fn: () => T | undefined
96
126
  ): void => {
97
127
  const ancestor = context.get_maybe();
98
128
  context.set(() => value_fn() ?? ancestor?.());
@@ -23,7 +23,7 @@
23
23
  *
24
24
  * @nodocs
25
25
  */
26
- export const mdz_debug_work: {enabled: boolean; total: number} = {enabled: false, total: 0};
26
+ export const mdz_debug_work: { enabled: boolean; total: number } = { enabled: false, total: 0 };
27
27
 
28
28
  /**
29
29
  * Reset the counter and set whether the hot paths accumulate into it. Tests call