diff --git a/src/parser/converts/block.ts b/src/parser/converts/block.ts index a25b2686..59f8c274 100644 --- a/src/parser/converts/block.ts +++ b/src/parser/converts/block.ts @@ -13,6 +13,30 @@ import type { Context } from "../../context" import { convertChildren } from "./element" import { getWithLoc, indexOf, lastIndexOf } from "./common" +/** Get start index of block */ +function startBlockIndex(code: string, endIndex: number): number { + return lastIndexOf( + code, + (c, index) => { + if (c !== "{") { + return false + } + for (let next = index + 1; next < code.length; next++) { + const nextC = code[next] + if (!nextC.trim()) { + continue + } + return ( + code.startsWith("#if", next) || + code.startsWith(":else", next) + ) + } + return false + }, + endIndex, + ) +} + /** Convert for IfBlock */ export function convertIfBlock( node: SvAST.IfBlock, @@ -22,7 +46,7 @@ export function convertIfBlock( // {#if expr} {:else} {/if} // {:else if expr} {/if} const nodeStart = node.elseif - ? ctx.code.lastIndexOf("{", node.start) + ? startBlockIndex(ctx.code, node.start - 1) : node.start const ifBlock: SvelteIfBlock = { type: "SvelteIfBlock", @@ -49,7 +73,7 @@ export function convertIfBlock( return ifBlock } - const elseStart = ctx.code.lastIndexOf("{", node.else.start) + const elseStart = startBlockIndex(ctx.code, node.else.start - 1) const elseBlock: SvelteElseBlock = { type: "SvelteElseBlock", @@ -149,7 +173,7 @@ export function convertEachBlock( return eachBlock } - const elseStart = ctx.code.lastIndexOf("{", node.else.start) + const elseStart = startBlockIndex(ctx.code, node.else.start - 1) const elseBlock: SvelteElseBlock = { type: "SvelteElseBlock", diff --git a/src/parser/converts/common.ts b/src/parser/converts/common.ts index 12980efb..57370bb7 100644 --- a/src/parser/converts/common.ts +++ b/src/parser/converts/common.ts @@ -17,12 +17,12 @@ export function indexOf( /** lastIndexOf */ export function lastIndexOf( str: string, - search: (c: string) => boolean, + search: (c: string, index: number) => boolean, end: number, ): number { for (let index = end; index >= 0; index--) { const c = str[index] - if (search(c)) { + if (search(c, index)) { return index } } diff --git a/tests/fixtures/parser/ast/if-block01-input.svelte b/tests/fixtures/parser/ast/if-block01-input.svelte new file mode 100644 index 00000000..0c10721d --- /dev/null +++ b/tests/fixtures/parser/ast/if-block01-input.svelte @@ -0,0 +1 @@ +{#if expression}{:else if expression}{:else}{/if} diff --git a/tests/fixtures/parser/ast/if-block01-no-undef-result.json b/tests/fixtures/parser/ast/if-block01-no-undef-result.json new file mode 100644 index 00000000..0cc7e078 --- /dev/null +++ b/tests/fixtures/parser/ast/if-block01-no-undef-result.json @@ -0,0 +1,14 @@ +[ + { + "ruleId": "no-undef", + "code": "expression", + "line": 1, + "column": 6 + }, + { + "ruleId": "no-undef", + "code": "expression", + "line": 1, + "column": 27 + } +] \ No newline at end of file diff --git a/tests/fixtures/parser/ast/if-block01-output.json b/tests/fixtures/parser/ast/if-block01-output.json new file mode 100644 index 00000000..359c6b8e --- /dev/null +++ b/tests/fixtures/parser/ast/if-block01-output.json @@ -0,0 +1,404 @@ +{ + "type": "Program", + "body": [ + { + "type": "SvelteIfBlock", + "elseif": false, + "expression": { + "type": "Identifier", + "name": "expression", + "range": [ + 5, + 15 + ], + "loc": { + "start": { + "line": 1, + "column": 5 + }, + "end": { + "line": 1, + "column": 15 + } + } + }, + "children": [], + "else": { + "type": "SvelteElseBlock", + "children": [ + { + "type": "SvelteIfBlock", + "elseif": true, + "expression": { + "type": "Identifier", + "name": "expression", + "range": [ + 26, + 36 + ], + "loc": { + "start": { + "line": 1, + "column": 26 + }, + "end": { + "line": 1, + "column": 36 + } + } + }, + "children": [], + "else": { + "type": "SvelteElseBlock", + "children": [], + "range": [ + 37, + 44 + ], + "loc": { + "start": { + "line": 1, + "column": 37 + }, + "end": { + "line": 1, + "column": 44 + } + } + }, + "range": [ + 16, + 49 + ], + "loc": { + "start": { + "line": 1, + "column": 16 + }, + "end": { + "line": 1, + "column": 49 + } + } + } + ], + "range": [ + 16, + 49 + ], + "loc": { + "start": { + "line": 1, + "column": 16 + }, + "end": { + "line": 1, + "column": 49 + } + } + }, + "range": [ + 0, + 49 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 49 + } + } + } + ], + "sourceType": "module", + "comments": [], + "tokens": [ + { + "type": "Punctuator", + "value": "{", + "range": [ + 0, + 1 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + { + "type": "MustacheKeyword", + "value": "#if", + "range": [ + 1, + 4 + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 4 + } + } + }, + { + "type": "Identifier", + "value": "expression", + "range": [ + 5, + 15 + ], + "loc": { + "start": { + "line": 1, + "column": 5 + }, + "end": { + "line": 1, + "column": 15 + } + } + }, + { + "type": "Punctuator", + "value": "}", + "range": [ + 15, + 16 + ], + "loc": { + "start": { + "line": 1, + "column": 15 + }, + "end": { + "line": 1, + "column": 16 + } + } + }, + { + "type": "Punctuator", + "value": "{", + "range": [ + 16, + 17 + ], + "loc": { + "start": { + "line": 1, + "column": 16 + }, + "end": { + "line": 1, + "column": 17 + } + } + }, + { + "type": "MustacheKeyword", + "value": ":else", + "range": [ + 17, + 22 + ], + "loc": { + "start": { + "line": 1, + "column": 17 + }, + "end": { + "line": 1, + "column": 22 + } + } + }, + { + "type": "MustacheKeyword", + "value": "if", + "range": [ + 23, + 25 + ], + "loc": { + "start": { + "line": 1, + "column": 23 + }, + "end": { + "line": 1, + "column": 25 + } + } + }, + { + "type": "Identifier", + "value": "expression", + "range": [ + 26, + 36 + ], + "loc": { + "start": { + "line": 1, + "column": 26 + }, + "end": { + "line": 1, + "column": 36 + } + } + }, + { + "type": "Punctuator", + "value": "}", + "range": [ + 36, + 37 + ], + "loc": { + "start": { + "line": 1, + "column": 36 + }, + "end": { + "line": 1, + "column": 37 + } + } + }, + { + "type": "Punctuator", + "value": "{", + "range": [ + 37, + 38 + ], + "loc": { + "start": { + "line": 1, + "column": 37 + }, + "end": { + "line": 1, + "column": 38 + } + } + }, + { + "type": "MustacheKeyword", + "value": ":else", + "range": [ + 38, + 43 + ], + "loc": { + "start": { + "line": 1, + "column": 38 + }, + "end": { + "line": 1, + "column": 43 + } + } + }, + { + "type": "Punctuator", + "value": "}", + "range": [ + 43, + 44 + ], + "loc": { + "start": { + "line": 1, + "column": 43 + }, + "end": { + "line": 1, + "column": 44 + } + } + }, + { + "type": "Punctuator", + "value": "{", + "range": [ + 44, + 45 + ], + "loc": { + "start": { + "line": 1, + "column": 44 + }, + "end": { + "line": 1, + "column": 45 + } + } + }, + { + "type": "MustacheKeyword", + "value": "/if", + "range": [ + 45, + 48 + ], + "loc": { + "start": { + "line": 1, + "column": 45 + }, + "end": { + "line": 1, + "column": 48 + } + } + }, + { + "type": "Punctuator", + "value": "}", + "range": [ + 48, + 49 + ], + "loc": { + "start": { + "line": 1, + "column": 48 + }, + "end": { + "line": 1, + "column": 49 + } + } + } + ], + "range": [ + 0, + 50 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 0 + } + } +} \ No newline at end of file diff --git a/tests/fixtures/parser/ast/if-block01-scope-output.json b/tests/fixtures/parser/ast/if-block01-scope-output.json new file mode 100644 index 00000000..dc781fe1 --- /dev/null +++ b/tests/fixtures/parser/ast/if-block01-scope-output.json @@ -0,0 +1,197 @@ +{ + "type": "global", + "variables": [ + { + "name": "$$slots", + "identifiers": [], + "defs": [], + "references": [] + }, + { + "name": "$$props", + "identifiers": [], + "defs": [], + "references": [] + }, + { + "name": "$$restProps", + "identifiers": [], + "defs": [], + "references": [] + } + ], + "references": [], + "childScopes": [ + { + "type": "module", + "variables": [], + "references": [ + { + "identifier": { + "type": "Identifier", + "name": "expression", + "range": [ + 5, + 15 + ], + "loc": { + "start": { + "line": 1, + "column": 5 + }, + "end": { + "line": 1, + "column": 15 + } + } + }, + "from": "module", + "init": null, + "resolved": null + }, + { + "identifier": { + "type": "Identifier", + "name": "expression", + "range": [ + 26, + 36 + ], + "loc": { + "start": { + "line": 1, + "column": 26 + }, + "end": { + "line": 1, + "column": 36 + } + } + }, + "from": "module", + "init": null, + "resolved": null + } + ], + "childScopes": [ + { + "type": "block", + "variables": [], + "references": [], + "childScopes": [], + "through": [] + }, + { + "type": "block", + "variables": [], + "references": [], + "childScopes": [], + "through": [] + }, + { + "type": "block", + "variables": [], + "references": [], + "childScopes": [], + "through": [] + } + ], + "through": [ + { + "identifier": { + "type": "Identifier", + "name": "expression", + "range": [ + 5, + 15 + ], + "loc": { + "start": { + "line": 1, + "column": 5 + }, + "end": { + "line": 1, + "column": 15 + } + } + }, + "from": "module", + "init": null, + "resolved": null + }, + { + "identifier": { + "type": "Identifier", + "name": "expression", + "range": [ + 26, + 36 + ], + "loc": { + "start": { + "line": 1, + "column": 26 + }, + "end": { + "line": 1, + "column": 36 + } + } + }, + "from": "module", + "init": null, + "resolved": null + } + ] + } + ], + "through": [ + { + "identifier": { + "type": "Identifier", + "name": "expression", + "range": [ + 5, + 15 + ], + "loc": { + "start": { + "line": 1, + "column": 5 + }, + "end": { + "line": 1, + "column": 15 + } + } + }, + "from": "module", + "init": null, + "resolved": null + }, + { + "identifier": { + "type": "Identifier", + "name": "expression", + "range": [ + 26, + 36 + ], + "loc": { + "start": { + "line": 1, + "column": 26 + }, + "end": { + "line": 1, + "column": 36 + } + } + }, + "from": "module", + "init": null, + "resolved": null + } + ] +} \ No newline at end of file