@markuplint/svelte-parser 5.0.0-rc.2 → 5.0.0-rc.5

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.
@@ -1,7 +1,4 @@
1
1
  import { parser } from './parser.js';
2
- /**
3
- * Extracts root element information from a parsed MLAST document.
4
- */
5
2
  function extractComponentInfo(doc) {
6
3
  const root = doc.nodeList.find((n) => n.type === 'starttag' && n.depth === 0 && !n.isFragment);
7
4
  if (!root) {
@@ -29,9 +26,8 @@ function extractComponentInfo(doc) {
29
26
  };
30
27
  }
31
28
  /**
32
- * Detects whether the parsed Svelte template contains slot usage.
33
- *
34
- * Supports:
29
+ * Slot usage can take several forms whose mapping to psblock node names is
30
+ * not derivable from this code:
35
31
  * - Svelte 4: `<slot>` element (parsed as psblock `#ps:SlotElement`)
36
32
  * - Svelte 5: `{@render children()}` (parsed as psblock `#ps:RenderTag`)
37
33
  * - Standard `<slot>` elements
@@ -41,7 +37,6 @@ function detectSlots(doc) {
41
37
  (n.type === 'psblock' && (n.nodeName === '#ps:SlotElement' || n.nodeName === '#ps:RenderTag')));
42
38
  }
43
39
  /**
44
- * Extracts the instance `<script>` block from a Svelte component source.
45
40
  * Prefers the instance script over `<script context="module">`.
46
41
  */
47
42
  function extractSvelteScript(source) {
@@ -63,9 +58,8 @@ function extractSvelteScript(source) {
63
58
  offset: contentStart,
64
59
  };
65
60
  if (!isModule) {
66
- return block; // Prefer instance script
61
+ return block;
67
62
  }
68
- // Remember module script as fallback
69
63
  moduleBlock ??= block;
70
64
  }
71
65
  return moduleBlock;
@@ -1,17 +1,6 @@
1
1
  import type { SvelteParser } from './parser.js';
2
2
  import type { ChildToken, Token } from '@markuplint/parser-utils';
3
3
  import type { SvelteBlock } from './svelte-parser/index.js';
4
- /**
5
- * Extracts the open and close tag tokens from a Svelte block construct
6
- * (e.g., `{#each}...{/each}`, `{#key}...{/key}`).
7
- * Locates the closing `{/xxx}` tag via regex and computes the opening token
8
- * based on the block's child fragment boundaries.
9
- *
10
- * @param parser - The SvelteParser instance used to slice source fragments
11
- * @param token - The child token representing the entire block range
12
- * @param originBlockNode - The Svelte AST block node being parsed
13
- * @returns An object containing the `openToken` and `closeToken` for the block
14
- */
15
4
  export declare function parseBlock(parser: SvelteParser, token: ChildToken, originBlockNode: SvelteBlock): {
16
5
  openToken: Token;
17
6
  closeToken: Token;
@@ -1,14 +1,3 @@
1
- /**
2
- * Extracts the open and close tag tokens from a Svelte block construct
3
- * (e.g., `{#each}...{/each}`, `{#key}...{/key}`).
4
- * Locates the closing `{/xxx}` tag via regex and computes the opening token
5
- * based on the block's child fragment boundaries.
6
- *
7
- * @param parser - The SvelteParser instance used to slice source fragments
8
- * @param token - The child token representing the entire block range
9
- * @param originBlockNode - The Svelte AST block node being parsed
10
- * @returns An object containing the `openToken` and `closeToken` for the block
11
- */
12
1
  export function parseBlock(
13
2
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
14
3
  parser, token,
@@ -16,6 +5,9 @@ parser, token,
16
5
  originBlockNode) {
17
6
  const range = token.raw;
18
7
  /**
8
+ * The close tag is the final `{/xxx}`; intermediate `{:xxx}` clauses
9
+ * (e.g. `{:else}`, `{:then}`) must not be matched, hence the `$` anchor.
10
+ *
19
11
  * `{#xxx}...{:xxx}...{/xxx}`
20
12
  * find___^
21
13
  */
@@ -24,9 +16,6 @@ originBlockNode) {
24
16
  if (eachCloseStartIndex == null) {
25
17
  throw new SyntaxError('Block close tag not found');
26
18
  }
27
- /**
28
- * `{/xxx}`
29
- */
30
19
  const closeToken = parser.sliceFragment(token.offset + eachCloseStartIndex, originBlockNode.end);
31
20
  const fragment = originBlockNode.type === 'IfBlock'
32
21
  ? originBlockNode.consequent.nodes
package/lib/parser.d.ts CHANGED
@@ -18,35 +18,11 @@ export declare class SvelteParser extends Parser<SvelteNode> {
18
18
  };
19
19
  parse(raw: string, options?: ParseOptions): import("@markuplint/ml-ast").MLASTDocument;
20
20
  parseError(error: any): ParserError;
21
- /**
22
- * Converts a Svelte AST node into markuplint node tree items.
23
- * Dispatches on the node type to handle Text, Comment, ExpressionTag,
24
- * elements (Component, RegularElement), and control flow blocks
25
- * (IfBlock, EachBlock, AwaitBlock, KeyBlock, SnippetBlock).
26
- *
27
- * @param originNode - The Svelte AST node to convert
28
- * @param parentNode - The parent node in the markuplint tree, or null for root nodes
29
- * @param depth - The nesting depth of the node
30
- * @returns An array of markuplint node tree items
31
- */
32
21
  nodeize(originNode: SvelteNode, parentNode: MLASTParentNode | null, depth: number): readonly MLASTNodeTreeItem[];
33
- /**
34
- * Visits a text token, converting `<script>` tags embedded in Svelte template
35
- * text into preprocessor-specific blocks rather than treating them as raw text.
36
- *
37
- * @param token - The child token representing the text content
38
- * @returns An array of markuplint node tree items
39
- */
40
22
  visitText(token: ChildToken): readonly MLASTNodeTreeItem[];
41
23
  /**
42
- * Visits a preprocessor-specific block token and enforces that exactly one
43
- * block node is produced. Throws a ParserError if the result is empty
44
- * or contains multiple nodes.
45
- *
46
- * @param token - The child token with node name and fragment flag
47
- * @param childNodes - The child Svelte AST nodes within the block
48
- * @param blockBehavior - The block behavior, or null
49
- * @returns A single-element tuple containing the preprocessor-specific block
24
+ * Invariant: exactly one block node must be produced; an empty or
25
+ * multi-node result indicates a parse error.
50
26
  */
51
27
  visitPsBlock(token: ChildToken & {
52
28
  readonly nodeName: string;
@@ -68,6 +44,11 @@ export declare class SvelteParser extends Parser<SvelteNode> {
68
44
  * Directive resolution (`bind:`, `class:`, `on:`, etc.) and IDL attribute
69
45
  * mapping are now handled declaratively by svelte-spec's directivePatterns
70
46
  * and ml-core's acceptedAttrNames.
47
+ * Because directivePatterns are applied later by ml-core's MLAttr
48
+ * constructor, the attribute returned here carries only parser-detectable
49
+ * flags; for example, `on:click` without a value shows
50
+ * `isDynamicValue: false` at the parser level but resolves to `true`
51
+ * at the core level.
71
52
  *
72
53
  * @param token - The token representing the attribute
73
54
  * @returns The parsed attribute node with Svelte-specific metadata
package/lib/parser.js CHANGED
@@ -47,17 +47,6 @@ export class SvelteParser extends Parser {
47
47
  }
48
48
  return super.parseError(error);
49
49
  }
50
- /**
51
- * Converts a Svelte AST node into markuplint node tree items.
52
- * Dispatches on the node type to handle Text, Comment, ExpressionTag,
53
- * elements (Component, RegularElement), and control flow blocks
54
- * (IfBlock, EachBlock, AwaitBlock, KeyBlock, SnippetBlock).
55
- *
56
- * @param originNode - The Svelte AST node to convert
57
- * @param parentNode - The parent node in the markuplint tree, or null for root nodes
58
- * @param depth - The nesting depth of the node
59
- * @returns An array of markuplint node tree items
60
- */
61
50
  nodeize(
62
51
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
63
52
  originNode, parentNode, depth) {
@@ -215,13 +204,6 @@ export class SvelteParser extends Parser {
215
204
  }
216
205
  }
217
206
  }
218
- /**
219
- * Visits a text token, converting `<script>` tags embedded in Svelte template
220
- * text into preprocessor-specific blocks rather than treating them as raw text.
221
- *
222
- * @param token - The child token representing the text content
223
- * @returns An array of markuplint node tree items
224
- */
225
207
  visitText(token) {
226
208
  const nodes = super.visitText(token, {
227
209
  researchTags: false,
@@ -240,14 +222,8 @@ export class SvelteParser extends Parser {
240
222
  });
241
223
  }
242
224
  /**
243
- * Visits a preprocessor-specific block token and enforces that exactly one
244
- * block node is produced. Throws a ParserError if the result is empty
245
- * or contains multiple nodes.
246
- *
247
- * @param token - The child token with node name and fragment flag
248
- * @param childNodes - The child Svelte AST nodes within the block
249
- * @param blockBehavior - The block behavior, or null
250
- * @returns A single-element tuple containing the preprocessor-specific block
225
+ * Invariant: exactly one block node must be produced; an empty or
226
+ * multi-node result indicates a parse error.
251
227
  */
252
228
  visitPsBlock(token, childNodes = [], blockBehavior = null) {
253
229
  const nodes = super.visitPsBlock(token, childNodes, blockBehavior);
@@ -284,6 +260,11 @@ export class SvelteParser extends Parser {
284
260
  * Directive resolution (`bind:`, `class:`, `on:`, etc.) and IDL attribute
285
261
  * mapping are now handled declaratively by svelte-spec's directivePatterns
286
262
  * and ml-core's acceptedAttrNames.
263
+ * Because directivePatterns are applied later by ml-core's MLAttr
264
+ * constructor, the attribute returned here carries only parser-detectable
265
+ * flags; for example, `on:click` without a value shows
266
+ * `isDynamicValue: false` at the parser level but resolves to `true`
267
+ * at the core level.
287
268
  *
288
269
  * @param token - The token representing the attribute
289
270
  * @returns The parsed attribute node with Svelte-specific metadata
@@ -329,15 +310,6 @@ export class SvelteParser extends Parser {
329
310
  detectElementType(nodeName) {
330
311
  return super.detectElementType(nodeName, /^[A-Z]|\./);
331
312
  }
332
- /**
333
- * Parses a Svelte `{#await}` block into its constituent preprocessor-specific blocks:
334
- * the await expression, optional `{:then}` branch, optional `{:catch}` branch,
335
- * and the closing `{/await}` tag.
336
- *
337
- * @param token - The child token representing the entire await block
338
- * @param originBlockNode - The Svelte AST AwaitBlock node
339
- * @returns An array of preprocessor-specific block nodes
340
- */
341
313
  #parseAwaitBlock(token,
342
314
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
343
315
  originBlockNode) {
@@ -473,15 +445,6 @@ export class SvelteParser extends Parser {
473
445
  })[0]);
474
446
  return expressions;
475
447
  }
476
- /**
477
- * Parses a Svelte `{#each}` block into its constituent preprocessor-specific blocks:
478
- * the each expression, optional `{:else}` fallback branch,
479
- * and the closing `{/each}` tag.
480
- *
481
- * @param token - The child token representing the entire each block
482
- * @param originBlockNode - The Svelte AST EachBlock node
483
- * @returns An array of preprocessor-specific block nodes
484
- */
485
448
  #parseEachBlock(token,
486
449
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
487
450
  originBlockNode) {
@@ -540,16 +503,6 @@ export class SvelteParser extends Parser {
540
503
  }, undefined, { type: 'end', expression: closeToken.raw })[0]);
541
504
  return expressions;
542
505
  }
543
- /**
544
- * Recursively traverses a Svelte `{#if}` block and its chained `{:else if}` / `{:else}`
545
- * branches, producing a flat list of token segments with their conditional type labels
546
- * and child node arrays.
547
- *
548
- * @param originBlockNode - The Svelte AST IfBlock node to traverse
549
- * @param start - The source offset where this block segment begins
550
- * @param type - The conditional branch type: 'if', 'elseif', or 'else'
551
- * @returns A flat array of token segments with children and type labels
552
- */
553
506
  #traverseIfBlock(
554
507
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
555
508
  originBlockNode, start, type = 'if') {
@@ -578,6 +531,9 @@ export class SvelteParser extends Parser {
578
531
  const start = result.at(-1)?.children.at(-1)?.end ?? originBlockNode.end;
579
532
  const end = originBlockNode.end;
580
533
  const tag = this.sliceFragment(start, end);
534
+ // The raw content is empty in recursive `elseif` calls because the
535
+ // outermost call emits the single `{/if}` closer; pushing only
536
+ // non-empty tags avoids duplicating the closer.
581
537
  if (tag.raw) {
582
538
  result.push({ ...tag, children: [], type: '/if' });
583
539
  }
@@ -1,23 +1,15 @@
1
1
  import type { AST } from 'svelte/compiler';
2
- /** Union of Svelte AST node types that can appear as children in a Svelte template fragment. */
3
2
  export type SvelteNode = AST.Text | AST.Comment | AST.Tag | AST.ElementLike | AST.Block;
4
- /** Represents a Svelte `{#if}` block with consequent, alternate, and elseif branches. */
5
3
  export type SvelteIfBlock = AST.IfBlock;
6
- /** Represents a Svelte `{#each}` block with iteration body and optional fallback. */
7
4
  export type SvelteEachBlock = AST.EachBlock;
8
- /** Represents a Svelte `{#await}` block with pending, then, and catch branches. */
9
5
  export type SvelteAwaitBlock = AST.AwaitBlock;
10
6
  /**
11
- * Parses a Svelte template string into an array of top-level AST nodes
12
- * using the Svelte compiler's modern parser mode.
13
- *
14
- * @param template - The raw Svelte template source code
15
- * @returns An array of top-level Svelte AST nodes from the template fragment
7
+ * The `modern: true` option opts into Svelte 5's modern AST format,
8
+ * which is required to receive Svelte 5 node types such as
9
+ * `SnippetBlock`, `RenderTag`, and `SvelteBoundary`.
16
10
  */
17
11
  export declare function svelteParse(template: string): SvelteNode[];
18
- /** Union of all Svelte directive and attribute types that can appear on elements. */
19
12
  export type SvelteDirective = Directive | AST.Attribute | AST.SpreadAttribute;
20
- /** Union of all Svelte block types that have opening/closing tag syntax. */
21
13
  export type SvelteBlock = AST.EachBlock | AST.IfBlock | AST.AwaitBlock | AST.KeyBlock | AST.SnippetBlock | AST.SvelteBoundary;
22
14
  type Directive = AST.AnimateDirective | AST.BindDirective | AST.ClassDirective | AST.LetDirective | AST.OnDirective | AST.StyleDirective | AST.TransitionDirective | AST.UseDirective;
23
15
  export {};
@@ -1,10 +1,8 @@
1
1
  import { parse } from 'svelte/compiler';
2
2
  /**
3
- * Parses a Svelte template string into an array of top-level AST nodes
4
- * using the Svelte compiler's modern parser mode.
5
- *
6
- * @param template - The raw Svelte template source code
7
- * @returns An array of top-level Svelte AST nodes from the template fragment
3
+ * The `modern: true` option opts into Svelte 5's modern AST format,
4
+ * which is required to receive Svelte 5 node types such as
5
+ * `SnippetBlock`, `RenderTag`, and `SvelteBoundary`.
8
6
  */
9
7
  export function svelteParse(template) {
10
8
  const ast = parse(template, { modern: true });
@@ -4,6 +4,11 @@ import { HtmlParser } from '@markuplint/html-parser';
4
4
  * Extends the standard HTML parser to handle SvelteKit placeholder tags
5
5
  * such as `%sveltekit.head%` and `%sveltekit.body%`, which are treated
6
6
  * as opaque preprocessor-specific blocks.
7
+ *
8
+ * Unlike `SvelteParser`, the app template is plain HTML whose `%sveltekit.*%`
9
+ * placeholders are replaced by SvelteKit at build time, so the template engine
10
+ * parser pattern (extending `HtmlParser` with `ignoreTags`) is the correct
11
+ * architectural choice and `svelte/compiler` is intentionally not involved.
7
12
  */
8
13
  declare class SvelteKitTemplateParser extends HtmlParser {
9
14
  constructor();
@@ -4,6 +4,11 @@ import { HtmlParser } from '@markuplint/html-parser';
4
4
  * Extends the standard HTML parser to handle SvelteKit placeholder tags
5
5
  * such as `%sveltekit.head%` and `%sveltekit.body%`, which are treated
6
6
  * as opaque preprocessor-specific blocks.
7
+ *
8
+ * Unlike `SvelteParser`, the app template is plain HTML whose `%sveltekit.*%`
9
+ * placeholders are replaced by SvelteKit at build time, so the template engine
10
+ * parser pattern (extending `HtmlParser` with `ignoreTags`) is the correct
11
+ * architectural choice and `svelte/compiler` is intentionally not involved.
7
12
  */
8
13
  class SvelteKitTemplateParser extends HtmlParser {
9
14
  constructor() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markuplint/svelte-parser",
3
- "version": "5.0.0-rc.2",
3
+ "version": "5.0.0-rc.5",
4
4
  "description": "Svelte parser for markuplint",
5
5
  "repository": {
6
6
  "type": "git",
@@ -10,7 +10,7 @@
10
10
  "author": "Yusuke Hirao <yusukehirao@me.com>",
11
11
  "license": "MIT",
12
12
  "engines": {
13
- "node": ">=22"
13
+ "node": ">=24"
14
14
  },
15
15
  "type": "module",
16
16
  "exports": {
@@ -40,10 +40,10 @@
40
40
  "clean": "tsc --build --clean tsconfig.build.json"
41
41
  },
42
42
  "dependencies": {
43
- "@markuplint/html-parser": "5.0.0-rc.2",
44
- "@markuplint/ml-ast": "5.0.0-rc.2",
45
- "@markuplint/parser-utils": "5.0.0-rc.2",
46
- "svelte": "5.55.4"
43
+ "@markuplint/html-parser": "5.0.0-rc.5",
44
+ "@markuplint/ml-ast": "5.0.0-rc.5",
45
+ "@markuplint/parser-utils": "5.0.0-rc.5",
46
+ "svelte": "5.55.7"
47
47
  },
48
- "gitHead": "e43763858d9234c417053becc73dbd088c1e7ea6"
48
+ "gitHead": "8d87463af2ff3f1b83fb28da20f1819362cf3555"
49
49
  }