Skip to content

Commit

Permalink
feat: <template lang="..."> to parse as raw text (#244)
Browse files Browse the repository at this point in the history
* ignore template tag

* Create real-dolphins-nail.md
  • Loading branch information
ota-meshi authored Nov 14, 2022
1 parent 4df6505 commit 7ebf326
Show file tree
Hide file tree
Showing 14 changed files with 2,223 additions and 66 deletions.
5 changes: 5 additions & 0 deletions .changeset/real-dolphins-nail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"svelte-eslint-parser": minor
---

feat: `<template lang="...">` to parse as raw text
2 changes: 1 addition & 1 deletion docs/AST.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ See [ESTree] for the AST node of the script generated by `espree`.
[variabledeclarator]: https://github.com/estree/estree/blob/master/es5.md#variabledeclarator
[pattern]: https://github.com/estree/estree/blob/master/es5.md#patterns

See details: [../src/ast.ts](../src/ast.ts)
See details: [../src/ast/*](../src/ast/)

## Common

Expand Down
6 changes: 6 additions & 0 deletions src/ast/base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { Locations } from "./common";

// internals
export interface BaseNode extends Locations {
type: string;
}
43 changes: 43 additions & 0 deletions src/ast/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { BaseNode } from "./base";

export interface Position {
/** >= 1 */
line: number;
/** >= 0 */
column: number;
}
export type Range = [number, number];
export interface SourceLocation {
start: Position;
end: Position;
}
export interface Locations {
loc: SourceLocation;
range: Range;
}

export interface Token extends BaseNode {
type:
| "Boolean"
| "Null"
| "Identifier"
| "Keyword"
| "Punctuator"
| "JSXIdentifier"
| "JSXText"
| "Numeric"
| "String"
| "RegularExpression"
| "Template"
// HTML
| "HTMLText"
| "HTMLIdentifier"
| "MustacheKeyword"
| "HTMLComment";
value: string;
}

export interface Comment extends BaseNode {
type: "Line" | "Block";
value: string;
}
63 changes: 5 additions & 58 deletions src/ast.ts → src/ast/html.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,8 @@
import type ESTree from "estree";
export type Range = [number, number];
import type { BaseNode } from "./base";
import type { Token, Comment } from "./common";

export interface SourceLocation {
start: Position;
end: Position;
}
export interface Locations {
loc: SourceLocation;
range: Range;
}

interface BaseNode extends Locations {
type: string;
}

export interface Token extends BaseNode {
type:
| "Boolean"
| "Null"
| "Identifier"
| "Keyword"
| "Punctuator"
| "JSXIdentifier"
| "JSXText"
| "Numeric"
| "String"
| "RegularExpression"
| "Template"
// HTML
| "HTMLText"
| "HTMLIdentifier"
| "MustacheKeyword"
| "HTMLComment";
value: string;
}

export interface Comment extends BaseNode {
type: "Line" | "Block";
value: string;
}

export interface Position {
/** >= 1 */
line: number;
/** >= 0 */
column: number;
}

export type SvelteNode =
export type SvelteHTMLNode =
| SvelteProgram
| SvelteScriptElement
| SvelteStyleElement
Expand Down Expand Up @@ -77,8 +32,7 @@ export type SvelteNode =
| SvelteSpecialDirective
| SvelteDirectiveKey
| SvelteSpecialDirectiveKey
| SvelteHTMLComment
| SvelteReactiveStatement;
| SvelteHTMLComment;

/** Node of Svelte program root */
export interface SvelteProgram extends BaseNode {
Expand All @@ -95,6 +49,7 @@ export type SvelteElement =
| SvelteHTMLElement
| SvelteComponentElement
| SvelteSpecialElement;

type BaseSvelteElement = BaseNode;

/** Node of `<script>` element. */
Expand Down Expand Up @@ -616,11 +571,3 @@ export interface SvelteSpecialDirective extends BaseNode {
expression: ESTree.Expression;
parent: SvelteStartTag /* & { parent: SvelteSpecialElement } */;
}

/** Node of `$` statement. */
export interface SvelteReactiveStatement extends BaseNode {
type: "SvelteReactiveStatement";
label: ESTree.Identifier & { name: "$" };
body: ESTree.Statement;
parent: ESTree.Node;
}
8 changes: 8 additions & 0 deletions src/ast/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { SvelteHTMLNode } from "./html";
import type { SvelteScriptNode } from "./script";

export * from "./common";
export * from "./html";
export * from "./script";

export type SvelteNode = SvelteHTMLNode | SvelteScriptNode;
12 changes: 12 additions & 0 deletions src/ast/script.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type ESTree from "estree";
import type { BaseNode } from "./base";

export type SvelteScriptNode = SvelteReactiveStatement;

/** Node of `$` statement. */
export interface SvelteReactiveStatement extends BaseNode {
type: "SvelteReactiveStatement";
label: ESTree.Identifier & { name: "$" };
body: ESTree.Statement;
parent: ESTree.Node;
}
35 changes: 29 additions & 6 deletions src/context/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import type {
Comment,
Locations,
Position,
SvelteElement,
SvelteName,
SvelteScriptElement,
SvelteStyleElement,
Token,
Expand Down Expand Up @@ -145,6 +147,12 @@ export class Context {

let start = 0;
for (const block of extractBlocks(code)) {
if (block.tag === "template") {
const lang = block.attrs.find((attr) => attr.key.name === "lang");
if (!lang || !lang.value || lang.value.value === "html") {
continue;
}
}
this.blocks.push(block);
templateCode +=
code.slice(start, block.contentRange[0]) +
Expand Down Expand Up @@ -183,6 +191,10 @@ export class Context {
};
}

public getIndexFromLoc(loc: { line: number; column: number }): number {
return this.locs.getIndexFromLoc(loc);
}

/**
* Get the location information of the given node.
* @param node The node.
Expand Down Expand Up @@ -278,9 +290,14 @@ export class Context {
}

public findBlock(
element: SvelteScriptElement | SvelteStyleElement
element: SvelteScriptElement | SvelteStyleElement | SvelteElement
): Block | undefined {
const tag = element.type === "SvelteScriptElement" ? "script" : "style";
const tag =
element.type === "SvelteScriptElement"
? "script"
: element.type === "SvelteStyleElement"
? "style"
: (element.name as SvelteName).name.toLowerCase();
return this.blocks.find(
(block) =>
block.tag === tag &&
Expand All @@ -291,16 +308,17 @@ export class Context {
}

type Block = {
tag: "script" | "style";
tag: "script" | "style" | "template";
attrs: AttributeToken[];
contentRange: [number, number];
};

/** Extract <script> blocks */
function* extractBlocks(code: string): IterableIterator<Block> {
const startTagOpenRe = /<!--[\s\S]*?-->|<(script|style)([\s>])/giu;
const startTagOpenRe = /<!--[\s\S]*?-->|<(script|style|template)([\s>])/giu;
const endScriptTagRe = /<\/script>/giu;
const endStyleTagRe = /<\/style>/giu;
const endTemplateTagRe = /<\/template>/giu;
let startTagOpenMatch;
while ((startTagOpenMatch = startTagOpenRe.exec(code))) {
const [, tag, nextChar] = startTagOpenMatch;
Expand All @@ -323,8 +341,13 @@ function* extractBlocks(code: string): IterableIterator<Block> {
continue;
}
}
const lowerTag = tag.toLowerCase() as "script" | "style" | "template";
const endTagRe =
tag.toLowerCase() === "script" ? endScriptTagRe : endStyleTagRe;
lowerTag === "script"
? endScriptTagRe
: lowerTag === "style"
? endStyleTagRe
: endTemplateTagRe;
endTagRe.lastIndex = startTagEnd;
const endTagMatch = endTagRe.exec(code);
if (endTagMatch) {
Expand All @@ -333,7 +356,7 @@ function* extractBlocks(code: string): IterableIterator<Block> {
yield {
contentRange,
attrs,
tag: tag as "script" | "style",
tag: lowerTag,
};
startTagOpenRe.lastIndex = endTagRe.lastIndex;
}
Expand Down
6 changes: 5 additions & 1 deletion src/parser/converts/element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,11 @@ function convertHTMLElement(
},
});

if (element.name.name === "script" || element.name.name === "style") {
if (
element.name.name === "script" ||
element.name.name === "style" ||
(element.name.name === "template" && ctx.findBlock(element))
) {
for (const child of element.children) {
if (child.type === "SvelteText") {
child.value = ctx.code.slice(...child.range);
Expand Down
8 changes: 8 additions & 0 deletions tests/fixtures/parser/ast/pug/each/each01-input.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script>
let objs = [{a: 1, b: 2}, {a: 2, b: 3}]
</script>

<template lang="pug">
+each("objs as obj")
p("{...obj}") {obj.a}
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[
{
"ruleId": "no-unused-vars",
"code": "objs",
"line": 2,
"column": 5
}
]
Loading

0 comments on commit 7ebf326

Please sign in to comment.