The main thing this parser does is parsing the *.svelte
file and return an AST that can be parsed by ESLint.
However, this parser does a few other things for a better experience with ESLint integration.
This parser parses *.svelte
via parseForESLint()
and returns the result to ESLint. This is a requirement for ESLint's custom parser.
See https://eslint.org/docs/latest/developer-guide/working-with-custom-parsers.
Returns the AST of the parses result.
Script AST is ESTree compliant AST by default. However, if you used @typescript-eslint/parser
as script parser, it may contain TypeScript AST.
However, the parser assigns a special node SvelteReactiveStatement
to the parsed result of $:
.
SvelteReactiveStatement
is a special node to avoid confusing ESLint check rules with LabeledStatement
.
The HTML template part returns a special AST. See AST.md.
The Program
node contains tokens
and comments
. This is a requirement for ESLint's custom parser.
See https://eslint.org/docs/latest/developer-guide/working-with-custom-parsers#the-ast-specification.
This parser returns the services
returned by the script parser.
In particular, typescript-eslint contains important information such as type information in services
. The parser does not edit the services
, but there is a trick in parsing the script to get the correct result of the services
returned by the script parser.
When parsing the script, the parser does not pass only the <script>
part, but generates virtual script code including the script that converted the HTML template into a script, and lets the script parser parse it.
For example, if you enter *.svelte
template to listen for input events:
<script lang="ts">
function inputHandler () {
// process
}
</script>
<input on:input={inputHandler}>
Parse the following virtual script code as a script:
function inputHandler () {
// process
}
;function $_render1(){
(inputHandler) as ((e:'input' extends keyof HTMLElementEventMap ? HTMLElementEventMap['input'] : CustomEvent<any>) => void );
}
This gives the correct type information to the inputHandler when used with on:input={inputHandler}
.
The script AST for the HTML template is then remapped to the template AST.
You can check what happens to virtual scripts in the Online Demo.
https://sveltejs.github.io/svelte-eslint-parser/virtual-script-code/
See also Scope Types section.
This parser returns a ScopeManager instance.
ScopeManager is used in variable analysis such as no-unused-vars and no-undef rules.
See https://eslint.org/docs/latest/developer-guide/scope-manager-interface for details.
The parser will generate a virtual script so that it can parse the correct scope.
For example, when using {#each}
and {@const}
:
<script lang="ts">
const array = [1, 2, 3]
</script>
{#each array as e}
{@const ee = e * 2}
{ee}
{/each}
Parse the following virtual script code as a script:
const array = [1, 2, 3]
;function $_render1(){
Array.from(array).forEach((e) => {
const ee = e * 2;
(ee);
});
}
This ensures that the variable e
defined by {#each}
is correctly scoped only within {#each}
.
Also, this parser returns special results for variables used in $: foo = expression
and $count
for proper analysis.
It also adds virtual references for variables that are marked specially used in *.svelte
(e.g. export let
and $ref
). This is a hack that is also used in typescript-eslint.
typescript-eslint/typescript-eslint#4508 (comment)
You can also check the results Online DEMO.
ESLint custom parsers that provide their own AST require visitorKeys
to properly traverse the node.
See https://eslint.org/docs/latest/developer-guide/working-with-custom-parsers.
TypeScript's type inference is pretty good, so parsing Svelte as-is gives some wrong type information.
e.g.
export let foo: { bar: number } | null = null
$: console.log(foo && foo.bar);
// ^ never type
(You can see it on TypeScript Online Playground)
In the above code, foo in $:
should be object
or null
in *.svelte
, but TypeScript infers that it is null
only.
To avoid this problem, the parser generates virtual code and traps statements within $:
to function scope.
Then restore it to have the correct AST and ScopeManager.
For example:
<script lang="ts">
export let foo: { bar: number } | null = null
$: console.log(foo && foo.bar);
$: r = foo && foo.bar;
$: ({ bar: n } = foo || { bar: 42 });
</script>
{foo && foo.bar}
Parse the following virtual script code as a script:
export let foo: { bar: number } | null = null
$: function $_reactiveStatementScopeFunction1(){
console.log(foo && foo.bar);
}
$: let r =$_reactiveVariableScopeFunction2();
function $_reactiveVariableScopeFunction2(){
let $_tmpVar3;
return ($_tmpVar3 = foo && foo.bar);
}
$: let { bar: n } =$_reactiveVariableScopeFunction4();
function $_reactiveVariableScopeFunction4(){
let $_tmpVar5;
return ($_tmpVar5 = foo || { bar: 42 });
}
;function $_render6(){
(foo && foo.bar);
}