Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: sveltejs/svelte-eslint-parser
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: main
Choose a base ref
...
head repository: AlbertMarashi/svelte-eslint-parser
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref
  • 2 commits
  • 35 files changed
  • 1 contributor

Commits on Aug 14, 2024

  1. Verified

    This commit was signed with the committer’s verified signature.
    AlbertMarashi Albert Marashi
    Copy the full SHA
    feeb791 View commit details
  2. WIP

    AlbertMarashi committed Aug 14, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    AlbertMarashi Albert Marashi
    Copy the full SHA
    d145ce0 View commit details
Showing with 7,216 additions and 915 deletions.
  1. +111 −0 src/context/render.ts
  2. +52 −8 src/context/script-let.ts
  3. +16 −2 src/parser/converts/attr.ts
  4. +2 −0 src/parser/converts/block.ts
  5. +1 −1 src/parser/converts/element.ts
  6. +13 −2 src/parser/converts/mustache.ts
  7. +2 −0 src/parser/converts/render.ts
  8. +2 −0 src/parser/typescript/analyze/index.ts
  9. +3 −3 tests/fixtures/parser/ast/docs/template-syntax/05-hash-if/01-no-undef-result.json
  10. +165 −20 tests/fixtures/parser/ast/docs/template-syntax/05-hash-if/01-output.json
  11. +18 −18 tests/fixtures/parser/ast/docs/template-syntax/05-hash-if/01-scope-output.json
  12. +3 −3 tests/fixtures/parser/ast/docs/template-syntax/05-hash-if/03-no-undef-result.json
  13. +332 −99 tests/fixtures/parser/ast/docs/template-syntax/05-hash-if/03-output.json
  14. +18 −18 tests/fixtures/parser/ast/docs/template-syntax/05-hash-if/03-scope-output.json
  15. +1 −0 tests/fixtures/parser/ast/docs/template-syntax/06-hash-each/01-input.svelte
  16. +7 −7 tests/fixtures/parser/ast/docs/template-syntax/06-hash-each/01-no-undef-result.json
  17. +7 −7 tests/fixtures/parser/ast/docs/template-syntax/06-hash-each/01-no-unused-vars-result.json
  18. +771 −390 tests/fixtures/parser/ast/docs/template-syntax/06-hash-each/01-output.json
  19. +296 −296 tests/fixtures/parser/ast/docs/template-syntax/06-hash-each/01-scope-output.json
  20. +3 −3 tests/fixtures/parser/ast/if-block01-no-undef-result.json
  21. +165 −20 tests/fixtures/parser/ast/if-block01-output.json
  22. +18 −18 tests/fixtures/parser/ast/if-block01-scope-output.json
  23. +8 −0 tests/fixtures/parser/ast/svelte-else-input.svelte
  24. +1,507 −0 tests/fixtures/parser/ast/svelte-else-output.json
  25. +8 −0 tests/fixtures/parser/ast/svelte-else-prefer-const-result.json
  26. +1,345 −0 tests/fixtures/parser/ast/svelte-else-scope-output.json
  27. +4 −0 tests/fixtures/parser/ast/ts-as-mustache-element-input.svelte
  28. +659 −0 tests/fixtures/parser/ast/ts-as-mustache-element-output.json
  29. +8 −0 tests/fixtures/parser/ast/ts-as-mustache-element-prefer-const-result.json
  30. +298 −0 tests/fixtures/parser/ast/ts-as-mustache-element-scope-output.json
  31. +4 −0 tests/fixtures/parser/ast/ts-as-template-string-input.svelte
  32. +1,060 −0 tests/fixtures/parser/ast/ts-as-template-string-output.json
  33. +8 −0 tests/fixtures/parser/ast/ts-as-template-string-prefer-const-result.json
  34. +298 −0 tests/fixtures/parser/ast/ts-as-template-string-scope-output.json
  35. +3 −0 tests/src/parser/test-utils.ts
111 changes: 111 additions & 0 deletions src/context/render.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import type * as ESTree from "estree";
import type { ScopeManager, Scope } from "eslint-scope";
import type {
Comment,
SvelteEachBlock,
SvelteElement,
SvelteElseBlockAlone,
SvelteElseBlockElseIf,
SvelteIfBlock,
SvelteIfBlockElseIf,
SvelteName,
SvelteNode,
SvelteSnippetBlock,
Token,
} from "../ast";

type Children = SvelteElement["children"];

enum RenderPartType {
real,
virtual,
}

type RenderPart =
| { code: string; type: RenderPartType.virtual }
| { code: string; type: RenderPartType.real; node: SvelteNode | ESTree.Node };

export class SvelteRenderContext {
private readonly code: string;

private readonly parts: RenderPart[] = [];

public constructor(code: string) {
this.code = code;
}

public nestIfBlock(ifBlock: SvelteIfBlock | SvelteIfBlockElseIf): void {
this.wrapInTemplateExpression(() => {
this.appendNode(ifBlock.expression);
this.appendVirtualFragment("?");
this.appendChildren(ifBlock.children);
this.appendVirtualFragment(":");
if (ifBlock.else) {
if (ifBlock.else.elseif) {
this.nestIfBlock(ifBlock.else.children[0]);
} else {
this.appendChildren(ifBlock.else.children);
}
} else {
this.appendVirtualFragment("``");
}
});
}

private appendChildren(children: Children) {
if (children.length === 0) this.appendVirtualFragment("");
if (children.length === 1) this.appendChild(children[0]);

this.wrapInTemplateString(() => {
children.map((child) =>
this.wrapInTemplateExpression(() => this.appendChild(child)),
);
});
}

private appendChild(_child: SvelteNode | ESTree.Node) {
// TODO
}

private nestEachBlock(eachBlock: SvelteEachBlock) {
if (eachBlock.else) {
// TODO: {array}.length
this.appendVirtualFragment("?");
}
// TODO: array
this.appendVirtualFragment(".map(");
// TODO: params, and key expression
this.appendVirtualFragment("=>");
this.appendChildren(eachBlock.children);
this.appendVirtualFragment(")");

if (eachBlock.else) {
this.appendVirtualFragment(":");
this.appendChildren(eachBlock.else.children);
}
}

private wrapInTemplateString(callback: () => void) {
this.appendVirtualFragment("`");
callback();
this.appendVirtualFragment("`");
}

private appendNode(node: SvelteNode | ESTree.Node) {
this.parts.push({
code: this.code.slice(...node.range!),
type: RenderPartType.Real,
node,
});
}

private wrapInTemplateExpression(callback: () => void) {
this.appendVirtualFragment("${");
callback();
this.appendVirtualFragment("}");
}

private appendVirtualFragment(str: string) {
this.parts.push({ code: str, type: RenderPartType.Virtual });
}
}
60 changes: 52 additions & 8 deletions src/context/script-let.ts
Original file line number Diff line number Diff line change
@@ -155,28 +155,63 @@ export class ScriptLetContext {
...callbacks: ScriptLetCallback<E>[]
): ScriptLetCallback<E>[] {
const range = getNodeRange(expression);
return this.addExpressionFromRange(range, parent, typing, ...callbacks);
return this.addExpressionFromRange(
range,
parent,
false,
typing,
...callbacks,
);
}

public addExpressionTemplateLiteral<E extends ESTree.Expression>(
expression: E,
parent: SvelteNode,
asTemplateLiteral: boolean,
typing?: string | null,
...callbacks: ScriptLetCallback<E>[]
): ScriptLetCallback<E>[] {
const range = getNodeRange(expression);
return this.addExpressionFromRange(
range,
parent,
asTemplateLiteral,
typing,
...callbacks,
);
}

public addExpressionFromRange<E extends ESTree.Expression>(
range: [number, number],
parent: SvelteNode,
asTemplateLiteral: boolean,
typing?: string | null,
...callbacks: ScriptLetCallback<E>[]
): ScriptLetCallback<E>[] {
const part = this.ctx.code.slice(...range);
const isTS = typing && this.ctx.isTypeScript();
const isTS = Boolean(typing) && this.ctx.isTypeScript();
this.appendScript(
`(${part})${isTS ? `as (${typing})` : ""};`,
range[0] - 1,
asTemplateLiteral
? `(\`\${${part}${isTS ? ` as ${typing}` : ""}}\`);`
: `(${part})${isTS ? `as (${typing})` : ""};`,
asTemplateLiteral ? range[0] - 4 : range[0] - 1,
this.currentScriptScopeKind,
"ExpressionStatement",
(st, tokens, comments, result) => {
const exprSt = st as ESTree.ExpressionStatement;
const tsAs: TSAsExpression | null = isTS
? (exprSt.expression as any)
: null;
const node: ESTree.Expression = tsAs?.expression || exprSt.expression;

const node: ESTree.Expression = tsAs
? asTemplateLiteral
? ((tsAs.expression as TSESTree.TemplateLiteral)
.expressions[0] as any)
: (tsAs.expression as TSESTree.Expression)
: asTemplateLiteral
? ((exprSt.expression as ESTree.TemplateLiteral)
.expressions[0] as any)
: exprSt.expression;
// Process for nodes
for (const callback of callbacks) {
callback(node as E, result);
@@ -186,6 +221,11 @@ export class ScriptLetContext {
tokens.pop(); // )
tokens.pop(); // ;

if (asTemplateLiteral) {
tokens.shift(); // `${
tokens.pop(); // }`
}

if (isTS) {
for (const scope of extractTypeNodeScopes(
tsAs!.typeAnnotation,
@@ -196,7 +236,7 @@ export class ScriptLetContext {
this.remapNodes(
[
{
offset: range[0] - 1,
offset: asTemplateLiteral ? range[0] - 4 : range[0] - 1,
range,
newNode: node,
},
@@ -622,9 +662,13 @@ export class ScriptLetContext {
resolvedParams = params;
}
if (!resolvedParams || resolvedParams.length === 0) {
const isElseBlock =
block.type === "SvelteElseBlock" &&
block.elseif === false &&
block.parent.type !== "SvelteEachBlock";
const restore = this.appendScript(
`{`,
block.range[0],
isElseBlock ? `else{` : `{`,
isElseBlock ? block.range[0] - 4 : block.range[0],
this.currentScriptScopeKind,
"BlockStatement",
(st, tokens, _comments, result) => {
18 changes: 16 additions & 2 deletions src/parser/converts/attr.ts
Original file line number Diff line number Diff line change
@@ -243,6 +243,8 @@ function processAttributeValue(
}
return v;
});

const containsLiterals = nodes.some((v) => v.type === "Text");
if (
nodes.length === 1 &&
(nodes[0].type === "ExpressionTag" || nodes[0].type === "MustacheTag") &&
@@ -253,7 +255,13 @@ function processAttributeValue(
attribute.key.name,
ctx,
);
const mustache = convertMustacheTag(nodes[0], attribute, typing, ctx);
const mustache = convertMustacheTag(
nodes[0],
attribute,
typing,
ctx,
false,
);
attribute.value.push(mustache);
return;
}
@@ -263,7 +271,13 @@ function processAttributeValue(
continue;
}
if (v.type === "ExpressionTag" || v.type === "MustacheTag") {
const mustache = convertMustacheTag(v, attribute, null, ctx);
const mustache = convertMustacheTag(
v,
attribute,
null,
ctx,
containsLiterals,
);
attribute.value.push(mustache);
continue;
}
2 changes: 2 additions & 0 deletions src/parser/converts/block.ts
Original file line number Diff line number Diff line change
@@ -278,6 +278,8 @@ export function convertEachBlock(
...ctx.getConvertLocation({ start: nodeStart, end: node.end }),
};

console.log(node);

let indexRange: null | { start: number; end: number } = null;

if (node.index) {
2 changes: 1 addition & 1 deletion src/parser/converts/element.ts
Original file line number Diff line number Diff line change
@@ -138,7 +138,7 @@ export function* convertChildren(
continue;
}
if (child.type === "ExpressionTag" || child.type === "MustacheTag") {
yield convertMustacheTag(child, parent, null, ctx);
yield convertMustacheTag(child, parent, null, ctx, true);
continue;
}
if (child.type === "HtmlTag" || child.type === "RawMustacheTag") {
15 changes: 13 additions & 2 deletions src/parser/converts/mustache.ts
Original file line number Diff line number Diff line change
@@ -15,8 +15,16 @@ export function convertMustacheTag(
parent: SvelteMustacheTag["parent"],
typing: string | null,
ctx: Context,
asTemplateLiteral: boolean,
): SvelteMustacheTagText {
return convertMustacheTag0(node, "text", parent, typing, ctx);
return convertMustacheTag0(
node,
"text",
parent,
typing,
ctx,
asTemplateLiteral,
);
}
/** Convert for MustacheTag */
export function convertRawMustacheTag(
@@ -30,6 +38,7 @@ export function convertRawMustacheTag(
parent,
null,
ctx,
true,
);
const atHtmlStart = ctx.code.indexOf("@html", mustache.range[0]);
ctx.addToken("MustacheKeyword", {
@@ -75,6 +84,7 @@ function convertMustacheTag0<T extends SvelteMustacheTag>(
parent: T["parent"],
typing: string | null,
ctx: Context,
asTemplateLiteral: boolean,
): T {
const mustache = {
type: "SvelteMustacheTag",
@@ -84,9 +94,10 @@ function convertMustacheTag0<T extends SvelteMustacheTag>(
...ctx.getConvertLocation(node),
} as T;

ctx.scriptLet.addExpression(
ctx.scriptLet.addExpressionTemplateLiteral(
node.expression,
mustache,
asTemplateLiteral,
hasTypeInfo(node.expression) ? null : typing,
(es) => {
mustache.expression = es;
2 changes: 2 additions & 0 deletions src/parser/converts/render.ts
Original file line number Diff line number Diff line change
@@ -20,6 +20,8 @@ export function convertRenderTag(
ctx.scriptLet.addExpressionFromRange(
[callRange.start, callRange.end],
mustache,
// TODO: not sure if this should be true, since we are rendering a string
false,
null,
(
expression:
2 changes: 2 additions & 0 deletions src/parser/typescript/analyze/index.ts
Original file line number Diff line number Diff line change
@@ -82,6 +82,8 @@ export function analyzeTypeScriptInSvelte(

ctx.appendOriginalToEnd();

console.log(code.render);

return ctx;
}
/**
Original file line number Diff line number Diff line change
@@ -19,8 +19,8 @@
},
{
"ruleId": "no-undef",
"code": "expression",
"line": 3,
"column": 6
"code": "",
"line": 5,
"column": 52
}
]
Loading