@fuzdev/fuz_ui 0.183.2 → 0.185.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.
- package/dist/declaration.svelte.d.ts +9 -0
- package/dist/declaration.svelte.d.ts.map +1 -1
- package/dist/declaration.svelte.js +1 -1
- package/dist/library.svelte.d.ts +22 -1
- package/dist/library.svelte.d.ts.map +1 -1
- package/dist/library.svelte.js +17 -1
- package/dist/mdz.d.ts +5 -2
- package/dist/mdz.d.ts.map +1 -1
- package/dist/mdz.js +149 -155
- package/dist/module.svelte.d.ts.map +1 -1
- package/dist/module.svelte.js +1 -1
- package/package.json +8 -9
- package/src/lib/declaration.svelte.ts +1 -1
- package/src/lib/library.svelte.ts +18 -1
- package/src/lib/mdz.ts +150 -155
- package/src/lib/module.svelte.ts +1 -1
|
@@ -52,6 +52,15 @@ export declare class Declaration {
|
|
|
52
52
|
description?: string | undefined;
|
|
53
53
|
default_value?: string | undefined;
|
|
54
54
|
bindable?: boolean | undefined;
|
|
55
|
+
examples?: string[] | undefined;
|
|
56
|
+
deprecated_message?: string | undefined;
|
|
57
|
+
see_also?: string[] | undefined;
|
|
58
|
+
throws?: {
|
|
59
|
+
[x: string]: unknown;
|
|
60
|
+
description: string;
|
|
61
|
+
type?: string | undefined;
|
|
62
|
+
}[] | undefined;
|
|
63
|
+
since?: string | undefined;
|
|
55
64
|
}[] | undefined;
|
|
56
65
|
return_type: string | undefined;
|
|
57
66
|
return_description: string | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"declaration.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/declaration.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,eAAe,EAGpB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,oBAAoB,CAAC;AAG/C;;GAEG;AACH,qBAAa,WAAW;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAiB;IACxC,QAAQ,CAAC,gBAAgB,EAAE,eAAe,CAAiB;IAE3D,OAAO,wCAAiC;IAExC;;OAEG;IACH,WAAW,SAA8B;IAEzC,IAAI,SAAwC;IAC5C,IAAI,4FAAwC;IAE5C;;OAEG;IACH,UAAU,qBAQR;IAEF;;OAEG;IACH,OAAO,
|
|
1
|
+
{"version":3,"file":"declaration.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/declaration.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,eAAe,EAGpB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,oBAAoB,CAAC;AAG/C;;GAEG;AACH,qBAAa,WAAW;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAiB;IACxC,QAAQ,CAAC,gBAAgB,EAAE,eAAe,CAAiB;IAE3D,OAAO,wCAAiC;IAExC;;OAEG;IACH,WAAW,SAA8B;IAEzC,IAAI,SAAwC;IAC5C,IAAI,4FAAwC;IAE5C;;OAEG;IACH,UAAU,qBAQR;IAEF;;OAEG;IACH,OAAO,SAAoF;IAE3F;;OAEG;IACH,gBAAgB,SAMd;IAEF;;OAEG;IACH,YAAY,qBAIV;IAEF;;OAEG;IACH,YAAY,SAAiE;IAE7E,cAAc,qBAAkD;IAChE,WAAW,qBAA+C;IAC1D,kBAAkB,qBAAsD;IACxE,UAAU;;;;;;;oBAA8C;IACxD,KAAK;;;;;;;;;;;;;;;;;oBAAyC;IAC9C,WAAW,qBAA+C;IAC1D,kBAAkB,qBAAsD;IACxE,cAAc;;;;;oBAAkD;IAChE,OAAO,uBAA2C;IAClD,UAAU,uBAA8C;IACxD,MAAM;;;;oBAA0C;IAChD,KAAK,qBAAyC;IAC9C,QAAQ,uBAA4C;IACpD,QAAQ,uBAA4C;IACpD,OAAO,EAAE,KAAK,CAAC,eAAe,CAAC,GAAG,SAAS,CAEzC;IACF,UAAU,EAAE,KAAK,CAAC,eAAe,CAAC,GAAG,SAAS,CAE5C;IAEF,YAAY,UAA2D;IACvE,aAAa,UAAuC;IACpD,iBAAiB,UAAgC;IACjD,cAAc,UAA+D;IAC7E,SAAS,UAAqD;IAC9D,YAAY,UAAuE;gBAEvE,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,eAAe;CAI7D"}
|
|
@@ -22,7 +22,7 @@ export class Declaration {
|
|
|
22
22
|
/**
|
|
23
23
|
* API documentation URL.
|
|
24
24
|
*/
|
|
25
|
-
url_api = $derived(`/docs/api/${this.module_path}#${this.name}`);
|
|
25
|
+
url_api = $derived(`/docs/api${this.library.url_prefix}/${this.module_path}#${this.name}`);
|
|
26
26
|
/**
|
|
27
27
|
* Generated TypeScript import statement.
|
|
28
28
|
*/
|
package/dist/library.svelte.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { LibraryJson } from '@fuzdev/fuz_util/library_json.js';
|
|
2
2
|
import { Declaration } from './declaration.svelte.js';
|
|
3
3
|
import { Module } from './module.svelte.js';
|
|
4
|
+
/**
|
|
5
|
+
* Normalizes a URL prefix: ensures leading `/`, strips trailing `/`, returns `''` for falsy and non-string values.
|
|
6
|
+
*/
|
|
7
|
+
export declare const parse_library_url_prefix: (value: unknown) => string;
|
|
4
8
|
/**
|
|
5
9
|
* Rich runtime representation of a library.
|
|
6
10
|
*
|
|
@@ -12,6 +16,12 @@ import { Module } from './module.svelte.js';
|
|
|
12
16
|
*/
|
|
13
17
|
export declare class Library {
|
|
14
18
|
readonly library_json: LibraryJson;
|
|
19
|
+
/**
|
|
20
|
+
* URL path prefix for multi-package documentation sites.
|
|
21
|
+
* Prepended to `/docs/api/` paths in `Module.url_api` and `Declaration.url_api`.
|
|
22
|
+
* Default `''` preserves single-package behavior.
|
|
23
|
+
*/
|
|
24
|
+
readonly url_prefix: string;
|
|
15
25
|
package_json: {
|
|
16
26
|
[x: string]: unknown;
|
|
17
27
|
name: string;
|
|
@@ -116,6 +126,7 @@ export declare class Library {
|
|
|
116
126
|
type?: string | undefined;
|
|
117
127
|
}[] | undefined;
|
|
118
128
|
since?: string | undefined;
|
|
129
|
+
mutates?: Record<string, string> | undefined;
|
|
119
130
|
extends?: string[] | undefined;
|
|
120
131
|
implements?: string[] | undefined;
|
|
121
132
|
members?: Record<string, unknown>[] | undefined;
|
|
@@ -128,11 +139,21 @@ export declare class Library {
|
|
|
128
139
|
description?: string | undefined;
|
|
129
140
|
default_value?: string | undefined;
|
|
130
141
|
bindable?: boolean | undefined;
|
|
142
|
+
examples?: string[] | undefined;
|
|
143
|
+
deprecated_message?: string | undefined;
|
|
144
|
+
see_also?: string[] | undefined;
|
|
145
|
+
throws?: {
|
|
146
|
+
[x: string]: unknown;
|
|
147
|
+
description: string;
|
|
148
|
+
type?: string | undefined;
|
|
149
|
+
}[] | undefined;
|
|
150
|
+
since?: string | undefined;
|
|
131
151
|
}[] | undefined;
|
|
132
152
|
also_exported_from?: string[] | undefined;
|
|
133
153
|
alias_of?: {
|
|
134
154
|
module: string;
|
|
135
155
|
name: string;
|
|
156
|
+
kind: "function" | "class" | "type" | "json" | "variable" | "constructor" | "component" | "css";
|
|
136
157
|
} | undefined;
|
|
137
158
|
}[] | undefined;
|
|
138
159
|
module_comment?: string | undefined;
|
|
@@ -171,7 +192,7 @@ export declare class Library {
|
|
|
171
192
|
* Declaration lookup map by name. Provides O(1) lookup.
|
|
172
193
|
*/
|
|
173
194
|
declaration_map: Map<string, Declaration>;
|
|
174
|
-
constructor(library_json: LibraryJson);
|
|
195
|
+
constructor(library_json: LibraryJson, url_prefix?: string);
|
|
175
196
|
/**
|
|
176
197
|
* Look up a declaration by name.
|
|
177
198
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"library.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/library.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,kCAAkC,CAAC;
|
|
1
|
+
{"version":3,"file":"library.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/library.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,kCAAkC,CAAC;AAIlE,OAAO,EAAC,WAAW,EAAC,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAC,MAAM,EAAC,MAAM,oBAAoB,CAAC;AAE1C;;GAEG;AACH,eAAO,MAAM,wBAAwB,GAAI,OAAO,OAAO,KAAG,MAGzD,CAAC;AAEF;;;;;;;;GAQG;AACH,qBAAa,OAAO;IACnB,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAiB;IAEnD;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAE5B,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAA4C;IACxD,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAA2C;IAEtD,IAAI,SAAoC;IACxC,SAAS,SAAyC;IAClD,QAAQ,wCAAwC;IAChD,UAAU,gBAA0C;IACpD,YAAY,+CAA4C;IACxD,QAAQ,+CAAwC;IAChD,QAAQ,SAAwC;IAChD,OAAO,+CAAuC;IAC9C,aAAa,+CAA6C;IAC1D,SAAS,UAAyC;IAElD;;OAEG;IACH,OAAO,gBAML;IAEF;;OAEG;IACH,OAAO,WAIL;IAEF;;OAEG;IACH,cAAc,WAA4E;IAE1F;;OAEG;IACH,YAAY,gBAAmE;IAE/E;;OAEG;IACH,eAAe,2BAAgE;gBAEnE,YAAY,EAAE,WAAW,EAAE,UAAU,SAAK;IAKtD;;OAEG;IACH,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAIzD;;OAEG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAItC;;OAEG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI/C;;OAEG;IACH,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC,WAAW,CAAC;CAGtD;AAED,eAAO,MAAM,eAAe;;;;CAA4B,CAAC"}
|
package/dist/library.svelte.js
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
|
+
import { ensure_start, strip_end } from '@fuzdev/fuz_util/string.js';
|
|
1
2
|
import { create_context } from './context_helpers.js';
|
|
2
3
|
import { Declaration } from './declaration.svelte.js';
|
|
3
4
|
import { Module } from './module.svelte.js';
|
|
5
|
+
/**
|
|
6
|
+
* Normalizes a URL prefix: ensures leading `/`, strips trailing `/`, returns `''` for falsy and non-string values.
|
|
7
|
+
*/
|
|
8
|
+
export const parse_library_url_prefix = (value) => {
|
|
9
|
+
if (!value || typeof value !== 'string')
|
|
10
|
+
return '';
|
|
11
|
+
return ensure_start(strip_end(value, '/'), '/');
|
|
12
|
+
};
|
|
4
13
|
/**
|
|
5
14
|
* Rich runtime representation of a library.
|
|
6
15
|
*
|
|
@@ -12,6 +21,12 @@ import { Module } from './module.svelte.js';
|
|
|
12
21
|
*/
|
|
13
22
|
export class Library {
|
|
14
23
|
library_json = $state.raw();
|
|
24
|
+
/**
|
|
25
|
+
* URL path prefix for multi-package documentation sites.
|
|
26
|
+
* Prepended to `/docs/api/` paths in `Module.url_api` and `Declaration.url_api`.
|
|
27
|
+
* Default `''` preserves single-package behavior.
|
|
28
|
+
*/
|
|
29
|
+
url_prefix;
|
|
15
30
|
package_json = $derived(this.library_json.package_json);
|
|
16
31
|
source_json = $derived(this.library_json.source_json);
|
|
17
32
|
name = $derived(this.library_json.name);
|
|
@@ -50,8 +65,9 @@ export class Library {
|
|
|
50
65
|
* Declaration lookup map by name. Provides O(1) lookup.
|
|
51
66
|
*/
|
|
52
67
|
declaration_map = $derived(new Map(this.declarations.map((d) => [d.name, d])));
|
|
53
|
-
constructor(library_json) {
|
|
68
|
+
constructor(library_json, url_prefix = '') {
|
|
54
69
|
this.library_json = library_json;
|
|
70
|
+
this.url_prefix = parse_library_url_prefix(url_prefix);
|
|
55
71
|
}
|
|
56
72
|
/**
|
|
57
73
|
* Look up a declaration by name.
|
package/dist/mdz.d.ts
CHANGED
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
*
|
|
17
17
|
* ## Design philosophy
|
|
18
18
|
*
|
|
19
|
-
* - **False negatives over false positives**:
|
|
20
|
-
*
|
|
19
|
+
* - **False negatives over false positives**: When in doubt, treat as plain text.
|
|
20
|
+
* Block elements can interrupt paragraphs without blank lines; inline formatting is strict.
|
|
21
21
|
* - **One way to do things**: Single unambiguous syntax per feature. No alternatives.
|
|
22
22
|
* - **Explicit over implicit**: Clear delimiters and column-0 requirements avoid ambiguity.
|
|
23
23
|
* - **Simple over complete**: Prefer simple parsing rules over complex edge case handling.
|
|
@@ -102,6 +102,9 @@ export declare class MdzParser {
|
|
|
102
102
|
/**
|
|
103
103
|
* Main parse method. Returns flat array of nodes,
|
|
104
104
|
* with paragraph nodes wrapping content between double newlines.
|
|
105
|
+
*
|
|
106
|
+
* Block elements (headings, HR, codeblocks) are detected at every column-0
|
|
107
|
+
* position — they can interrupt paragraphs without requiring blank lines.
|
|
105
108
|
*/
|
|
106
109
|
parse(): Array<MdzNode>;
|
|
107
110
|
}
|
package/dist/mdz.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mdz.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAIH;;GAEG;AACH,eAAO,MAAM,SAAS,GAAI,MAAM,MAAM,KAAG,KAAK,CAAC,OAAO,CAAgC,CAAC;AAEvF,MAAM,MAAM,OAAO,GAChB,WAAW,GACX,WAAW,GACX,gBAAgB,GAChB,WAAW,GACX,aAAa,GACb,oBAAoB,GACpB,WAAW,GACX,gBAAgB,GAChB,SAAS,GACT,cAAc,GACd,cAAc,GACd,gBAAgB,CAAC;AAEpB,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACpD,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;CACzB;AAED,MAAM,WAAW,aAAc,SAAQ,WAAW;IACjD,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;CACzB;AAED,MAAM,WAAW,oBAAqB,SAAQ,WAAW;IACxD,IAAI,EAAE,eAAe,CAAC;IACtB,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;CACzB;AAED,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IACzB,SAAS,EAAE,UAAU,GAAG,UAAU,CAAC;CACnC;AAED,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACpD,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;CACzB;AAED,MAAM,WAAW,SAAU,SAAQ,WAAW;IAC7C,IAAI,EAAE,IAAI,CAAC;CACX;AAED,MAAM,WAAW,cAAe,SAAQ,WAAW;IAClD,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;CACzB;AAED,MAAM,WAAW,cAAe,SAAQ,WAAW;IAClD,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;CACzB;AAED,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACpD,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;CACzB;AA+CD;;;;GAIG;AACH,qBAAa,SAAS;;gBAQT,QAAQ,EAAE,MAAM;IAI5B
|
|
1
|
+
{"version":3,"file":"mdz.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAIH;;GAEG;AACH,eAAO,MAAM,SAAS,GAAI,MAAM,MAAM,KAAG,KAAK,CAAC,OAAO,CAAgC,CAAC;AAEvF,MAAM,MAAM,OAAO,GAChB,WAAW,GACX,WAAW,GACX,gBAAgB,GAChB,WAAW,GACX,aAAa,GACb,oBAAoB,GACpB,WAAW,GACX,gBAAgB,GAChB,SAAS,GACT,cAAc,GACd,cAAc,GACd,gBAAgB,CAAC;AAEpB,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACpD,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;CACzB;AAED,MAAM,WAAW,aAAc,SAAQ,WAAW;IACjD,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;CACzB;AAED,MAAM,WAAW,oBAAqB,SAAQ,WAAW;IACxD,IAAI,EAAE,eAAe,CAAC;IACtB,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;CACzB;AAED,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IACzB,SAAS,EAAE,UAAU,GAAG,UAAU,CAAC;CACnC;AAED,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACpD,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;CACzB;AAED,MAAM,WAAW,SAAU,SAAQ,WAAW;IAC7C,IAAI,EAAE,IAAI,CAAC;CACX;AAED,MAAM,WAAW,cAAe,SAAQ,WAAW;IAClD,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;CACzB;AAED,MAAM,WAAW,cAAe,SAAQ,WAAW;IAClD,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;CACzB;AAED,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACpD,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;CACzB;AA+CD;;;;GAIG;AACH,qBAAa,SAAS;;gBAQT,QAAQ,EAAE,MAAM;IAI5B;;;;;;OAMG;IACH,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC;CAslDvB;AAQD,eAAO,MAAM,UAAU,GAAI,GAAG,MAAM,KAAG,OAA8B,CAAC"}
|
package/dist/mdz.js
CHANGED
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
*
|
|
17
17
|
* ## Design philosophy
|
|
18
18
|
*
|
|
19
|
-
* - **False negatives over false positives**:
|
|
20
|
-
*
|
|
19
|
+
* - **False negatives over false positives**: When in doubt, treat as plain text.
|
|
20
|
+
* Block elements can interrupt paragraphs without blank lines; inline formatting is strict.
|
|
21
21
|
* - **One way to do things**: Single unambiguous syntax per feature. No alternatives.
|
|
22
22
|
* - **Explicit over implicit**: Clear delimiters and column-0 requirements avoid ambiguity.
|
|
23
23
|
* - **Simple over complete**: Prefer simple parsing rules over complex edge case handling.
|
|
@@ -95,88 +95,121 @@ export class MdzParser {
|
|
|
95
95
|
/**
|
|
96
96
|
* Main parse method. Returns flat array of nodes,
|
|
97
97
|
* with paragraph nodes wrapping content between double newlines.
|
|
98
|
+
*
|
|
99
|
+
* Block elements (headings, HR, codeblocks) are detected at every column-0
|
|
100
|
+
* position — they can interrupt paragraphs without requiring blank lines.
|
|
98
101
|
*/
|
|
99
102
|
parse() {
|
|
100
103
|
this.#nodes.length = 0;
|
|
101
104
|
const root_nodes = [];
|
|
102
105
|
const paragraph_children = [];
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
if (start_block) {
|
|
106
|
-
root_nodes.push(start_block);
|
|
107
|
-
}
|
|
106
|
+
// Skip leading newlines
|
|
107
|
+
this.#skip_newlines();
|
|
108
108
|
while (this.#index < this.#template.length) {
|
|
109
|
+
// Peek for block element (read-only match), flush paragraph first, then parse.
|
|
110
|
+
// Flush must happen before parse because parse modifies accumulation state.
|
|
111
|
+
const block_type = this.#peek_block_element();
|
|
112
|
+
if (block_type) {
|
|
113
|
+
const flushed = this.#flush_paragraph(paragraph_children, true);
|
|
114
|
+
if (flushed)
|
|
115
|
+
root_nodes.push(flushed);
|
|
116
|
+
if (block_type === 'heading')
|
|
117
|
+
root_nodes.push(this.#parse_heading());
|
|
118
|
+
else if (block_type === 'hr')
|
|
119
|
+
root_nodes.push(this.#parse_hr());
|
|
120
|
+
else
|
|
121
|
+
root_nodes.push(this.#parse_code_block());
|
|
122
|
+
this.#skip_newlines();
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
109
125
|
// Check for paragraph break (double newline)
|
|
110
126
|
if (this.#is_at_paragraph_break()) {
|
|
111
|
-
this.#
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const tag = paragraph_children.find((n) => n.type === 'Component' || n.type === 'Element');
|
|
122
|
-
root_nodes.push(tag);
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
// Regular paragraph
|
|
126
|
-
root_nodes.push({
|
|
127
|
-
type: 'Paragraph',
|
|
128
|
-
children: paragraph_children.slice(),
|
|
129
|
-
start: paragraph_children[0].start,
|
|
130
|
-
end: paragraph_children[paragraph_children.length - 1].end,
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
paragraph_children.length = 0;
|
|
134
|
-
}
|
|
135
|
-
// Consume the paragraph break
|
|
136
|
-
this.#eat('\n\n');
|
|
137
|
-
// Check for block element after paragraph break
|
|
138
|
-
const block = this.#try_parse_block_element();
|
|
139
|
-
if (block) {
|
|
140
|
-
root_nodes.push(block);
|
|
141
|
-
}
|
|
127
|
+
const flushed = this.#flush_paragraph(paragraph_children, true);
|
|
128
|
+
if (flushed)
|
|
129
|
+
root_nodes.push(flushed);
|
|
130
|
+
this.#skip_newlines();
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
// Parse inline content
|
|
134
|
+
const node = this.#parse_node();
|
|
135
|
+
if (node.type === 'Text') {
|
|
136
|
+
this.#accumulate_text(node.content, node.start);
|
|
142
137
|
}
|
|
143
138
|
else {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
this.#nodes.push(node);
|
|
151
|
-
}
|
|
152
|
-
if (this.#nodes.length > 0) {
|
|
153
|
-
paragraph_children.push(...this.#nodes);
|
|
154
|
-
this.#nodes.length = 0;
|
|
155
|
-
}
|
|
139
|
+
this.#flush_text();
|
|
140
|
+
this.#nodes.push(node);
|
|
141
|
+
}
|
|
142
|
+
if (this.#nodes.length > 0) {
|
|
143
|
+
paragraph_children.push(...this.#nodes);
|
|
144
|
+
this.#nodes.length = 0;
|
|
156
145
|
}
|
|
157
146
|
}
|
|
147
|
+
// Flush remaining content as final paragraph
|
|
148
|
+
const final_paragraph = this.#flush_paragraph(paragraph_children, true);
|
|
149
|
+
if (final_paragraph)
|
|
150
|
+
root_nodes.push(final_paragraph);
|
|
151
|
+
return root_nodes;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Flush accumulated inline content as a paragraph node (or single tag).
|
|
155
|
+
* When `trim_trailing` is true, trims trailing newlines from the last text node
|
|
156
|
+
* (the line break before a block element or paragraph break).
|
|
157
|
+
*
|
|
158
|
+
* @mutates paragraph_children - trims last text node, removes whitespace-only nodes, clears array
|
|
159
|
+
*/
|
|
160
|
+
#flush_paragraph(paragraph_children, trim_trailing = false) {
|
|
158
161
|
this.#flush_text();
|
|
159
162
|
if (this.#nodes.length > 0) {
|
|
160
163
|
paragraph_children.push(...this.#nodes);
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
if (paragraph_children.length
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
164
|
+
this.#nodes.length = 0;
|
|
165
|
+
}
|
|
166
|
+
if (paragraph_children.length === 0) {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
if (trim_trailing) {
|
|
170
|
+
// Trim trailing newlines from the last text node (line break before block element)
|
|
171
|
+
const last = paragraph_children[paragraph_children.length - 1];
|
|
172
|
+
if (last.type === 'Text') {
|
|
173
|
+
const trimmed = last.content.replace(/\n+$/, '');
|
|
174
|
+
if (trimmed) {
|
|
175
|
+
last.content = trimmed;
|
|
176
|
+
last.end = last.start + trimmed.length;
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
paragraph_children.pop();
|
|
180
|
+
}
|
|
168
181
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
start: paragraph_children[0].start,
|
|
175
|
-
end: paragraph_children[paragraph_children.length - 1].end,
|
|
176
|
-
});
|
|
182
|
+
// Skip whitespace-only paragraphs (e.g. newlines between consecutive blocks)
|
|
183
|
+
const has_content = paragraph_children.some((n) => n.type !== 'Text' || n.content.trim().length > 0);
|
|
184
|
+
if (!has_content) {
|
|
185
|
+
paragraph_children.length = 0;
|
|
186
|
+
return null;
|
|
177
187
|
}
|
|
178
188
|
}
|
|
179
|
-
|
|
189
|
+
// Single tag (component/element) - add directly without paragraph wrapper (MDX convention)
|
|
190
|
+
const single_tag = this.#extract_single_tag(paragraph_children);
|
|
191
|
+
if (single_tag) {
|
|
192
|
+
paragraph_children.length = 0;
|
|
193
|
+
return single_tag;
|
|
194
|
+
}
|
|
195
|
+
// Regular paragraph
|
|
196
|
+
const result = {
|
|
197
|
+
type: 'Paragraph',
|
|
198
|
+
children: paragraph_children.slice(),
|
|
199
|
+
start: paragraph_children[0].start,
|
|
200
|
+
end: paragraph_children[paragraph_children.length - 1].end,
|
|
201
|
+
};
|
|
202
|
+
paragraph_children.length = 0;
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Consume consecutive newline characters.
|
|
207
|
+
*/
|
|
208
|
+
#skip_newlines() {
|
|
209
|
+
while (this.#index < this.#template.length &&
|
|
210
|
+
this.#template.charCodeAt(this.#index) === NEWLINE) {
|
|
211
|
+
this.#index++;
|
|
212
|
+
}
|
|
180
213
|
}
|
|
181
214
|
/**
|
|
182
215
|
* Accumulate text for later flushing (performance optimization).
|
|
@@ -667,50 +700,42 @@ export class MdzParser {
|
|
|
667
700
|
};
|
|
668
701
|
}
|
|
669
702
|
/**
|
|
670
|
-
*
|
|
671
|
-
*
|
|
672
|
-
*
|
|
703
|
+
* Extract a single tag (component or element) if it's the only non-whitespace content.
|
|
704
|
+
* Returns the tag node if paragraph wrapping should be skipped (MDX convention),
|
|
705
|
+
* or null if the content should be wrapped in a paragraph.
|
|
673
706
|
*/
|
|
674
|
-
#
|
|
675
|
-
let
|
|
707
|
+
#extract_single_tag(nodes) {
|
|
708
|
+
let tag = null;
|
|
676
709
|
for (const node of nodes) {
|
|
677
710
|
if (node.type === 'Component' || node.type === 'Element') {
|
|
678
|
-
if (
|
|
679
|
-
return
|
|
680
|
-
|
|
711
|
+
if (tag)
|
|
712
|
+
return null; // Multiple tags
|
|
713
|
+
tag = node;
|
|
681
714
|
}
|
|
682
715
|
else if (node.type === 'Text') {
|
|
683
716
|
// Allow only whitespace-only text nodes
|
|
684
717
|
if (node.content.trim() !== '')
|
|
685
|
-
return
|
|
718
|
+
return null;
|
|
686
719
|
}
|
|
687
720
|
else {
|
|
688
721
|
// Any other node type means not a single tag
|
|
689
|
-
return
|
|
722
|
+
return null;
|
|
690
723
|
}
|
|
691
724
|
}
|
|
692
|
-
return
|
|
725
|
+
return tag;
|
|
693
726
|
}
|
|
694
727
|
/**
|
|
695
|
-
*
|
|
696
|
-
*
|
|
697
|
-
*
|
|
698
|
-
* Block elements must:
|
|
699
|
-
* - Start at column 0 (no leading whitespace)
|
|
700
|
-
* - Be followed by blank line or EOF
|
|
701
|
-
*
|
|
702
|
-
* This helper eliminates duplication between document start and post-paragraph-break parsing.
|
|
728
|
+
* Read-only check if current position matches a block element.
|
|
729
|
+
* Does not modify parser state — used to peek before flushing paragraph.
|
|
730
|
+
* Returns which block type matched, or null if none.
|
|
703
731
|
*/
|
|
704
|
-
#
|
|
705
|
-
if (this.#match_heading())
|
|
706
|
-
return
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
else if (this.#match_code_block()) {
|
|
712
|
-
return this.#parse_code_block();
|
|
713
|
-
}
|
|
732
|
+
#peek_block_element() {
|
|
733
|
+
if (this.#match_heading())
|
|
734
|
+
return 'heading';
|
|
735
|
+
if (this.#match_hr())
|
|
736
|
+
return 'hr';
|
|
737
|
+
if (this.#match_code_block())
|
|
738
|
+
return 'codeblock';
|
|
714
739
|
return null;
|
|
715
740
|
}
|
|
716
741
|
/**
|
|
@@ -1037,6 +1062,20 @@ export class MdzParser {
|
|
|
1037
1062
|
if (this.#is_at_paragraph_break()) {
|
|
1038
1063
|
break;
|
|
1039
1064
|
}
|
|
1065
|
+
// When next line could start a block element, consume the newline and stop.
|
|
1066
|
+
// The main loop will try block detection at the next character.
|
|
1067
|
+
// Consuming the newline here avoids a 3-iteration detour (break before \n,
|
|
1068
|
+
// fail peek at \n, safety-increment, then succeed peek at block char).
|
|
1069
|
+
if (char_code === NEWLINE) {
|
|
1070
|
+
const next_i = this.#index + 1;
|
|
1071
|
+
if (next_i < this.#template.length) {
|
|
1072
|
+
const next_char = this.#template.charCodeAt(next_i);
|
|
1073
|
+
if (next_char === HASH || next_char === HYPHEN || next_char === BACKTICK) {
|
|
1074
|
+
this.#index++; // consume the newline
|
|
1075
|
+
break;
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1040
1079
|
// Check for URL or internal path mid-text
|
|
1041
1080
|
if (this.#is_at_url() || this.#is_at_internal_path()) {
|
|
1042
1081
|
break;
|
|
@@ -1127,18 +1166,15 @@ export class MdzParser {
|
|
|
1127
1166
|
}
|
|
1128
1167
|
/**
|
|
1129
1168
|
* Check if current position matches a horizontal rule.
|
|
1130
|
-
* HR must be exactly `---` at column 0, followed by
|
|
1169
|
+
* HR must be exactly `---` at column 0, followed by newline or EOF.
|
|
1131
1170
|
*
|
|
1132
|
-
*
|
|
1133
|
-
*
|
|
1134
|
-
* Without this, `---` followed by regular text would create an hr and treat
|
|
1135
|
-
* the next line as a new paragraph, which could be surprising. The blank line
|
|
1136
|
-
* makes block element boundaries explicit and predictable.
|
|
1171
|
+
* mdz has no setext headings, so `---` after a paragraph is unambiguous
|
|
1172
|
+
* (always an HR, unlike CommonMark where it becomes a setext heading).
|
|
1137
1173
|
*/
|
|
1138
1174
|
#match_hr() {
|
|
1139
1175
|
let i = this.#index;
|
|
1140
|
-
// Must start at column 0 (
|
|
1141
|
-
if (i
|
|
1176
|
+
// Must start at column 0 (beginning of input or after newline)
|
|
1177
|
+
if (i > 0 && this.#template.charCodeAt(i - 1) !== NEWLINE) {
|
|
1142
1178
|
return false;
|
|
1143
1179
|
}
|
|
1144
1180
|
// Must have exactly three hyphens
|
|
@@ -1153,15 +1189,7 @@ export class MdzParser {
|
|
|
1153
1189
|
while (i < this.#template.length) {
|
|
1154
1190
|
const char_code = this.#template.charCodeAt(i);
|
|
1155
1191
|
if (char_code === NEWLINE) {
|
|
1156
|
-
|
|
1157
|
-
const next_i = i + 1;
|
|
1158
|
-
if (next_i >= this.#template.length) {
|
|
1159
|
-
return true; // hr followed by newline + EOF
|
|
1160
|
-
}
|
|
1161
|
-
if (this.#template.charCodeAt(next_i) === NEWLINE) {
|
|
1162
|
-
return true; // hr followed by blank line
|
|
1163
|
-
}
|
|
1164
|
-
return false; // hr followed by single newline + content
|
|
1192
|
+
return true;
|
|
1165
1193
|
}
|
|
1166
1194
|
if (char_code !== SPACE) {
|
|
1167
1195
|
return false; // Non-whitespace after ---, not an hr
|
|
@@ -1194,17 +1222,12 @@ export class MdzParser {
|
|
|
1194
1222
|
/**
|
|
1195
1223
|
* Check if current position matches a heading.
|
|
1196
1224
|
* Heading must be 1-6 hashes at column 0, followed by space and content,
|
|
1197
|
-
* followed by
|
|
1198
|
-
*
|
|
1199
|
-
* Blank line requirement rationale:
|
|
1200
|
-
* Ensures headings are visually and semantically separate from following content.
|
|
1201
|
-
* Without this, `# Heading\nText` would be ambiguous - is the text part of the
|
|
1202
|
-
* heading or a new paragraph? The blank line makes document structure explicit.
|
|
1225
|
+
* followed by newline or EOF.
|
|
1203
1226
|
*/
|
|
1204
1227
|
#match_heading() {
|
|
1205
1228
|
let i = this.#index;
|
|
1206
|
-
// Must start at column 0 (
|
|
1207
|
-
if (i
|
|
1229
|
+
// Must start at column 0 (beginning of input or after newline)
|
|
1230
|
+
if (i > 0 && this.#template.charCodeAt(i - 1) !== NEWLINE) {
|
|
1208
1231
|
return false;
|
|
1209
1232
|
}
|
|
1210
1233
|
// Count hashes (must be 1-6)
|
|
@@ -1235,23 +1258,8 @@ export class MdzParser {
|
|
|
1235
1258
|
if (!has_content) {
|
|
1236
1259
|
return false; // heading with only whitespace, treat as plain text
|
|
1237
1260
|
}
|
|
1238
|
-
// At
|
|
1239
|
-
|
|
1240
|
-
if (i >= this.#template.length) {
|
|
1241
|
-
return true; // heading at EOF
|
|
1242
|
-
}
|
|
1243
|
-
// Must have newline
|
|
1244
|
-
if (this.#template.charCodeAt(i) !== NEWLINE) {
|
|
1245
|
-
return false;
|
|
1246
|
-
}
|
|
1247
|
-
const next_i = i + 1;
|
|
1248
|
-
if (next_i >= this.#template.length) {
|
|
1249
|
-
return true; // heading followed by newline + EOF
|
|
1250
|
-
}
|
|
1251
|
-
if (this.#template.charCodeAt(next_i) === NEWLINE) {
|
|
1252
|
-
return true; // heading followed by blank line
|
|
1253
|
-
}
|
|
1254
|
-
return false; // heading followed by single newline + content
|
|
1261
|
+
// At newline or EOF — both are valid
|
|
1262
|
+
return true;
|
|
1255
1263
|
}
|
|
1256
1264
|
/**
|
|
1257
1265
|
* Parse heading: `# Heading text`
|
|
@@ -1312,19 +1320,13 @@ export class MdzParser {
|
|
|
1312
1320
|
}
|
|
1313
1321
|
/**
|
|
1314
1322
|
* Check if current position matches a code block.
|
|
1315
|
-
* Code block must be 3+ backticks at column 0, followed by
|
|
1323
|
+
* Code block must be 3+ backticks at column 0, closing fence followed by newline or EOF.
|
|
1316
1324
|
* Empty code blocks (no content) are treated as invalid.
|
|
1317
|
-
*
|
|
1318
|
-
* Blank line requirement rationale:
|
|
1319
|
-
* Separates code blocks from following content to prevent ambiguity.
|
|
1320
|
-
* Codeblocks are distinct semantic units that should be visually isolated.
|
|
1321
|
-
* The blank line makes it explicit where the code block ends and regular
|
|
1322
|
-
* content begins, following the "explicit over implicit" design principle.
|
|
1323
1325
|
*/
|
|
1324
1326
|
#match_code_block() {
|
|
1325
1327
|
let i = this.#index;
|
|
1326
|
-
// Must start at column 0 (
|
|
1327
|
-
if (i
|
|
1328
|
+
// Must start at column 0 (beginning of input or after newline)
|
|
1329
|
+
if (i > 0 && this.#template.charCodeAt(i - 1) !== NEWLINE) {
|
|
1328
1330
|
return false;
|
|
1329
1331
|
}
|
|
1330
1332
|
// Must have at least three backticks
|
|
@@ -1386,15 +1388,7 @@ export class MdzParser {
|
|
|
1386
1388
|
// closing fence has non-whitespace after it on same line - not a code block
|
|
1387
1389
|
return false;
|
|
1388
1390
|
}
|
|
1389
|
-
//
|
|
1390
|
-
const next_j = j + 1;
|
|
1391
|
-
if (next_j >= this.#template.length) {
|
|
1392
|
-
return true; // code block followed by newline + EOF
|
|
1393
|
-
}
|
|
1394
|
-
if (this.#template.charCodeAt(next_j) === NEWLINE) {
|
|
1395
|
-
return true; // code block followed by blank line
|
|
1396
|
-
}
|
|
1397
|
-
return false; // code block followed by single newline + content
|
|
1391
|
+
return true; // code block followed by newline or EOF
|
|
1398
1392
|
}
|
|
1399
1393
|
}
|
|
1400
1394
|
i++;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/module.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,iCAAiC,CAAC;AAEhE,OAAO,EAAC,WAAW,EAAC,MAAM,yBAAyB,CAAC;AACpD,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,qBAAqB,CAAC;AAGjD;;GAEG;AACH,qBAAa,MAAM;IAClB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAiB;IAC1C,QAAQ,CAAC,WAAW,EAAE,UAAU,CAAiB;IAEjD;;OAEG;IACH,IAAI,SAAmC;IAEvC;;OAEG;IACH,WAAW,SAA8B;IAEzC,cAAc,qBAA6C;IAE3D;;OAEG;IACH,YAAY,gBAMV;IAEF;;OAEG;IACH,OAAO,
|
|
1
|
+
{"version":3,"file":"module.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/module.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,iCAAiC,CAAC;AAEhE,OAAO,EAAC,WAAW,EAAC,MAAM,yBAAyB,CAAC;AACpD,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,qBAAqB,CAAC;AAGjD;;GAEG;AACH,qBAAa,MAAM;IAClB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAiB;IAC1C,QAAQ,CAAC,WAAW,EAAE,UAAU,CAAiB;IAEjD;;OAEG;IACH,IAAI,SAAmC;IAEvC;;OAEG;IACH,WAAW,SAA8B;IAEzC,cAAc,qBAA6C;IAE3D;;OAEG;IACH,YAAY,gBAMV;IAEF;;OAEG;IACH,OAAO,SAAgE;IAEvE;;OAEG;IACH,UAAU,qBAIR;IAEF,gBAAgB,EAAE,OAAO,CAEvB;IAEF,kBAAkB,EAAE,OAAO,CAA+C;IAE1E;;OAEG;IACH,YAAY,uBAA2C;IAEvD;;OAEG;IACH,UAAU,uBAAyC;gBAEvC,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU;IAKrD;;OAEG;IACH,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;CAG9D"}
|
package/dist/module.svelte.js
CHANGED
package/package.json
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fuzdev/fuz_ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.185.0",
|
|
4
4
|
"description": "Svelte UI library",
|
|
5
5
|
"motto": "friendly user zystem",
|
|
6
6
|
"glyph": "🧶",
|
|
7
7
|
"logo": "logo.svg",
|
|
8
8
|
"logo_alt": "a friendly brown spider facing you",
|
|
9
|
-
"public": true,
|
|
10
9
|
"license": "MIT",
|
|
11
10
|
"homepage": "https://ui.fuz.dev/",
|
|
12
11
|
"author": {
|
|
@@ -35,10 +34,10 @@
|
|
|
35
34
|
"node": ">=22.15"
|
|
36
35
|
},
|
|
37
36
|
"peerDependencies": {
|
|
38
|
-
"@fuzdev/fuz_code": ">=0.45.
|
|
39
|
-
"@fuzdev/fuz_css": ">=0.
|
|
40
|
-
"@fuzdev/fuz_util": ">=0.
|
|
41
|
-
"@fuzdev/gro": ">=0.
|
|
37
|
+
"@fuzdev/fuz_code": ">=0.45.1",
|
|
38
|
+
"@fuzdev/fuz_css": ">=0.53.0",
|
|
39
|
+
"@fuzdev/fuz_util": ">=0.52.0",
|
|
40
|
+
"@fuzdev/gro": ">=0.195.0",
|
|
42
41
|
"@jridgewell/trace-mapping": "^0.3",
|
|
43
42
|
"@sveltejs/kit": "^2.47.3",
|
|
44
43
|
"@types/estree": "^1",
|
|
@@ -76,9 +75,9 @@
|
|
|
76
75
|
"devDependencies": {
|
|
77
76
|
"@changesets/changelog-git": "^0.2.1",
|
|
78
77
|
"@fuzdev/fuz_code": "^0.45.1",
|
|
79
|
-
"@fuzdev/fuz_css": "^0.
|
|
80
|
-
"@fuzdev/fuz_util": "^0.
|
|
81
|
-
"@fuzdev/gro": "^0.
|
|
78
|
+
"@fuzdev/fuz_css": "^0.53.0",
|
|
79
|
+
"@fuzdev/fuz_util": "^0.52.0",
|
|
80
|
+
"@fuzdev/gro": "^0.195.0",
|
|
82
81
|
"@jridgewell/trace-mapping": "^0.3.31",
|
|
83
82
|
"@ryanatkn/eslint-config": "^0.9.0",
|
|
84
83
|
"@sveltejs/adapter-static": "^3.0.10",
|
|
@@ -40,7 +40,7 @@ export class Declaration {
|
|
|
40
40
|
/**
|
|
41
41
|
* API documentation URL.
|
|
42
42
|
*/
|
|
43
|
-
url_api = $derived(`/docs/api/${this.module_path}#${this.name}`);
|
|
43
|
+
url_api = $derived(`/docs/api${this.library.url_prefix}/${this.module_path}#${this.name}`);
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
46
|
* Generated TypeScript import statement.
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import type {LibraryJson} from '@fuzdev/fuz_util/library_json.js';
|
|
2
|
+
import {ensure_start, strip_end} from '@fuzdev/fuz_util/string.js';
|
|
2
3
|
|
|
3
4
|
import {create_context} from './context_helpers.js';
|
|
4
5
|
import {Declaration} from './declaration.svelte.js';
|
|
5
6
|
import {Module} from './module.svelte.js';
|
|
6
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Normalizes a URL prefix: ensures leading `/`, strips trailing `/`, returns `''` for falsy and non-string values.
|
|
10
|
+
*/
|
|
11
|
+
export const parse_library_url_prefix = (value: unknown): string => {
|
|
12
|
+
if (!value || typeof value !== 'string') return '';
|
|
13
|
+
return ensure_start(strip_end(value, '/'), '/');
|
|
14
|
+
};
|
|
15
|
+
|
|
7
16
|
/**
|
|
8
17
|
* Rich runtime representation of a library.
|
|
9
18
|
*
|
|
@@ -16,6 +25,13 @@ import {Module} from './module.svelte.js';
|
|
|
16
25
|
export class Library {
|
|
17
26
|
readonly library_json: LibraryJson = $state.raw()!;
|
|
18
27
|
|
|
28
|
+
/**
|
|
29
|
+
* URL path prefix for multi-package documentation sites.
|
|
30
|
+
* Prepended to `/docs/api/` paths in `Module.url_api` and `Declaration.url_api`.
|
|
31
|
+
* Default `''` preserves single-package behavior.
|
|
32
|
+
*/
|
|
33
|
+
readonly url_prefix: string;
|
|
34
|
+
|
|
19
35
|
package_json = $derived(this.library_json.package_json);
|
|
20
36
|
source_json = $derived(this.library_json.source_json);
|
|
21
37
|
|
|
@@ -65,8 +81,9 @@ export class Library {
|
|
|
65
81
|
*/
|
|
66
82
|
declaration_map = $derived(new Map(this.declarations.map((d) => [d.name, d])));
|
|
67
83
|
|
|
68
|
-
constructor(library_json: LibraryJson) {
|
|
84
|
+
constructor(library_json: LibraryJson, url_prefix = '') {
|
|
69
85
|
this.library_json = library_json;
|
|
86
|
+
this.url_prefix = parse_library_url_prefix(url_prefix);
|
|
70
87
|
}
|
|
71
88
|
|
|
72
89
|
/**
|
package/src/lib/mdz.ts
CHANGED
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
*
|
|
17
17
|
* ## Design philosophy
|
|
18
18
|
*
|
|
19
|
-
* - **False negatives over false positives**:
|
|
20
|
-
*
|
|
19
|
+
* - **False negatives over false positives**: When in doubt, treat as plain text.
|
|
20
|
+
* Block elements can interrupt paragraphs without blank lines; inline formatting is strict.
|
|
21
21
|
* - **One way to do things**: Single unambiguous syntax per feature. No alternatives.
|
|
22
22
|
* - **Explicit over implicit**: Clear delimiters and column-0 requirements avoid ambiguity.
|
|
23
23
|
* - **Simple over complete**: Prefer simple parsing rules over complex edge case handling.
|
|
@@ -186,92 +186,130 @@ export class MdzParser {
|
|
|
186
186
|
/**
|
|
187
187
|
* Main parse method. Returns flat array of nodes,
|
|
188
188
|
* with paragraph nodes wrapping content between double newlines.
|
|
189
|
+
*
|
|
190
|
+
* Block elements (headings, HR, codeblocks) are detected at every column-0
|
|
191
|
+
* position — they can interrupt paragraphs without requiring blank lines.
|
|
189
192
|
*/
|
|
190
193
|
parse(): Array<MdzNode> {
|
|
191
194
|
this.#nodes.length = 0;
|
|
192
195
|
const root_nodes: Array<MdzNode> = [];
|
|
193
196
|
const paragraph_children: Array<MdzNode> = [];
|
|
194
197
|
|
|
195
|
-
//
|
|
196
|
-
|
|
197
|
-
if (start_block) {
|
|
198
|
-
root_nodes.push(start_block);
|
|
199
|
-
}
|
|
198
|
+
// Skip leading newlines
|
|
199
|
+
this.#skip_newlines();
|
|
200
200
|
|
|
201
201
|
while (this.#index < this.#template.length) {
|
|
202
|
+
// Peek for block element (read-only match), flush paragraph first, then parse.
|
|
203
|
+
// Flush must happen before parse because parse modifies accumulation state.
|
|
204
|
+
const block_type = this.#peek_block_element();
|
|
205
|
+
if (block_type) {
|
|
206
|
+
const flushed = this.#flush_paragraph(paragraph_children, true);
|
|
207
|
+
if (flushed) root_nodes.push(flushed);
|
|
208
|
+
if (block_type === 'heading') root_nodes.push(this.#parse_heading());
|
|
209
|
+
else if (block_type === 'hr') root_nodes.push(this.#parse_hr());
|
|
210
|
+
else root_nodes.push(this.#parse_code_block());
|
|
211
|
+
this.#skip_newlines();
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
|
|
202
215
|
// Check for paragraph break (double newline)
|
|
203
216
|
if (this.#is_at_paragraph_break()) {
|
|
204
|
-
this.#
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
// Wrap accumulated nodes in paragraph (or add single tag directly)
|
|
211
|
-
if (paragraph_children.length > 0) {
|
|
212
|
-
if (this.#is_single_tag(paragraph_children)) {
|
|
213
|
-
// Single tag (component/element) - add directly without paragraph wrapper (MDX convention)
|
|
214
|
-
const tag = paragraph_children.find(
|
|
215
|
-
(n) => n.type === 'Component' || n.type === 'Element',
|
|
216
|
-
)!;
|
|
217
|
-
root_nodes.push(tag);
|
|
218
|
-
} else {
|
|
219
|
-
// Regular paragraph
|
|
220
|
-
root_nodes.push({
|
|
221
|
-
type: 'Paragraph',
|
|
222
|
-
children: paragraph_children.slice(),
|
|
223
|
-
start: paragraph_children[0]!.start,
|
|
224
|
-
end: paragraph_children[paragraph_children.length - 1]!.end,
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
paragraph_children.length = 0;
|
|
228
|
-
}
|
|
229
|
-
// Consume the paragraph break
|
|
230
|
-
this.#eat('\n\n');
|
|
217
|
+
const flushed = this.#flush_paragraph(paragraph_children, true);
|
|
218
|
+
if (flushed) root_nodes.push(flushed);
|
|
219
|
+
this.#skip_newlines();
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
231
222
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}
|
|
223
|
+
// Parse inline content
|
|
224
|
+
const node = this.#parse_node();
|
|
225
|
+
if (node.type === 'Text') {
|
|
226
|
+
this.#accumulate_text(node.content, node.start);
|
|
237
227
|
} else {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}
|
|
245
|
-
if (this.#nodes.length > 0) {
|
|
246
|
-
paragraph_children.push(...this.#nodes);
|
|
247
|
-
this.#nodes.length = 0;
|
|
248
|
-
}
|
|
228
|
+
this.#flush_text();
|
|
229
|
+
this.#nodes.push(node);
|
|
230
|
+
}
|
|
231
|
+
if (this.#nodes.length > 0) {
|
|
232
|
+
paragraph_children.push(...this.#nodes);
|
|
233
|
+
this.#nodes.length = 0;
|
|
249
234
|
}
|
|
250
235
|
}
|
|
251
236
|
|
|
237
|
+
// Flush remaining content as final paragraph
|
|
238
|
+
const final_paragraph = this.#flush_paragraph(paragraph_children, true);
|
|
239
|
+
if (final_paragraph) root_nodes.push(final_paragraph);
|
|
240
|
+
|
|
241
|
+
return root_nodes;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Flush accumulated inline content as a paragraph node (or single tag).
|
|
246
|
+
* When `trim_trailing` is true, trims trailing newlines from the last text node
|
|
247
|
+
* (the line break before a block element or paragraph break).
|
|
248
|
+
*
|
|
249
|
+
* @mutates paragraph_children - trims last text node, removes whitespace-only nodes, clears array
|
|
250
|
+
*/
|
|
251
|
+
#flush_paragraph(paragraph_children: Array<MdzNode>, trim_trailing = false): MdzNode | null {
|
|
252
252
|
this.#flush_text();
|
|
253
253
|
if (this.#nodes.length > 0) {
|
|
254
254
|
paragraph_children.push(...this.#nodes);
|
|
255
|
+
this.#nodes.length = 0;
|
|
255
256
|
}
|
|
256
257
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
start
|
|
269
|
-
|
|
270
|
-
|
|
258
|
+
if (paragraph_children.length === 0) {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (trim_trailing) {
|
|
263
|
+
// Trim trailing newlines from the last text node (line break before block element)
|
|
264
|
+
const last = paragraph_children[paragraph_children.length - 1]!;
|
|
265
|
+
if (last.type === 'Text') {
|
|
266
|
+
const trimmed = last.content.replace(/\n+$/, '');
|
|
267
|
+
if (trimmed) {
|
|
268
|
+
last.content = trimmed;
|
|
269
|
+
last.end = last.start + trimmed.length;
|
|
270
|
+
} else {
|
|
271
|
+
paragraph_children.pop();
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Skip whitespace-only paragraphs (e.g. newlines between consecutive blocks)
|
|
276
|
+
const has_content = paragraph_children.some(
|
|
277
|
+
(n) => n.type !== 'Text' || n.content.trim().length > 0,
|
|
278
|
+
);
|
|
279
|
+
if (!has_content) {
|
|
280
|
+
paragraph_children.length = 0;
|
|
281
|
+
return null;
|
|
271
282
|
}
|
|
272
283
|
}
|
|
273
284
|
|
|
274
|
-
|
|
285
|
+
// Single tag (component/element) - add directly without paragraph wrapper (MDX convention)
|
|
286
|
+
const single_tag = this.#extract_single_tag(paragraph_children);
|
|
287
|
+
if (single_tag) {
|
|
288
|
+
paragraph_children.length = 0;
|
|
289
|
+
return single_tag;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Regular paragraph
|
|
293
|
+
const result: MdzParagraphNode = {
|
|
294
|
+
type: 'Paragraph',
|
|
295
|
+
children: paragraph_children.slice(),
|
|
296
|
+
start: paragraph_children[0]!.start,
|
|
297
|
+
end: paragraph_children[paragraph_children.length - 1]!.end,
|
|
298
|
+
};
|
|
299
|
+
paragraph_children.length = 0;
|
|
300
|
+
return result;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Consume consecutive newline characters.
|
|
305
|
+
*/
|
|
306
|
+
#skip_newlines(): void {
|
|
307
|
+
while (
|
|
308
|
+
this.#index < this.#template.length &&
|
|
309
|
+
this.#template.charCodeAt(this.#index) === NEWLINE
|
|
310
|
+
) {
|
|
311
|
+
this.#index++;
|
|
312
|
+
}
|
|
275
313
|
}
|
|
276
314
|
|
|
277
315
|
/**
|
|
@@ -870,47 +908,38 @@ export class MdzParser {
|
|
|
870
908
|
}
|
|
871
909
|
|
|
872
910
|
/**
|
|
873
|
-
*
|
|
874
|
-
*
|
|
875
|
-
*
|
|
911
|
+
* Extract a single tag (component or element) if it's the only non-whitespace content.
|
|
912
|
+
* Returns the tag node if paragraph wrapping should be skipped (MDX convention),
|
|
913
|
+
* or null if the content should be wrapped in a paragraph.
|
|
876
914
|
*/
|
|
877
|
-
#
|
|
878
|
-
let
|
|
915
|
+
#extract_single_tag(nodes: Array<MdzNode>): MdzComponentNode | MdzElementNode | null {
|
|
916
|
+
let tag: MdzComponentNode | MdzElementNode | null = null;
|
|
879
917
|
|
|
880
918
|
for (const node of nodes) {
|
|
881
919
|
if (node.type === 'Component' || node.type === 'Element') {
|
|
882
|
-
if (
|
|
883
|
-
|
|
920
|
+
if (tag) return null; // Multiple tags
|
|
921
|
+
tag = node;
|
|
884
922
|
} else if (node.type === 'Text') {
|
|
885
923
|
// Allow only whitespace-only text nodes
|
|
886
|
-
if (node.content.trim() !== '') return
|
|
924
|
+
if (node.content.trim() !== '') return null;
|
|
887
925
|
} else {
|
|
888
926
|
// Any other node type means not a single tag
|
|
889
|
-
return
|
|
927
|
+
return null;
|
|
890
928
|
}
|
|
891
929
|
}
|
|
892
930
|
|
|
893
|
-
return
|
|
931
|
+
return tag;
|
|
894
932
|
}
|
|
895
933
|
|
|
896
934
|
/**
|
|
897
|
-
*
|
|
898
|
-
*
|
|
899
|
-
*
|
|
900
|
-
* Block elements must:
|
|
901
|
-
* - Start at column 0 (no leading whitespace)
|
|
902
|
-
* - Be followed by blank line or EOF
|
|
903
|
-
*
|
|
904
|
-
* This helper eliminates duplication between document start and post-paragraph-break parsing.
|
|
935
|
+
* Read-only check if current position matches a block element.
|
|
936
|
+
* Does not modify parser state — used to peek before flushing paragraph.
|
|
937
|
+
* Returns which block type matched, or null if none.
|
|
905
938
|
*/
|
|
906
|
-
#
|
|
907
|
-
if (this.#match_heading())
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
return this.#parse_hr();
|
|
911
|
-
} else if (this.#match_code_block()) {
|
|
912
|
-
return this.#parse_code_block();
|
|
913
|
-
}
|
|
939
|
+
#peek_block_element(): 'heading' | 'hr' | 'codeblock' | null {
|
|
940
|
+
if (this.#match_heading()) return 'heading';
|
|
941
|
+
if (this.#match_hr()) return 'hr';
|
|
942
|
+
if (this.#match_code_block()) return 'codeblock';
|
|
914
943
|
return null;
|
|
915
944
|
}
|
|
916
945
|
|
|
@@ -1288,6 +1317,21 @@ export class MdzParser {
|
|
|
1288
1317
|
break;
|
|
1289
1318
|
}
|
|
1290
1319
|
|
|
1320
|
+
// When next line could start a block element, consume the newline and stop.
|
|
1321
|
+
// The main loop will try block detection at the next character.
|
|
1322
|
+
// Consuming the newline here avoids a 3-iteration detour (break before \n,
|
|
1323
|
+
// fail peek at \n, safety-increment, then succeed peek at block char).
|
|
1324
|
+
if (char_code === NEWLINE) {
|
|
1325
|
+
const next_i = this.#index + 1;
|
|
1326
|
+
if (next_i < this.#template.length) {
|
|
1327
|
+
const next_char = this.#template.charCodeAt(next_i);
|
|
1328
|
+
if (next_char === HASH || next_char === HYPHEN || next_char === BACKTICK) {
|
|
1329
|
+
this.#index++; // consume the newline
|
|
1330
|
+
break;
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1291
1335
|
// Check for URL or internal path mid-text
|
|
1292
1336
|
if (this.#is_at_url() || this.#is_at_internal_path()) {
|
|
1293
1337
|
break;
|
|
@@ -1395,19 +1439,16 @@ export class MdzParser {
|
|
|
1395
1439
|
|
|
1396
1440
|
/**
|
|
1397
1441
|
* Check if current position matches a horizontal rule.
|
|
1398
|
-
* HR must be exactly `---` at column 0, followed by
|
|
1442
|
+
* HR must be exactly `---` at column 0, followed by newline or EOF.
|
|
1399
1443
|
*
|
|
1400
|
-
*
|
|
1401
|
-
*
|
|
1402
|
-
* Without this, `---` followed by regular text would create an hr and treat
|
|
1403
|
-
* the next line as a new paragraph, which could be surprising. The blank line
|
|
1404
|
-
* makes block element boundaries explicit and predictable.
|
|
1444
|
+
* mdz has no setext headings, so `---` after a paragraph is unambiguous
|
|
1445
|
+
* (always an HR, unlike CommonMark where it becomes a setext heading).
|
|
1405
1446
|
*/
|
|
1406
1447
|
#match_hr(): boolean {
|
|
1407
1448
|
let i = this.#index;
|
|
1408
1449
|
|
|
1409
|
-
// Must start at column 0 (
|
|
1410
|
-
if (i
|
|
1450
|
+
// Must start at column 0 (beginning of input or after newline)
|
|
1451
|
+
if (i > 0 && this.#template.charCodeAt(i - 1) !== NEWLINE) {
|
|
1411
1452
|
return false;
|
|
1412
1453
|
}
|
|
1413
1454
|
|
|
@@ -1426,15 +1467,7 @@ export class MdzParser {
|
|
|
1426
1467
|
while (i < this.#template.length) {
|
|
1427
1468
|
const char_code = this.#template.charCodeAt(i);
|
|
1428
1469
|
if (char_code === NEWLINE) {
|
|
1429
|
-
|
|
1430
|
-
const next_i = i + 1;
|
|
1431
|
-
if (next_i >= this.#template.length) {
|
|
1432
|
-
return true; // hr followed by newline + EOF
|
|
1433
|
-
}
|
|
1434
|
-
if (this.#template.charCodeAt(next_i) === NEWLINE) {
|
|
1435
|
-
return true; // hr followed by blank line
|
|
1436
|
-
}
|
|
1437
|
-
return false; // hr followed by single newline + content
|
|
1470
|
+
return true;
|
|
1438
1471
|
}
|
|
1439
1472
|
if (char_code !== SPACE) {
|
|
1440
1473
|
return false; // Non-whitespace after ---, not an hr
|
|
@@ -1476,18 +1509,13 @@ export class MdzParser {
|
|
|
1476
1509
|
/**
|
|
1477
1510
|
* Check if current position matches a heading.
|
|
1478
1511
|
* Heading must be 1-6 hashes at column 0, followed by space and content,
|
|
1479
|
-
* followed by
|
|
1480
|
-
*
|
|
1481
|
-
* Blank line requirement rationale:
|
|
1482
|
-
* Ensures headings are visually and semantically separate from following content.
|
|
1483
|
-
* Without this, `# Heading\nText` would be ambiguous - is the text part of the
|
|
1484
|
-
* heading or a new paragraph? The blank line makes document structure explicit.
|
|
1512
|
+
* followed by newline or EOF.
|
|
1485
1513
|
*/
|
|
1486
1514
|
#match_heading(): boolean {
|
|
1487
1515
|
let i = this.#index;
|
|
1488
1516
|
|
|
1489
|
-
// Must start at column 0 (
|
|
1490
|
-
if (i
|
|
1517
|
+
// Must start at column 0 (beginning of input or after newline)
|
|
1518
|
+
if (i > 0 && this.#template.charCodeAt(i - 1) !== NEWLINE) {
|
|
1491
1519
|
return false;
|
|
1492
1520
|
}
|
|
1493
1521
|
|
|
@@ -1526,27 +1554,8 @@ export class MdzParser {
|
|
|
1526
1554
|
return false; // heading with only whitespace, treat as plain text
|
|
1527
1555
|
}
|
|
1528
1556
|
|
|
1529
|
-
// At
|
|
1530
|
-
|
|
1531
|
-
if (i >= this.#template.length) {
|
|
1532
|
-
return true; // heading at EOF
|
|
1533
|
-
}
|
|
1534
|
-
|
|
1535
|
-
// Must have newline
|
|
1536
|
-
if (this.#template.charCodeAt(i) !== NEWLINE) {
|
|
1537
|
-
return false;
|
|
1538
|
-
}
|
|
1539
|
-
|
|
1540
|
-
const next_i = i + 1;
|
|
1541
|
-
if (next_i >= this.#template.length) {
|
|
1542
|
-
return true; // heading followed by newline + EOF
|
|
1543
|
-
}
|
|
1544
|
-
|
|
1545
|
-
if (this.#template.charCodeAt(next_i) === NEWLINE) {
|
|
1546
|
-
return true; // heading followed by blank line
|
|
1547
|
-
}
|
|
1548
|
-
|
|
1549
|
-
return false; // heading followed by single newline + content
|
|
1557
|
+
// At newline or EOF — both are valid
|
|
1558
|
+
return true;
|
|
1550
1559
|
}
|
|
1551
1560
|
|
|
1552
1561
|
/**
|
|
@@ -1616,20 +1625,14 @@ export class MdzParser {
|
|
|
1616
1625
|
|
|
1617
1626
|
/**
|
|
1618
1627
|
* Check if current position matches a code block.
|
|
1619
|
-
* Code block must be 3+ backticks at column 0, followed by
|
|
1628
|
+
* Code block must be 3+ backticks at column 0, closing fence followed by newline or EOF.
|
|
1620
1629
|
* Empty code blocks (no content) are treated as invalid.
|
|
1621
|
-
*
|
|
1622
|
-
* Blank line requirement rationale:
|
|
1623
|
-
* Separates code blocks from following content to prevent ambiguity.
|
|
1624
|
-
* Codeblocks are distinct semantic units that should be visually isolated.
|
|
1625
|
-
* The blank line makes it explicit where the code block ends and regular
|
|
1626
|
-
* content begins, following the "explicit over implicit" design principle.
|
|
1627
1630
|
*/
|
|
1628
1631
|
#match_code_block(): boolean {
|
|
1629
1632
|
let i = this.#index;
|
|
1630
1633
|
|
|
1631
|
-
// Must start at column 0 (
|
|
1632
|
-
if (i
|
|
1634
|
+
// Must start at column 0 (beginning of input or after newline)
|
|
1635
|
+
if (i > 0 && this.#template.charCodeAt(i - 1) !== NEWLINE) {
|
|
1633
1636
|
return false;
|
|
1634
1637
|
}
|
|
1635
1638
|
|
|
@@ -1704,15 +1707,7 @@ export class MdzParser {
|
|
|
1704
1707
|
return false;
|
|
1705
1708
|
}
|
|
1706
1709
|
|
|
1707
|
-
//
|
|
1708
|
-
const next_j = j + 1;
|
|
1709
|
-
if (next_j >= this.#template.length) {
|
|
1710
|
-
return true; // code block followed by newline + EOF
|
|
1711
|
-
}
|
|
1712
|
-
if (this.#template.charCodeAt(next_j) === NEWLINE) {
|
|
1713
|
-
return true; // code block followed by blank line
|
|
1714
|
-
}
|
|
1715
|
-
return false; // code block followed by single newline + content
|
|
1710
|
+
return true; // code block followed by newline or EOF
|
|
1716
1711
|
}
|
|
1717
1712
|
}
|
|
1718
1713
|
i++;
|
package/src/lib/module.svelte.ts
CHANGED