@fuzdev/mdz 0.4.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 (47) hide show
  1. package/LICENSE +10 -17
  2. package/README.md +2 -3
  3. package/dist/MdzNodeView.svelte +59 -23
  4. package/dist/MdzNodeView.svelte.d.ts.map +1 -1
  5. package/dist/MdzStreamNodeView.svelte +63 -23
  6. package/dist/MdzStreamNodeView.svelte.d.ts.map +1 -1
  7. package/dist/mdz.d.ts +12 -1
  8. package/dist/mdz.d.ts.map +1 -1
  9. package/dist/mdz.js +1 -1
  10. package/dist/mdz_contexts.d.ts +33 -6
  11. package/dist/mdz_contexts.d.ts.map +1 -1
  12. package/dist/mdz_helpers.d.ts +31 -3
  13. package/dist/mdz_helpers.d.ts.map +1 -1
  14. package/dist/mdz_helpers.js +102 -10
  15. package/dist/mdz_lexer.d.ts +3 -1
  16. package/dist/mdz_lexer.d.ts.map +1 -1
  17. package/dist/mdz_lexer.js +89 -15
  18. package/dist/mdz_opcodes.d.ts +3 -1
  19. package/dist/mdz_opcodes.d.ts.map +1 -1
  20. package/dist/mdz_opcodes_to_nodes.d.ts.map +1 -1
  21. package/dist/mdz_opcodes_to_nodes.js +3 -0
  22. package/dist/mdz_stream_parser_link.d.ts.map +1 -1
  23. package/dist/mdz_stream_parser_link.js +81 -9
  24. package/dist/mdz_stream_parser_table.js +8 -1
  25. package/dist/mdz_stream_state.svelte.d.ts +3 -1
  26. package/dist/mdz_stream_state.svelte.d.ts.map +1 -1
  27. package/dist/mdz_stream_state.svelte.js +4 -0
  28. package/dist/mdz_to_svelte.d.ts +17 -4
  29. package/dist/mdz_to_svelte.d.ts.map +1 -1
  30. package/dist/mdz_to_svelte.js +22 -9
  31. package/dist/mdz_token_parser.d.ts.map +1 -1
  32. package/dist/mdz_token_parser.js +16 -2
  33. package/dist/svelte_preprocess_mdz.d.ts +5 -2
  34. package/dist/svelte_preprocess_mdz.d.ts.map +1 -1
  35. package/package.json +7 -7
  36. package/src/lib/mdz.ts +13 -1
  37. package/src/lib/mdz_contexts.ts +36 -6
  38. package/src/lib/mdz_helpers.ts +129 -14
  39. package/src/lib/mdz_lexer.ts +98 -15
  40. package/src/lib/mdz_opcodes.ts +3 -1
  41. package/src/lib/mdz_opcodes_to_nodes.ts +5 -0
  42. package/src/lib/mdz_stream_parser_link.ts +77 -7
  43. package/src/lib/mdz_stream_parser_table.ts +8 -1
  44. package/src/lib/mdz_stream_state.svelte.ts +4 -1
  45. package/src/lib/mdz_to_svelte.ts +57 -13
  46. package/src/lib/mdz_token_parser.ts +16 -2
  47. package/src/lib/svelte_preprocess_mdz.ts +6 -4
@@ -6,6 +6,7 @@
6
6
  *
7
7
  * @module
8
8
  */
9
+ import { DEV } from 'esm-env';
9
10
  // Character codes for performance
10
11
  /** @nodocs */
11
12
  export const BACKTICK = 96; // `
@@ -67,6 +68,8 @@ export const AMPERSAND = 38; // &
67
68
  /** @nodocs */
68
69
  export const APOSTROPHE = 39; // '
69
70
  /** @nodocs */
71
+ export const DOUBLE_QUOTE = 34; // "
72
+ /** @nodocs */
70
73
  export const PLUS = 43; // +
71
74
  /** @nodocs */
72
75
  export const EQUALS = 61; // =
@@ -713,6 +716,77 @@ const VOID_ELEMENTS = new Set([
713
716
  // pair with children that renderers silently drop; consider treating void tags as
714
717
  // implicitly self-closing at parse time, or rejecting the open/close form
715
718
  export const mdz_is_void_element = (name) => VOID_ELEMENTS.has(name);
719
+ /**
720
+ * The closed, global set of attribute names allowed on rendered HTML elements.
721
+ * Every name is inert on any element (no URL, no script, no focus or DOM
722
+ * clobbering), so element safety is independent of which element names a
723
+ * consumer registers. Anything outside this set is dropped by
724
+ * `mdz_filter_element_attributes`.
725
+ */
726
+ export const ALLOWED_ELEMENT_ATTRIBUTES = new Set([
727
+ 'class',
728
+ 'title',
729
+ 'lang',
730
+ 'dir',
731
+ 'role',
732
+ 'aria-label',
733
+ 'aria-hidden',
734
+ 'aria-describedby',
735
+ 'aria-labelledby'
736
+ ]);
737
+ // filters an element's parsed attributes against the allowlist, returning a
738
+ // name→value object ready to spread onto `<svelte:element {...}>`. Disallowed
739
+ // names are dropped (DEV-warned). Used identically by MdzNodeView,
740
+ // MdzStreamNodeView, and mdz_to_svelte, so precompiled and runtime output match.
741
+ export const mdz_filter_element_attributes = (element_name, attributes, allowlist) => {
742
+ const filtered = {};
743
+ for (const attr of attributes) {
744
+ if (allowlist.has(attr.name)) {
745
+ filtered[attr.name] = attr.value;
746
+ }
747
+ else if (DEV) {
748
+ console.warn(`mdz: dropped disallowed attribute \`${attr.name}\` on <${element_name}> — not in the element attribute allowlist`);
749
+ }
750
+ }
751
+ return filtered;
752
+ };
753
+ // filters a component's parsed attributes against its registered `props`,
754
+ // returning a name→value props object spread onto `<Component {...props}>`.
755
+ // Omitted `props` passes nothing, `'all'` passes everything; dropped names are
756
+ // DEV-warned. Used identically by MdzNodeView, MdzStreamNodeView, and
757
+ // mdz_to_svelte, so precompiled and runtime output match.
758
+ export const mdz_filter_component_attributes = (component_name, attributes, props) => {
759
+ const filtered = {};
760
+ for (const attr of attributes) {
761
+ if (props === 'all' || props?.includes(attr.name)) {
762
+ filtered[attr.name] = attr.value;
763
+ }
764
+ else if (DEV) {
765
+ console.warn(`mdz: dropped attribute \`${attr.name}\` on <${component_name}> — not in its registered props`);
766
+ }
767
+ }
768
+ return filtered;
769
+ };
770
+ // normalizes a component registry entry to its config form
771
+ export const mdz_component_config = (entry) => (typeof entry === 'function' ? { component: entry } : entry);
772
+ // reconstructs authored attribute text (` name="value"` / ` name`) for the
773
+ // unregistered-tag literal placeholder. The quote char is chosen so a value
774
+ // containing one quote type stays balanced — a parsed value holds at most one
775
+ // (a `"`-delimited value can't contain `"`, a `'`-delimited one can't contain
776
+ // `'`). The caller interpolates the result as Svelte text (auto-escaped).
777
+ export const mdz_format_unregistered_attributes = (attributes) => {
778
+ let text = '';
779
+ for (const attr of attributes) {
780
+ if (attr.value === true) {
781
+ text += ` ${attr.name}`;
782
+ }
783
+ else {
784
+ const quote = attr.value.includes('"') ? "'" : '"';
785
+ text += ` ${attr.name}=${quote}${attr.value}${quote}`;
786
+ }
787
+ }
788
+ return text;
789
+ };
716
790
  /**
717
791
  * Resolves a relative path (`./` or `../`) against a base path.
718
792
  * The base is treated as a directory regardless of trailing slash
@@ -747,9 +821,29 @@ export const mdz_resolve_relative_path = (reference, base) => {
747
821
  segments.push('');
748
822
  return segments.join('/');
749
823
  };
824
+ // `/a` → `a`, `//a` → `a` — the pathname form `resolve()` takes; `\` counts as a
825
+ // slash too, and tab/LF/CR are dropped, since browsers read `/\host` and
826
+ // `/<tab>/host` as protocol-relative
827
+ const strip_leading_slashes = (path) => {
828
+ let i = 0;
829
+ for (;;) {
830
+ const c = path.charCodeAt(i);
831
+ if (c !== SLASH && c !== BACKSLASH && c !== TAB && c !== NEWLINE && c !== CARRIAGE_RETURN)
832
+ break;
833
+ i++;
834
+ }
835
+ return path.slice(i);
836
+ };
750
837
  /**
751
838
  * Classify a `Link` reference into how it should render. Pure — no rendering,
752
839
  * no context; `base` is the resolved base path (or `undefined`).
840
+ *
841
+ * Absolute references are authored URLs, not SvelteKit route ids: they classify
842
+ * as `resolve` with the base-relative pathname, never a leading-slash route id,
843
+ * whose resolution would drop `(group)` and empty segments, decode `[x+nn]`
844
+ * escapes, and throw on `[param]` segments. A leading `/` followed by any `/`
845
+ * or `\` run collapses to one `/`, so `//host` and `/\host` stay under the base
846
+ * rather than becoming protocol-relative.
753
847
  * @nodocs
754
848
  */
755
849
  export const mdz_classify_link = (reference, link_type, base) => {
@@ -757,17 +851,15 @@ export const mdz_classify_link = (reference, link_type, base) => {
757
851
  return { kind: 'unsafe' };
758
852
  if (link_type === 'internal') {
759
853
  if (reference.startsWith('.') && base) {
760
- return { kind: 'resolve', href: mdz_resolve_relative_path(reference, base) };
761
- }
762
- // fragment/query/relative/bare — `resolve()` only accepts absolute paths or
763
- // route ids and throws on anything else
764
- if (reference.startsWith('#') ||
765
- reference.startsWith('?') ||
766
- reference.startsWith('.') ||
767
- !reference.startsWith('/')) {
768
- return { kind: 'internal', href: reference };
854
+ return {
855
+ kind: 'resolve',
856
+ path: strip_leading_slashes(mdz_resolve_relative_path(reference, base))
857
+ };
769
858
  }
770
- return { kind: 'resolve', href: reference };
859
+ // fragment/query/relative/bare — not under the base, so a raw `href`
860
+ if (!reference.startsWith('/'))
861
+ return { kind: 'internal', href: reference };
862
+ return { kind: 'resolve', path: strip_leading_slashes(reference) };
771
863
  }
772
864
  return { kind: 'external', href: reference };
773
865
  };
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * @module
8
8
  */
9
- import type { MdzTableAlign } from './mdz.js';
9
+ import type { MdzAttribute, MdzTableAlign } from './mdz.js';
10
10
  export interface MdzTokenBase {
11
11
  start: number;
12
12
  end: number;
@@ -70,11 +70,13 @@ export interface MdzTokenTagOpen extends MdzTokenBase {
70
70
  type: 'tag_open';
71
71
  name: string;
72
72
  is_component: boolean;
73
+ attributes: Array<MdzAttribute>;
73
74
  }
74
75
  export interface MdzTokenTagSelfClose extends MdzTokenBase {
75
76
  type: 'tag_self_close';
76
77
  name: string;
77
78
  is_component: boolean;
79
+ attributes: Array<MdzAttribute>;
78
80
  }
79
81
  export interface MdzTokenTagClose extends MdzTokenBase {
80
82
  type: 'tag_close';
@@ -1 +1 @@
1
- {"version":3,"file":"mdz_lexer.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz_lexer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mdz_lexer.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz_lexer.ts"],"names":[],"mappings":"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"}
package/dist/mdz_lexer.js CHANGED
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * @module
8
8
  */
9
- import { mdz_is_url, mdz_is_safe_reference, is_letter, is_tag_name_char, is_word_char, is_valid_path_char, mdz_trim_url_punctuation, BACKTICK, ASTERISK, UNDERSCORE, TILDE, NEWLINE, HYPHEN, HASH, SPACE, LEFT_ANGLE, RIGHT_ANGLE, SLASH, PERIOD, LEFT_BRACKET, LEFT_PAREN, RIGHT_BRACKET, A_UPPER, Z_UPPER, ZERO, ONE, NINE, HR_HYPHEN_COUNT, MIN_CODEBLOCK_BACKTICKS, MAX_HEADING_LEVEL, MAX_INLINE_NESTING_DEPTH, MAX_LIST_NUMBER_DIGITS, match_url_prefix_case_insensitive, is_at_absolute_path, is_at_relative_path, is_line_whitespace, mdz_match_blockquote_prefix, mdz_blockquote_line_has_content, mdz_blockquote_strip_width, mdz_remap_segments, mdz_split_table_row, mdz_parse_table_delimiter, PIPE, TEXT_CLASS_BACKSLASH, TEXT_CLASS_NEWLINE, TEXT_CLASS_STOP, TEXT_CLASS_URL, TEXT_SCAN_CLASS } from './mdz_helpers.js';
9
+ import { mdz_is_url, mdz_is_safe_reference, is_letter, is_tag_name_char, is_word_char, is_valid_path_char, mdz_trim_url_punctuation, BACKTICK, ASTERISK, UNDERSCORE, TILDE, NEWLINE, HYPHEN, HASH, SPACE, LEFT_ANGLE, RIGHT_ANGLE, SLASH, EQUALS, APOSTROPHE, DOUBLE_QUOTE, PERIOD, LEFT_BRACKET, LEFT_PAREN, RIGHT_BRACKET, A_UPPER, Z_UPPER, ZERO, ONE, NINE, HR_HYPHEN_COUNT, MIN_CODEBLOCK_BACKTICKS, MAX_HEADING_LEVEL, MAX_INLINE_NESTING_DEPTH, MAX_LIST_NUMBER_DIGITS, match_url_prefix_case_insensitive, is_at_absolute_path, is_at_relative_path, is_line_whitespace, mdz_match_blockquote_prefix, mdz_blockquote_line_has_content, mdz_blockquote_strip_width, mdz_remap_segments, mdz_split_table_row, mdz_parse_table_delimiter, PIPE, TEXT_CLASS_BACKSLASH, TEXT_CLASS_NEWLINE, TEXT_CLASS_STOP, TEXT_CLASS_URL, TEXT_SCAN_CLASS } from './mdz_helpers.js';
10
10
  import { mdz_debug_work } from './mdz_debug_work.js';
11
11
  //
12
12
  // Lexer
@@ -1421,9 +1421,75 @@ export class MdzLexer {
1421
1421
  const tag_name = this.#text.slice(tag_name_start, this.#index);
1422
1422
  const first_char_code = tag_name.charCodeAt(0);
1423
1423
  const is_component = first_char_code >= A_UPPER && first_char_code <= Z_UPPER;
1424
- // Skip whitespace
1425
- while (this.#index < this.#text.length && this.#text.charCodeAt(this.#index) === SPACE) {
1426
- this.#index++;
1424
+ // Attribute loop: consume space-separated attributes until `/>` or `>`.
1425
+ // Registry-blind — every syntactically valid attribute is parsed here;
1426
+ // policy (allowlist filtering) happens at render/build time. The scan is
1427
+ // a single forward pass bounded by the line: any newline (or a missing
1428
+ // separator, bad value, or duplicate name) rejects the whole tag to
1429
+ // literal text via the shared `#reject_tag` bailout.
1430
+ const attributes = [];
1431
+ const seen = new Set(); // O(1) duplicate-name detection
1432
+ for (;;) {
1433
+ // skip a run of literal spaces only (tabs/newlines are not SPACE, so
1434
+ // they fall through to the reject path — the single-line, space-only rule)
1435
+ let had_space = false;
1436
+ while (this.#index < this.#text.length && this.#text.charCodeAt(this.#index) === SPACE) {
1437
+ this.#index++;
1438
+ had_space = true;
1439
+ }
1440
+ const c = this.#index < this.#text.length ? this.#text.charCodeAt(this.#index) : -1;
1441
+ // `/>` or `>` ends the open tag — break to the existing self-close / commit
1442
+ if (c === SLASH || c === RIGHT_ANGLE)
1443
+ break;
1444
+ // otherwise an attribute name must follow, space-separated from what
1445
+ // precedes it (`<a x="1"y="2">` rejects — `y` abuts the closing quote)
1446
+ if (c === -1 || !had_space || !is_letter(c)) {
1447
+ this.#reject_tag(start, bound);
1448
+ return;
1449
+ }
1450
+ // collect the attribute name (same grammar as tag names)
1451
+ const attr_start = this.#index;
1452
+ this.#index++; // first char already passed is_letter
1453
+ while (this.#index < this.#text.length &&
1454
+ is_tag_name_char(this.#text.charCodeAt(this.#index))) {
1455
+ this.#index++;
1456
+ }
1457
+ const name = this.#text.slice(attr_start, this.#index);
1458
+ // duplicate names are ambiguous (Svelte compile-errors) → literal tag
1459
+ if (seen.has(name)) {
1460
+ this.#reject_tag(start, bound);
1461
+ return;
1462
+ }
1463
+ seen.add(name);
1464
+ let value = true; // bare name is boolean `true`
1465
+ if (this.#index < this.#text.length && this.#text.charCodeAt(this.#index) === EQUALS) {
1466
+ this.#index++; // consume =
1467
+ // the value must be immediately quoted — no space after `=`, no
1468
+ // `{` brace literal, no unquoted value (this one check covers all)
1469
+ const quote = this.#index < this.#text.length ? this.#text.charCodeAt(this.#index) : -1;
1470
+ if (quote !== DOUBLE_QUOTE && quote !== APOSTROPHE) {
1471
+ this.#reject_tag(start, bound);
1472
+ return;
1473
+ }
1474
+ this.#index++; // consume opening quote
1475
+ const value_start = this.#index;
1476
+ // scan to the matching quote, bounded by newline / text end — NOT
1477
+ // by `#max_search_index` (the open-tag scan never is); a `>` inside
1478
+ // the value is content, consumed atomically before `>` is sought
1479
+ while (this.#index < this.#text.length &&
1480
+ this.#text.charCodeAt(this.#index) !== quote &&
1481
+ this.#text.charCodeAt(this.#index) !== NEWLINE) {
1482
+ this.#index++;
1483
+ }
1484
+ if (this.#index >= this.#text.length || this.#text.charCodeAt(this.#index) === NEWLINE) {
1485
+ // unterminated quote (or newline inside the value) → literal tag
1486
+ this.#reject_tag(start, bound);
1487
+ return;
1488
+ }
1489
+ value = this.#text.slice(value_start, this.#index);
1490
+ this.#index++; // consume closing quote
1491
+ }
1492
+ attributes.push({ name, value, start: attr_start, end: this.#index });
1427
1493
  }
1428
1494
  // Check for self-closing />
1429
1495
  if (this.#index + 1 < this.#text.length &&
@@ -1434,6 +1500,7 @@ export class MdzLexer {
1434
1500
  type: 'tag_self_close',
1435
1501
  name: tag_name,
1436
1502
  is_component,
1503
+ attributes,
1437
1504
  start,
1438
1505
  end: this.#index
1439
1506
  });
@@ -1441,9 +1508,7 @@ export class MdzLexer {
1441
1508
  }
1442
1509
  // Check for >
1443
1510
  if (this.#index >= this.#text.length || this.#text.charCodeAt(this.#index) !== RIGHT_ANGLE) {
1444
- this.#failed_tag.add(`${start}:${bound}`);
1445
- this.#index = start + 1;
1446
- this.#emit_text('<', start);
1511
+ this.#reject_tag(start, bound);
1447
1512
  return;
1448
1513
  }
1449
1514
  this.#index++; // consume >
@@ -1459,20 +1524,23 @@ export class MdzLexer {
1459
1524
  const search_limit = Math.min(this.#max_search_index, this.#text.length);
1460
1525
  const closing_tag_pos = this.#index_of(closing_tag, this.#index);
1461
1526
  if (closing_tag_pos === -1 || closing_tag_pos > search_limit) {
1462
- this.#failed_tag.add(`${start}:${bound}`);
1463
- this.#index = start + 1;
1464
- this.#emit_text('<', start);
1527
+ this.#reject_tag(start, bound);
1465
1528
  return;
1466
1529
  }
1467
1530
  if (this.#has_paragraph_break_between(this.#index, closing_tag_pos)) {
1468
- this.#failed_tag.add(`${start}:${bound}`);
1469
- this.#index = start + 1;
1470
- this.#emit_text('<', start);
1531
+ this.#reject_tag(start, bound);
1471
1532
  return;
1472
1533
  }
1473
1534
  // Emit tag_open
1474
1535
  const open_token_index = this.#tokens.length;
1475
- this.#tokens.push({ type: 'tag_open', name: tag_name, is_component, start, end: this.#index });
1536
+ this.#tokens.push({
1537
+ type: 'tag_open',
1538
+ name: tag_name,
1539
+ is_component,
1540
+ attributes,
1541
+ start,
1542
+ end: this.#index
1543
+ });
1476
1544
  // Tokenize children until the closing tag. Each iteration narrows
1477
1545
  // `#max_search_index` to the next closer so a child's delimiter scan
1478
1546
  // can never cross it (`` <b>`a</b>x` `` must not swallow `</b>` into
@@ -1509,8 +1577,14 @@ export class MdzLexer {
1509
1577
  // (the `#revert_tokens_from_link_open` pattern; the old fallthrough
1510
1578
  // left a dangling `tag_open` whose parser fallback discarded content)
1511
1579
  this.#max_search_index = saved_max;
1512
- this.#failed_tag.add(`${start}:${bound}`);
1513
1580
  this.#tokens.splice(open_token_index);
1581
+ this.#reject_tag(start, bound);
1582
+ }
1583
+ // a tag open at `start` that can't commit: memoizes the failure for this
1584
+ // `bound` so re-entries bail fast, then emits the `<` as text and resumes
1585
+ // scanning after it — the rest re-parses as ordinary content
1586
+ #reject_tag(start, bound) {
1587
+ this.#failed_tag.add(`${start}:${bound}`);
1514
1588
  this.#index = start + 1;
1515
1589
  this.#emit_text('<', start);
1516
1590
  }
@@ -11,7 +11,7 @@
11
11
  *
12
12
  * @module
13
13
  */
14
- import type { MdzTableAlign } from './mdz.js';
14
+ import type { MdzAttribute, MdzTableAlign } from './mdz.js';
15
15
  /**
16
16
  * Unique monotonic identifier for each node created by the parser.
17
17
  * IDs are never reused within a parser instance.
@@ -44,6 +44,8 @@ export interface MdzOpcodeOpen {
44
44
  level?: 1 | 2 | 3 | 4 | 5 | 6;
45
45
  /** Tag name. Present when `node_type` is `'Element'` or `'Component'`. */
46
46
  name?: string;
47
+ /** Parsed attributes. Present (possibly empty) when `node_type` is `'Element'` or `'Component'`. */
48
+ attributes?: Array<MdzAttribute>;
47
49
  /** Language hint. Present when `node_type` is `'Codeblock'`. */
48
50
  lang?: string | null;
49
51
  /** Whether the list is ordered. Present when `node_type` is `'List'`. */
@@ -1 +1 @@
1
- {"version":3,"file":"mdz_opcodes.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz_opcodes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mdz_opcodes.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz_opcodes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE5D;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC;AAE/B;;;;;;;GAOG;AACH,MAAM,MAAM,oBAAoB,GAC7B,WAAW,GACX,MAAM,GACN,QAAQ,GACR,eAAe,GACf,MAAM,GACN,SAAS,GACT,MAAM,GACN,UAAU,GACV,YAAY,GACZ,SAAS,GACT,WAAW,GACX,WAAW,GACX,MAAM,GACN,OAAO,GACP,UAAU,GACV,WAAW,CAAC;AAEf,mDAAmD;AACnD,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC;AAEnC,4FAA4F;AAC5F,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,CAAC;AAE9C;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,SAAS,CAAC;IACd,SAAS,EAAE,oBAAoB,CAAC;IAChC,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC9B,0EAA0E;IAC1E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oGAAoG;IACpG,UAAU,CAAC,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;IACjC,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,yEAAyE;IACzE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,+FAA+F;IAC/F,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yFAAyF;IACzF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0FAA0F;IAC1F,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;IAC7B,+FAA+F;IAC/F,MAAM,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,OAAO,CAAC;IACd,EAAE,EAAE,SAAS,CAAC;IACd,6EAA6E;IAC7E,GAAG,EAAE,MAAM,CAAC;IACZ,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,SAAS,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;IACpC,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,SAAS,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,eAAe,CAAC;IAC3B,2EAA2E;IAC3E,KAAK,EAAE,MAAM,CAAC;IACd,2FAA2F;IAC3F,GAAG,EAAE,MAAM,CAAC;CACZ;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,aAAa,CAAC;IACpB,EAAE,EAAE,SAAS,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,WAAW,CAAC;IAClB,EAAE,EAAE,SAAS,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,SAAS,CAAC;IACd,SAAS,EAAE,eAAe,CAAC;IAC3B,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,GAAG,EAAE,MAAM,CAAC;CACZ;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,QAAQ,CAAC;IACf,EAAE,EAAE,SAAS,CAAC;IACd,sFAAsF;IACtF,gBAAgB,EAAE,MAAM,CAAC;IACzB,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,EAAE,EAAE,SAAS,CAAC;IACd,2DAA2D;IAC3D,SAAS,EAAE,MAAM,CAAC;IAClB,4CAA4C;IAC5C,SAAS,EAAE,SAAS,CAAC;IACrB,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,6DAA6D;IAC7D,SAAS,EAAE,UAAU,GAAG,UAAU,CAAC;IACnC,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,mFAAmF;IACnF,GAAG,EAAE,MAAM,CAAC;IACZ,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,SAAS,CAAC;CACpB;AAED,sDAAsD;AACtD,MAAM,MAAM,WAAW,GAAG,oBAAoB,GAAG,eAAe,GAAG,eAAe,CAAC;AAEnF,8CAA8C;AAC9C,MAAM,MAAM,SAAS,GAClB,aAAa,GACb,cAAc,GACd,aAAa,GACb,mBAAmB,GACnB,iBAAiB,GACjB,aAAa,GACb,eAAe,GACf,aAAa,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"mdz_opcodes_to_nodes.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz_opcodes_to_nodes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,KAAK,EACX,OAAO,EASP,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,kBAAkB,CAAC;AAyB7D;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS,KAAK,CAAC,SAAS,CAAC,KAAG,KAAK,CAAC,OAAO,CAgM7E,CAAC"}
1
+ {"version":3,"file":"mdz_opcodes_to_nodes.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz_opcodes_to_nodes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,KAAK,EAEX,OAAO,EASP,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,kBAAkB,CAAC;AA0B7D;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS,KAAK,CAAC,SAAS,CAAC,KAAG,KAAK,CAAC,OAAO,CAiM7E,CAAC"}
@@ -39,6 +39,7 @@ export const mdz_opcodes_to_nodes = (opcodes) => {
39
39
  start: op.start,
40
40
  level: op.level,
41
41
  name: op.name,
42
+ attributes: op.attributes,
42
43
  lang: op.lang,
43
44
  ordered: op.ordered,
44
45
  start_number: op.start_number,
@@ -423,6 +424,7 @@ const build_node = (frame, frame_children) => {
423
424
  return {
424
425
  type: 'Element',
425
426
  name: frame.name ?? '',
427
+ attributes: frame.attributes ?? [],
426
428
  children: mdz_merge_adjacent_text(frame_children),
427
429
  start: frame.start,
428
430
  end: frame.end
@@ -431,6 +433,7 @@ const build_node = (frame, frame_children) => {
431
433
  return {
432
434
  type: 'Component',
433
435
  name: frame.name ?? '',
436
+ attributes: frame.attributes ?? [],
434
437
  children: mdz_merge_adjacent_text(frame_children),
435
438
  start: frame.start,
436
439
  end: frame.end
@@ -1 +1 @@
1
- {"version":3,"file":"mdz_stream_parser_link.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz_stream_parser_link.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAoBH,OAAO,EACN,KAAK,oBAAoB,EACzB,KAAK,SAAS,EAYd,MAAM,8BAA8B,CAAC;AAMtC,cAAc;AACd,eAAO,MAAM,aAAa,GAAI,OAAO,oBAAoB,KAAG,OAoB3D,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,GAC9B,OAAO,oBAAoB,EAC3B,gBAAgB,MAAM,EACtB,aAAa,MAAM,KACjB,IAUF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,oBAAoB,EAAE,gBAAgB,MAAM,KAAG,OA4EvF,CAAC;AAIF,cAAc;AACd,eAAO,MAAM,YAAY,GAAI,OAAO,oBAAoB,KAAG,SAqF1D,CAAC;AAEF,cAAc;AACd,eAAO,MAAM,aAAa,GAAI,OAAO,oBAAoB,KAAG,SAiD3D,CAAC"}
1
+ {"version":3,"file":"mdz_stream_parser_link.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz_stream_parser_link.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAwBH,OAAO,EACN,KAAK,oBAAoB,EACzB,KAAK,SAAS,EAYd,MAAM,8BAA8B,CAAC;AAMtC,cAAc;AACd,eAAO,MAAM,aAAa,GAAI,OAAO,oBAAoB,KAAG,OAoB3D,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,GAC9B,OAAO,oBAAoB,EAC3B,gBAAgB,MAAM,EACtB,aAAa,MAAM,KACjB,IAUF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,oBAAoB,EAAE,gBAAgB,MAAM,KAAG,OA4EvF,CAAC;AAIF,cAAc;AACd,eAAO,MAAM,YAAY,GAAI,OAAO,oBAAoB,KAAG,SAuJ1D,CAAC;AAEF,cAAc;AACd,eAAO,MAAM,aAAa,GAAI,OAAO,oBAAoB,KAAG,SAiD3D,CAAC"}
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * @module
6
6
  */
7
- import { A_UPPER, LEFT_BRACKET, LEFT_PAREN, MAX_INLINE_NESTING_DEPTH, RIGHT_ANGLE, RIGHT_BRACKET, RIGHT_PAREN, SLASH, SPACE, Z_UPPER, is_letter, is_tag_name_char, is_valid_path_char, mdz_is_safe_reference, mdz_is_url } from './mdz_helpers.js';
7
+ import { A_UPPER, APOSTROPHE, DOUBLE_QUOTE, EQUALS, LEFT_BRACKET, LEFT_PAREN, MAX_INLINE_NESTING_DEPTH, RIGHT_ANGLE, RIGHT_BRACKET, RIGHT_PAREN, SLASH, SPACE, Z_UPPER, is_letter, is_tag_name_char, is_valid_path_char, mdz_is_safe_reference, mdz_is_url } from './mdz_helpers.js';
8
8
  import { accumulate_text, alloc_id, buffer_index_of, emit, ensure_paragraph, flush_text, is_run_boundary, offset, pop_stack_entry, push_stack_entry, revert_above } from './mdz_stream_parser_state.js';
9
9
  import { consume_delimiter_as_text } from './mdz_stream_parser_text.js';
10
10
  import { mdz_debug_work } from './mdz_debug_work.js';
@@ -161,12 +161,84 @@ export const try_tag_open = (state) => {
161
161
  if (i >= state.buffer.length)
162
162
  return 'need_more';
163
163
  const name = state.buffer.slice(name_start, i);
164
- // skip whitespace
165
- while (i < state.buffer.length && state.buffer.charCodeAt(i) === SPACE) {
166
- i++;
164
+ // Attribute loop: consume space-separated attributes until `/>` or `>`.
165
+ // This widens the held-tag-open rescan surface (the TODO above) with the
166
+ // attribute/value text — still line-bounded, since a newline anywhere aborts
167
+ // the whole tag (`not_match`). Every buffer-end point holds (`need_more`) so
168
+ // the same tree is produced regardless of where feeds split. The quoted-value
169
+ // terminator search reuses `buffer_index_of` (memoized, append-aware) so a
170
+ // long streamed value doesn't rescan quadratically across held retries,
171
+ // matching `try_complete_link`.
172
+ const attributes = [];
173
+ const seen = new Set();
174
+ for (;;) {
175
+ // skip a run of literal spaces only (tabs/newlines are not SPACE)
176
+ let had_space = false;
177
+ while (i < state.buffer.length && state.buffer.charCodeAt(i) === SPACE) {
178
+ i++;
179
+ had_space = true;
180
+ }
181
+ if (i >= state.buffer.length)
182
+ return 'need_more';
183
+ const c = state.buffer.charCodeAt(i);
184
+ // `/>` or `>` ends the open tag — break to the existing self-close / commit
185
+ if (c === SLASH || c === RIGHT_ANGLE)
186
+ break;
187
+ // otherwise an attribute name must follow, space-separated from what
188
+ // precedes it (`<a x="1"y="2">` — `y` abuts the closing quote — reverts)
189
+ if (!is_letter(c))
190
+ return 'not_match';
191
+ if (!had_space)
192
+ return 'not_match';
193
+ // collect the attribute name (same grammar as tag names)
194
+ const attr_start = i;
195
+ i++; // first char already passed is_letter
196
+ while (i < state.buffer.length && is_tag_name_char(state.buffer.charCodeAt(i))) {
197
+ i++;
198
+ }
199
+ if (i >= state.buffer.length)
200
+ return 'need_more'; // mid-name — could still grow
201
+ const attr_name = state.buffer.slice(attr_start, i);
202
+ // duplicate names are ambiguous (Svelte compile-errors) → literal tag
203
+ if (seen.has(attr_name))
204
+ return 'not_match';
205
+ seen.add(attr_name);
206
+ let value = true; // bare name is boolean `true`
207
+ if (state.buffer.charCodeAt(i) === EQUALS) {
208
+ i++; // consume =
209
+ if (i >= state.buffer.length)
210
+ return 'need_more'; // nothing after `=` yet
211
+ // the value must be immediately quoted — no space, no `{`, no unquoted
212
+ const quote_code = state.buffer.charCodeAt(i);
213
+ if (quote_code !== DOUBLE_QUOTE && quote_code !== APOSTROPHE)
214
+ return 'not_match';
215
+ i++; // consume opening quote
216
+ const value_start = i;
217
+ // find the matching quote and the next newline via the memoized,
218
+ // append-aware search; a `>` inside the value is content, consumed
219
+ // atomically here so a `</Name>`-shaped substring never reaches
220
+ // `try_close_tag`
221
+ const quote_str = quote_code === DOUBLE_QUOTE ? '"' : "'";
222
+ const q = buffer_index_of(state, quote_str, value_start);
223
+ const n = buffer_index_of(state, '\n', value_start);
224
+ if (q !== -1 && (n === -1 || q < n)) {
225
+ value = state.buffer.slice(value_start, q);
226
+ i = q + 1; // past the closing quote
227
+ }
228
+ else if (n !== -1 && (q === -1 || n < q)) {
229
+ return 'not_match'; // newline inside the value → literal tag
230
+ }
231
+ else {
232
+ return 'need_more'; // neither found yet — unterminated so far, hold
233
+ }
234
+ }
235
+ attributes.push({
236
+ name: attr_name,
237
+ value,
238
+ start: offset(state, attr_start),
239
+ end: offset(state, i)
240
+ });
167
241
  }
168
- if (i >= state.buffer.length)
169
- return 'need_more';
170
242
  // check for self-closing />
171
243
  if (state.buffer.charCodeAt(i) === SLASH && i + 1 >= state.buffer.length) {
172
244
  return 'need_more'; // `/` at the buffer end can't classify yet (`/>` or junk)
@@ -179,7 +251,7 @@ export const try_tag_open = (state) => {
179
251
  ensure_paragraph(state);
180
252
  const id = alloc_id(state);
181
253
  const tag_start = offset(state, start);
182
- emit(state, { type: 'open', id, node_type, start: tag_start, name });
254
+ emit(state, { type: 'open', id, node_type, start: tag_start, name, attributes });
183
255
  emit(state, { type: 'close', id, end: offset(state, i + 2) });
184
256
  state.active_text_id = null;
185
257
  state.pos = i + 2;
@@ -199,8 +271,8 @@ export const try_tag_open = (state) => {
199
271
  ensure_paragraph(state);
200
272
  const id = alloc_id(state);
201
273
  const tag_start = offset(state, start);
202
- const delimiter = state.buffer.slice(start, i); // e.g., "<Alert>"
203
- emit(state, { type: 'open', id, node_type, start: tag_start, name });
274
+ const delimiter = state.buffer.slice(start, i); // e.g., "<Alert>" or "<Alert x="1">"
275
+ emit(state, { type: 'open', id, node_type, start: tag_start, name, attributes });
204
276
  push_stack_entry(state, id, node_type, tag_start, delimiter, name);
205
277
  state.active_text_id = null;
206
278
  state.pos = i;
@@ -344,7 +344,14 @@ const emit_inline_nodes = (state, nodes) => {
344
344
  case 'Component': {
345
345
  const id = alloc_id(state);
346
346
  const s = offset(state, node.start);
347
- emit(state, { type: 'open', id, node_type: node.type, start: s, name: node.name });
347
+ emit(state, {
348
+ type: 'open',
349
+ id,
350
+ node_type: node.type,
351
+ start: s,
352
+ name: node.name,
353
+ attributes: node.attributes
354
+ });
348
355
  push_stack_entry(state, id, node.type, s, '', node.name);
349
356
  emit_inline_nodes(state, node.children);
350
357
  pop_stack_entry(state);
@@ -8,7 +8,7 @@
8
8
  * @module
9
9
  */
10
10
  import type { MdzNodeId, MdzNodeType, MdzOpcode } from './mdz_opcodes.js';
11
- import type { MdzTableAlign } from './mdz.js';
11
+ import type { MdzAttribute, MdzTableAlign } from './mdz.js';
12
12
  /**
13
13
  * A reactive node in the stream renderer tree.
14
14
  * Fields are `$state` so Svelte re-renders only what changes.
@@ -22,6 +22,8 @@ export declare class MdzStreamNode {
22
22
  reference?: string;
23
23
  link_type?: 'external' | 'internal';
24
24
  name?: string;
25
+ /** Parsed tag attributes (Element/Component nodes); known at open, never mutated, so plain not `$state`. */
26
+ attributes?: Array<MdzAttribute>;
25
27
  lang?: string | null;
26
28
  heading_id?: string;
27
29
  ordered?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"mdz_stream_state.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz_stream_state.svelte.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C;;;GAGG;AACH,qBAAa,aAAa;IACzB,EAAE,EAAE,SAAS,CAAC;IACd,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,CAAc;IAC7B,QAAQ,EAAE,KAAK,CAAC,aAAa,CAAC,CAAc;IAE5C,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAY;IAC9B,SAAS,CAAC,EAAE,UAAU,GAAG,UAAU,CAAY;IAC/C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAY;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;IAC7B,uDAAuD;IACvD,MAAM,CAAC,EAAE,OAAO,CAAC;gBAEL,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW;CAI5C;AAED;;;;;;;;;GASG;AACH,qBAAa,cAAc;;IAC1B,IAAI,EAAE,KAAK,CAAC,aAAa,CAAC,CAAc;IAqCxC;;OAEG;IACH,KAAK,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IA6R9B;;OAEG;IACH,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,GAAG,IAAI;CAK5C"}
1
+ {"version":3,"file":"mdz_stream_state.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz_stream_state.svelte.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE5D;;;GAGG;AACH,qBAAa,aAAa;IACzB,EAAE,EAAE,SAAS,CAAC;IACd,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,CAAc;IAC7B,QAAQ,EAAE,KAAK,CAAC,aAAa,CAAC,CAAc;IAE5C,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAY;IAC9B,SAAS,CAAC,EAAE,UAAU,GAAG,UAAU,CAAY;IAC/C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4GAA4G;IAC5G,UAAU,CAAC,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAY;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;IAC7B,uDAAuD;IACvD,MAAM,CAAC,EAAE,OAAO,CAAC;gBAEL,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW;CAI5C;AAED;;;;;;;;;GASG;AACH,qBAAa,cAAc;;IAC1B,IAAI,EAAE,KAAK,CAAC,aAAa,CAAC,CAAc;IAqCxC;;OAEG;IACH,KAAK,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IA8R9B;;OAEG;IACH,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,GAAG,IAAI;CAK5C"}
@@ -22,6 +22,8 @@ export class MdzStreamNode {
22
22
  reference = $state();
23
23
  link_type = $state();
24
24
  name;
25
+ /** Parsed tag attributes (Element/Component nodes); known at open, never mutated, so plain not `$state`. */
26
+ attributes;
25
27
  lang;
26
28
  heading_id = $state();
27
29
  ordered;
@@ -92,6 +94,8 @@ export class MdzStreamState {
92
94
  node.level = opcode.level;
93
95
  if (opcode.name !== undefined)
94
96
  node.name = opcode.name;
97
+ if (opcode.attributes !== undefined)
98
+ node.attributes = opcode.attributes;
95
99
  if (opcode.lang !== undefined)
96
100
  node.lang = opcode.lang;
97
101
  if (opcode.ordered !== undefined)
@@ -8,6 +8,15 @@
8
8
  * @module
9
9
  */
10
10
  import type { MdzNode } from './mdz.js';
11
+ import type { MdzComponentProps } from './mdz_contexts.js';
12
+ /**
13
+ * A build-time component registry entry: an import path, or an import path
14
+ * plus the attribute names passed as props (see `MdzComponentProps`).
15
+ */
16
+ export type MdzComponentImport = string | {
17
+ path: string;
18
+ props?: MdzComponentProps;
19
+ };
11
20
  /**
12
21
  * Result of converting `MdzNode` arrays to Svelte markup.
13
22
  */
@@ -27,10 +36,13 @@ export interface MdzToSvelteResult {
27
36
  */
28
37
  export interface MdzToSvelteOptions {
29
38
  /**
30
- * Component name to import path mapping (e.g., `{Alert: '#lib/Alert.svelte'}`).
39
+ * Component name to import mapping, mirroring the runtime `MdzComponents`
40
+ * registry: a bare import path receives no attributes
41
+ * (`{Note: '#lib/Note.svelte'}`), and the config form names the attributes
42
+ * passed as props (`{Alert: {path: '#lib/Alert.svelte', props: ['status']}}`).
31
43
  * If content references a component not in this map, `has_unconfigured_tags` is set.
32
44
  */
33
- components: Record<string, string>;
45
+ components: Record<string, MdzComponentImport>;
34
46
  /**
35
47
  * Allowed HTML elements (e.g., `{aside: true, details: true}`).
36
48
  * Values are boolean placeholders for future per-element configuration,
@@ -40,8 +52,9 @@ export interface MdzToSvelteOptions {
40
52
  elements: Record<string, boolean>;
41
53
  /**
42
54
  * Base path for resolving relative links (e.g., `'/docs/usage/'`).
43
- * When provided, relative references (`./`, `../`) are resolved to absolute paths
44
- * and passed through `resolve()`. Trailing slash recommended.
55
+ * When provided, relative references (`./`, `../`) are resolved against it and,
56
+ * like absolute references, passed through `resolve()` in its pathname form.
57
+ * Trailing slash recommended.
45
58
  */
46
59
  base?: string;
47
60
  /**