@fuzdev/fuz_ui 0.204.0 → 0.205.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 (95) hide show
  1. package/dist/ApiModule.svelte +14 -1
  2. package/dist/ApiModule.svelte.d.ts.map +1 -1
  3. package/dist/DeclarationDetail.svelte +15 -4
  4. package/dist/DeclarationDetail.svelte.d.ts.map +1 -1
  5. package/package.json +12 -36
  6. package/dist/Mdz.svelte +0 -34
  7. package/dist/Mdz.svelte.d.ts +0 -11
  8. package/dist/Mdz.svelte.d.ts.map +0 -1
  9. package/dist/MdzNodeView.svelte +0 -107
  10. package/dist/MdzNodeView.svelte.d.ts +0 -9
  11. package/dist/MdzNodeView.svelte.d.ts.map +0 -1
  12. package/dist/MdzPrecompiled.svelte +0 -30
  13. package/dist/MdzPrecompiled.svelte.d.ts +0 -11
  14. package/dist/MdzPrecompiled.svelte.d.ts.map +0 -1
  15. package/dist/MdzRoot.svelte +0 -30
  16. package/dist/MdzRoot.svelte.d.ts +0 -12
  17. package/dist/MdzRoot.svelte.d.ts.map +0 -1
  18. package/dist/MdzStream.svelte +0 -32
  19. package/dist/MdzStream.svelte.d.ts +0 -12
  20. package/dist/MdzStream.svelte.d.ts.map +0 -1
  21. package/dist/MdzStreamNodeView.svelte +0 -106
  22. package/dist/MdzStreamNodeView.svelte.d.ts +0 -9
  23. package/dist/MdzStreamNodeView.svelte.d.ts.map +0 -1
  24. package/dist/mdz.d.ts +0 -112
  25. package/dist/mdz.d.ts.map +0 -1
  26. package/dist/mdz.js +0 -1186
  27. package/dist/mdz_components.d.ts +0 -63
  28. package/dist/mdz_components.d.ts.map +0 -1
  29. package/dist/mdz_components.js +0 -32
  30. package/dist/mdz_helpers.d.ts +0 -164
  31. package/dist/mdz_helpers.d.ts.map +0 -1
  32. package/dist/mdz_helpers.js +0 -424
  33. package/dist/mdz_lexer.d.ts +0 -93
  34. package/dist/mdz_lexer.d.ts.map +0 -1
  35. package/dist/mdz_lexer.js +0 -732
  36. package/dist/mdz_opcodes.d.ts +0 -174
  37. package/dist/mdz_opcodes.d.ts.map +0 -1
  38. package/dist/mdz_opcodes.js +0 -14
  39. package/dist/mdz_opcodes_to_nodes.d.ts +0 -20
  40. package/dist/mdz_opcodes_to_nodes.d.ts.map +0 -1
  41. package/dist/mdz_opcodes_to_nodes.js +0 -332
  42. package/dist/mdz_stream_parser.d.ts +0 -80
  43. package/dist/mdz_stream_parser.d.ts.map +0 -1
  44. package/dist/mdz_stream_parser.js +0 -354
  45. package/dist/mdz_stream_parser_block.d.ts +0 -76
  46. package/dist/mdz_stream_parser_block.d.ts.map +0 -1
  47. package/dist/mdz_stream_parser_block.js +0 -458
  48. package/dist/mdz_stream_parser_inline.d.ts +0 -52
  49. package/dist/mdz_stream_parser_inline.d.ts.map +0 -1
  50. package/dist/mdz_stream_parser_inline.js +0 -378
  51. package/dist/mdz_stream_parser_link.d.ts +0 -22
  52. package/dist/mdz_stream_parser_link.d.ts.map +0 -1
  53. package/dist/mdz_stream_parser_link.js +0 -215
  54. package/dist/mdz_stream_parser_state.d.ts +0 -187
  55. package/dist/mdz_stream_parser_state.d.ts.map +0 -1
  56. package/dist/mdz_stream_parser_state.js +0 -398
  57. package/dist/mdz_stream_parser_text.d.ts +0 -14
  58. package/dist/mdz_stream_parser_text.d.ts.map +0 -1
  59. package/dist/mdz_stream_parser_text.js +0 -50
  60. package/dist/mdz_stream_parser_url.d.ts +0 -60
  61. package/dist/mdz_stream_parser_url.d.ts.map +0 -1
  62. package/dist/mdz_stream_parser_url.js +0 -307
  63. package/dist/mdz_stream_state.svelte.d.ts +0 -44
  64. package/dist/mdz_stream_state.svelte.d.ts.map +0 -1
  65. package/dist/mdz_stream_state.svelte.js +0 -357
  66. package/dist/mdz_to_svelte.d.ts +0 -41
  67. package/dist/mdz_to_svelte.d.ts.map +0 -1
  68. package/dist/mdz_to_svelte.js +0 -100
  69. package/dist/mdz_token_parser.d.ts +0 -14
  70. package/dist/mdz_token_parser.d.ts.map +0 -1
  71. package/dist/mdz_token_parser.js +0 -344
  72. package/dist/svelte_preprocess_mdz.d.ts +0 -65
  73. package/dist/svelte_preprocess_mdz.d.ts.map +0 -1
  74. package/dist/svelte_preprocess_mdz.js +0 -529
  75. package/dist/tsdoc_mdz.d.ts +0 -45
  76. package/dist/tsdoc_mdz.d.ts.map +0 -1
  77. package/dist/tsdoc_mdz.js +0 -88
  78. package/src/lib/mdz.ts +0 -1532
  79. package/src/lib/mdz_components.ts +0 -64
  80. package/src/lib/mdz_helpers.ts +0 -449
  81. package/src/lib/mdz_lexer.ts +0 -1012
  82. package/src/lib/mdz_opcodes.ts +0 -205
  83. package/src/lib/mdz_opcodes_to_nodes.ts +0 -375
  84. package/src/lib/mdz_stream_parser.ts +0 -415
  85. package/src/lib/mdz_stream_parser_block.ts +0 -532
  86. package/src/lib/mdz_stream_parser_inline.ts +0 -414
  87. package/src/lib/mdz_stream_parser_link.ts +0 -271
  88. package/src/lib/mdz_stream_parser_state.ts +0 -539
  89. package/src/lib/mdz_stream_parser_text.ts +0 -77
  90. package/src/lib/mdz_stream_parser_url.ts +0 -365
  91. package/src/lib/mdz_stream_state.svelte.ts +0 -387
  92. package/src/lib/mdz_to_svelte.ts +0 -141
  93. package/src/lib/mdz_token_parser.ts +0 -434
  94. package/src/lib/svelte_preprocess_mdz.ts +0 -742
  95. package/src/lib/tsdoc_mdz.ts +0 -97
@@ -1,529 +0,0 @@
1
- /**
2
- * Svelte preprocessor that compiles static `Mdz` content to Svelte markup at build time.
3
- *
4
- * Detects `Mdz` components with static string `content` props, parses the mdz content,
5
- * renders each `MdzNode` to equivalent Svelte markup via `mdz_to_svelte`, and replaces
6
- * the `Mdz` with `MdzPrecompiled` containing pre-rendered children.
7
- *
8
- * Also handles ternary chains (`content={a ? 'x' : b ? 'y' : 'z'}`) where all leaf
9
- * values are statically resolvable strings, emitting `{#if a}markup_x{:else if b}markup_y{:else}markup_z{/if}`
10
- * as children of a single `MdzPrecompiled`.
11
- *
12
- * Truly dynamic `content` props are left untouched.
13
- *
14
- * @module
15
- */
16
- import { parse } from 'svelte/compiler';
17
- import MagicString from 'magic-string';
18
- import { walk } from 'zimmerframe';
19
- import { should_exclude_path } from '@fuzdev/fuz_util/path.js';
20
- import { find_attribute, extract_static_string, try_extract_conditional_chain, build_static_bindings, resolve_component_names, has_identifier_in_tree, find_import_insert_position, generate_import_lines, remove_variable_declaration, remove_import_declaration, remove_import_specifier, handle_preprocess_error, } from '@fuzdev/fuz_util/svelte_preprocess_helpers.js';
21
- import { mdz_parse } from './mdz.js';
22
- import { mdz_to_svelte } from './mdz_to_svelte.js';
23
- const PRECOMPILED_NAME = 'MdzPrecompiled';
24
- /**
25
- * Creates a Svelte preprocessor that compiles static `Mdz` content at build time.
26
- *
27
- * @param options - configuration for component/element resolution and file filtering
28
- * @returns a Svelte `PreprocessorGroup` for use in `svelte.config.js`
29
- */
30
- export const svelte_preprocess_mdz = (options = {}) => {
31
- const { exclude = [], components = {}, elements: elements_array = [], component_imports = ['@fuzdev/fuz_ui/Mdz.svelte'], compiled_component_import = '@fuzdev/fuz_ui/MdzPrecompiled.svelte', on_error = process.env.CI === 'true' ? 'throw' : 'log', } = options;
32
- const elements = new Set(elements_array);
33
- return {
34
- name: 'fuz-mdz',
35
- markup: ({ content, filename }) => {
36
- if (should_exclude_path(filename, exclude)) {
37
- return { code: content };
38
- }
39
- // Quick bail: does file mention any known Mdz import source?
40
- if (!component_imports.some((source) => content.includes(source))) {
41
- return { code: content };
42
- }
43
- const ast = parse(content, { filename, modern: true });
44
- // Resolve which local names map to the Mdz component
45
- const mdz_names = resolve_component_names(ast, component_imports);
46
- if (mdz_names.size === 0) {
47
- return { code: content };
48
- }
49
- // Check for MdzPrecompiled name collision
50
- if (has_name_collision(ast, compiled_component_import)) {
51
- return { code: content };
52
- }
53
- const s = new MagicString(content);
54
- const bindings = build_static_bindings(ast);
55
- // Find and transform Mdz usages with static content
56
- const { transformations, total_usages, transformed_usages } = find_mdz_usages(ast, mdz_names, {
57
- components,
58
- elements,
59
- filename,
60
- source: content,
61
- bindings,
62
- on_error,
63
- });
64
- if (transformations.length === 0) {
65
- return { code: content };
66
- }
67
- // Apply transformations
68
- for (const t of transformations) {
69
- s.overwrite(t.start, t.end, t.replacement);
70
- }
71
- // Remove dead const bindings that were consumed by transformations
72
- remove_dead_const_bindings(s, ast, transformations, content);
73
- // Determine which Mdz imports can be removed
74
- const removable_imports = find_removable_mdz_imports(ast, mdz_names, total_usages, transformed_usages);
75
- // Add required imports and remove unused Mdz imports
76
- manage_imports(s, ast, transformations, removable_imports, compiled_component_import, content);
77
- return {
78
- code: s.toString(),
79
- map: s.generateMap({ hires: true }),
80
- };
81
- },
82
- };
83
- };
84
- /**
85
- * Checks if `MdzPrecompiled` is already imported from a different source.
86
- * If so, the preprocessor bails to avoid name collisions.
87
- */
88
- const has_name_collision = (ast, compiled_component_import) => {
89
- for (const script of [ast.instance, ast.module]) {
90
- if (!script)
91
- continue;
92
- for (const node of script.content.body) {
93
- if (node.type !== 'ImportDeclaration')
94
- continue;
95
- const source_path = node.source.value;
96
- for (const spec of node.specifiers) {
97
- if (spec.local.name === PRECOMPILED_NAME && source_path !== compiled_component_import) {
98
- return true;
99
- }
100
- }
101
- }
102
- }
103
- return false;
104
- };
105
- /**
106
- * Collects identifiers from an expression that resolved through bindings.
107
- * Only collects top-level Identifier nodes that appear in the bindings map.
108
- *
109
- * TODO: support transitive dead const removal — currently only directly-consumed
110
- * bindings (identifiers in the expression AST) are tracked. Transitive dependencies
111
- * (e.g., `const a = 'x'; const b = a; content={b}` — `a` is not removed) are not
112
- * traced. After removing a dead const, re-check whether identifiers in its
113
- * initializer became dead too.
114
- */
115
- const collect_consumed_bindings = (value, bindings) => {
116
- const consumed = new Set();
117
- if (value === true || Array.isArray(value))
118
- return consumed;
119
- const collect_from_expr = (expr) => {
120
- if (expr.type === 'Identifier' && bindings.has(expr.name)) {
121
- consumed.add(expr.name);
122
- }
123
- else if (expr.type === 'BinaryExpression' && expr.operator === '+') {
124
- collect_from_expr(expr.left);
125
- collect_from_expr(expr.right);
126
- }
127
- else if (expr.type === 'TemplateLiteral') {
128
- for (const e of expr.expressions) {
129
- collect_from_expr(e);
130
- }
131
- }
132
- else if (expr.type === 'ConditionalExpression') {
133
- collect_from_expr(expr.consequent);
134
- collect_from_expr(expr.alternate);
135
- }
136
- };
137
- collect_from_expr(value.expression);
138
- return consumed;
139
- };
140
- /**
141
- * Walks the AST to find `Mdz` component usages with static `content` props
142
- * and generates transformations to replace them with `MdzPrecompiled` children.
143
- */
144
- const find_mdz_usages = (ast, mdz_names, context) => {
145
- const transformations = [];
146
- const total_usages = new Map();
147
- const transformed_usages = new Map();
148
- // zimmerframe types visitors against {type: string}, requiring explicit annotations
149
- // on the callback parameters for Svelte-specific AST types like AST.Component
150
- walk(ast.fragment, null, {
151
- Component(node, ctx) {
152
- // Always recurse into children so nested Mdz components are found
153
- ctx.next();
154
- if (!mdz_names.has(node.name))
155
- return;
156
- // Track total usages per name
157
- total_usages.set(node.name, (total_usages.get(node.name) ?? 0) + 1);
158
- // Skip if spread attributes present — can't determine content statically
159
- if (node.attributes.some((attr) => attr.type === 'SpreadAttribute'))
160
- return;
161
- const content_attr = find_attribute(node, 'content');
162
- if (!content_attr)
163
- return;
164
- // Extract optional static base prop for relative path resolution.
165
- // If base is present but dynamic, skip precompilation entirely —
166
- // MdzPrecompiled doesn't resolve relative paths at runtime,
167
- // so precompiling with unresolved relative links would be wrong.
168
- const base_attr = find_attribute(node, 'base');
169
- let base;
170
- if (base_attr) {
171
- const base_value = extract_static_string(base_attr.value, context.bindings);
172
- if (base_value === null)
173
- return; // dynamic base — fall back to runtime
174
- base = base_value;
175
- }
176
- // Collect attributes to exclude from precompiled output
177
- const exclude_attrs = new Set([content_attr]);
178
- if (base_attr)
179
- exclude_attrs.add(base_attr);
180
- // Extract static string value
181
- const content_value = extract_static_string(content_attr.value, context.bindings);
182
- if (content_value !== null) {
183
- // Parse mdz content and render to Svelte markup
184
- let result;
185
- try {
186
- const nodes = mdz_parse(content_value);
187
- result = mdz_to_svelte(nodes, context.components, context.elements, base);
188
- }
189
- catch (error) {
190
- handle_preprocess_error(error, '[fuz-mdz]', context.filename, context.on_error);
191
- return;
192
- }
193
- // If content has unconfigured tags, skip this usage (fall back to runtime)
194
- if (result.has_unconfigured_tags)
195
- return;
196
- const consumed = collect_consumed_bindings(content_attr.value, context.bindings);
197
- const replacement = build_replacement(node, exclude_attrs, result.markup, context.source);
198
- transformed_usages.set(node.name, (transformed_usages.get(node.name) ?? 0) + 1);
199
- transformations.push({
200
- start: node.start,
201
- end: node.end,
202
- replacement,
203
- required_imports: result.imports,
204
- consumed_bindings: consumed,
205
- component_node: node,
206
- });
207
- return;
208
- }
209
- // Try conditional chain (handles both simple and nested ternaries)
210
- const chain = try_extract_conditional_chain(content_attr.value, context.source, context.bindings);
211
- if (chain === null)
212
- return;
213
- // Parse and render each branch
214
- const branch_results = [];
215
- try {
216
- for (const branch of chain) {
217
- const nodes = mdz_parse(branch.value);
218
- const result = mdz_to_svelte(nodes, context.components, context.elements, base);
219
- if (result.has_unconfigured_tags)
220
- return;
221
- branch_results.push({ markup: result.markup, imports: result.imports });
222
- }
223
- }
224
- catch (error) {
225
- handle_preprocess_error(error, '[fuz-mdz]', context.filename, context.on_error);
226
- return;
227
- }
228
- // Build {#if}/{:else if}/{:else} markup
229
- let children_markup = '';
230
- for (let i = 0; i < chain.length; i++) {
231
- const branch = chain[i];
232
- const result = branch_results[i];
233
- if (i === 0) {
234
- children_markup += `{#if ${branch.test_source}}${result.markup}`;
235
- }
236
- else if (branch.test_source !== null) {
237
- children_markup += `{:else if ${branch.test_source}}${result.markup}`;
238
- }
239
- else {
240
- children_markup += `{:else}${result.markup}`;
241
- }
242
- }
243
- children_markup += '{/if}';
244
- const replacement = build_replacement(node, exclude_attrs, children_markup, context.source);
245
- // Merge imports from all branches
246
- const merged_imports = new Map();
247
- for (const result of branch_results) {
248
- for (const [name, info] of result.imports) {
249
- merged_imports.set(name, info);
250
- }
251
- }
252
- const consumed = collect_consumed_bindings(content_attr.value, context.bindings);
253
- transformed_usages.set(node.name, (transformed_usages.get(node.name) ?? 0) + 1);
254
- transformations.push({
255
- start: node.start,
256
- end: node.end,
257
- replacement,
258
- required_imports: merged_imports,
259
- consumed_bindings: consumed,
260
- component_node: node,
261
- });
262
- },
263
- });
264
- return { transformations, total_usages, transformed_usages };
265
- };
266
- /**
267
- * Removes dead `const` bindings from the instance script that were consumed
268
- * by transformations and are no longer referenced anywhere else in the file.
269
- *
270
- * Only handles single-declarator `const` statements. Skips `const a = 'x', b = 'y'`
271
- * and module script variables (which could be exported/imported by other files).
272
- */
273
- const remove_dead_const_bindings = (s, ast, transformations, source) => {
274
- // Collect all consumed binding names across transformations
275
- const all_consumed = new Set();
276
- for (const t of transformations) {
277
- for (const name of t.consumed_bindings) {
278
- all_consumed.add(name);
279
- }
280
- }
281
- if (all_consumed.size === 0)
282
- return;
283
- // Only remove from instance script (module script variables could be exported)
284
- const instance = ast.instance;
285
- if (!instance)
286
- return;
287
- // Build a skip set of transformed component nodes so their attribute
288
- // expressions (which still contain the old identifiers) don't false-match
289
- const skip = new Set();
290
- for (const t of transformations) {
291
- skip.add(t.component_node);
292
- }
293
- for (const name of all_consumed) {
294
- // Find the VariableDeclaration containing this binding in instance script
295
- let declaration_node = null;
296
- for (const node of instance.content.body) {
297
- if (node.type !== 'VariableDeclaration' || node.kind !== 'const')
298
- continue;
299
- for (const decl of node.declarations) {
300
- if (decl.id.type === 'Identifier' && decl.id.name === name) {
301
- declaration_node = node;
302
- break;
303
- }
304
- }
305
- if (declaration_node)
306
- break;
307
- }
308
- if (!declaration_node)
309
- continue;
310
- // Only handle single-declarator statements
311
- if (declaration_node.declarations.length !== 1)
312
- continue;
313
- // Check if the identifier is referenced anywhere else
314
- const id_skip = new Set([...skip, declaration_node]);
315
- // Check instance script (excluding the declaration itself)
316
- if (has_identifier_in_tree(instance.content, name, id_skip))
317
- continue;
318
- // Check module script
319
- if (ast.module?.content && has_identifier_in_tree(ast.module.content, name))
320
- continue;
321
- // Check template — skip transformed component nodes whose attribute expressions
322
- // still contain the old identifier references in the AST
323
- if (has_identifier_in_tree(ast.fragment, name, id_skip))
324
- continue;
325
- remove_variable_declaration(s, declaration_node, source);
326
- // Add to skip set so chained dead const checks don't find references to this removed node
327
- skip.add(declaration_node);
328
- }
329
- };
330
- /**
331
- * Builds the replacement string for a transformed `Mdz` component.
332
- *
333
- * Reconstructs the opening tag as `<MdzPrecompiled` with all attributes except `content`,
334
- * using source text slicing to preserve exact formatting and dynamic expressions.
335
- */
336
- const build_replacement = (node, exclude_attrs, children_markup, source) => {
337
- // Collect source ranges of all attributes except excluded ones (content, base when resolved)
338
- const other_attr_ranges = [];
339
- for (const attr of node.attributes) {
340
- if (exclude_attrs.has(attr))
341
- continue;
342
- other_attr_ranges.push({ start: attr.start, end: attr.end });
343
- }
344
- // Build opening tag with MdzPrecompiled name
345
- let opening = `<${PRECOMPILED_NAME}`;
346
- for (const range of other_attr_ranges) {
347
- opening += ' ' + source.slice(range.start, range.end);
348
- }
349
- opening += '>';
350
- return `${opening}${children_markup}</${PRECOMPILED_NAME}>`;
351
- };
352
- /**
353
- * Determines which `Mdz` import declarations can be safely removed or trimmed.
354
- *
355
- * An import is removable when:
356
- * 1. All template usages of that name were successfully transformed.
357
- * 2. The identifier is not referenced elsewhere in script or template expressions.
358
- *
359
- * For multi-specifier imports, only the `Mdz` specifier is removed (partial removal).
360
- * For single-specifier imports, the entire declaration is removed.
361
- */
362
- const find_removable_mdz_imports = (ast, mdz_names, total_usages, transformed_usages) => {
363
- const removable = new Map();
364
- for (const [name, { import_node, specifier }] of mdz_names) {
365
- const total = total_usages.get(name) ?? 0;
366
- const transformed = transformed_usages.get(name) ?? 0;
367
- // Only remove if ALL template usages were transformed
368
- if (total === 0 || transformed < total)
369
- continue;
370
- // Check if identifier is referenced elsewhere in the AST
371
- const skip = new Set([import_node]);
372
- // Check instance script body (excluding the import itself)
373
- if (ast.instance?.content && has_identifier_in_tree(ast.instance.content, name, skip)) {
374
- continue;
375
- }
376
- // Check module script body (excluding the import itself)
377
- if (ast.module?.content && has_identifier_in_tree(ast.module.content, name, skip)) {
378
- continue;
379
- }
380
- // Check template for expression references (Component.name is a string, not Identifier)
381
- if (has_identifier_in_tree(ast.fragment, name)) {
382
- continue;
383
- }
384
- const positioned_node = import_node;
385
- if (import_node.specifiers.length === 1) {
386
- removable.set(name, { node: positioned_node, kind: 'full' });
387
- }
388
- else {
389
- removable.set(name, {
390
- node: positioned_node,
391
- kind: 'partial',
392
- specifier_to_remove: specifier,
393
- });
394
- }
395
- }
396
- return removable;
397
- };
398
- /**
399
- * Manages import additions and removals.
400
- *
401
- * Adds the `MdzPrecompiled` import and other required imports (DocsLink, Code, resolve).
402
- * Removes `Mdz` import declarations that are no longer referenced.
403
- *
404
- * Handles both full removal (single-specifier imports) and partial removal
405
- * (multi-specifier imports where only the `Mdz` specifier is removed).
406
- *
407
- * To avoid MagicString boundary conflicts when the insertion position falls inside
408
- * a removal range, one removable `Mdz` import is overwritten with the `MdzPrecompiled`
409
- * import line instead of using separate remove + appendLeft.
410
- */
411
- const manage_imports = (s, ast, transformations, removable_imports, compiled_component_import, source) => {
412
- // Collect all required imports across transformations
413
- const required = new Map();
414
- for (const t of transformations) {
415
- for (const [name, info] of t.required_imports) {
416
- required.set(name, info);
417
- }
418
- }
419
- // Always need MdzPrecompiled when transformations occur
420
- required.set(PRECOMPILED_NAME, { path: compiled_component_import, kind: 'default' });
421
- const script = ast.instance;
422
- // Separate full removals from partial removals
423
- const full_removals = new Set();
424
- const partial_removals = [];
425
- for (const [, action] of removable_imports) {
426
- if (action.kind === 'full') {
427
- full_removals.add(action.node);
428
- }
429
- else {
430
- partial_removals.push(action);
431
- }
432
- }
433
- if (!script) {
434
- // No instance script — removable_imports won't apply (imports are in module script if any)
435
- // Just add all required imports
436
- const lines = generate_import_lines(required);
437
- if (ast.module) {
438
- s.appendLeft(ast.module.end, `\n\n<script lang="ts">\n${lines}\n</script>`);
439
- }
440
- else {
441
- s.prepend(`<script lang="ts">\n${lines}\n</script>\n\n`);
442
- }
443
- // Remove Mdz imports from module script if removable
444
- for (const node of full_removals) {
445
- remove_import_declaration(s, node, source);
446
- }
447
- // Apply partial removals
448
- for (const action of partial_removals) {
449
- remove_import_specifier(s, action.node, action.specifier_to_remove, source);
450
- }
451
- return;
452
- }
453
- // Check existing imports to avoid duplicates — tracks both name AND source path
454
- const existing = new Map();
455
- for (const node of script.content.body) {
456
- if (node.type === 'ImportDeclaration') {
457
- const source_path = node.source.value;
458
- for (const spec of node.specifiers) {
459
- existing.set(spec.local.name, source_path);
460
- }
461
- }
462
- }
463
- const to_add = new Map();
464
- for (const [name, info] of required) {
465
- const existing_path = existing.get(name);
466
- if (existing_path === undefined) {
467
- to_add.set(name, info);
468
- }
469
- }
470
- // Strategy: if we're both adding MdzPrecompiled and removing an Mdz import,
471
- // overwrite one full-removable import with the MdzPrecompiled line to avoid
472
- // MagicString boundary conflicts. Other imports use normal appendLeft.
473
- let overwrite_target = null;
474
- if (to_add.has(PRECOMPILED_NAME) && full_removals.size > 0) {
475
- // Pick the first removable import to overwrite
476
- overwrite_target = full_removals.values().next().value ?? null;
477
- }
478
- // Generate the MdzPrecompiled import line separately if using overwrite
479
- if (overwrite_target) {
480
- const node_start = overwrite_target.start;
481
- const node_end = overwrite_target.end;
482
- // Find the start of the line (consume leading whitespace)
483
- let line_start = node_start;
484
- while (line_start > 0 && (source[line_start - 1] === '\t' || source[line_start - 1] === ' ')) {
485
- line_start--;
486
- }
487
- // Extract indentation from the original import line rather than hardcoding a tab.
488
- // The import node's start is after any leading whitespace, so the indentation is
489
- // the characters between the line start and the node start.
490
- const original_indent = source.slice(line_start, node_start);
491
- const precompiled_line = `${original_indent}import ${PRECOMPILED_NAME} from '${compiled_component_import}';`;
492
- s.overwrite(line_start, node_end, precompiled_line);
493
- to_add.delete(PRECOMPILED_NAME);
494
- }
495
- // When there are partial removals but no full-removal overwrite target,
496
- // the appendLeft at find_import_insert_position can conflict with the
497
- // partial removal's overwrite (they share the same node.end boundary).
498
- // Bundle the new imports into the first partial removal's overwrite instead.
499
- const insert_pos = find_import_insert_position(script);
500
- let partial_carrier = null;
501
- if (to_add.size > 0 && partial_removals.length > 0) {
502
- for (const action of partial_removals) {
503
- if (action.node.end === insert_pos) {
504
- partial_carrier = action;
505
- break;
506
- }
507
- }
508
- }
509
- // Add remaining imports — either bundled with a partial removal or via appendLeft
510
- let carrier_lines = '';
511
- if (partial_carrier && to_add.size > 0) {
512
- carrier_lines = '\n' + generate_import_lines(to_add);
513
- to_add.clear();
514
- }
515
- if (to_add.size > 0) {
516
- const lines = generate_import_lines(to_add);
517
- s.appendLeft(insert_pos, '\n' + lines);
518
- }
519
- // Remove remaining full Mdz imports (skip the overwrite target)
520
- for (const node of full_removals) {
521
- if (node === overwrite_target)
522
- continue;
523
- remove_import_declaration(s, node, source);
524
- }
525
- // Apply partial import removals
526
- for (const action of partial_removals) {
527
- remove_import_specifier(s, action.node, action.specifier_to_remove, source, action === partial_carrier ? carrier_lines : '');
528
- }
529
- };
@@ -1,45 +0,0 @@
1
- /**
2
- * Bridge between TSDoc format and mdz for rendering.
3
- *
4
- * This module converts raw TSDoc syntax (from the analysis library) to mdz format
5
- * (Fuz's documentation rendering dialect). It lives in fuz_ui, not the extracted
6
- * analysis library, to keep that library format-agnostic.
7
- *
8
- * @module
9
- */
10
- /**
11
- * Convert raw TSDoc `@see` content to mdz format for rendering.
12
- *
13
- * Handles TSDoc link syntax:
14
- * - `{@link url|text}` → `[text](url)` (markdown link, TSDoc canonical form)
15
- * - `{@link url text}` → `[text](url)` (TS-lenient space-separated form)
16
- * - `{@link https://...}` → `https://...` (bare URL, auto-linked by mdz)
17
- * - `{@link identifier}` → `` `identifier` `` (code formatting)
18
- * - Bare URLs → returned as-is
19
- * - Bare markdown links (`[text](url)` ...) → returned as-is
20
- * - Bare identifiers → wrapped in backticks
21
- * - `identifier description text` → `` `identifier` description text `` (first token is the reference)
22
- *
23
- * @param content - raw `@see` tag content in TSDoc format
24
- * @returns mdz-formatted string ready for `Mdz` component
25
- *
26
- * @example
27
- * ```ts
28
- * tsdoc_see_to_mdz('{@link https://fuz.dev|API Docs}')
29
- * // → '[API Docs](https://fuz.dev)'
30
- *
31
- * tsdoc_see_to_mdz('{@link SomeType}')
32
- * // → '`SomeType`'
33
- *
34
- * tsdoc_see_to_mdz('https://fuz.dev')
35
- * // → 'https://fuz.dev'
36
- *
37
- * tsdoc_see_to_mdz('[svelte-docinfo](https://github.com/ryanatkn/svelte-docinfo) for the analysis library')
38
- * // → '[svelte-docinfo](https://github.com/ryanatkn/svelte-docinfo) for the analysis library'
39
- *
40
- * tsdoc_see_to_mdz('tome.ts for the documentation system')
41
- * // → '`tome.ts` for the documentation system'
42
- * ```
43
- */
44
- export declare const tsdoc_see_to_mdz: (content: string) => string;
45
- //# sourceMappingURL=tsdoc_mdz.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tsdoc_mdz.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/tsdoc_mdz.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,eAAO,MAAM,gBAAgB,GAAI,SAAS,MAAM,KAAG,MA+ClD,CAAC"}
package/dist/tsdoc_mdz.js DELETED
@@ -1,88 +0,0 @@
1
- /**
2
- * Bridge between TSDoc format and mdz for rendering.
3
- *
4
- * This module converts raw TSDoc syntax (from the analysis library) to mdz format
5
- * (Fuz's documentation rendering dialect). It lives in fuz_ui, not the extracted
6
- * analysis library, to keep that library format-agnostic.
7
- *
8
- * @module
9
- */
10
- import { mdz_is_url } from './mdz_helpers.js';
11
- /** Format a reference as mdz: URLs pass through, identifiers get backticks. */
12
- const format_reference = (ref) => (mdz_is_url(ref) ? ref : `\`${ref}\``);
13
- /**
14
- * Convert raw TSDoc `@see` content to mdz format for rendering.
15
- *
16
- * Handles TSDoc link syntax:
17
- * - `{@link url|text}` → `[text](url)` (markdown link, TSDoc canonical form)
18
- * - `{@link url text}` → `[text](url)` (TS-lenient space-separated form)
19
- * - `{@link https://...}` → `https://...` (bare URL, auto-linked by mdz)
20
- * - `{@link identifier}` → `` `identifier` `` (code formatting)
21
- * - Bare URLs → returned as-is
22
- * - Bare markdown links (`[text](url)` ...) → returned as-is
23
- * - Bare identifiers → wrapped in backticks
24
- * - `identifier description text` → `` `identifier` description text `` (first token is the reference)
25
- *
26
- * @param content - raw `@see` tag content in TSDoc format
27
- * @returns mdz-formatted string ready for `Mdz` component
28
- *
29
- * @example
30
- * ```ts
31
- * tsdoc_see_to_mdz('{@link https://fuz.dev|API Docs}')
32
- * // → '[API Docs](https://fuz.dev)'
33
- *
34
- * tsdoc_see_to_mdz('{@link SomeType}')
35
- * // → '`SomeType`'
36
- *
37
- * tsdoc_see_to_mdz('https://fuz.dev')
38
- * // → 'https://fuz.dev'
39
- *
40
- * tsdoc_see_to_mdz('[svelte-docinfo](https://github.com/ryanatkn/svelte-docinfo) for the analysis library')
41
- * // → '[svelte-docinfo](https://github.com/ryanatkn/svelte-docinfo) for the analysis library'
42
- *
43
- * tsdoc_see_to_mdz('tome.ts for the documentation system')
44
- * // → '`tome.ts` for the documentation system'
45
- * ```
46
- */
47
- export const tsdoc_see_to_mdz = (content) => {
48
- const trimmed = content.trim();
49
- if (!trimmed)
50
- return '';
51
- // Check for {@link ...} or {@see ...} syntax
52
- const link_match = /^\{@(?:link|see)\s+([^}]+)\}$/.exec(trimmed);
53
- if (link_match) {
54
- const inner = link_match[1].trim();
55
- // Pipe separator takes precedence (TSDoc canonical form)
56
- const pipe_index = inner.indexOf('|');
57
- if (pipe_index !== -1) {
58
- const reference = inner.slice(0, pipe_index).trim();
59
- const display_text = inner.slice(pipe_index + 1).trim();
60
- return `[${display_text}](${reference})`;
61
- }
62
- // Space-separated form: TS accepts `{@link url text}` as equivalent to `{@link url|text}`.
63
- // Only treat space as a separator when the first token looks like a link target (URL),
64
- // so identifier-style references like `module.function` aren't split.
65
- const space_index = inner.indexOf(' ');
66
- if (space_index !== -1) {
67
- const reference = inner.slice(0, space_index);
68
- if (mdz_is_url(reference)) {
69
- const display_text = inner.slice(space_index + 1).trim();
70
- return `[${display_text}](${reference})`;
71
- }
72
- }
73
- return format_reference(inner);
74
- }
75
- // Pass through bare markdown links (`[text](url)` optionally followed by description)
76
- // so authors can write `@see [text](url) for context` directly.
77
- if (trimmed.charCodeAt(0) === 91 /* [ */ && /^\[[^\]]+\]\([^)\s]+\)/.test(trimmed)) {
78
- return trimmed;
79
- }
80
- // Split at first whitespace: first token is the reference, rest is description
81
- const space_index = trimmed.indexOf(' ');
82
- if (space_index === -1) {
83
- return format_reference(trimmed);
84
- }
85
- const reference = trimmed.slice(0, space_index);
86
- const description = trimmed.slice(space_index); // preserve leading space
87
- return format_reference(reference) + description;
88
- };