Skip to content

Commit 7ebf326

Browse files
authored
feat: <template lang="..."> to parse as raw text (#244)
* ignore template tag * Create real-dolphins-nail.md
1 parent 4df6505 commit 7ebf326

14 files changed

+2223
-66
lines changed

.changeset/real-dolphins-nail.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte-eslint-parser": minor
3+
---
4+
5+
feat: `<template lang="...">` to parse as raw text

docs/AST.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ See [ESTree] for the AST node of the script generated by `espree`.
1515
[variabledeclarator]: https://github.com/estree/estree/blob/master/es5.md#variabledeclarator
1616
[pattern]: https://github.com/estree/estree/blob/master/es5.md#patterns
1717

18-
See details: [../src/ast.ts](../src/ast.ts)
18+
See details: [../src/ast/*](../src/ast/)
1919

2020
## Common
2121

src/ast/base.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import type { Locations } from "./common";
2+
3+
// internals
4+
export interface BaseNode extends Locations {
5+
type: string;
6+
}

src/ast/common.ts

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import type { BaseNode } from "./base";
2+
3+
export interface Position {
4+
/** >= 1 */
5+
line: number;
6+
/** >= 0 */
7+
column: number;
8+
}
9+
export type Range = [number, number];
10+
export interface SourceLocation {
11+
start: Position;
12+
end: Position;
13+
}
14+
export interface Locations {
15+
loc: SourceLocation;
16+
range: Range;
17+
}
18+
19+
export interface Token extends BaseNode {
20+
type:
21+
| "Boolean"
22+
| "Null"
23+
| "Identifier"
24+
| "Keyword"
25+
| "Punctuator"
26+
| "JSXIdentifier"
27+
| "JSXText"
28+
| "Numeric"
29+
| "String"
30+
| "RegularExpression"
31+
| "Template"
32+
// HTML
33+
| "HTMLText"
34+
| "HTMLIdentifier"
35+
| "MustacheKeyword"
36+
| "HTMLComment";
37+
value: string;
38+
}
39+
40+
export interface Comment extends BaseNode {
41+
type: "Line" | "Block";
42+
value: string;
43+
}

src/ast.ts src/ast/html.ts

+5-58
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,8 @@
11
import type ESTree from "estree";
2-
export type Range = [number, number];
2+
import type { BaseNode } from "./base";
3+
import type { Token, Comment } from "./common";
34

4-
export interface SourceLocation {
5-
start: Position;
6-
end: Position;
7-
}
8-
export interface Locations {
9-
loc: SourceLocation;
10-
range: Range;
11-
}
12-
13-
interface BaseNode extends Locations {
14-
type: string;
15-
}
16-
17-
export interface Token extends BaseNode {
18-
type:
19-
| "Boolean"
20-
| "Null"
21-
| "Identifier"
22-
| "Keyword"
23-
| "Punctuator"
24-
| "JSXIdentifier"
25-
| "JSXText"
26-
| "Numeric"
27-
| "String"
28-
| "RegularExpression"
29-
| "Template"
30-
// HTML
31-
| "HTMLText"
32-
| "HTMLIdentifier"
33-
| "MustacheKeyword"
34-
| "HTMLComment";
35-
value: string;
36-
}
37-
38-
export interface Comment extends BaseNode {
39-
type: "Line" | "Block";
40-
value: string;
41-
}
42-
43-
export interface Position {
44-
/** >= 1 */
45-
line: number;
46-
/** >= 0 */
47-
column: number;
48-
}
49-
50-
export type SvelteNode =
5+
export type SvelteHTMLNode =
516
| SvelteProgram
527
| SvelteScriptElement
538
| SvelteStyleElement
@@ -77,8 +32,7 @@ export type SvelteNode =
7732
| SvelteSpecialDirective
7833
| SvelteDirectiveKey
7934
| SvelteSpecialDirectiveKey
80-
| SvelteHTMLComment
81-
| SvelteReactiveStatement;
35+
| SvelteHTMLComment;
8236

8337
/** Node of Svelte program root */
8438
export interface SvelteProgram extends BaseNode {
@@ -95,6 +49,7 @@ export type SvelteElement =
9549
| SvelteHTMLElement
9650
| SvelteComponentElement
9751
| SvelteSpecialElement;
52+
9853
type BaseSvelteElement = BaseNode;
9954

10055
/** Node of `<script>` element. */
@@ -616,11 +571,3 @@ export interface SvelteSpecialDirective extends BaseNode {
616571
expression: ESTree.Expression;
617572
parent: SvelteStartTag /* & { parent: SvelteSpecialElement } */;
618573
}
619-
620-
/** Node of `$` statement. */
621-
export interface SvelteReactiveStatement extends BaseNode {
622-
type: "SvelteReactiveStatement";
623-
label: ESTree.Identifier & { name: "$" };
624-
body: ESTree.Statement;
625-
parent: ESTree.Node;
626-
}

src/ast/index.ts

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import type { SvelteHTMLNode } from "./html";
2+
import type { SvelteScriptNode } from "./script";
3+
4+
export * from "./common";
5+
export * from "./html";
6+
export * from "./script";
7+
8+
export type SvelteNode = SvelteHTMLNode | SvelteScriptNode;

src/ast/script.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import type ESTree from "estree";
2+
import type { BaseNode } from "./base";
3+
4+
export type SvelteScriptNode = SvelteReactiveStatement;
5+
6+
/** Node of `$` statement. */
7+
export interface SvelteReactiveStatement extends BaseNode {
8+
type: "SvelteReactiveStatement";
9+
label: ESTree.Identifier & { name: "$" };
10+
body: ESTree.Statement;
11+
parent: ESTree.Node;
12+
}

src/context/index.ts

+29-6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import type {
44
Comment,
55
Locations,
66
Position,
7+
SvelteElement,
8+
SvelteName,
79
SvelteScriptElement,
810
SvelteStyleElement,
911
Token,
@@ -145,6 +147,12 @@ export class Context {
145147

146148
let start = 0;
147149
for (const block of extractBlocks(code)) {
150+
if (block.tag === "template") {
151+
const lang = block.attrs.find((attr) => attr.key.name === "lang");
152+
if (!lang || !lang.value || lang.value.value === "html") {
153+
continue;
154+
}
155+
}
148156
this.blocks.push(block);
149157
templateCode +=
150158
code.slice(start, block.contentRange[0]) +
@@ -183,6 +191,10 @@ export class Context {
183191
};
184192
}
185193

194+
public getIndexFromLoc(loc: { line: number; column: number }): number {
195+
return this.locs.getIndexFromLoc(loc);
196+
}
197+
186198
/**
187199
* Get the location information of the given node.
188200
* @param node The node.
@@ -278,9 +290,14 @@ export class Context {
278290
}
279291

280292
public findBlock(
281-
element: SvelteScriptElement | SvelteStyleElement
293+
element: SvelteScriptElement | SvelteStyleElement | SvelteElement
282294
): Block | undefined {
283-
const tag = element.type === "SvelteScriptElement" ? "script" : "style";
295+
const tag =
296+
element.type === "SvelteScriptElement"
297+
? "script"
298+
: element.type === "SvelteStyleElement"
299+
? "style"
300+
: (element.name as SvelteName).name.toLowerCase();
284301
return this.blocks.find(
285302
(block) =>
286303
block.tag === tag &&
@@ -291,16 +308,17 @@ export class Context {
291308
}
292309

293310
type Block = {
294-
tag: "script" | "style";
311+
tag: "script" | "style" | "template";
295312
attrs: AttributeToken[];
296313
contentRange: [number, number];
297314
};
298315

299316
/** Extract <script> blocks */
300317
function* extractBlocks(code: string): IterableIterator<Block> {
301-
const startTagOpenRe = /<!--[\s\S]*?-->|<(script|style)([\s>])/giu;
318+
const startTagOpenRe = /<!--[\s\S]*?-->|<(script|style|template)([\s>])/giu;
302319
const endScriptTagRe = /<\/script>/giu;
303320
const endStyleTagRe = /<\/style>/giu;
321+
const endTemplateTagRe = /<\/template>/giu;
304322
let startTagOpenMatch;
305323
while ((startTagOpenMatch = startTagOpenRe.exec(code))) {
306324
const [, tag, nextChar] = startTagOpenMatch;
@@ -323,8 +341,13 @@ function* extractBlocks(code: string): IterableIterator<Block> {
323341
continue;
324342
}
325343
}
344+
const lowerTag = tag.toLowerCase() as "script" | "style" | "template";
326345
const endTagRe =
327-
tag.toLowerCase() === "script" ? endScriptTagRe : endStyleTagRe;
346+
lowerTag === "script"
347+
? endScriptTagRe
348+
: lowerTag === "style"
349+
? endStyleTagRe
350+
: endTemplateTagRe;
328351
endTagRe.lastIndex = startTagEnd;
329352
const endTagMatch = endTagRe.exec(code);
330353
if (endTagMatch) {
@@ -333,7 +356,7 @@ function* extractBlocks(code: string): IterableIterator<Block> {
333356
yield {
334357
contentRange,
335358
attrs,
336-
tag: tag as "script" | "style",
359+
tag: lowerTag,
337360
};
338361
startTagOpenRe.lastIndex = endTagRe.lastIndex;
339362
}

src/parser/converts/element.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,11 @@ function convertHTMLElement(
263263
},
264264
});
265265

266-
if (element.name.name === "script" || element.name.name === "style") {
266+
if (
267+
element.name.name === "script" ||
268+
element.name.name === "style" ||
269+
(element.name.name === "template" && ctx.findBlock(element))
270+
) {
267271
for (const child of element.children) {
268272
if (child.type === "SvelteText") {
269273
child.value = ctx.code.slice(...child.range);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<script>
2+
let objs = [{a: 1, b: 2}, {a: 2, b: 3}]
3+
</script>
4+
5+
<template lang="pug">
6+
+each("objs as obj")
7+
p("{...obj}") {obj.a}
8+
</template>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[
2+
{
3+
"ruleId": "no-unused-vars",
4+
"code": "objs",
5+
"line": 2,
6+
"column": 5
7+
}
8+
]

0 commit comments

Comments
 (0)