@markuplint/svelte-parser 4.7.12 → 5.0.0-alpha.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/lib/index.d.ts +7 -0
- package/lib/index.js +7 -0
- package/lib/parse-block.d.ts +11 -0
- package/lib/parse-block.js +14 -3
- package/lib/parser.d.ts +61 -11
- package/lib/parser.js +303 -232
- package/lib/svelte-parser/index.d.ts +13 -0
- package/lib/svelte-parser/index.js +7 -0
- package/lib/sveltekit-parser.d.ts +6 -0
- package/lib/sveltekit-parser.js +6 -0
- package/package.json +9 -6
package/lib/index.d.ts
CHANGED
|
@@ -1 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* Svelte component parser for markuplint. Provides a parser that transforms Svelte
|
|
4
|
+
* template syntax into markuplint's AST, supporting Svelte-specific constructs such as
|
|
5
|
+
* `{#if}`, `{#each}`, `{#await}`, `{#key}`, `{#snippet}`, expression tags,
|
|
6
|
+
* and bind/class/event directives.
|
|
7
|
+
*/
|
|
1
8
|
export { parser } from './parser.js';
|
package/lib/index.js
CHANGED
|
@@ -1 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* Svelte component parser for markuplint. Provides a parser that transforms Svelte
|
|
4
|
+
* template syntax into markuplint's AST, supporting Svelte-specific constructs such as
|
|
5
|
+
* `{#if}`, `{#each}`, `{#await}`, `{#key}`, `{#snippet}`, expression tags,
|
|
6
|
+
* and bind/class/event directives.
|
|
7
|
+
*/
|
|
1
8
|
export { parser } from './parser.js';
|
package/lib/parse-block.d.ts
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
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
|
+
*/
|
|
4
15
|
export declare function parseBlock(parser: SvelteParser, token: ChildToken, originBlockNode: SvelteBlock): {
|
|
5
16
|
openToken: Token;
|
|
6
17
|
closeToken: Token;
|
package/lib/parse-block.js
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
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
|
+
*/
|
|
1
12
|
export function parseBlock(
|
|
2
13
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
3
14
|
parser, token,
|
|
@@ -16,7 +27,7 @@ originBlockNode) {
|
|
|
16
27
|
/**
|
|
17
28
|
* `{/xxx}`
|
|
18
29
|
*/
|
|
19
|
-
const closeToken = parser.sliceFragment(token.
|
|
30
|
+
const closeToken = parser.sliceFragment(token.offset + eachCloseStartIndex, originBlockNode.end);
|
|
20
31
|
const fragment = originBlockNode.type === 'IfBlock'
|
|
21
32
|
? originBlockNode.consequent.nodes
|
|
22
33
|
: originBlockNode.type === 'AwaitBlock'
|
|
@@ -37,10 +48,10 @@ originBlockNode) {
|
|
|
37
48
|
*/
|
|
38
49
|
let openToken;
|
|
39
50
|
if (fragStart != null && fragEnd != null) {
|
|
40
|
-
openToken = parser.sliceFragment(token.
|
|
51
|
+
openToken = parser.sliceFragment(token.offset, fragStart);
|
|
41
52
|
}
|
|
42
53
|
else {
|
|
43
|
-
openToken = parser.sliceFragment(token.
|
|
54
|
+
openToken = parser.sliceFragment(token.offset, eachCloseStartIndex);
|
|
44
55
|
}
|
|
45
56
|
return {
|
|
46
57
|
openToken,
|
package/lib/parser.d.ts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import type { SvelteNode } from './svelte-parser/index.js';
|
|
2
|
-
import type { MLASTNodeTreeItem, MLASTParentNode, MLASTPreprocessorSpecificBlock,
|
|
2
|
+
import type { MLASTNodeTreeItem, MLASTParentNode, MLASTPreprocessorSpecificBlock, MLASTBlockBehavior } from '@markuplint/ml-ast';
|
|
3
3
|
import type { ChildToken, ParseOptions, Token } from '@markuplint/parser-utils';
|
|
4
4
|
import { ParserError, Parser } from '@markuplint/parser-utils';
|
|
5
|
+
/**
|
|
6
|
+
* Parser implementation for Svelte component templates.
|
|
7
|
+
* Extends the base Parser to handle Svelte elements, text, comments,
|
|
8
|
+
* expression tags, control flow blocks (`{#if}`, `{#each}`, `{#await}`,
|
|
9
|
+
* `{#key}`, `{#snippet}`), directives (`bind:`, `class:`, `on:`),
|
|
10
|
+
* and shorthand attribute syntax.
|
|
11
|
+
*/
|
|
5
12
|
export declare class SvelteParser extends Parser<SvelteNode> {
|
|
6
13
|
#private;
|
|
7
|
-
readonly specificBindDirective: ReadonlySet<string>;
|
|
8
14
|
constructor();
|
|
9
15
|
tokenize(): {
|
|
10
16
|
ast: SvelteNode[];
|
|
@@ -12,19 +18,64 @@ export declare class SvelteParser extends Parser<SvelteNode> {
|
|
|
12
18
|
};
|
|
13
19
|
parse(raw: string, options?: ParseOptions): import("@markuplint/ml-ast").MLASTDocument;
|
|
14
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
|
+
*/
|
|
15
32
|
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
|
+
*/
|
|
16
40
|
visitText(token: ChildToken): readonly MLASTNodeTreeItem[];
|
|
41
|
+
/**
|
|
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
|
|
50
|
+
*/
|
|
17
51
|
visitPsBlock(token: ChildToken & {
|
|
18
52
|
readonly nodeName: string;
|
|
19
53
|
readonly isFragment: boolean;
|
|
20
|
-
}, childNodes?: readonly SvelteNode[],
|
|
54
|
+
}, childNodes?: readonly SvelteNode[], blockBehavior?: MLASTBlockBehavior | null): readonly [MLASTPreprocessorSpecificBlock];
|
|
55
|
+
/**
|
|
56
|
+
* Visits child nodes and verifies that no sibling nodes with differing
|
|
57
|
+
* hierarchy levels are produced. Throws a ParserError if unexpected
|
|
58
|
+
* sibling nodes are discovered.
|
|
59
|
+
*
|
|
60
|
+
* @param children - The child Svelte AST nodes to visit
|
|
61
|
+
* @param parentNode - The parent node in the markuplint tree
|
|
62
|
+
* @returns An empty array (all children are attached via the visitor)
|
|
63
|
+
*/
|
|
21
64
|
visitChildren(children: readonly SvelteNode[], parentNode: MLASTParentNode | null): never[];
|
|
65
|
+
/**
|
|
66
|
+
* Visits an attribute token, handling Svelte-specific syntax including
|
|
67
|
+
* curly-brace expression values and shorthand attributes (`{name}`).
|
|
68
|
+
* Directive resolution (`bind:`, `class:`, `on:`, etc.) and IDL attribute
|
|
69
|
+
* mapping are now handled declaratively by svelte-spec's directivePatterns
|
|
70
|
+
* and ml-core's useIDLAttributeNames.
|
|
71
|
+
*
|
|
72
|
+
* @param token - The token representing the attribute
|
|
73
|
+
* @returns The parsed attribute node with Svelte-specific metadata
|
|
74
|
+
*/
|
|
22
75
|
visitAttr(token: Token): (import("@markuplint/ml-ast").MLASTSpreadAttr & {
|
|
23
76
|
__rightText?: string;
|
|
24
77
|
}) | {
|
|
25
78
|
isDynamicValue: true | undefined;
|
|
26
|
-
isDirective: true | undefined;
|
|
27
|
-
isDuplicatable: boolean;
|
|
28
79
|
potentialName: string | undefined;
|
|
29
80
|
type: "attr";
|
|
30
81
|
nodeName: string;
|
|
@@ -36,17 +87,16 @@ export declare class SvelteParser extends Parser<SvelteNode> {
|
|
|
36
87
|
startQuote: import("@markuplint/ml-ast").MLASTToken;
|
|
37
88
|
value: import("@markuplint/ml-ast").MLASTToken;
|
|
38
89
|
endQuote: import("@markuplint/ml-ast").MLASTToken;
|
|
90
|
+
isDirective?: true;
|
|
39
91
|
potentialValue?: string;
|
|
40
92
|
valueType?: "string" | "number" | "boolean" | "code";
|
|
41
93
|
candidate?: string;
|
|
94
|
+
isDuplicatable: boolean;
|
|
42
95
|
uuid: string;
|
|
43
96
|
raw: string;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
endLine: number;
|
|
48
|
-
startCol: number;
|
|
49
|
-
endCol: number;
|
|
97
|
+
offset: number;
|
|
98
|
+
line: number;
|
|
99
|
+
col: number;
|
|
50
100
|
__rightText?: string;
|
|
51
101
|
};
|
|
52
102
|
/**
|
package/lib/parser.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
2
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
3
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
4
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
|
-
};
|
|
6
|
-
var _SvelteParser_instances, _SvelteParser_parseAwaitBlock, _SvelteParser_parseEachBlock, _SvelteParser_traverseIfBlock;
|
|
7
|
-
import { getNamespace } from '@markuplint/html-parser';
|
|
8
1
|
import { ParserError, Parser, AttrState } from '@markuplint/parser-utils';
|
|
9
2
|
import { parseBlock } from './parse-block.js';
|
|
10
3
|
import { svelteParse } from './svelte-parser/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* Parser implementation for Svelte component templates.
|
|
6
|
+
* Extends the base Parser to handle Svelte elements, text, comments,
|
|
7
|
+
* expression tags, control flow blocks (`{#if}`, `{#each}`, `{#await}`,
|
|
8
|
+
* `{#key}`, `{#snippet}`), directives (`bind:`, `class:`, `on:`),
|
|
9
|
+
* and shorthand attribute syntax.
|
|
10
|
+
*/
|
|
11
11
|
export class SvelteParser extends Parser {
|
|
12
12
|
constructor() {
|
|
13
13
|
super({
|
|
@@ -26,8 +26,6 @@ export class SvelteParser extends Parser {
|
|
|
26
26
|
],
|
|
27
27
|
maskChar: '-',
|
|
28
28
|
});
|
|
29
|
-
_SvelteParser_instances.add(this);
|
|
30
|
-
this.specificBindDirective = new Set(['group', 'this']);
|
|
31
29
|
}
|
|
32
30
|
tokenize() {
|
|
33
31
|
return {
|
|
@@ -49,11 +47,21 @@ export class SvelteParser extends Parser {
|
|
|
49
47
|
}
|
|
50
48
|
return super.parseError(error);
|
|
51
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
|
+
*/
|
|
52
61
|
nodeize(
|
|
53
62
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
54
63
|
originNode, parentNode, depth) {
|
|
55
64
|
const token = this.sliceFragment(originNode.start, originNode.end);
|
|
56
|
-
const parentNamespace = parentNode && 'namespace' in parentNode ? parentNode.namespace : 'http://www.w3.org/1999/xhtml';
|
|
57
65
|
switch (originNode.type) {
|
|
58
66
|
case 'Text': {
|
|
59
67
|
return this.visitText({
|
|
@@ -84,14 +92,13 @@ export class SvelteParser extends Parser {
|
|
|
84
92
|
const reEndTag = new RegExp(`</${originNode.name}\\s*>$`, 'i');
|
|
85
93
|
const startTagEndOffset = children.length > 0
|
|
86
94
|
? (children[0]?.start ?? 0)
|
|
87
|
-
: token.raw.replace(reEndTag, '').length + token.
|
|
88
|
-
const startTagLocation = this.sliceFragment(token.
|
|
95
|
+
: token.raw.replace(reEndTag, '').length + token.offset;
|
|
96
|
+
const startTagLocation = this.sliceFragment(token.offset, startTagEndOffset);
|
|
89
97
|
return this.visitElement({
|
|
90
98
|
...startTagLocation,
|
|
91
99
|
depth,
|
|
92
100
|
parentNode,
|
|
93
101
|
nodeName: originNode.name,
|
|
94
|
-
namespace: getNamespace(originNode.name, parentNamespace),
|
|
95
102
|
}, originNode.fragment.nodes, {
|
|
96
103
|
createEndTagToken: () => {
|
|
97
104
|
if (!reEndTag.test(token.raw)) {
|
|
@@ -102,7 +109,7 @@ export class SvelteParser extends Parser {
|
|
|
102
109
|
throw new Error('Parse error');
|
|
103
110
|
}
|
|
104
111
|
const endTagRaw = endTagRawMatched[0];
|
|
105
|
-
const endTagStartOffset = token.
|
|
112
|
+
const endTagStartOffset = token.offset + token.raw.lastIndexOf(endTagRaw);
|
|
106
113
|
const endTagEndOffset = endTagStartOffset + endTagRaw.length;
|
|
107
114
|
const endTagLocation = this.sliceFragment(endTagStartOffset, endTagEndOffset);
|
|
108
115
|
return {
|
|
@@ -115,7 +122,7 @@ export class SvelteParser extends Parser {
|
|
|
115
122
|
}
|
|
116
123
|
case 'IfBlock': {
|
|
117
124
|
const expressions = [];
|
|
118
|
-
const ifElseBlocks =
|
|
125
|
+
const ifElseBlocks = this.#traverseIfBlock(originNode, token.offset);
|
|
119
126
|
for (const ifElseBlock of ifElseBlocks) {
|
|
120
127
|
const expression = this.visitPsBlock({
|
|
121
128
|
...ifElseBlock,
|
|
@@ -124,24 +131,27 @@ export class SvelteParser extends Parser {
|
|
|
124
131
|
nodeName: ifElseBlock.type,
|
|
125
132
|
isFragment: false,
|
|
126
133
|
}, ifElseBlock.children, {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
134
|
+
type: {
|
|
135
|
+
if: 'if',
|
|
136
|
+
elseif: 'if:elseif',
|
|
137
|
+
else: 'if:else',
|
|
138
|
+
'/if': 'end',
|
|
139
|
+
}[ifElseBlock.type],
|
|
140
|
+
expression: ifElseBlock.raw,
|
|
141
|
+
})[0];
|
|
132
142
|
expressions.push(expression);
|
|
133
143
|
}
|
|
134
144
|
return expressions;
|
|
135
145
|
}
|
|
136
146
|
case 'EachBlock': {
|
|
137
|
-
return
|
|
147
|
+
return this.#parseEachBlock({
|
|
138
148
|
...token,
|
|
139
149
|
depth,
|
|
140
150
|
parentNode,
|
|
141
151
|
}, originNode);
|
|
142
152
|
}
|
|
143
153
|
case 'AwaitBlock': {
|
|
144
|
-
return
|
|
154
|
+
return this.#parseAwaitBlock({
|
|
145
155
|
...token,
|
|
146
156
|
depth,
|
|
147
157
|
parentNode,
|
|
@@ -205,6 +215,13 @@ export class SvelteParser extends Parser {
|
|
|
205
215
|
}
|
|
206
216
|
}
|
|
207
217
|
}
|
|
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
|
+
*/
|
|
208
225
|
visitText(token) {
|
|
209
226
|
const nodes = super.visitText(token, {
|
|
210
227
|
researchTags: false,
|
|
@@ -221,8 +238,18 @@ export class SvelteParser extends Parser {
|
|
|
221
238
|
return node;
|
|
222
239
|
});
|
|
223
240
|
}
|
|
224
|
-
|
|
225
|
-
|
|
241
|
+
/**
|
|
242
|
+
* Visits a preprocessor-specific block token and enforces that exactly one
|
|
243
|
+
* block node is produced. Throws a ParserError if the result is empty
|
|
244
|
+
* or contains multiple nodes.
|
|
245
|
+
*
|
|
246
|
+
* @param token - The child token with node name and fragment flag
|
|
247
|
+
* @param childNodes - The child Svelte AST nodes within the block
|
|
248
|
+
* @param blockBehavior - The block behavior, or null
|
|
249
|
+
* @returns A single-element tuple containing the preprocessor-specific block
|
|
250
|
+
*/
|
|
251
|
+
visitPsBlock(token, childNodes = [], blockBehavior = null) {
|
|
252
|
+
const nodes = super.visitPsBlock(token, childNodes, blockBehavior);
|
|
226
253
|
const block = nodes.at(0);
|
|
227
254
|
if (!block || block.type !== 'psblock') {
|
|
228
255
|
throw new ParserError('Parse error', token);
|
|
@@ -232,6 +259,15 @@ export class SvelteParser extends Parser {
|
|
|
232
259
|
}
|
|
233
260
|
return [block];
|
|
234
261
|
}
|
|
262
|
+
/**
|
|
263
|
+
* Visits child nodes and verifies that no sibling nodes with differing
|
|
264
|
+
* hierarchy levels are produced. Throws a ParserError if unexpected
|
|
265
|
+
* sibling nodes are discovered.
|
|
266
|
+
*
|
|
267
|
+
* @param children - The child Svelte AST nodes to visit
|
|
268
|
+
* @param parentNode - The parent node in the markuplint tree
|
|
269
|
+
* @returns An empty array (all children are attached via the visitor)
|
|
270
|
+
*/
|
|
235
271
|
visitChildren(
|
|
236
272
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
237
273
|
children, parentNode) {
|
|
@@ -241,6 +277,16 @@ export class SvelteParser extends Parser {
|
|
|
241
277
|
}
|
|
242
278
|
return [];
|
|
243
279
|
}
|
|
280
|
+
/**
|
|
281
|
+
* Visits an attribute token, handling Svelte-specific syntax including
|
|
282
|
+
* curly-brace expression values and shorthand attributes (`{name}`).
|
|
283
|
+
* Directive resolution (`bind:`, `class:`, `on:`, etc.) and IDL attribute
|
|
284
|
+
* mapping are now handled declaratively by svelte-spec's directivePatterns
|
|
285
|
+
* and ml-core's useIDLAttributeNames.
|
|
286
|
+
*
|
|
287
|
+
* @param token - The token representing the attribute
|
|
288
|
+
* @returns The parsed attribute node with Svelte-specific metadata
|
|
289
|
+
*/
|
|
244
290
|
visitAttr(token) {
|
|
245
291
|
const attr = super.visitAttr(token, {
|
|
246
292
|
quoteSet: [
|
|
@@ -257,35 +303,17 @@ export class SvelteParser extends Parser {
|
|
|
257
303
|
}
|
|
258
304
|
let isDynamicValue = attr.startQuote.raw === '{' || undefined;
|
|
259
305
|
let potentialName;
|
|
260
|
-
|
|
261
|
-
let isDuplicatable = false;
|
|
306
|
+
// Shorthand {name} → potentialName = value
|
|
262
307
|
if (isDynamicValue && attr.name.raw === '') {
|
|
263
308
|
potentialName = attr.value.raw;
|
|
264
309
|
}
|
|
265
|
-
|
|
266
|
-
if (subName) {
|
|
267
|
-
isDirective = true;
|
|
268
|
-
if (baseName === 'bind' && !this.specificBindDirective.has(subName)) {
|
|
269
|
-
potentialName = subName;
|
|
270
|
-
isDirective = undefined;
|
|
271
|
-
isDynamicValue = true;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
if (baseName?.toLowerCase() === 'class') {
|
|
275
|
-
isDuplicatable = true;
|
|
276
|
-
if (subName) {
|
|
277
|
-
potentialName = 'class';
|
|
278
|
-
isDynamicValue = true;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
310
|
+
// Final curly brace check
|
|
281
311
|
if (attr.startQuote.raw === '{' && attr.endQuote.raw === '}') {
|
|
282
312
|
isDynamicValue = true;
|
|
283
313
|
}
|
|
284
314
|
return {
|
|
285
315
|
...attr,
|
|
286
316
|
isDynamicValue,
|
|
287
|
-
isDirective,
|
|
288
|
-
isDuplicatable,
|
|
289
317
|
potentialName,
|
|
290
318
|
};
|
|
291
319
|
}
|
|
@@ -300,217 +328,260 @@ export class SvelteParser extends Parser {
|
|
|
300
328
|
detectElementType(nodeName) {
|
|
301
329
|
return super.detectElementType(nodeName, /^[A-Z]|\./);
|
|
302
330
|
}
|
|
303
|
-
}
|
|
304
|
-
_SvelteParser_instances = new WeakSet(), _SvelteParser_parseAwaitBlock = function _SvelteParser_parseAwaitBlock(token,
|
|
305
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
306
|
-
originBlockNode) {
|
|
307
|
-
const { closeToken } = parseBlock(this, token, originBlockNode);
|
|
308
|
-
const pendingNodes = originBlockNode.pending?.nodes ?? [];
|
|
309
|
-
const thenNodes = originBlockNode.then?.nodes ?? [];
|
|
310
|
-
const pendingEnd = pendingNodes.at(-1)?.end;
|
|
311
|
-
const thenEnd = thenNodes.at(-1)?.end;
|
|
312
|
-
// @ts-ignore - new Svelte Compiler Type doesn't support `start` and `end` yet.
|
|
313
|
-
const awaitConditionEnd = originBlockNode.expression.end;
|
|
314
331
|
/**
|
|
315
|
-
* `{#await
|
|
316
|
-
*
|
|
332
|
+
* Parses a Svelte `{#await}` block into its constituent preprocessor-specific blocks:
|
|
333
|
+
* the await expression, optional `{:then}` branch, optional `{:catch}` branch,
|
|
334
|
+
* and the closing `{/await}` tag.
|
|
317
335
|
*
|
|
318
|
-
*
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* `}...{:then name}...{:catch name}...{/await}`
|
|
323
|
-
* ^___find
|
|
324
|
-
*/
|
|
325
|
-
const awaitExpEnd = awaitConditionEnd + rawAwaitConditionBelow.indexOf('}') + 1;
|
|
326
|
-
/**
|
|
327
|
-
* `{#await expression}`
|
|
328
|
-
*/
|
|
329
|
-
const awaitExpToken = this.sliceFragment(token.startOffset, awaitExpEnd);
|
|
330
|
-
let thenToken = null;
|
|
331
|
-
/**
|
|
332
|
-
* `{#await expression}...{:then name}...{:catch name}...{/await}`
|
|
333
|
-
* find___^
|
|
334
|
-
*/
|
|
335
|
-
const thenExpStart = pendingEnd ?? awaitExpEnd;
|
|
336
|
-
/**
|
|
337
|
-
* `{:then name}...{:catch name}...{/await}`
|
|
336
|
+
* @param token - The child token representing the entire await block
|
|
337
|
+
* @param originBlockNode - The Svelte AST AwaitBlock node
|
|
338
|
+
* @returns An array of preprocessor-specific block nodes
|
|
338
339
|
*/
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
340
|
+
#parseAwaitBlock(token,
|
|
341
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
342
|
+
originBlockNode) {
|
|
343
|
+
const { closeToken } = parseBlock(this, token, originBlockNode);
|
|
344
|
+
const pendingNodes = originBlockNode.pending?.nodes ?? [];
|
|
345
|
+
const thenNodes = originBlockNode.then?.nodes ?? [];
|
|
346
|
+
const pendingEnd = pendingNodes.at(-1)?.end;
|
|
347
|
+
const thenEnd = thenNodes.at(-1)?.end;
|
|
348
|
+
// @ts-ignore - new Svelte Compiler Type doesn't support `start` and `end` yet.
|
|
349
|
+
const awaitConditionEnd = originBlockNode.expression.end;
|
|
350
|
+
/**
|
|
351
|
+
* `{#await expression}...{:then name}...{:catch name}...{/await}`
|
|
352
|
+
* find___^ and cut
|
|
353
|
+
*
|
|
354
|
+
* `}...{:then name}...{:catch name}...{/await}`
|
|
355
|
+
*/
|
|
356
|
+
const rawAwaitConditionBelow = this.rawCode.slice(awaitConditionEnd, originBlockNode.end);
|
|
357
|
+
/**
|
|
358
|
+
* `}...{:then name}...{:catch name}...{/await}`
|
|
359
|
+
* ^___find
|
|
360
|
+
*/
|
|
361
|
+
const awaitExpEnd = awaitConditionEnd + rawAwaitConditionBelow.indexOf('}') + 1;
|
|
362
|
+
/**
|
|
363
|
+
* `{#await expression}`
|
|
364
|
+
*/
|
|
365
|
+
const awaitExpToken = this.sliceFragment(token.offset, awaitExpEnd);
|
|
366
|
+
let thenToken = null;
|
|
367
|
+
/**
|
|
368
|
+
* `{#await expression}...{:then name}...{:catch name}...{/await}`
|
|
369
|
+
* find___^
|
|
370
|
+
*/
|
|
371
|
+
const thenExpStart = pendingEnd ?? awaitExpEnd;
|
|
372
|
+
/**
|
|
373
|
+
* `{:then name}...{:catch name}...{/await}`
|
|
374
|
+
*/
|
|
375
|
+
const rawPendingNodesBelow = this.rawCode.slice(thenExpStart, originBlockNode.end);
|
|
376
|
+
if (
|
|
377
|
+
// eslint-disable-next-line regexp/strict
|
|
378
|
+
/^{\s*:then[\s|}]/.test(rawPendingNodesBelow)) {
|
|
379
|
+
let thenExpEndCharOffset;
|
|
380
|
+
if (originBlockNode.value) {
|
|
381
|
+
const thenIdentifierEnd =
|
|
382
|
+
// @ts-ignore - new Svelte Compiler Type doesn't support `start` and `end` yet.
|
|
383
|
+
originBlockNode.value.end;
|
|
384
|
+
const rawThenExpCloseCharAndBelow = this.rawCode.slice(thenIdentifierEnd, originBlockNode.end);
|
|
385
|
+
const thenExpEndCharIndex = rawThenExpCloseCharAndBelow.indexOf('}') + 1;
|
|
386
|
+
thenExpEndCharOffset = thenIdentifierEnd + thenExpEndCharIndex;
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
thenExpEndCharOffset = thenExpStart + rawPendingNodesBelow.indexOf('}') + 1;
|
|
390
|
+
}
|
|
391
|
+
thenToken = this.sliceFragment(token.offset + thenExpStart, thenExpEndCharOffset);
|
|
351
392
|
}
|
|
352
|
-
|
|
353
|
-
|
|
393
|
+
let catchToken = null;
|
|
394
|
+
/**
|
|
395
|
+
* `{#await expression}...{:then name}...{:catch name}...{/await}`
|
|
396
|
+
* find___^
|
|
397
|
+
*
|
|
398
|
+
* If `then` block is not found:
|
|
399
|
+
*
|
|
400
|
+
* `{#await expression}...{:catch name}...{/await}`
|
|
401
|
+
* find___^
|
|
402
|
+
*/
|
|
403
|
+
const catchExpStart = thenToken
|
|
404
|
+
? (thenEnd ?? thenToken.offset + thenToken.raw.length)
|
|
405
|
+
: (pendingEnd ?? awaitExpEnd);
|
|
406
|
+
/**
|
|
407
|
+
* `{:catch name}...{/await}`
|
|
408
|
+
*/
|
|
409
|
+
const rawThenNodesBelow = this.rawCode.slice(catchExpStart, originBlockNode.end);
|
|
410
|
+
if (
|
|
411
|
+
// eslint-disable-next-line regexp/strict
|
|
412
|
+
/^{\s*:catch[\s|}]/.test(rawThenNodesBelow)) {
|
|
413
|
+
let catchExpEndCharOffset;
|
|
414
|
+
if (originBlockNode.error) {
|
|
415
|
+
const catchIdentifierEnd =
|
|
416
|
+
// @ts-ignore - new Svelte Compiler Type doesn't support `start` and `end` yet.
|
|
417
|
+
originBlockNode.error.end;
|
|
418
|
+
const rawCatchExpCloseCharAndBelow = this.rawCode.slice(catchIdentifierEnd, originBlockNode.end);
|
|
419
|
+
const catchExpEndCharIndex = rawCatchExpCloseCharAndBelow.indexOf('}') + 1;
|
|
420
|
+
catchExpEndCharOffset = catchIdentifierEnd + catchExpEndCharIndex;
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
catchExpEndCharOffset = catchExpStart + rawThenNodesBelow.indexOf('}') + 1;
|
|
424
|
+
}
|
|
425
|
+
catchToken = this.sliceFragment(token.offset + catchExpStart, catchExpEndCharOffset);
|
|
354
426
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
if (originBlockNode.error) {
|
|
379
|
-
const catchIdentifierEnd =
|
|
380
|
-
// @ts-ignore - new Svelte Compiler Type doesn't support `start` and `end` yet.
|
|
381
|
-
originBlockNode.error.end;
|
|
382
|
-
const rawCatchExpCloseCharAndBelow = this.rawCode.slice(catchIdentifierEnd, originBlockNode.end);
|
|
383
|
-
const catchExpEndCharIndex = rawCatchExpCloseCharAndBelow.indexOf('}') + 1;
|
|
384
|
-
catchExpEndCharOffset = catchIdentifierEnd + catchExpEndCharIndex;
|
|
427
|
+
const expressions = [
|
|
428
|
+
this.visitPsBlock({
|
|
429
|
+
...awaitExpToken,
|
|
430
|
+
depth: token.depth,
|
|
431
|
+
parentNode: token.parentNode,
|
|
432
|
+
nodeName: 'await',
|
|
433
|
+
isFragment: false,
|
|
434
|
+
}, originBlockNode.pending?.nodes, {
|
|
435
|
+
type: 'await',
|
|
436
|
+
expression: awaitExpToken.raw,
|
|
437
|
+
})[0],
|
|
438
|
+
];
|
|
439
|
+
if (thenToken) {
|
|
440
|
+
expressions.push(this.visitPsBlock({
|
|
441
|
+
...thenToken,
|
|
442
|
+
depth: token.depth,
|
|
443
|
+
parentNode: token.parentNode,
|
|
444
|
+
nodeName: 'await:then',
|
|
445
|
+
isFragment: false,
|
|
446
|
+
}, originBlockNode.then?.nodes, {
|
|
447
|
+
type: 'await:then',
|
|
448
|
+
expression: thenToken.raw,
|
|
449
|
+
})[0]);
|
|
385
450
|
}
|
|
386
|
-
|
|
387
|
-
|
|
451
|
+
if (catchToken) {
|
|
452
|
+
expressions.push(this.visitPsBlock({
|
|
453
|
+
...catchToken,
|
|
454
|
+
depth: token.depth,
|
|
455
|
+
parentNode: token.parentNode,
|
|
456
|
+
nodeName: 'await:catch',
|
|
457
|
+
isFragment: false,
|
|
458
|
+
}, originBlockNode.catch?.nodes, {
|
|
459
|
+
type: 'await:catch',
|
|
460
|
+
expression: catchToken.raw,
|
|
461
|
+
})[0]);
|
|
388
462
|
}
|
|
389
|
-
catchToken = this.sliceFragment(token.startOffset + catchExpStart, catchExpEndCharOffset);
|
|
390
|
-
}
|
|
391
|
-
const expressions = [];
|
|
392
|
-
expressions.push(this.visitPsBlock({
|
|
393
|
-
...awaitExpToken,
|
|
394
|
-
depth: token.depth,
|
|
395
|
-
parentNode: token.parentNode,
|
|
396
|
-
nodeName: 'await',
|
|
397
|
-
isFragment: false,
|
|
398
|
-
}, originBlockNode.pending?.nodes, 'await')[0]);
|
|
399
|
-
if (thenToken) {
|
|
400
463
|
expressions.push(this.visitPsBlock({
|
|
401
|
-
...
|
|
464
|
+
...closeToken,
|
|
402
465
|
depth: token.depth,
|
|
403
466
|
parentNode: token.parentNode,
|
|
404
|
-
nodeName: 'await
|
|
467
|
+
nodeName: '/await',
|
|
405
468
|
isFragment: false,
|
|
406
|
-
},
|
|
469
|
+
}, undefined, {
|
|
470
|
+
type: 'end',
|
|
471
|
+
expression: closeToken.raw,
|
|
472
|
+
})[0]);
|
|
473
|
+
return expressions;
|
|
407
474
|
}
|
|
408
|
-
|
|
475
|
+
/**
|
|
476
|
+
* Parses a Svelte `{#each}` block into its constituent preprocessor-specific blocks:
|
|
477
|
+
* the each expression, optional `{:else}` fallback branch,
|
|
478
|
+
* and the closing `{/each}` tag.
|
|
479
|
+
*
|
|
480
|
+
* @param token - The child token representing the entire each block
|
|
481
|
+
* @param originBlockNode - The Svelte AST EachBlock node
|
|
482
|
+
* @returns An array of preprocessor-specific block nodes
|
|
483
|
+
*/
|
|
484
|
+
#parseEachBlock(token,
|
|
485
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
486
|
+
originBlockNode) {
|
|
487
|
+
const expressions = [];
|
|
488
|
+
/**
|
|
489
|
+
* `{/each}`
|
|
490
|
+
*/
|
|
491
|
+
const { closeToken } = parseBlock(this, token, originBlockNode);
|
|
492
|
+
/**
|
|
493
|
+
* `{#each expression as name}...{:else}...{/each}`
|
|
494
|
+
* find___^
|
|
495
|
+
*/
|
|
496
|
+
const bodyStart = originBlockNode.body.nodes.at(0)?.start ?? closeToken.offset;
|
|
497
|
+
/**
|
|
498
|
+
* `{#each expression as name}...{:else}...{/each}`
|
|
499
|
+
* find___^
|
|
500
|
+
*/
|
|
501
|
+
const fallbackScopeStart = originBlockNode.fallback?.nodes.at(0)?.start ?? closeToken.offset;
|
|
502
|
+
/**
|
|
503
|
+
* `{#each expression as name}...{:else}`
|
|
504
|
+
*/
|
|
505
|
+
const rawUntilFallbackScope = this.rawCode.slice(token.offset, fallbackScopeStart);
|
|
506
|
+
let elseToken = null;
|
|
507
|
+
/**
|
|
508
|
+
* `{#each expression as name}...{:else}`
|
|
509
|
+
* find___^
|
|
510
|
+
*/
|
|
511
|
+
// eslint-disable-next-line regexp/strict
|
|
512
|
+
const elseTokenStart = rawUntilFallbackScope.match(/{\s*:else\s*}$/)?.index;
|
|
513
|
+
if (elseTokenStart != null) {
|
|
514
|
+
elseToken = this.sliceFragment(token.offset + elseTokenStart, fallbackScopeStart);
|
|
515
|
+
}
|
|
516
|
+
const eachToken = this.sliceFragment(token.offset, bodyStart);
|
|
409
517
|
expressions.push(this.visitPsBlock({
|
|
410
|
-
...
|
|
518
|
+
...eachToken,
|
|
411
519
|
depth: token.depth,
|
|
412
520
|
parentNode: token.parentNode,
|
|
413
|
-
nodeName: '
|
|
521
|
+
nodeName: 'each',
|
|
414
522
|
isFragment: false,
|
|
415
|
-
}, originBlockNode.
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
}, _SvelteParser_parseEachBlock = function _SvelteParser_parseEachBlock(token,
|
|
426
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
427
|
-
originBlockNode) {
|
|
428
|
-
const expressions = [];
|
|
429
|
-
/**
|
|
430
|
-
* `{/each}`
|
|
431
|
-
*/
|
|
432
|
-
const { closeToken } = parseBlock(this, token, originBlockNode);
|
|
433
|
-
/**
|
|
434
|
-
* `{#each expression as name}...{:else}...{/each}`
|
|
435
|
-
* find___^
|
|
436
|
-
*/
|
|
437
|
-
const bodyStart = originBlockNode.body.nodes.at(0)?.start ?? closeToken.startOffset;
|
|
438
|
-
/**
|
|
439
|
-
* `{#each expression as name}...{:else}...{/each}`
|
|
440
|
-
* find___^
|
|
441
|
-
*/
|
|
442
|
-
const fallbackScopeStart = originBlockNode.fallback?.nodes.at(0)?.start ?? closeToken.startOffset;
|
|
443
|
-
/**
|
|
444
|
-
* `{#each expression as name}...{:else}`
|
|
445
|
-
*/
|
|
446
|
-
const rawUntilFallbackScope = this.rawCode.slice(token.startOffset, fallbackScopeStart);
|
|
447
|
-
let elseToken = null;
|
|
448
|
-
/**
|
|
449
|
-
* `{#each expression as name}...{:else}`
|
|
450
|
-
* find___^
|
|
451
|
-
*/
|
|
452
|
-
// eslint-disable-next-line regexp/strict
|
|
453
|
-
const elseTokenStart = rawUntilFallbackScope.match(/{\s*:else\s*}$/)?.index;
|
|
454
|
-
if (elseTokenStart != null) {
|
|
455
|
-
elseToken = this.sliceFragment(token.startOffset + elseTokenStart, fallbackScopeStart);
|
|
456
|
-
}
|
|
457
|
-
const eachToken = this.sliceFragment(token.startOffset, bodyStart);
|
|
458
|
-
expressions.push(this.visitPsBlock({
|
|
459
|
-
...eachToken,
|
|
460
|
-
depth: token.depth,
|
|
461
|
-
parentNode: token.parentNode,
|
|
462
|
-
nodeName: 'each',
|
|
463
|
-
isFragment: false,
|
|
464
|
-
}, originBlockNode.body.nodes, 'each')[0]);
|
|
465
|
-
if (elseToken) {
|
|
523
|
+
}, originBlockNode.body.nodes, { type: 'each', expression: eachToken.raw })[0]);
|
|
524
|
+
if (elseToken) {
|
|
525
|
+
expressions.push(this.visitPsBlock({
|
|
526
|
+
...elseToken,
|
|
527
|
+
depth: token.depth,
|
|
528
|
+
parentNode: token.parentNode,
|
|
529
|
+
nodeName: 'each:empty',
|
|
530
|
+
isFragment: false,
|
|
531
|
+
}, originBlockNode.fallback?.nodes, { type: 'each:empty', expression: elseToken.raw })[0]);
|
|
532
|
+
}
|
|
466
533
|
expressions.push(this.visitPsBlock({
|
|
467
|
-
...
|
|
534
|
+
...closeToken,
|
|
468
535
|
depth: token.depth,
|
|
469
536
|
parentNode: token.parentNode,
|
|
470
|
-
nodeName: 'each
|
|
537
|
+
nodeName: '/each',
|
|
471
538
|
isFragment: false,
|
|
472
|
-
},
|
|
539
|
+
}, undefined, { type: 'end', expression: closeToken.raw })[0]);
|
|
540
|
+
return expressions;
|
|
473
541
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
542
|
+
/**
|
|
543
|
+
* Recursively traverses a Svelte `{#if}` block and its chained `{:else if}` / `{:else}`
|
|
544
|
+
* branches, producing a flat list of token segments with their conditional type labels
|
|
545
|
+
* and child node arrays.
|
|
546
|
+
*
|
|
547
|
+
* @param originBlockNode - The Svelte AST IfBlock node to traverse
|
|
548
|
+
* @param start - The source offset where this block segment begins
|
|
549
|
+
* @param type - The conditional branch type: 'if', 'elseif', or 'else'
|
|
550
|
+
* @returns A flat array of token segments with children and type labels
|
|
551
|
+
*/
|
|
552
|
+
#traverseIfBlock(
|
|
553
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
554
|
+
originBlockNode, start, type = 'if') {
|
|
555
|
+
const result = [];
|
|
556
|
+
const end = originBlockNode.consequent.nodes?.[0]?.start ?? originBlockNode.end;
|
|
557
|
+
const tag = this.sliceFragment(start, end);
|
|
558
|
+
const children = originBlockNode.consequent.nodes;
|
|
559
|
+
result.push({ ...tag, children, type });
|
|
560
|
+
if (originBlockNode.alternate) {
|
|
561
|
+
if (originBlockNode.alternate.nodes?.[0]?.type === 'IfBlock') {
|
|
562
|
+
const elseif = this.#traverseIfBlock(originBlockNode.alternate.nodes[0], children.at(-1)?.end ?? start, 'elseif');
|
|
563
|
+
result.push(...elseif);
|
|
564
|
+
}
|
|
565
|
+
else {
|
|
566
|
+
const start = children.at(-1)?.end ?? originBlockNode.end;
|
|
567
|
+
const end = originBlockNode.alternate.nodes?.[0]?.start;
|
|
568
|
+
const tag = this.sliceFragment(start, end);
|
|
569
|
+
result.push({
|
|
570
|
+
...tag,
|
|
571
|
+
children: originBlockNode.alternate.nodes,
|
|
572
|
+
type: 'else',
|
|
573
|
+
});
|
|
574
|
+
}
|
|
494
575
|
}
|
|
495
|
-
|
|
496
|
-
const start = children.at(-1)?.end ?? originBlockNode.end;
|
|
497
|
-
const end = originBlockNode.
|
|
576
|
+
{
|
|
577
|
+
const start = result.at(-1)?.children.at(-1)?.end ?? originBlockNode.end;
|
|
578
|
+
const end = originBlockNode.end;
|
|
498
579
|
const tag = this.sliceFragment(start, end);
|
|
499
|
-
|
|
500
|
-
...tag,
|
|
501
|
-
|
|
502
|
-
type: 'else',
|
|
503
|
-
});
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
{
|
|
507
|
-
const start = result.at(-1)?.children.at(-1)?.end ?? originBlockNode.end;
|
|
508
|
-
const end = originBlockNode.end;
|
|
509
|
-
const tag = this.sliceFragment(start, end);
|
|
510
|
-
if (tag.raw) {
|
|
511
|
-
result.push({ ...tag, children: [], type: '/if' });
|
|
580
|
+
if (tag.raw) {
|
|
581
|
+
result.push({ ...tag, children: [], type: '/if' });
|
|
582
|
+
}
|
|
512
583
|
}
|
|
584
|
+
return result;
|
|
513
585
|
}
|
|
514
|
-
|
|
515
|
-
};
|
|
586
|
+
}
|
|
516
587
|
export const parser = new SvelteParser();
|
|
@@ -1,10 +1,23 @@
|
|
|
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. */
|
|
2
3
|
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. */
|
|
3
5
|
export type SvelteIfBlock = AST.IfBlock;
|
|
6
|
+
/** Represents a Svelte `{#each}` block with iteration body and optional fallback. */
|
|
4
7
|
export type SvelteEachBlock = AST.EachBlock;
|
|
8
|
+
/** Represents a Svelte `{#await}` block with pending, then, and catch branches. */
|
|
5
9
|
export type SvelteAwaitBlock = AST.AwaitBlock;
|
|
10
|
+
/**
|
|
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
|
|
16
|
+
*/
|
|
6
17
|
export declare function svelteParse(template: string): SvelteNode[];
|
|
18
|
+
/** Union of all Svelte directive and attribute types that can appear on elements. */
|
|
7
19
|
export type SvelteDirective = Directive | AST.Attribute | AST.SpreadAttribute;
|
|
20
|
+
/** Union of all Svelte block types that have opening/closing tag syntax. */
|
|
8
21
|
export type SvelteBlock = AST.EachBlock | AST.IfBlock | AST.AwaitBlock | AST.KeyBlock | AST.SnippetBlock | AST.SvelteBoundary;
|
|
9
22
|
type Directive = AST.AnimateDirective | AST.BindDirective | AST.ClassDirective | AST.LetDirective | AST.OnDirective | AST.StyleDirective | AST.TransitionDirective | AST.UseDirective;
|
|
10
23
|
export {};
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { parse } from 'svelte/compiler';
|
|
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
|
|
8
|
+
*/
|
|
2
9
|
export function svelteParse(template) {
|
|
3
10
|
const ast = parse(template, { modern: true });
|
|
4
11
|
return ast.fragment.nodes ?? [];
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { HtmlParser } from '@markuplint/html-parser';
|
|
2
|
+
/**
|
|
3
|
+
* Parser for SvelteKit app template files (e.g., `app.html`).
|
|
4
|
+
* Extends the standard HTML parser to handle SvelteKit placeholder tags
|
|
5
|
+
* such as `%sveltekit.head%` and `%sveltekit.body%`, which are treated
|
|
6
|
+
* as opaque preprocessor-specific blocks.
|
|
7
|
+
*/
|
|
2
8
|
declare class SvelteKitTemplateParser extends HtmlParser {
|
|
3
9
|
constructor();
|
|
4
10
|
}
|
package/lib/sveltekit-parser.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { HtmlParser } from '@markuplint/html-parser';
|
|
2
|
+
/**
|
|
3
|
+
* Parser for SvelteKit app template files (e.g., `app.html`).
|
|
4
|
+
* Extends the standard HTML parser to handle SvelteKit placeholder tags
|
|
5
|
+
* such as `%sveltekit.head%` and `%sveltekit.body%`, which are treated
|
|
6
|
+
* as opaque preprocessor-specific blocks.
|
|
7
|
+
*/
|
|
2
8
|
class SvelteKitTemplateParser extends HtmlParser {
|
|
3
9
|
constructor() {
|
|
4
10
|
super({
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markuplint/svelte-parser",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0-alpha.0",
|
|
4
4
|
"description": "Svelte parser for markuplint",
|
|
5
5
|
"repository": "git@github.com:markuplint/markuplint.git",
|
|
6
6
|
"author": "Yusuke Hirao <yusukehirao@me.com>",
|
|
7
7
|
"license": "MIT",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=22"
|
|
10
|
+
},
|
|
8
11
|
"type": "module",
|
|
9
12
|
"exports": {
|
|
10
13
|
".": {
|
|
@@ -29,10 +32,10 @@
|
|
|
29
32
|
"clean": "tsc --build --clean tsconfig.build.json"
|
|
30
33
|
},
|
|
31
34
|
"dependencies": {
|
|
32
|
-
"@markuplint/html-parser": "
|
|
33
|
-
"@markuplint/ml-ast": "
|
|
34
|
-
"@markuplint/parser-utils": "
|
|
35
|
-
"svelte": "5.
|
|
35
|
+
"@markuplint/html-parser": "5.0.0-alpha.0",
|
|
36
|
+
"@markuplint/ml-ast": "5.0.0-alpha.0",
|
|
37
|
+
"@markuplint/parser-utils": "5.0.0-alpha.0",
|
|
38
|
+
"svelte": "5.53.0"
|
|
36
39
|
},
|
|
37
|
-
"gitHead": "
|
|
40
|
+
"gitHead": "13dcfc84ec83d87360c720e253383b60767e1b56"
|
|
38
41
|
}
|