@markuplint/svelte-parser 4.0.0-dev.28 → 4.0.0-rc.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/LICENSE +1 -1
- package/README.md +12 -0
- package/lib/index.d.ts +1 -2
- package/lib/index.js +1 -2
- package/lib/parser.d.ts +54 -0
- package/lib/parser.js +267 -0
- package/lib/svelte-parser/index.d.ts +2 -1
- package/lib/svelte-parser/index.js +1 -1
- package/lib/sveltekit-parser.d.ts +6 -0
- package/lib/sveltekit-parser.js +15 -0
- package/package.json +9 -6
- package/lib/attr.d.ts +0 -5
- package/lib/attr.js +0 -61
- package/lib/directive-tokenizer.d.ts +0 -2
- package/lib/directive-tokenizer.js +0 -101
- package/lib/nodeize.d.ts +0 -3
- package/lib/nodeize.js +0 -289
- package/lib/parse-ctrl-block.d.ts +0 -3
- package/lib/parse-ctrl-block.js +0 -169
- package/lib/parse.d.ts +0 -2
- package/lib/parse.js +0 -47
- package/lib/traverse.d.ts +0 -3
- package/lib/traverse.js +0 -38
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -23,3 +23,15 @@ Add `parser` option to your [configuration](https://markuplint.dev/configuration
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
```
|
|
26
|
+
|
|
27
|
+
### Use with [SvelteKit](https://kit.svelte.dev/)
|
|
28
|
+
|
|
29
|
+
```diff
|
|
30
|
+
{
|
|
31
|
+
"parser": {
|
|
32
|
+
-- ".svelte$": "@markuplint/svelte-parser"
|
|
33
|
+
++ ".svelte$": "@markuplint/svelte-parser",
|
|
34
|
+
++ ".html$": "@markuplint/svelte-parser/kit"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
package/lib/index.d.ts
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export declare const endTag = "xml";
|
|
1
|
+
export { parser } from './parser.js';
|
package/lib/index.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export const endTag = 'xml';
|
|
1
|
+
export { parser } from './parser.js';
|
package/lib/parser.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/// <reference types="svelte" />
|
|
2
|
+
import type { SvelteNode } from './svelte-parser/index.js';
|
|
3
|
+
import type { MLASTParentNode, MLASTPreprocessorSpecificBlock } from '@markuplint/ml-ast';
|
|
4
|
+
import type { ChildToken, ParseOptions, Token } from '@markuplint/parser-utils';
|
|
5
|
+
import { ParserError, Parser } from '@markuplint/parser-utils';
|
|
6
|
+
declare class SvelteParser extends Parser<SvelteNode> {
|
|
7
|
+
readonly specificBindDirective: ReadonlySet<string>;
|
|
8
|
+
constructor();
|
|
9
|
+
tokenize(): {
|
|
10
|
+
ast: import("svelte/types/compiler/interfaces").TemplateNode[];
|
|
11
|
+
isFragment: boolean;
|
|
12
|
+
};
|
|
13
|
+
parse(raw: string, options?: ParseOptions): import("@markuplint/ml-ast").MLASTDocument;
|
|
14
|
+
parseError(error: any): ParserError;
|
|
15
|
+
nodeize(originNode: SvelteNode, parentNode: MLASTParentNode | null, depth: number): readonly import("@markuplint/ml-ast").MLASTNodeTreeItem[];
|
|
16
|
+
visitPsBlock(token: ChildToken & {
|
|
17
|
+
readonly nodeName: string;
|
|
18
|
+
}, childNodes?: readonly SvelteNode[]): readonly [MLASTPreprocessorSpecificBlock];
|
|
19
|
+
visitChildren(children: readonly SvelteNode[], parentNode: MLASTParentNode | null): never[];
|
|
20
|
+
visitAttr(token: Token): (import("@markuplint/ml-ast").MLASTSpreadAttr & {
|
|
21
|
+
__rightText?: string | undefined;
|
|
22
|
+
}) | {
|
|
23
|
+
isDynamicValue: true | undefined;
|
|
24
|
+
isDirective: true | undefined;
|
|
25
|
+
isDuplicatable: boolean;
|
|
26
|
+
potentialName: string | undefined;
|
|
27
|
+
type: "attr";
|
|
28
|
+
nodeName: string;
|
|
29
|
+
spacesBeforeName: import("@markuplint/ml-ast").MLASTToken;
|
|
30
|
+
name: import("@markuplint/ml-ast").MLASTToken;
|
|
31
|
+
spacesBeforeEqual: import("@markuplint/ml-ast").MLASTToken;
|
|
32
|
+
equal: import("@markuplint/ml-ast").MLASTToken;
|
|
33
|
+
spacesAfterEqual: import("@markuplint/ml-ast").MLASTToken;
|
|
34
|
+
startQuote: import("@markuplint/ml-ast").MLASTToken;
|
|
35
|
+
value: import("@markuplint/ml-ast").MLASTToken;
|
|
36
|
+
endQuote: import("@markuplint/ml-ast").MLASTToken;
|
|
37
|
+
potentialValue?: string | undefined;
|
|
38
|
+
valueType?: "string" | "number" | "boolean" | "code" | undefined;
|
|
39
|
+
candidate?: string | undefined;
|
|
40
|
+
uuid: string;
|
|
41
|
+
raw: string;
|
|
42
|
+
startOffset: number;
|
|
43
|
+
endOffset: number;
|
|
44
|
+
startLine: number;
|
|
45
|
+
endLine: number;
|
|
46
|
+
startCol: number;
|
|
47
|
+
endCol: number;
|
|
48
|
+
__rightText?: string | undefined;
|
|
49
|
+
};
|
|
50
|
+
detectElementType(nodeName: string): import("@markuplint/ml-ast").ElementType;
|
|
51
|
+
visitExpression(token: ChildToken, originBlockNode: SvelteNode): MLASTPreprocessorSpecificBlock[];
|
|
52
|
+
}
|
|
53
|
+
export declare const parser: SvelteParser;
|
|
54
|
+
export {};
|
package/lib/parser.js
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { getNamespace } from '@markuplint/html-parser';
|
|
2
|
+
import { ParserError, Parser, AttrState } from '@markuplint/parser-utils';
|
|
3
|
+
import { svelteParse } from './svelte-parser/index.js';
|
|
4
|
+
class SvelteParser extends Parser {
|
|
5
|
+
constructor() {
|
|
6
|
+
super({
|
|
7
|
+
endTagType: 'xml',
|
|
8
|
+
ignoreTags: [
|
|
9
|
+
{
|
|
10
|
+
type: 'Script',
|
|
11
|
+
start: '<script',
|
|
12
|
+
end: '</script>',
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
type: 'Style',
|
|
16
|
+
start: '<style',
|
|
17
|
+
end: '</style>',
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
maskChar: '-',
|
|
21
|
+
});
|
|
22
|
+
this.specificBindDirective = new Set(['group', 'this']);
|
|
23
|
+
}
|
|
24
|
+
tokenize() {
|
|
25
|
+
return {
|
|
26
|
+
ast: svelteParse(this.rawCode),
|
|
27
|
+
isFragment: true,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
parse(raw, options) {
|
|
31
|
+
return super.parse(raw, {
|
|
32
|
+
...options,
|
|
33
|
+
ignoreFrontMatter: false,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
parseError(error) {
|
|
37
|
+
if (error instanceof Error && 'start' in error && 'end' in error && 'frame' in error) {
|
|
38
|
+
// @ts-ignore
|
|
39
|
+
const token = this.sliceFragment(error.start.character, error.end.character);
|
|
40
|
+
throw new ParserError(error.message + '\n' + error.frame, token);
|
|
41
|
+
}
|
|
42
|
+
return super.parseError(error);
|
|
43
|
+
}
|
|
44
|
+
nodeize(
|
|
45
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
46
|
+
originNode, parentNode, depth) {
|
|
47
|
+
const token = this.sliceFragment(originNode.start, originNode.end);
|
|
48
|
+
const parentNamespace = parentNode && 'namespace' in parentNode ? parentNode.namespace : 'http://www.w3.org/1999/xhtml';
|
|
49
|
+
switch (originNode.type) {
|
|
50
|
+
case 'Text': {
|
|
51
|
+
return this.visitText({
|
|
52
|
+
...token,
|
|
53
|
+
depth,
|
|
54
|
+
parentNode,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
case 'Comment': {
|
|
58
|
+
return this.visitComment({
|
|
59
|
+
...token,
|
|
60
|
+
depth,
|
|
61
|
+
parentNode,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
case 'MustacheTag': {
|
|
65
|
+
return this.visitPsBlock({
|
|
66
|
+
...token,
|
|
67
|
+
depth,
|
|
68
|
+
parentNode,
|
|
69
|
+
nodeName: 'MustacheTag',
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
case 'InlineComponent':
|
|
73
|
+
case 'Element': {
|
|
74
|
+
const children = originNode.children ?? [];
|
|
75
|
+
const reEndTag = new RegExp(`</${originNode.name}\\s*>$`, 'i');
|
|
76
|
+
const startTagEndOffset = children.length > 0
|
|
77
|
+
? children[0]?.start ?? 0
|
|
78
|
+
: token.raw.replace(reEndTag, '').length + token.startOffset;
|
|
79
|
+
const startTagLocation = this.sliceFragment(token.startOffset, startTagEndOffset);
|
|
80
|
+
return this.visitElement({
|
|
81
|
+
...startTagLocation,
|
|
82
|
+
depth,
|
|
83
|
+
parentNode,
|
|
84
|
+
nodeName: originNode.name,
|
|
85
|
+
namespace: getNamespace(originNode.name, parentNamespace),
|
|
86
|
+
}, originNode.children, {
|
|
87
|
+
createEndTagToken: () => {
|
|
88
|
+
if (!reEndTag.test(token.raw)) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
const endTagRawMatched = token.raw.match(reEndTag);
|
|
92
|
+
if (!endTagRawMatched) {
|
|
93
|
+
throw new Error('Parse error');
|
|
94
|
+
}
|
|
95
|
+
const endTagRaw = endTagRawMatched[0];
|
|
96
|
+
const endTagStartOffset = token.startOffset + token.raw.lastIndexOf(endTagRaw);
|
|
97
|
+
const endTagEndOffset = endTagStartOffset + endTagRaw.length;
|
|
98
|
+
const endTagLocation = this.sliceFragment(endTagStartOffset, endTagEndOffset);
|
|
99
|
+
return {
|
|
100
|
+
...endTagLocation,
|
|
101
|
+
depth,
|
|
102
|
+
parentNode,
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
default: {
|
|
108
|
+
return this.visitExpression({
|
|
109
|
+
...token,
|
|
110
|
+
depth,
|
|
111
|
+
parentNode,
|
|
112
|
+
}, originNode);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
visitPsBlock(token, childNodes = []) {
|
|
117
|
+
const nodes = super.visitPsBlock(token, childNodes);
|
|
118
|
+
const block = nodes.at(0);
|
|
119
|
+
if (!block || block.type !== 'psblock') {
|
|
120
|
+
throw new ParserError('Parse error', token);
|
|
121
|
+
}
|
|
122
|
+
if (nodes.length > 1) {
|
|
123
|
+
throw new ParserError('Parse error', nodes.at(1));
|
|
124
|
+
}
|
|
125
|
+
return [block];
|
|
126
|
+
}
|
|
127
|
+
visitChildren(
|
|
128
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
129
|
+
children, parentNode) {
|
|
130
|
+
const siblings = super.visitChildren(children, parentNode);
|
|
131
|
+
if (siblings.length > 0) {
|
|
132
|
+
throw new ParserError('Discovered child nodes with differing hierarchy levels', siblings[0]);
|
|
133
|
+
}
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
visitAttr(token) {
|
|
137
|
+
const attr = super.visitAttr(token, {
|
|
138
|
+
quoteSet: [
|
|
139
|
+
{ start: '"', end: '"' },
|
|
140
|
+
{ start: "'", end: "'" },
|
|
141
|
+
{ start: '{', end: '}' },
|
|
142
|
+
],
|
|
143
|
+
quoteInValueChars: [
|
|
144
|
+
{ start: '"', end: '"' },
|
|
145
|
+
{ start: "'", end: "'" },
|
|
146
|
+
{ start: '`', end: '`' },
|
|
147
|
+
{ start: '${', end: '}' },
|
|
148
|
+
],
|
|
149
|
+
startState:
|
|
150
|
+
// is shorthand attribute
|
|
151
|
+
token.raw.trim().startsWith('{') ? AttrState.BeforeValue : AttrState.BeforeName,
|
|
152
|
+
});
|
|
153
|
+
if (attr.type === 'spread') {
|
|
154
|
+
return attr;
|
|
155
|
+
}
|
|
156
|
+
let isDynamicValue = attr.startQuote.raw === '{' || undefined;
|
|
157
|
+
let potentialName;
|
|
158
|
+
let isDirective;
|
|
159
|
+
let isDuplicatable = false;
|
|
160
|
+
if (isDynamicValue && attr.name.raw === '') {
|
|
161
|
+
potentialName = attr.value.raw;
|
|
162
|
+
}
|
|
163
|
+
const [baseName, subName] = attr.name.raw.split(':');
|
|
164
|
+
if (subName) {
|
|
165
|
+
isDirective = true;
|
|
166
|
+
if (baseName === 'bind' && !this.specificBindDirective.has(subName)) {
|
|
167
|
+
potentialName = subName;
|
|
168
|
+
isDirective = undefined;
|
|
169
|
+
isDynamicValue = true;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (baseName?.toLowerCase() === 'class') {
|
|
173
|
+
isDuplicatable = true;
|
|
174
|
+
if (subName) {
|
|
175
|
+
potentialName = 'class';
|
|
176
|
+
isDynamicValue = true;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (attr.startQuote.raw === '{' && attr.endQuote.raw === '}') {
|
|
180
|
+
isDynamicValue = true;
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
...attr,
|
|
184
|
+
isDynamicValue,
|
|
185
|
+
isDirective,
|
|
186
|
+
isDuplicatable,
|
|
187
|
+
potentialName,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
detectElementType(nodeName) {
|
|
191
|
+
return super.detectElementType(nodeName, /[.A-Z]/);
|
|
192
|
+
}
|
|
193
|
+
visitExpression(token,
|
|
194
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
195
|
+
originBlockNode) {
|
|
196
|
+
const props = ['', 'else', 'pending', 'then', 'catch'];
|
|
197
|
+
const expressions = [];
|
|
198
|
+
const blockType = originBlockNode.type.toLowerCase().replace('block', '');
|
|
199
|
+
const nodeList = new Map();
|
|
200
|
+
for (const prop of props) {
|
|
201
|
+
let node = (originBlockNode[prop] ?? originBlockNode);
|
|
202
|
+
if (nodeList.has(node)) {
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (node.type === 'ElseBlock' && node.children?.[0]?.elseif) {
|
|
206
|
+
node = node.children[0];
|
|
207
|
+
while (node != null) {
|
|
208
|
+
if (!['IfBlock', 'ElseBlock'].includes(node.type)) {
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
const type = node.elseif ? 'elseif' : 'else';
|
|
212
|
+
nodeList.set(node, type);
|
|
213
|
+
node = node.else ?? node.children?.[0] ?? null;
|
|
214
|
+
}
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
let type = prop || blockType;
|
|
218
|
+
if (prop === 'pending') {
|
|
219
|
+
type = 'await';
|
|
220
|
+
}
|
|
221
|
+
nodeList.set(node, type);
|
|
222
|
+
}
|
|
223
|
+
let lastChild = null;
|
|
224
|
+
for (const [node, type] of nodeList.entries()) {
|
|
225
|
+
let start = node.start;
|
|
226
|
+
let end = node.end;
|
|
227
|
+
if (type === 'await') {
|
|
228
|
+
start = originBlockNode.start;
|
|
229
|
+
}
|
|
230
|
+
end = node.children?.[0]?.start ?? end;
|
|
231
|
+
if (type === 'else' && originBlockNode.type === 'EachBlock') {
|
|
232
|
+
start = lastChild?.end ?? start;
|
|
233
|
+
}
|
|
234
|
+
if (['else', 'elseif'].includes(type) && originBlockNode.type === 'IfBlock') {
|
|
235
|
+
start = lastChild?.end ?? start;
|
|
236
|
+
}
|
|
237
|
+
const tag = this.sliceFragment(start, end);
|
|
238
|
+
if (node.children && Array.isArray(node.children)) {
|
|
239
|
+
lastChild = node.children.at(-1) ?? null;
|
|
240
|
+
}
|
|
241
|
+
const expression = this.visitPsBlock({
|
|
242
|
+
...tag,
|
|
243
|
+
depth: token.depth,
|
|
244
|
+
parentNode: token.parentNode,
|
|
245
|
+
nodeName: type,
|
|
246
|
+
}, node.children)[0];
|
|
247
|
+
expressions.push(expression);
|
|
248
|
+
}
|
|
249
|
+
const lastText = this.sliceFragment(lastChild?.end ?? originBlockNode.end, originBlockNode.end);
|
|
250
|
+
if (lastText.raw) {
|
|
251
|
+
// Cut before whitespace
|
|
252
|
+
const index = lastText.raw.search(/\S/);
|
|
253
|
+
const lastToken = this.sliceFragment(lastText.startOffset + index, originBlockNode.end);
|
|
254
|
+
if (lastToken.raw) {
|
|
255
|
+
const expression = this.visitPsBlock({
|
|
256
|
+
...lastToken,
|
|
257
|
+
depth: token.depth,
|
|
258
|
+
parentNode: token.parentNode,
|
|
259
|
+
nodeName: '/' + blockType,
|
|
260
|
+
})[0];
|
|
261
|
+
expressions.push(expression);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return expressions;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
export const parser = new SvelteParser();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
+
/// <reference types="svelte" />
|
|
1
2
|
import type { Directive, TemplateNode, Attribute, SpreadAttribute } from 'svelte/types/compiler/interfaces';
|
|
2
3
|
export type SvelteNode = TemplateNode;
|
|
3
|
-
export
|
|
4
|
+
export declare function svelteParse(template: string): SvelteNode[];
|
|
4
5
|
export type SvelteDirective = Directive | Attribute | SpreadAttribute;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { HtmlParser } from '@markuplint/html-parser';
|
|
2
|
+
class SvelteKitTemplateParser extends HtmlParser {
|
|
3
|
+
constructor() {
|
|
4
|
+
super({
|
|
5
|
+
ignoreTags: [
|
|
6
|
+
{
|
|
7
|
+
type: 'sveltekit-placeholder',
|
|
8
|
+
start: '%sveltekit.',
|
|
9
|
+
end: '%',
|
|
10
|
+
},
|
|
11
|
+
],
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export const parser = new SvelteKitTemplateParser();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markuplint/svelte-parser",
|
|
3
|
-
"version": "4.0.0-
|
|
3
|
+
"version": "4.0.0-rc.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>",
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
"exports": {
|
|
11
11
|
".": {
|
|
12
12
|
"import": "./lib/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./kit": {
|
|
15
|
+
"import": "./lib/sveltekit-parser.js"
|
|
13
16
|
}
|
|
14
17
|
},
|
|
15
18
|
"types": "lib/index.d.ts",
|
|
@@ -21,10 +24,10 @@
|
|
|
21
24
|
"clean": "tsc --build --clean"
|
|
22
25
|
},
|
|
23
26
|
"dependencies": {
|
|
24
|
-
"@markuplint/html-parser": "4.0.0-
|
|
25
|
-
"@markuplint/ml-ast": "4.0.0-
|
|
26
|
-
"@markuplint/parser-utils": "4.0.0-
|
|
27
|
-
"svelte": "^4.2.
|
|
27
|
+
"@markuplint/html-parser": "4.0.0-rc.0",
|
|
28
|
+
"@markuplint/ml-ast": "4.0.0-rc.0",
|
|
29
|
+
"@markuplint/parser-utils": "4.0.0-rc.0",
|
|
30
|
+
"svelte": "^4.2.9"
|
|
28
31
|
},
|
|
29
|
-
"gitHead": "
|
|
32
|
+
"gitHead": "3fdeb45cb69ed52b3a215a7520cea1181601443f"
|
|
30
33
|
}
|
package/lib/attr.d.ts
DELETED
package/lib/attr.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { defaultValueDelimiters, parseAttr, sliceFragment } from '@markuplint/parser-utils';
|
|
2
|
-
import directiveTokenizer from './directive-tokenizer.js';
|
|
3
|
-
const mustacheTag = {
|
|
4
|
-
start: '{',
|
|
5
|
-
end: '}',
|
|
6
|
-
};
|
|
7
|
-
const specificBindDirective = new Set(['bind:group', 'bind:this']);
|
|
8
|
-
export function attr(
|
|
9
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
10
|
-
attr, rawHTML) {
|
|
11
|
-
const isShorthand = attr.value && Array.isArray(attr.value)
|
|
12
|
-
? attr.value.some((val) => val.type === 'AttributeShorthand')
|
|
13
|
-
: false;
|
|
14
|
-
const { start, end } = attr;
|
|
15
|
-
if (attr.type === 'Spread') {
|
|
16
|
-
return {
|
|
17
|
-
__spreadAttr: true,
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
let token;
|
|
21
|
-
if (attr.type === 'Attribute' && !isShorthand) {
|
|
22
|
-
const { raw } = sliceFragment(rawHTML, start, end);
|
|
23
|
-
token = parseAttr(raw, start, rawHTML, {
|
|
24
|
-
valueDelimiters: [...defaultValueDelimiters, mustacheTag],
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
const { raw, startLine, startCol, startOffset } = sliceFragment(rawHTML, start, end);
|
|
29
|
-
const valueToken = isShorthand
|
|
30
|
-
? attr.name
|
|
31
|
-
: attr.expression && 'start' in attr.expression && 'end' in attr.expression
|
|
32
|
-
? sliceFragment(rawHTML, attr.expression.start, attr.expression.end).raw
|
|
33
|
-
: '';
|
|
34
|
-
token = directiveTokenizer(raw, valueToken, startLine, startCol, startOffset);
|
|
35
|
-
}
|
|
36
|
-
if (!specificBindDirective.has(token.name.raw) && /^bind:/i.test(token.name.raw)) {
|
|
37
|
-
// Remove "bind:"
|
|
38
|
-
token.potentialName = token.name.raw.slice(5);
|
|
39
|
-
token.isDirective = undefined;
|
|
40
|
-
token.isDynamicValue = true;
|
|
41
|
-
}
|
|
42
|
-
if (isShorthand) {
|
|
43
|
-
token.potentialName = token.value.raw.trim();
|
|
44
|
-
token.isDirective = undefined;
|
|
45
|
-
token.isDynamicValue = true;
|
|
46
|
-
}
|
|
47
|
-
const [baseName, subName] = token.name.raw.split(':');
|
|
48
|
-
if (baseName?.toLowerCase() === 'class') {
|
|
49
|
-
token.isDuplicatable = true;
|
|
50
|
-
if (subName) {
|
|
51
|
-
token.potentialName = 'class';
|
|
52
|
-
token.isDynamicValue = true;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
if (token.startQuote.raw === '{' && token.endQuote.raw === '}') {
|
|
56
|
-
token.isDynamicValue = true;
|
|
57
|
-
}
|
|
58
|
-
return {
|
|
59
|
-
...token,
|
|
60
|
-
};
|
|
61
|
-
}
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { attrTokenizer } from '@markuplint/html-parser';
|
|
2
|
-
import { tokenizer, uuid } from '@markuplint/parser-utils';
|
|
3
|
-
// eslint-disable-next-line no-control-regex
|
|
4
|
-
const reNameOnly = /^[^\u0000-\u001F /=>{\u007F-\u009F]+/;
|
|
5
|
-
// eslint-disable-next-line no-control-regex
|
|
6
|
-
const reBeforeStructure = /^(\s*)([^\u0000-\u001F /=>{\u007F-\u009F]+)(\s*)(=)(\s*){(\s*)$/;
|
|
7
|
-
const reBeforeStructureWithoutName = /^{(\s*)$/;
|
|
8
|
-
const reAfterStructure = /(\s*)}/;
|
|
9
|
-
export default function directiveTokenizer(raw, rawValue, line, col, startOffset) {
|
|
10
|
-
let spacesBeforeAttrString = '';
|
|
11
|
-
let nameChars = '';
|
|
12
|
-
let spacesBeforeEqualChars = '';
|
|
13
|
-
let equalChars = null;
|
|
14
|
-
let spacesAfterEqualChars = '';
|
|
15
|
-
let valueChars = '';
|
|
16
|
-
const [before, after] = raw.split(rawValue);
|
|
17
|
-
const beforeMatchedMap = before?.match(reBeforeStructure);
|
|
18
|
-
const beforeWithoutNameMatchedMap = before?.match(reBeforeStructureWithoutName);
|
|
19
|
-
const afterMatchedMap = after?.match(reAfterStructure);
|
|
20
|
-
if (beforeMatchedMap && afterMatchedMap) {
|
|
21
|
-
spacesBeforeAttrString = beforeMatchedMap[1] ?? '';
|
|
22
|
-
nameChars = beforeMatchedMap[2] ?? '';
|
|
23
|
-
spacesBeforeEqualChars = beforeMatchedMap[3] ?? '';
|
|
24
|
-
equalChars = beforeMatchedMap[4] ?? null;
|
|
25
|
-
spacesAfterEqualChars = beforeMatchedMap[5] ?? '';
|
|
26
|
-
valueChars = (beforeMatchedMap[6] ?? '') + rawValue + (afterMatchedMap[1] ?? '');
|
|
27
|
-
}
|
|
28
|
-
else if (beforeWithoutNameMatchedMap && afterMatchedMap) {
|
|
29
|
-
valueChars = (beforeWithoutNameMatchedMap[1] ?? '') + rawValue + (afterMatchedMap[1] ?? '');
|
|
30
|
-
}
|
|
31
|
-
else if (reNameOnly.test(raw)) {
|
|
32
|
-
const token = attrTokenizer(raw, line, col, startOffset);
|
|
33
|
-
token.isDirective = true;
|
|
34
|
-
return token;
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
throw new SyntaxError('Illegal attribute token');
|
|
38
|
-
}
|
|
39
|
-
let offset = startOffset;
|
|
40
|
-
const attrToken = tokenizer(raw, line, col, offset);
|
|
41
|
-
const spacesBeforeName = tokenizer(spacesBeforeAttrString, line, col, offset);
|
|
42
|
-
line = spacesBeforeName.endLine;
|
|
43
|
-
col = spacesBeforeName.endCol;
|
|
44
|
-
offset = spacesBeforeName.endOffset;
|
|
45
|
-
const name = tokenizer(nameChars, line, col, offset);
|
|
46
|
-
line = name.endLine;
|
|
47
|
-
col = name.endCol;
|
|
48
|
-
offset = name.endOffset;
|
|
49
|
-
const spacesBeforeEqual = tokenizer(spacesBeforeEqualChars, line, col, offset);
|
|
50
|
-
line = spacesBeforeEqual.endLine;
|
|
51
|
-
col = spacesBeforeEqual.endCol;
|
|
52
|
-
offset = spacesBeforeEqual.endOffset;
|
|
53
|
-
const equal = tokenizer(equalChars, line, col, offset);
|
|
54
|
-
line = equal.endLine;
|
|
55
|
-
col = equal.endCol;
|
|
56
|
-
offset = equal.endOffset;
|
|
57
|
-
const spacesAfterEqual = tokenizer(spacesAfterEqualChars, line, col, offset);
|
|
58
|
-
line = spacesAfterEqual.endLine;
|
|
59
|
-
col = spacesAfterEqual.endCol;
|
|
60
|
-
offset = spacesAfterEqual.endOffset;
|
|
61
|
-
const startQuote = tokenizer('{', line, col, offset);
|
|
62
|
-
line = startQuote.endLine;
|
|
63
|
-
col = startQuote.endCol;
|
|
64
|
-
offset = startQuote.endOffset;
|
|
65
|
-
const value = tokenizer(valueChars, line, col, offset);
|
|
66
|
-
line = value.endLine;
|
|
67
|
-
col = value.endCol;
|
|
68
|
-
offset = value.endOffset;
|
|
69
|
-
const endQuote = tokenizer('}', line, col, offset);
|
|
70
|
-
line = endQuote.endLine;
|
|
71
|
-
col = endQuote.endCol;
|
|
72
|
-
offset = endQuote.endOffset;
|
|
73
|
-
const result = {
|
|
74
|
-
type: 'html-attr',
|
|
75
|
-
uuid: uuid(),
|
|
76
|
-
raw: attrToken.raw,
|
|
77
|
-
startOffset: attrToken.startOffset,
|
|
78
|
-
endOffset: attrToken.endOffset,
|
|
79
|
-
startLine: attrToken.startLine,
|
|
80
|
-
endLine: attrToken.endLine,
|
|
81
|
-
startCol: attrToken.startCol,
|
|
82
|
-
endCol: attrToken.endCol,
|
|
83
|
-
spacesBeforeName,
|
|
84
|
-
name,
|
|
85
|
-
spacesBeforeEqual,
|
|
86
|
-
equal,
|
|
87
|
-
spacesAfterEqual,
|
|
88
|
-
startQuote,
|
|
89
|
-
value,
|
|
90
|
-
endQuote,
|
|
91
|
-
isDirective: true,
|
|
92
|
-
isDuplicatable: false,
|
|
93
|
-
nodeName: name.raw,
|
|
94
|
-
parentNode: null,
|
|
95
|
-
nextNode: null,
|
|
96
|
-
prevNode: null,
|
|
97
|
-
isFragment: false,
|
|
98
|
-
isGhost: false,
|
|
99
|
-
};
|
|
100
|
-
return result;
|
|
101
|
-
}
|
package/lib/nodeize.d.ts
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import type { SvelteNode } from './svelte-parser/index.js';
|
|
2
|
-
import type { MLASTNode, MLASTParentNode, ParserOptions } from '@markuplint/ml-ast';
|
|
3
|
-
export declare function nodeize(originNode: SvelteNode, prevNode: MLASTNode | null, parentNode: MLASTParentNode | null, rawHtml: string, options?: ParserOptions): MLASTNode | MLASTNode[] | null;
|
package/lib/nodeize.js
DELETED
|
@@ -1,289 +0,0 @@
|
|
|
1
|
-
import { getNamespace, parseRawTag } from '@markuplint/html-parser';
|
|
2
|
-
import { detectElementType, sliceFragment, uuid } from '@markuplint/parser-utils';
|
|
3
|
-
import { attr } from './attr.js';
|
|
4
|
-
import { parseCtrlBlock } from './parse-ctrl-block.js';
|
|
5
|
-
import { traverse } from './traverse.js';
|
|
6
|
-
export function nodeize(
|
|
7
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
8
|
-
originNode,
|
|
9
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
10
|
-
prevNode,
|
|
11
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
12
|
-
parentNode, rawHtml, options) {
|
|
13
|
-
const nextNode = null;
|
|
14
|
-
const { startOffset, endOffset, startLine, endLine, startCol, endCol, raw } = sliceFragment(rawHtml, originNode.start, originNode.end);
|
|
15
|
-
const parentNamespace = parentNode && 'namespace' in parentNode ? parentNode.namespace : 'http://www.w3.org/1999/xhtml';
|
|
16
|
-
switch (originNode.type) {
|
|
17
|
-
case 'Text': {
|
|
18
|
-
const node = {
|
|
19
|
-
uuid: uuid(),
|
|
20
|
-
raw,
|
|
21
|
-
startOffset,
|
|
22
|
-
endOffset,
|
|
23
|
-
startLine,
|
|
24
|
-
endLine,
|
|
25
|
-
startCol,
|
|
26
|
-
endCol,
|
|
27
|
-
nodeName: '#text',
|
|
28
|
-
type: 'text',
|
|
29
|
-
parentNode,
|
|
30
|
-
prevNode,
|
|
31
|
-
nextNode,
|
|
32
|
-
isFragment: false,
|
|
33
|
-
isGhost: false,
|
|
34
|
-
};
|
|
35
|
-
return node;
|
|
36
|
-
}
|
|
37
|
-
case 'MustacheTag': {
|
|
38
|
-
return {
|
|
39
|
-
uuid: uuid(),
|
|
40
|
-
raw,
|
|
41
|
-
startOffset,
|
|
42
|
-
endOffset,
|
|
43
|
-
startLine,
|
|
44
|
-
endLine,
|
|
45
|
-
startCol,
|
|
46
|
-
endCol,
|
|
47
|
-
nodeName: '#ps:MustacheTag',
|
|
48
|
-
type: 'psblock',
|
|
49
|
-
parentNode,
|
|
50
|
-
prevNode,
|
|
51
|
-
nextNode,
|
|
52
|
-
isFragment: false,
|
|
53
|
-
isGhost: false,
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
case 'InlineComponent':
|
|
57
|
-
case 'Element': {
|
|
58
|
-
const children = originNode.children ?? [];
|
|
59
|
-
const reEndTag = new RegExp(`</${originNode.name}\\s*>$`, 'i');
|
|
60
|
-
const startTagEndOffset = children.length > 0 ? children[0]?.start ?? 0 : raw.replace(reEndTag, '').length + startOffset;
|
|
61
|
-
const startTagLocation = sliceFragment(rawHtml, startOffset, startTagEndOffset);
|
|
62
|
-
let endTag = null;
|
|
63
|
-
if (reEndTag.test(raw)) {
|
|
64
|
-
const endTagRawMatched = raw.match(reEndTag);
|
|
65
|
-
if (!endTagRawMatched) {
|
|
66
|
-
throw new Error('Parse error');
|
|
67
|
-
}
|
|
68
|
-
const endTagRaw = endTagRawMatched[0];
|
|
69
|
-
const endTagStartOffset = startOffset + raw.lastIndexOf(endTagRaw);
|
|
70
|
-
const endTagEndOffset = endTagStartOffset + endTagRaw.length;
|
|
71
|
-
const endTagLocation = sliceFragment(rawHtml, endTagStartOffset, endTagEndOffset);
|
|
72
|
-
const namespace = getNamespace(originNode.name, parentNamespace);
|
|
73
|
-
endTag = {
|
|
74
|
-
uuid: uuid(),
|
|
75
|
-
raw: endTagRaw,
|
|
76
|
-
startOffset: endTagStartOffset,
|
|
77
|
-
endOffset: endTagEndOffset,
|
|
78
|
-
startLine: endTagLocation.startLine,
|
|
79
|
-
endLine: endTagLocation.endLine,
|
|
80
|
-
startCol: endTagLocation.startCol,
|
|
81
|
-
endCol: endTagLocation.endCol,
|
|
82
|
-
nodeName: originNode.name,
|
|
83
|
-
type: 'endtag',
|
|
84
|
-
namespace,
|
|
85
|
-
attributes: [],
|
|
86
|
-
parentNode,
|
|
87
|
-
prevNode,
|
|
88
|
-
nextNode,
|
|
89
|
-
pearNode: null,
|
|
90
|
-
isFragment: false,
|
|
91
|
-
isGhost: false,
|
|
92
|
-
tagOpenChar: '</',
|
|
93
|
-
tagCloseChar: '>',
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
const directives = originNode.attributes.map(a => attr(a, rawHtml)) ?? [];
|
|
97
|
-
const attributes = directives.filter((d) => !('__spreadAttr' in d));
|
|
98
|
-
const hasSpreadAttr = directives.some(d => '__spreadAttr' in d);
|
|
99
|
-
const tagTokens = parseRawTag(startTagLocation.raw, startTagLocation.startLine, startTagLocation.startCol, startTagLocation.startOffset);
|
|
100
|
-
const namespace = getNamespace(originNode.name, parentNamespace);
|
|
101
|
-
const startTag = {
|
|
102
|
-
uuid: uuid(),
|
|
103
|
-
...startTagLocation,
|
|
104
|
-
nodeName: originNode.name,
|
|
105
|
-
type: 'starttag',
|
|
106
|
-
namespace,
|
|
107
|
-
elementType: detectElementType(originNode.name, options?.authoredElementName, /[A-Z]|\./),
|
|
108
|
-
attributes,
|
|
109
|
-
hasSpreadAttr,
|
|
110
|
-
parentNode,
|
|
111
|
-
prevNode,
|
|
112
|
-
nextNode,
|
|
113
|
-
pearNode: endTag,
|
|
114
|
-
selfClosingSolidus: tagTokens.selfClosingSolidus,
|
|
115
|
-
endSpace: tagTokens.endSpace,
|
|
116
|
-
isFragment: false,
|
|
117
|
-
isGhost: false,
|
|
118
|
-
tagOpenChar: '<',
|
|
119
|
-
tagCloseChar: '>',
|
|
120
|
-
};
|
|
121
|
-
if (endTag) {
|
|
122
|
-
endTag.pearNode = startTag;
|
|
123
|
-
}
|
|
124
|
-
if (originNode.children) {
|
|
125
|
-
startTag.childNodes = traverse(originNode.children, startTag, rawHtml, options);
|
|
126
|
-
}
|
|
127
|
-
return startTag;
|
|
128
|
-
}
|
|
129
|
-
case 'IfBlock': {
|
|
130
|
-
const ifBlocks = parseCtrlBlock('if', originNode, raw, rawHtml, startOffset, parentNode, prevNode, nextNode, options);
|
|
131
|
-
return ifBlocks;
|
|
132
|
-
}
|
|
133
|
-
case 'EachBlock': {
|
|
134
|
-
return parseCtrlBlock('each', originNode, raw, rawHtml, startOffset, parentNode, prevNode, nextNode, options);
|
|
135
|
-
}
|
|
136
|
-
case 'AwaitBlock': {
|
|
137
|
-
const pendingNode = originNode.pending;
|
|
138
|
-
const pendingTag = sliceFragment(rawHtml, originNode.start, pendingNode.start);
|
|
139
|
-
const pending = {
|
|
140
|
-
uuid: uuid(),
|
|
141
|
-
...pendingTag,
|
|
142
|
-
nodeName: pendingNode.type,
|
|
143
|
-
type: 'psblock',
|
|
144
|
-
parentNode,
|
|
145
|
-
prevNode,
|
|
146
|
-
nextNode,
|
|
147
|
-
isFragment: false,
|
|
148
|
-
isGhost: false,
|
|
149
|
-
};
|
|
150
|
-
if (pendingNode.children) {
|
|
151
|
-
pending.childNodes = traverse(pendingNode.children, pending, rawHtml, options);
|
|
152
|
-
}
|
|
153
|
-
let then = null;
|
|
154
|
-
if (originNode.then) {
|
|
155
|
-
const thenNode = originNode.then;
|
|
156
|
-
const thenTag = sliceFragment(rawHtml, thenNode.start, (thenNode.children && thenNode.children[0] && thenNode.children[0].start) ?? thenNode.end);
|
|
157
|
-
then = {
|
|
158
|
-
uuid: uuid(),
|
|
159
|
-
...thenTag,
|
|
160
|
-
nodeName: thenNode.type,
|
|
161
|
-
type: 'psblock',
|
|
162
|
-
parentNode,
|
|
163
|
-
prevNode,
|
|
164
|
-
nextNode,
|
|
165
|
-
isFragment: false,
|
|
166
|
-
isGhost: false,
|
|
167
|
-
};
|
|
168
|
-
if (thenNode.children) {
|
|
169
|
-
then.childNodes = traverse(thenNode.children, then, rawHtml, options);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
let awaitCatch = null;
|
|
173
|
-
if (originNode.catch) {
|
|
174
|
-
const awaitCatchNode = originNode.catch;
|
|
175
|
-
const awaitCatchTag = sliceFragment(rawHtml, awaitCatchNode.start, (awaitCatchNode.children && awaitCatchNode.children[0] && awaitCatchNode.children[0].start) ??
|
|
176
|
-
awaitCatchNode.end);
|
|
177
|
-
awaitCatch = {
|
|
178
|
-
uuid: uuid(),
|
|
179
|
-
...awaitCatchTag,
|
|
180
|
-
nodeName: awaitCatchNode.type,
|
|
181
|
-
type: 'psblock',
|
|
182
|
-
parentNode,
|
|
183
|
-
prevNode,
|
|
184
|
-
nextNode,
|
|
185
|
-
isFragment: false,
|
|
186
|
-
isGhost: false,
|
|
187
|
-
};
|
|
188
|
-
if (awaitCatchNode.children) {
|
|
189
|
-
awaitCatch.childNodes = traverse(awaitCatchNode.children, awaitCatch, rawHtml, options);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
const reEndTag = new RegExp('{/await}$', 'i');
|
|
193
|
-
let endTag = null;
|
|
194
|
-
if (reEndTag.test(raw)) {
|
|
195
|
-
const endTagRawMatched = raw.match(reEndTag);
|
|
196
|
-
if (!endTagRawMatched) {
|
|
197
|
-
throw new Error('Parse error');
|
|
198
|
-
}
|
|
199
|
-
const endTagRaw = endTagRawMatched[0];
|
|
200
|
-
const endTagStartOffset = startOffset + raw.indexOf(endTagRaw);
|
|
201
|
-
const endTagEndOffset = endTagStartOffset + endTagRaw.length;
|
|
202
|
-
const endTagLocation = sliceFragment(rawHtml, endTagStartOffset, endTagEndOffset);
|
|
203
|
-
endTag = {
|
|
204
|
-
uuid: uuid(),
|
|
205
|
-
raw: endTagRaw,
|
|
206
|
-
startOffset: endTagStartOffset,
|
|
207
|
-
endOffset: endTagEndOffset,
|
|
208
|
-
startLine: endTagLocation.startLine,
|
|
209
|
-
endLine: endTagLocation.endLine,
|
|
210
|
-
startCol: endTagLocation.startCol,
|
|
211
|
-
endCol: endTagLocation.endCol,
|
|
212
|
-
nodeName: originNode.type,
|
|
213
|
-
type: 'psblock',
|
|
214
|
-
parentNode,
|
|
215
|
-
prevNode,
|
|
216
|
-
nextNode,
|
|
217
|
-
isFragment: false,
|
|
218
|
-
isGhost: false,
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
const tags = [pending];
|
|
222
|
-
if (then) {
|
|
223
|
-
tags.push(then);
|
|
224
|
-
}
|
|
225
|
-
if (awaitCatch) {
|
|
226
|
-
tags.push(awaitCatch);
|
|
227
|
-
}
|
|
228
|
-
if (endTag) {
|
|
229
|
-
tags.push(endTag);
|
|
230
|
-
}
|
|
231
|
-
return tags;
|
|
232
|
-
}
|
|
233
|
-
default: {
|
|
234
|
-
const startTag = {
|
|
235
|
-
uuid: uuid(),
|
|
236
|
-
raw,
|
|
237
|
-
startOffset,
|
|
238
|
-
endOffset,
|
|
239
|
-
startLine,
|
|
240
|
-
endLine,
|
|
241
|
-
startCol,
|
|
242
|
-
endCol,
|
|
243
|
-
nodeName: originNode.name || originNode.type,
|
|
244
|
-
type: 'psblock',
|
|
245
|
-
parentNode,
|
|
246
|
-
prevNode,
|
|
247
|
-
nextNode,
|
|
248
|
-
isFragment: false,
|
|
249
|
-
isGhost: false,
|
|
250
|
-
};
|
|
251
|
-
let endTag = null;
|
|
252
|
-
if (originNode.children) {
|
|
253
|
-
startTag.childNodes = traverse(originNode.children, startTag, rawHtml, options);
|
|
254
|
-
const firstChild = startTag.childNodes[0];
|
|
255
|
-
if (firstChild) {
|
|
256
|
-
startTag.endOffset = firstChild.startOffset;
|
|
257
|
-
startTag.endLine = firstChild.startLine;
|
|
258
|
-
startTag.endCol = firstChild.startCol;
|
|
259
|
-
startTag.raw = rawHtml.slice(startTag.startOffset, startTag.endOffset);
|
|
260
|
-
}
|
|
261
|
-
const lastChild = startTag.childNodes.at(-1);
|
|
262
|
-
if (lastChild && lastChild.endOffset > startTag.endOffset) {
|
|
263
|
-
const startOffset = lastChild.endOffset;
|
|
264
|
-
const startLine = lastChild.endLine;
|
|
265
|
-
const startCol = lastChild.endCol;
|
|
266
|
-
const raw = rawHtml.slice(startOffset, endOffset);
|
|
267
|
-
endTag = {
|
|
268
|
-
uuid: uuid(),
|
|
269
|
-
raw,
|
|
270
|
-
startOffset,
|
|
271
|
-
endOffset,
|
|
272
|
-
startLine,
|
|
273
|
-
endLine,
|
|
274
|
-
startCol,
|
|
275
|
-
endCol,
|
|
276
|
-
nodeName: originNode.name || originNode.type,
|
|
277
|
-
type: 'psblock',
|
|
278
|
-
parentNode,
|
|
279
|
-
prevNode,
|
|
280
|
-
nextNode,
|
|
281
|
-
isFragment: false,
|
|
282
|
-
isGhost: false,
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
return endTag == null ? startTag : [startTag, endTag];
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
}
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import type { SvelteNode } from './svelte-parser/index.js';
|
|
2
|
-
import type { MLASTNode, MLASTParentNode, MLASTPreprocessorSpecificBlock, ParserOptions } from '@markuplint/ml-ast';
|
|
3
|
-
export declare function parseCtrlBlock(ctrlName: 'if' | 'each', originNode: SvelteNode, raw: string, rawHtml: string, startOffset: number, parentNode: MLASTParentNode | null, prevNode: MLASTNode | null, nextNode: MLASTNode | null, options?: ParserOptions): MLASTPreprocessorSpecificBlock[];
|
package/lib/parse-ctrl-block.js
DELETED
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import { sliceFragment, uuid } from '@markuplint/parser-utils';
|
|
2
|
-
import { traverse } from './traverse.js';
|
|
3
|
-
export function parseCtrlBlock(ctrlName,
|
|
4
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
5
|
-
originNode, raw, rawHtml, startOffset,
|
|
6
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
7
|
-
parentNode,
|
|
8
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
9
|
-
prevNode,
|
|
10
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
11
|
-
nextNode, options) {
|
|
12
|
-
if (ctrlName === 'if') {
|
|
13
|
-
return parseIfBlock(originNode, raw, rawHtml, startOffset, originNode.start, parentNode, prevNode, nextNode, options);
|
|
14
|
-
}
|
|
15
|
-
const children = originNode.children ?? [];
|
|
16
|
-
const reEndTag = new RegExp('{/each}$', 'i');
|
|
17
|
-
const startTagEndOffset = children.length > 0 ? children[0]?.start ?? 0 : raw.replace(reEndTag, '').length + startOffset;
|
|
18
|
-
const startTagLocation = sliceFragment(rawHtml, startOffset, startTagEndOffset);
|
|
19
|
-
const tag = {
|
|
20
|
-
uuid: uuid(),
|
|
21
|
-
...startTagLocation,
|
|
22
|
-
nodeName: originNode.type,
|
|
23
|
-
type: 'psblock',
|
|
24
|
-
parentNode,
|
|
25
|
-
prevNode,
|
|
26
|
-
nextNode,
|
|
27
|
-
isFragment: false,
|
|
28
|
-
isGhost: false,
|
|
29
|
-
};
|
|
30
|
-
if (originNode.children) {
|
|
31
|
-
tag.childNodes = traverse(originNode.children, tag, rawHtml, options);
|
|
32
|
-
}
|
|
33
|
-
let elseTag = null;
|
|
34
|
-
if (originNode.else) {
|
|
35
|
-
const elseNode = originNode.else;
|
|
36
|
-
const elseTagStartOffset = children.length > 0 ? children.at(-1)?.end ?? 0 : startTagLocation.endOffset;
|
|
37
|
-
const elseTagLocation = sliceFragment(rawHtml, elseTagStartOffset, elseNode.start);
|
|
38
|
-
elseTag = {
|
|
39
|
-
uuid: uuid(),
|
|
40
|
-
...elseTagLocation,
|
|
41
|
-
nodeName: elseNode.type,
|
|
42
|
-
type: 'psblock',
|
|
43
|
-
parentNode,
|
|
44
|
-
prevNode,
|
|
45
|
-
nextNode,
|
|
46
|
-
isFragment: false,
|
|
47
|
-
isGhost: false,
|
|
48
|
-
};
|
|
49
|
-
if (elseNode.children) {
|
|
50
|
-
elseTag.childNodes = traverse(elseNode.children, elseTag, rawHtml, options);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
let endTag = null;
|
|
54
|
-
const endTagRawMatched = raw.match(reEndTag);
|
|
55
|
-
if (endTagRawMatched) {
|
|
56
|
-
const endTagRaw = endTagRawMatched[0];
|
|
57
|
-
const endTagStartOffset = originNode.end - endTagRaw.length;
|
|
58
|
-
const endTagEndOffset = originNode.end;
|
|
59
|
-
const endTagLocation = sliceFragment(rawHtml, endTagStartOffset, endTagEndOffset);
|
|
60
|
-
endTag = {
|
|
61
|
-
uuid: uuid(),
|
|
62
|
-
raw: endTagRaw,
|
|
63
|
-
startOffset: endTagStartOffset,
|
|
64
|
-
endOffset: endTagEndOffset,
|
|
65
|
-
startLine: endTagLocation.startLine,
|
|
66
|
-
endLine: endTagLocation.endLine,
|
|
67
|
-
startCol: endTagLocation.startCol,
|
|
68
|
-
endCol: endTagLocation.endCol,
|
|
69
|
-
nodeName: originNode.type,
|
|
70
|
-
type: 'psblock',
|
|
71
|
-
parentNode,
|
|
72
|
-
prevNode,
|
|
73
|
-
nextNode,
|
|
74
|
-
isFragment: false,
|
|
75
|
-
isGhost: false,
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
const tags = [tag];
|
|
79
|
-
if (elseTag) {
|
|
80
|
-
tags.push(elseTag);
|
|
81
|
-
}
|
|
82
|
-
if (endTag) {
|
|
83
|
-
tags.push(endTag);
|
|
84
|
-
}
|
|
85
|
-
return tags;
|
|
86
|
-
}
|
|
87
|
-
function parseIfBlock(
|
|
88
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
89
|
-
originNode, raw, rawHtml, statementStartOffset, tokenStartOffset,
|
|
90
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
91
|
-
parentNode,
|
|
92
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
93
|
-
prevNode,
|
|
94
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
95
|
-
nextNode, options) {
|
|
96
|
-
const children = originNode.children ?? [];
|
|
97
|
-
const startTagEndOffset = children[0]?.start ?? tokenStartOffset;
|
|
98
|
-
const startTagLocation = sliceFragment(rawHtml, tokenStartOffset, startTagEndOffset);
|
|
99
|
-
const tag = {
|
|
100
|
-
uuid: uuid(),
|
|
101
|
-
...startTagLocation,
|
|
102
|
-
nodeName: originNode.elseif ? 'ElseIfBlock' : originNode.type,
|
|
103
|
-
type: 'psblock',
|
|
104
|
-
parentNode,
|
|
105
|
-
prevNode,
|
|
106
|
-
nextNode,
|
|
107
|
-
isFragment: false,
|
|
108
|
-
isGhost: false,
|
|
109
|
-
};
|
|
110
|
-
if (originNode.children) {
|
|
111
|
-
tag.childNodes = traverse(originNode.children, tag, rawHtml, options);
|
|
112
|
-
}
|
|
113
|
-
const elseOrElseIfTags = [];
|
|
114
|
-
if (originNode.else) {
|
|
115
|
-
const elseNode = originNode.else;
|
|
116
|
-
const elseTagStartOffset = children.length > 0 ? children.at(-1)?.end ?? 0 : startTagLocation.endOffset;
|
|
117
|
-
const elseTagLocation = sliceFragment(rawHtml, elseTagStartOffset, elseNode.start);
|
|
118
|
-
if (elseNode.children) {
|
|
119
|
-
if (elseNode.children.length === 1 && elseNode.children[0]?.type === 'IfBlock') {
|
|
120
|
-
const elseIfTags = parseIfBlock(elseNode.children[0], elseTagLocation.raw, rawHtml, statementStartOffset, originNode.children?.[0]?.end ?? startTagLocation.endOffset, parentNode, null, null, options);
|
|
121
|
-
elseOrElseIfTags.push(...elseIfTags);
|
|
122
|
-
}
|
|
123
|
-
else {
|
|
124
|
-
const elseTag = {
|
|
125
|
-
uuid: uuid(),
|
|
126
|
-
...elseTagLocation,
|
|
127
|
-
nodeName: elseNode.type,
|
|
128
|
-
type: 'psblock',
|
|
129
|
-
parentNode,
|
|
130
|
-
prevNode,
|
|
131
|
-
nextNode,
|
|
132
|
-
isFragment: false,
|
|
133
|
-
isGhost: false,
|
|
134
|
-
};
|
|
135
|
-
elseTag.childNodes = traverse(elseNode.children, elseTag, rawHtml, options);
|
|
136
|
-
elseOrElseIfTags.push(elseTag);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
if (originNode.elseif) {
|
|
141
|
-
return [tag, ...elseOrElseIfTags];
|
|
142
|
-
}
|
|
143
|
-
const endTagRawMatched = raw.match('{/if}');
|
|
144
|
-
if (!endTagRawMatched) {
|
|
145
|
-
throw new Error('Missing the end token `{/if}`');
|
|
146
|
-
}
|
|
147
|
-
const endTagRaw = endTagRawMatched[0];
|
|
148
|
-
const endTagStartOffset = originNode.end - endTagRaw.length;
|
|
149
|
-
const endTagEndOffset = originNode.end;
|
|
150
|
-
const endTagLocation = sliceFragment(rawHtml, endTagStartOffset, endTagEndOffset);
|
|
151
|
-
const endTag = {
|
|
152
|
-
uuid: uuid(),
|
|
153
|
-
raw: endTagRaw,
|
|
154
|
-
startOffset: endTagStartOffset,
|
|
155
|
-
endOffset: endTagEndOffset,
|
|
156
|
-
startLine: endTagLocation.startLine,
|
|
157
|
-
endLine: endTagLocation.endLine,
|
|
158
|
-
startCol: endTagLocation.startCol,
|
|
159
|
-
endCol: endTagLocation.endCol,
|
|
160
|
-
nodeName: originNode.type,
|
|
161
|
-
type: 'psblock',
|
|
162
|
-
parentNode,
|
|
163
|
-
prevNode,
|
|
164
|
-
nextNode,
|
|
165
|
-
isFragment: false,
|
|
166
|
-
isGhost: false,
|
|
167
|
-
};
|
|
168
|
-
return [tag, ...elseOrElseIfTags, endTag];
|
|
169
|
-
}
|
package/lib/parse.d.ts
DELETED
package/lib/parse.js
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { flattenNodes, ParserError, ignoreBlock, restoreNode } from '@markuplint/parser-utils';
|
|
2
|
-
import svelteParse from './svelte-parser/index.js';
|
|
3
|
-
import { traverse } from './traverse.js';
|
|
4
|
-
export const parse = (rawCode, options) => {
|
|
5
|
-
const blocks = ignoreBlock(rawCode, [
|
|
6
|
-
{
|
|
7
|
-
type: 'Script',
|
|
8
|
-
start: /<script/,
|
|
9
|
-
end: /<\/script>/,
|
|
10
|
-
},
|
|
11
|
-
{
|
|
12
|
-
type: 'Style',
|
|
13
|
-
start: /<style/,
|
|
14
|
-
end: /<\/style>/,
|
|
15
|
-
},
|
|
16
|
-
], '-');
|
|
17
|
-
let ast;
|
|
18
|
-
try {
|
|
19
|
-
ast = svelteParse(blocks.replaced);
|
|
20
|
-
}
|
|
21
|
-
catch (error) {
|
|
22
|
-
if (error instanceof Error && 'start' in error && 'end' in error && 'frame' in error) {
|
|
23
|
-
// @ts-ignore
|
|
24
|
-
const raw = rawCode.slice(error.start.character, error.end.character);
|
|
25
|
-
throw new ParserError(
|
|
26
|
-
// @ts-ignore
|
|
27
|
-
error.message + '\n' + error.frame, {
|
|
28
|
-
// @ts-ignore
|
|
29
|
-
line: error.start.line,
|
|
30
|
-
// @ts-ignore
|
|
31
|
-
col: error.start.column,
|
|
32
|
-
raw,
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
return {
|
|
36
|
-
nodeList: [],
|
|
37
|
-
isFragment: true,
|
|
38
|
-
parseError: error instanceof Error ? error.message : new Error(`${error}`).message,
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
const nodes = traverse(ast, null, blocks.replaced, options);
|
|
42
|
-
const nodeList = restoreNode(flattenNodes(nodes, blocks.replaced), blocks);
|
|
43
|
-
return {
|
|
44
|
-
nodeList,
|
|
45
|
-
isFragment: true,
|
|
46
|
-
};
|
|
47
|
-
};
|
package/lib/traverse.d.ts
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import type { SvelteNode } from './svelte-parser/index.js';
|
|
2
|
-
import type { MLASTNode, MLASTParentNode, ParserOptions } from '@markuplint/ml-ast';
|
|
3
|
-
export declare function traverse(astNodes: readonly SvelteNode[], parentNode: MLASTParentNode | null | undefined, rawHtml: string, options?: ParserOptions): MLASTNode[];
|
package/lib/traverse.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { nodeize } from './nodeize.js';
|
|
2
|
-
export function traverse(
|
|
3
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
4
|
-
astNodes, parentNode = null, rawHtml, options) {
|
|
5
|
-
const nodeList = [];
|
|
6
|
-
let prevNode = null;
|
|
7
|
-
for (const astNode of astNodes) {
|
|
8
|
-
const nodes = nodeize(astNode, prevNode, parentNode, rawHtml, options);
|
|
9
|
-
if (!nodes) {
|
|
10
|
-
continue;
|
|
11
|
-
}
|
|
12
|
-
let node;
|
|
13
|
-
if (Array.isArray(nodes)) {
|
|
14
|
-
const lastNode = nodes.at(-1);
|
|
15
|
-
if (!lastNode) {
|
|
16
|
-
continue;
|
|
17
|
-
}
|
|
18
|
-
node = lastNode;
|
|
19
|
-
}
|
|
20
|
-
else {
|
|
21
|
-
node = nodes;
|
|
22
|
-
}
|
|
23
|
-
if (prevNode) {
|
|
24
|
-
if (node.type !== 'endtag') {
|
|
25
|
-
prevNode.nextNode = node;
|
|
26
|
-
}
|
|
27
|
-
node.prevNode = prevNode;
|
|
28
|
-
}
|
|
29
|
-
prevNode = node;
|
|
30
|
-
if (Array.isArray(nodes)) {
|
|
31
|
-
nodeList.push(...nodes);
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
nodeList.push(nodes);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
return nodeList;
|
|
38
|
-
}
|