feat: Phase 1 — extract the client (web SPA + desktop) to dtoro/oikos-web
Some checks failed
ci / build-test (push) Has been cancelled
ci / docker-build (push) Has been cancelled

Problem: the hexagonal refactor churns the backend tree for nine more
phases; the UI delivery stack (web/ SPA, cmd/desktop Wails wrapper,
compose/web image) must move to its own repo first so doc/layout
rewrites land once on a backend-only tree.

Change:
- New repo git.hubris.network/dtoro/oikos-web (v0.33.0): web/, desktop/
  (updateURL repointed to oikos-web releases), compose/, own CI (web +
  desktop jobs), own deploy script (CI-green gate, TOCTOU guard,
  version-tagged images, prune-to-3), own webhook receiver on :9798 +
  launchd unit, own compose project publishing the same 8091:80.
- Cutover executed on mac-mini in order: oikos stack's web service
  stopped+removed, oikos-web project brought up on 8091; outer Caddy
  untouched (targets the published port) — serving + Authentik flow +
  /wails 404 quirk verified post-cutover.
- Stripped from oikos: web/, cmd/desktop/, compose/web/, desktop CI
  workflow, ci.yml web job, Makefile ui/desktop/desktop-package/install
  targets, the compose web service, oikos-web from deploy.sh's fallback
  prune list; wails + go-keyring dropped from go.mod, vendor synced.
- README / CONTRIBUTING / AGENTS.md / .agents dev+operations docs now
  point at the new repo; mbse + mascot design docs carry a path note.

Risk: production SPA serving depends on the new pipeline now; rollback
is versioned-image re-up of the old web service from a pre-split
checkout (port 8091). Desktop builds installed before the split still
check dtoro/oikos releases — one manual reinstall, noted in the
oikos-web release notes.

Verification: go vet, make test (race), make generate-check, golangci
(no new findings; baseline down 400→365); post-cutover curls —
localhost:8091 200, /wails/runtime.js 404, outer Caddy 302 Authentik.
This commit is contained in:
2026-08-15 22:27:52 +02:00
parent e074f04bdf
commit d4d99a7473
18854 changed files with 2615729 additions and 173735 deletions

21
web/node_modules/svelte-eslint-parser/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Yosuke Ota
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

449
web/node_modules/svelte-eslint-parser/README.md generated vendored Normal file
View File

@@ -0,0 +1,449 @@
# svelte-eslint-parser
[Svelte] parser for [ESLint].
You can check it on [Online DEMO](https://sveltejs.github.io/svelte-eslint-parser/playground).
**_Note that this parser has experimental support for Svelte v5, but may break with new versions of Svelte v5._**
[![NPM license](https://img.shields.io/npm/l/svelte-eslint-parser.svg)](https://www.npmjs.com/package/svelte-eslint-parser)
[![NPM version](https://img.shields.io/npm/v/svelte-eslint-parser.svg)](https://www.npmjs.com/package/svelte-eslint-parser)
[![NPM downloads](https://img.shields.io/badge/dynamic/json.svg?label=downloads&colorB=green&suffix=/day&query=$.downloads&uri=https://api.npmjs.org//downloads/point/last-day/svelte-eslint-parser&maxAge=3600)](http://www.npmtrends.com/svelte-eslint-parser)
[![NPM downloads](https://img.shields.io/npm/dw/svelte-eslint-parser.svg)](http://www.npmtrends.com/svelte-eslint-parser)
[![NPM downloads](https://img.shields.io/npm/dm/svelte-eslint-parser.svg)](http://www.npmtrends.com/svelte-eslint-parser)
[![NPM downloads](https://img.shields.io/npm/dy/svelte-eslint-parser.svg)](http://www.npmtrends.com/svelte-eslint-parser)
[![NPM downloads](https://img.shields.io/npm/dt/svelte-eslint-parser.svg)](http://www.npmtrends.com/svelte-eslint-parser)
[![Build Status](https://github.com/sveltejs/svelte-eslint-parser/workflows/CI/badge.svg?branch=main)](https://github.com/sveltejs/svelte-eslint-parser/actions?query=workflow%3ACI)
[![Coverage Status](https://coveralls.io/repos/github/sveltejs/svelte-eslint-parser/badge.svg?branch=main)](https://coveralls.io/github/sveltejs/svelte-eslint-parser?branch=main)
## ⤴️ Motivation
The [svelte-eslint-parser] aims to make it easy to create your own ESLint rules for [Svelte].
The [`eslint-plugin-svelte`] is an ESLint plugin that uses the [svelte-eslint-parser]. I have already [implemented some rules].
[`eslint-plugin-svelte`]: https://github.com/sveltejs/eslint-plugin-svelte
[implemented some rules]: https://sveltejs.github.io/eslint-plugin-svelte/rules/
### ESLint Plugins Using svelte-eslint-parser
#### [eslint-plugin-svelte](https://sveltejs.github.io/eslint-plugin-svelte/)
ESLint plugin for Svelte.
It provides many unique check rules by using the template AST.
#### [@intlify/eslint-plugin-svelte](https://github.com/intlify/eslint-plugin-svelte)
ESLint plugin for internationalization (i18n) with Svelte.
It provides rules to help internationalization your application created with Svelte.
## ❗ Attention
The [svelte-eslint-parser] can not be used with the [eslint-plugin-svelte3].
[svelte-eslint-parser]: https://github.com/sveltejs/svelte-eslint-parser
## 💿 Installation
```bash
npm install --save-dev eslint svelte-eslint-parser
```
## 📖 Usage
1. Write `parser` option into your ESLint Config file.
2. Use glob patterns or `--ext .svelte` CLI option.
### ESLint Config (`eslint.config.js`)
```js
import js from "@eslint/js";
import svelteParser from "svelte-eslint-parser";
export default [
js.configs.recommended,
{
files: ["**/*.svelte", "*.svelte"],
languageOptions: {
parser: svelteParser,
},
},
];
```
### ESLint Config (`.eslintrc.*`)
```json
{
"extends": "eslint:recommended",
"overrides": [
{
"files": ["*.svelte"],
"parser": "svelte-eslint-parser"
}
]
}
```
### CLI
```console
$ eslint "src/**/*.{js,svelte}"
# or
$ eslint src --ext .svelte
```
## 🔧 Options
[`parserOptions`] has the same properties as what [espree](https://github.com/eslint/espree#usage), the default parser of ESLint, is supporting.
For example:
```json
{
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 2021,
"ecmaFeatures": {
"globalReturn": false,
"impliedStrict": false,
"jsx": false
}
}
}
```
[`parserOptions`]: https://eslint.org/docs/latest/use/configure/parser#configure-parser-options
### parserOptions.parser
You can use `parserOptions.parser` property to specify a custom parser to parse `<script>` tags.
Other properties than parser would be given to the specified parser.
For example in `eslint.config.js`:
```js
import tsParser from "@typescript-eslint/parser";
export default [
{
files: ["**/*.svelte", "*.svelte"],
languageOptions: {
parser: svelteParser,
parserOptions: {
parser: tsParser,
},
},
},
];
```
For example in `.eslintrc.*`:
```json
{
"parser": "svelte-eslint-parser",
"parserOptions": {
"parser": "@typescript-eslint/parser"
}
}
```
If you are using the `"@typescript-eslint/parser"`, and if you want to use TypeScript in `<script>` of `.svelte`, you need to add more `parserOptions` configuration.
For example in `eslint.config.js`:
```js
import tsParser from "@typescript-eslint/parser";
export default [
// ...
{
// ...
languageOptions: {
parser: tsParser,
parserOptions: {
// ...
project: "path/to/your/tsconfig.json",
extraFileExtensions: [".svelte"], // This is a required setting in `@typescript-eslint/parser` v4.24.0.
},
},
},
{
files: ["**/*.svelte", "*.svelte"],
languageOptions: {
parser: svelteParser,
// Parse the `<script>` in `.svelte` as TypeScript by adding the following configuration.
parserOptions: {
parser: tsParser,
},
},
// ...
},
];
```
For example in `.eslintrc.*`:
```js
module.exports = {
// ...
parser: "@typescript-eslint/parser",
parserOptions: {
// ...
project: "path/to/your/tsconfig.json",
extraFileExtensions: [".svelte"], // This is a required setting in `@typescript-eslint/parser` v4.24.0.
},
overrides: [
{
files: ["*.svelte"],
parser: "svelte-eslint-parser",
// Parse the `<script>` in `.svelte` as TypeScript by adding the following configuration.
parserOptions: {
parser: "@typescript-eslint/parser",
},
},
// ...
],
// ...
};
```
#### Multiple parsers
If you want to switch the parser for each lang, specify the object.
For example in `eslint.config.js`:
```js
import tsParser from "@typescript-eslint/parser";
import espree from "espree";
export default [
{
files: ["**/*.svelte", "*.svelte"],
languageOptions: {
parser: svelteParser,
parserOptions: {
parser: {
ts: tsParser,
js: espree,
typescript: tsParser,
},
},
},
},
];
```
For example in `.eslintrc.*`:
```json
{
"parser": "svelte-eslint-parser",
"parserOptions": {
"parser": {
"ts": "@typescript-eslint/parser",
"js": "espree",
"typescript": "@typescript-eslint/parser"
}
}
}
```
### parserOptions.svelteConfig
If you are using `eslint.config.js`, you can provide a `svelte.config.js` in the `parserOptions.svelteConfig` property.
For example:
```js
import svelteConfig from "./svelte.config.js";
export default [
{
files: ["**/*.svelte", "*.svelte"],
languageOptions: {
parser: svelteParser,
parserOptions: {
svelteConfig: svelteConfig,
},
},
},
];
```
If `parserOptions.svelteConfig` is not specified, some config will be statically parsed from the `svelte.config.js` file.
The `.eslintrc.*` style configuration cannot load `svelte.config.js` because it cannot use ESM. We recommend using the `eslint.config.js` style configuration.
### parserOptions.svelteFeatures
You can use `parserOptions.svelteFeatures` property to specify how to parse related to Svelte features.
For example in `eslint.config.js`:
```js
export default [
{
files: ["**/*.svelte", "*.svelte"],
languageOptions: {
parser: svelteParser,
parserOptions: {
svelteFeatures: {
/* -- Experimental Svelte Features -- */
/* It may be changed or removed in minor versions without notice. */
// This option is for Svelte 5. The default value is `true`.
// If `false`, ESLint will not recognize rune symbols.
// If not configured this option, The parser will try to read the option from `compilerOptions.runes` from `svelte.config.js`.
// If `parserOptions.svelteConfig` is not specified and the file cannot be parsed by static analysis, it will behave as `true`.
runes: true,
/* -- Experimental Svelte Features -- */
/* It may be changed or removed in minor versions without notice. */
// Whether to parse the `generics` attribute.
// See https://github.com/sveltejs/rfcs/pull/38
experimentalGenerics: false,
},
},
},
},
];
```
For example in `.eslintrc.*`:
```jsonc
{
"parser": "svelte-eslint-parser",
"parserOptions": {
"svelteFeatures": {
/* -- Experimental Svelte Features -- */
/* It may be changed or removed in minor versions without notice. */
// This option is for Svelte 5. The default value is `true`.
// If `false`, ESLint will not recognize rune symbols.
// If not configured this option, The parser will try to read the option from `compilerOptions.runes` from `svelte.config.js`.
// If `parserOptions.svelteConfig` is not specified and the file cannot be parsed by static analysis, it will behave as `true`.
"runes": true,
/* -- Experimental Svelte Features -- */
/* It may be changed or removed in minor versions without notice. */
// Whether to parse the `generics` attribute.
// See https://github.com/sveltejs/rfcs/pull/38
"experimentalGenerics": false,
},
},
}
```
### Runes support
**_This is an experimental feature. It may be changed or removed in minor versions without notice._**
If you install Svelte v5 the parser will be able to parse runes, and will also be able to parse `*.js` and `*.ts` files.
If you don't want to use Runes, you may need to configure. Please read [parserOptions.svelteFeatures](#parseroptionssveltefeatures) for more details.
When using this mode in an ESLint configuration, it is recommended to set it per file pattern as below.
For example in `eslint.config.js`:
```js
import svelteConfig from "./svelte.config.js";
export default [
{
files: ["**/*.svelte", "*.svelte"],
languageOptions: {
parser: svelteParser,
parserOptions: {
parser: "...",
svelteConfig,
/* ... */
},
},
},
{
files: ["**/*.svelte.js", "*.svelte.js"],
languageOptions: {
parser: svelteParser,
parserOptions: {
svelteConfig,
/* ... */
},
},
},
{
files: ["**/*.svelte.ts", "*.svelte.ts"],
languageOptions: {
parser: svelteParser,
parserOptions: {
parser: "...(ts parser)...",
svelteConfig,
/* ... */
},
},
},
];
```
For example in `.eslintrc.*`:
```jsonc
{
"overrides": [
{
"files": ["*.svelte"],
"parser": "svelte-eslint-parser",
"parserOptions": {
"parser": "...",
/* ... */
},
},
{
"files": ["*.svelte.js"],
"parser": "svelte-eslint-parser",
"parserOptions": {
/* ... */
},
},
{
"files": ["*.svelte.ts"],
"parser": "svelte-eslint-parser",
"parserOptions": {
"parser": "...(ts parser)...",
/* ... */
},
},
],
}
```
## :computer: Editor Integrations
### Visual Studio Code
Use the [dbaeumer.vscode-eslint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) extension that Microsoft provides officially.
You have to configure the `eslint.validate` option of the extension to check `.svelte` files, because the extension targets only `*.js` or `*.jsx` files by default.
Example **.vscode/settings.json**:
```json
{
"eslint.validate": ["javascript", "javascriptreact", "svelte"]
}
```
## Usage for Custom Rules / Plugins
- [AST.md](./docs/AST.md) is AST specification. You can check it on the [Online DEMO](https://sveltejs.github.io/svelte-eslint-parser/).
- The parser will generate its own [ScopeManager](https://eslint.org/docs/developer-guide/scope-manager-interface). You can check it on the [Online DEMO](https://sveltejs.github.io/svelte-eslint-parser/scope).
- I have already [implemented some rules] in the [`eslint-plugin-svelte`]. The source code for these rules will be helpful to you.
## :beers: Contributing
Welcome contributing!
Please use GitHub's Issues/PRs.
See also the documentation for the internal mechanism.
- [internal-mechanism.md](./docs/internal-mechanism.md)
## :lock: License
See the [LICENSE](LICENSE) file for license rights and limitations (MIT).
[Svelte]: https://svelte.dev/
[ESLint]: https://eslint.org/
[eslint-plugin-svelte3]: https://github.com/sveltejs/eslint-plugin-svelte3

View File

@@ -0,0 +1,4 @@
import type { Locations } from "./common";
export interface BaseNode extends Locations {
type: string;
}

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,24 @@
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" | "HTMLText" | "HTMLIdentifier" | "MustacheKeyword" | "HTMLComment";
value: string;
}
export interface Comment extends BaseNode {
type: "Line" | "Block";
value: string;
}

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

409
web/node_modules/svelte-eslint-parser/lib/ast/html.d.ts generated vendored Normal file
View File

@@ -0,0 +1,409 @@
import type ESTree from "estree";
import type { TSESTree } from "@typescript-eslint/types";
import type { BaseNode } from "./base";
import type { Token, Comment } from "./common";
export type SvelteHTMLNode = SvelteProgram | SvelteScriptElement | SvelteStyleElement | SvelteElement | SvelteStartTag | SvelteEndTag | SvelteName | SvelteMemberExpressionName | SvelteText | SvelteLiteral | SvelteMustacheTag | SvelteDebugTag | SvelteConstTag | SvelteRenderTag | SvelteIfBlock | SvelteElseBlock | SvelteEachBlock | SvelteAwaitBlock | SvelteAwaitPendingBlock | SvelteAwaitThenBlock | SvelteAwaitCatchBlock | SvelteKeyBlock | SvelteSnippetBlock | SvelteAttribute | SvelteShorthandAttribute | SvelteSpreadAttribute | SvelteDirective | SvelteStyleDirective | SvelteSpecialDirective | SvelteGenericsDirective | SvelteDirectiveKey | SvelteSpecialDirectiveKey | SvelteHTMLComment;
/** Node of Svelte program root */
export interface SvelteProgram extends BaseNode {
type: "Program";
body: (SvelteScriptElement | SvelteStyleElement | Child)[];
sourceType: "script" | "module";
comments: Comment[];
tokens: Token[];
parent: null;
}
/** Node of elements like HTML element. */
export type SvelteElement = SvelteHTMLElement | SvelteComponentElement | SvelteSpecialElement;
type BaseSvelteElement = BaseNode;
/** Node of `<script>` element. */
export interface SvelteScriptElement extends BaseSvelteElement {
type: "SvelteScriptElement";
name: SvelteName;
startTag: SvelteStartTag;
body: ESTree.Program["body"];
endTag: SvelteEndTag | null;
parent: SvelteProgram;
}
/** Node of `<style>` element. */
export interface SvelteStyleElement extends BaseSvelteElement {
type: "SvelteStyleElement";
name: SvelteName;
startTag: SvelteStartTag;
children: [SvelteText];
endTag: SvelteEndTag | null;
parent: SvelteProgram;
}
/** Node of HTML element. */
export interface SvelteHTMLElement extends BaseSvelteElement {
type: "SvelteElement";
kind: "html";
name: SvelteName;
startTag: SvelteStartTag;
children: Child[];
endTag: SvelteEndTag | null;
parent: SvelteProgram | SvelteElement | SvelteIfBlock | SvelteElseBlockAlone | SvelteEachBlock | SvelteAwaitPendingBlock | SvelteAwaitThenBlock | SvelteAwaitCatchBlock | SvelteKeyBlock | SvelteSnippetBlock;
}
/** Node of Svelte component element. */
export interface SvelteComponentElement extends BaseSvelteElement {
type: "SvelteElement";
kind: "component";
name: ESTree.Identifier | SvelteMemberExpressionName;
startTag: SvelteStartTag;
children: Child[];
endTag: SvelteEndTag | null;
parent: SvelteProgram | SvelteElement | SvelteIfBlock | SvelteElseBlockAlone | SvelteEachBlock | SvelteAwaitPendingBlock | SvelteAwaitThenBlock | SvelteAwaitCatchBlock | SvelteKeyBlock | SvelteSnippetBlock;
}
/** Node of Svelte special component element. e.g. `<svelte:window>` */
export interface SvelteSpecialElement extends BaseSvelteElement {
type: "SvelteElement";
kind: "special";
name: SvelteName;
startTag: SvelteStartTag;
children: Child[];
endTag: SvelteEndTag | null;
parent: SvelteProgram | SvelteElement | SvelteIfBlock | SvelteElseBlockAlone | SvelteEachBlock | SvelteAwaitPendingBlock | SvelteAwaitThenBlock | SvelteAwaitCatchBlock | SvelteKeyBlock | SvelteSnippetBlock;
}
/** Node of start tag. */
export interface SvelteStartTag extends BaseNode {
type: "SvelteStartTag";
attributes: (SvelteAttribute | SvelteShorthandAttribute | SvelteSpreadAttribute | SvelteDirective | SvelteStyleDirective | SvelteSpecialDirective | SvelteGenericsDirective)[];
selfClosing: boolean;
parent: SvelteElement | SvelteScriptElement | SvelteStyleElement;
}
/** Node of end tag. */
export interface SvelteEndTag extends BaseNode {
type: "SvelteEndTag";
parent: SvelteElement | SvelteScriptElement | SvelteStyleElement;
}
/** Node of names. It is used for element names other than components and normal attribute names. */
export interface SvelteName extends BaseNode {
type: "SvelteName";
name: string;
parent: SvelteElement | SvelteScriptElement | SvelteStyleElement | SvelteAttribute | SvelteMemberExpressionName | SvelteDirectiveKey;
}
/** Nodes that may be used in component names. The component names separated by dots. */
export interface SvelteMemberExpressionName extends BaseNode {
type: "SvelteMemberExpressionName";
object: SvelteMemberExpressionName | ESTree.Identifier;
property: SvelteName;
parent: SvelteComponentElement;
}
type Child = SvelteElement | SvelteText | SvelteMustacheTag | SvelteDebugTag | SvelteConstTag | SvelteRenderTag | SvelteIfBlockAlone | SvelteEachBlock | SvelteAwaitBlock | SvelteKeyBlock | SvelteSnippetBlock | SvelteHTMLComment;
/** Node of text. like HTML text. */
export interface SvelteText extends BaseNode {
type: "SvelteText";
value: string;
parent: SvelteProgram | SvelteElement | SvelteStyleElement | SvelteIfBlock | SvelteElseBlockAlone | SvelteEachBlock | SvelteAwaitPendingBlock | SvelteAwaitThenBlock | SvelteAwaitCatchBlock | SvelteKeyBlock | SvelteSnippetBlock;
}
/** Node of literal. */
export interface SvelteLiteral extends BaseNode {
type: "SvelteLiteral";
value: string;
parent: SvelteAttribute | SvelteStyleDirective;
}
/** Node of mustache tag. e.g. `{...}`, `{@html ...}`. Like JSXExpressionContainer */
export type SvelteMustacheTag = SvelteMustacheTagText | SvelteMustacheTagRaw;
interface BaseSvelteMustacheTag extends BaseNode {
type: "SvelteMustacheTag";
kind: "text" | "raw";
expression: ESTree.Expression;
parent: SvelteProgram | SvelteElement | SvelteIfBlock | SvelteElseBlockAlone | SvelteEachBlock | SvelteAwaitPendingBlock | SvelteAwaitThenBlock | SvelteAwaitCatchBlock | SvelteKeyBlock | SvelteSnippetBlock | SvelteAttribute | SvelteStyleDirective;
}
/** Node of mustache tag. e.g. `{...}``. Like JSXExpressionContainer */
export interface SvelteMustacheTagText extends BaseSvelteMustacheTag {
kind: "text";
}
/** Node of mustache tag. e.g. `{@html ...}`. Like JSXExpressionContainer */
export interface SvelteMustacheTagRaw extends BaseSvelteMustacheTag {
kind: "raw";
}
/** Node of debug mustache tag. e.g. `{@debug}` */
export interface SvelteDebugTag extends BaseNode {
type: "SvelteDebugTag";
identifiers: ESTree.Identifier[];
parent: SvelteProgram | SvelteElement | SvelteIfBlock | SvelteElseBlockAlone | SvelteEachBlock | SvelteAwaitPendingBlock | SvelteAwaitThenBlock | SvelteAwaitCatchBlock | SvelteKeyBlock | SvelteSnippetBlock | SvelteAttribute;
}
/** Node of const tag. e.g. `{@const}` */
export interface SvelteConstTag extends BaseNode {
type: "SvelteConstTag";
declaration: ESTree.VariableDeclarator;
parent: SvelteProgram | SvelteElement | SvelteIfBlock | SvelteElseBlockAlone | SvelteEachBlock | SvelteAwaitPendingBlock | SvelteAwaitThenBlock | SvelteAwaitCatchBlock | SvelteKeyBlock | SvelteSnippetBlock | SvelteAttribute;
}
/** Node of render tag. e.g. `{@render}` */
export interface SvelteRenderTag extends BaseNode {
type: "SvelteRenderTag";
expression: ESTree.SimpleCallExpression | (ESTree.ChainExpression & {
expression: ESTree.SimpleCallExpression;
});
parent: SvelteProgram | SvelteElement | SvelteIfBlock | SvelteElseBlockAlone | SvelteEachBlock | SvelteAwaitPendingBlock | SvelteAwaitThenBlock | SvelteAwaitCatchBlock | SvelteKeyBlock | SvelteSnippetBlock;
}
/** Node of if block. e.g. `{#if}` */
export type SvelteIfBlock = SvelteIfBlockAlone | SvelteIfBlockElseIf;
interface BaseSvelteIfBlock extends BaseNode {
type: "SvelteIfBlock";
elseif: boolean;
expression: ESTree.Expression;
children: Child[];
else: SvelteElseBlock | null;
parent: SvelteProgram | SvelteElement | SvelteIfBlock | SvelteElseBlock | SvelteEachBlock | SvelteAwaitPendingBlock | SvelteAwaitThenBlock | SvelteAwaitCatchBlock | SvelteKeyBlock | SvelteSnippetBlock;
}
/** Node of if block. e.g. `{#if}` */
export interface SvelteIfBlockAlone extends BaseSvelteIfBlock {
elseif: false;
parent: Exclude<BaseSvelteIfBlock["parent"], SvelteElseBlockElseIf>;
}
/** Node of if block. e.g. `{:else if}` */
export interface SvelteIfBlockElseIf extends BaseSvelteIfBlock {
elseif: true;
parent: SvelteElseBlockElseIf;
}
/** Node of else block. e.g. `{:else}` */
export type SvelteElseBlock = SvelteElseBlockAlone | SvelteElseBlockElseIf;
interface BaseSvelteElseBlock extends BaseNode {
type: "SvelteElseBlock";
elseif: boolean;
children: (Child | SvelteIfBlockElseIf)[];
parent: SvelteIfBlock | SvelteEachBlock;
}
/** Node of else block. e.g. `{:else}` */
export interface SvelteElseBlockAlone extends BaseSvelteElseBlock {
elseif: false;
children: Child[];
}
/** Node of else block. e.g. `{:else if ...}` */
export interface SvelteElseBlockElseIf extends BaseSvelteElseBlock {
elseif: true;
children: [SvelteIfBlockElseIf];
}
/** Node of each block. e.g. `{#each}` */
export interface SvelteEachBlock extends BaseNode {
type: "SvelteEachBlock";
expression: ESTree.Expression;
context: ESTree.Pattern;
index: ESTree.Identifier | null;
key: ESTree.Expression | null;
children: Child[];
else: SvelteElseBlockAlone | null;
parent: SvelteProgram | SvelteElement | SvelteIfBlock | SvelteElseBlockAlone | SvelteEachBlock | SvelteAwaitPendingBlock | SvelteAwaitThenBlock | SvelteAwaitCatchBlock | SvelteKeyBlock | SvelteSnippetBlock;
}
/** Node of await block. e.g. `{#await}`, `{#await ... then ... }`, `{#await ... catch ... }` */
export type SvelteAwaitBlock = SvelteAwaitBlockAwaitPending | SvelteAwaitBlockAwaitThen | SvelteAwaitBlockAwaitCatch;
interface BaseSvelteAwaitBlock extends BaseNode {
type: "SvelteAwaitBlock";
expression: ESTree.Expression;
kind: "await" | "await-then" | "await-catch";
pending: SvelteAwaitPendingBlock | null;
then: SvelteAwaitThenBlock | null;
catch: SvelteAwaitCatchBlock | null;
parent: SvelteProgram | SvelteElement | SvelteIfBlock | SvelteElseBlockAlone | SvelteEachBlock | SvelteAwaitPendingBlock | SvelteAwaitThenBlock | SvelteAwaitCatchBlock | SvelteKeyBlock | SvelteSnippetBlock;
}
/** Node of await block. e.g. `{#await}` */
export interface SvelteAwaitBlockAwaitPending extends BaseSvelteAwaitBlock {
kind: "await";
pending: SvelteAwaitPendingBlock;
then: SvelteAwaitThenBlockAlone | null;
catch: SvelteAwaitCatchBlockAlone | null;
}
/** Node of await block. e.g. `{#await ... then ... }` */
export interface SvelteAwaitBlockAwaitThen extends BaseSvelteAwaitBlock {
kind: "await-then";
pending: null;
then: SvelteAwaitThenBlockAwaitThen;
catch: SvelteAwaitCatchBlockAlone | null;
}
/** Node of await block. e.g. `{#await ... catch ... }` */
export interface SvelteAwaitBlockAwaitCatch extends BaseSvelteAwaitBlock {
kind: "await-catch";
pending: null;
then: null;
catch: SvelteAwaitCatchBlockAwaitCatch;
}
/** Node of await pending block. e.g. `{#await expr} ... {:then}` */
export interface SvelteAwaitPendingBlock extends BaseNode {
type: "SvelteAwaitPendingBlock";
children: Child[];
parent: SvelteAwaitBlock;
}
/** Node of await then block. e.g. `{:then}`, `{#await ... then ...}` */
export type SvelteAwaitThenBlock = SvelteAwaitThenBlockAlone | SvelteAwaitThenBlockAwaitThen;
interface BaseSvelteAwaitThenBlock extends BaseNode {
type: "SvelteAwaitThenBlock";
awaitThen: boolean;
value: ESTree.Pattern | null;
children: Child[];
parent: SvelteAwaitBlock;
}
/** Node of await then block. e.g. `{:then}` */
export interface SvelteAwaitThenBlockAlone extends BaseSvelteAwaitThenBlock {
awaitThen: false;
parent: SvelteAwaitBlockAwaitPending;
}
/** Node of await then block. e.g. `{#await ... then ...}` */
export interface SvelteAwaitThenBlockAwaitThen extends BaseSvelteAwaitThenBlock {
awaitThen: true;
parent: SvelteAwaitBlockAwaitThen;
}
/** Node of await catch block. e.g. `{:catch}`, `{#await ... catch ... }` */
export type SvelteAwaitCatchBlock = SvelteAwaitCatchBlockAlone | SvelteAwaitCatchBlockAwaitCatch;
interface BaseSvelteAwaitCatchBlock extends BaseNode {
type: "SvelteAwaitCatchBlock";
awaitCatch: boolean;
error: ESTree.Pattern | null;
children: Child[];
parent: SvelteAwaitBlock;
}
/** Node of await catch block. e.g. `{:catch}` */
export interface SvelteAwaitCatchBlockAlone extends BaseSvelteAwaitCatchBlock {
awaitCatch: false;
parent: SvelteAwaitBlockAwaitPending | SvelteAwaitBlockAwaitThen;
}
/** Node of await catch block. e.g. `{#await ... catch ... }` */
export interface SvelteAwaitCatchBlockAwaitCatch extends BaseSvelteAwaitCatchBlock {
awaitCatch: true;
error: ESTree.Pattern | null;
children: Child[];
parent: SvelteAwaitBlockAwaitCatch;
}
/** Node of key block. e.g. `{#key}` */
export interface SvelteKeyBlock extends BaseNode {
type: "SvelteKeyBlock";
expression: ESTree.Expression;
children: Child[];
parent: SvelteProgram | SvelteElement | SvelteIfBlock | SvelteElseBlockAlone | SvelteEachBlock | SvelteAwaitPendingBlock | SvelteAwaitThenBlock | SvelteAwaitCatchBlock | SvelteKeyBlock | SvelteSnippetBlock;
}
/** Node of snippet block. e.g. `{#snippet}` */
export interface SvelteSnippetBlock extends BaseNode {
type: "SvelteSnippetBlock";
id: ESTree.Identifier;
params: ESTree.Pattern[];
children: Child[];
parent: SvelteProgram | SvelteElement | SvelteIfBlock | SvelteElseBlockAlone | SvelteEachBlock | SvelteAwaitPendingBlock | SvelteAwaitThenBlock | SvelteAwaitCatchBlock | SvelteKeyBlock | SvelteSnippetBlock;
}
/** Node of HTML comment. */
export interface SvelteHTMLComment extends BaseNode {
type: "SvelteHTMLComment";
value: string;
parent: SvelteProgram | SvelteElement | SvelteIfBlock | SvelteElseBlockAlone | SvelteEachBlock | SvelteAwaitPendingBlock | SvelteAwaitThenBlock | SvelteAwaitCatchBlock | SvelteKeyBlock | SvelteSnippetBlock;
}
/** Node of HTML attribute. */
export interface SvelteAttribute extends BaseNode {
type: "SvelteAttribute";
key: SvelteName;
boolean: boolean;
value: (SvelteLiteral | SvelteMustacheTagText)[];
parent: SvelteStartTag;
}
/** Node of shorthand attribute. e.g. `<img {src}>` */
export interface SvelteShorthandAttribute extends BaseNode {
type: "SvelteShorthandAttribute";
key: ESTree.Identifier;
value: ESTree.Identifier;
parent: SvelteStartTag;
}
/** Node of spread attribute. e.g. `<Info {...pkg}/>`. Like JSXSpreadAttribute */
export interface SvelteSpreadAttribute extends BaseNode {
type: "SvelteSpreadAttribute";
argument: ESTree.Expression;
parent: SvelteStartTag;
}
/** Node of directive. e.g. `<input bind:value />` */
export type SvelteDirective = SvelteActionDirective | SvelteAnimationDirective | SvelteBindingDirective | SvelteClassDirective | SvelteEventHandlerDirective | SvelteLetDirective | SvelteRefDirective | SvelteTransitionDirective;
export type SvelteDirectiveKey = SvelteDirectiveKeyTextName | SvelteDirectiveKeyFunctionName | SvelteDirectiveKeyForEventHandler | SvelteDirectiveKeyForAction | SvelteDirectiveKeyForStyleShorthand;
interface BaseSvelteDirectiveKey<N extends ESTree.Expression | SvelteName> extends BaseNode {
type: "SvelteDirectiveKey";
name: N;
modifiers: string[];
parent: SvelteDirective | SvelteStyleDirective;
}
export type SvelteDirectiveKeyTextName = BaseSvelteDirectiveKey<SvelteName>;
export type SvelteDirectiveKeyFunctionName = BaseSvelteDirectiveKey<ESTree.Identifier>;
export type SvelteDirectiveKeyForEventHandler = BaseSvelteDirectiveKey<SvelteName>;
export type SvelteDirectiveKeyForAction = BaseSvelteDirectiveKey<ESTree.Identifier | ESTree.MemberExpression | SvelteName>;
export type SvelteDirectiveKeyForStyleShorthand = BaseSvelteDirectiveKey<ESTree.Identifier>;
interface BaseSvelteDirective extends BaseNode {
type: "SvelteDirective";
key: SvelteDirectiveKey;
parent: SvelteStartTag;
}
export interface SvelteActionDirective extends BaseSvelteDirective {
kind: "Action";
key: SvelteDirectiveKeyForAction;
expression: null | ESTree.Expression;
}
export interface SvelteAnimationDirective extends BaseSvelteDirective {
kind: "Animation";
key: SvelteDirectiveKeyFunctionName;
expression: null | ESTree.Expression;
}
export interface SvelteBindingDirective extends BaseSvelteDirective {
kind: "Binding";
key: SvelteDirectiveKeyTextName;
shorthand: boolean;
expression: null | ESTree.Expression;
}
export interface SvelteClassDirective extends BaseSvelteDirective {
kind: "Class";
key: SvelteDirectiveKeyTextName;
shorthand: boolean;
expression: null | ESTree.Expression;
}
export interface SvelteEventHandlerDirective extends BaseSvelteDirective {
kind: "EventHandler";
key: SvelteDirectiveKeyForEventHandler;
expression: null | ESTree.Expression;
}
export interface SvelteLetDirective extends BaseSvelteDirective {
kind: "Let";
key: SvelteDirectiveKeyTextName;
expression: null | ESTree.Pattern;
}
export interface SvelteRefDirective extends BaseSvelteDirective {
kind: "Ref";
key: SvelteDirectiveKeyTextName;
expression: null | ESTree.Expression;
}
export interface SvelteTransitionDirective extends BaseSvelteDirective {
kind: "Transition";
key: SvelteDirectiveKeyFunctionName;
intro: boolean;
outro: boolean;
expression: null | ESTree.Expression;
}
/** Node of style directive. e.g. `<input style:color />` */
export type SvelteStyleDirective = SvelteStyleDirectiveShorthand | SvelteStyleDirectiveLongform;
interface BaseSvelteStyleDirective extends BaseNode {
type: "SvelteStyleDirective";
key: SvelteDirectiveKeyTextName | SvelteDirectiveKeyForStyleShorthand;
value: (SvelteLiteral | SvelteMustacheTagText)[];
parent: SvelteStartTag;
}
export interface SvelteStyleDirectiveShorthand extends BaseSvelteStyleDirective {
key: SvelteDirectiveKeyForStyleShorthand;
shorthand: true;
value: [];
}
export interface SvelteStyleDirectiveLongform extends BaseSvelteStyleDirective {
key: SvelteDirectiveKeyTextName;
shorthand: false;
value: (SvelteLiteral | SvelteMustacheTagText)[];
}
export interface SvelteSpecialDirectiveKey extends BaseNode {
type: "SvelteSpecialDirectiveKey";
parent: SvelteSpecialDirective;
}
export interface SvelteSpecialDirective extends BaseNode {
type: "SvelteSpecialDirective";
kind: "this";
key: SvelteSpecialDirectiveKey;
expression: ESTree.Expression;
parent: SvelteStartTag;
}
export interface SvelteGenericsDirective extends BaseNode {
type: "SvelteGenericsDirective";
key: SvelteName & {
name: "generics";
};
params: TSESTree.TSTypeParameterDeclaration["params"];
parent: SvelteStartTag;
}
export {};

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,6 @@
import type { SvelteHTMLNode } from "./html";
import type { SvelteScriptNode } from "./script";
export * from "./common";
export * from "./html";
export * from "./script";
export type SvelteNode = SvelteHTMLNode | SvelteScriptNode;

19
web/node_modules/svelte-eslint-parser/lib/ast/index.js generated vendored Normal file
View File

@@ -0,0 +1,19 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./common"), exports);
__exportStar(require("./html"), exports);
__exportStar(require("./script"), exports);

View File

@@ -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;
}

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,7 @@
import type * as ESTree from "estree";
import type { Comment, Token } from "../ast";
import type { Context } from ".";
/** Fix locations */
export declare function fixLocations(node: ESTree.Node, tokens: Token[], comments: Comment[], offset: number, visitorKeys: {
[type: string]: string[];
} | undefined, ctx: Context): void;

View File

@@ -0,0 +1,65 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.fixLocations = fixLocations;
const traverse_1 = require("../traverse");
/** Fix locations */
function fixLocations(node, tokens, comments, offset, visitorKeys, ctx) {
if (offset === 0) {
return;
}
const traversed = new Set();
(0, traverse_1.traverseNodes)(node, {
visitorKeys,
enterNode: (n) => {
if (traversed.has(n)) {
return;
}
traversed.add(n);
if (traversed.has(n.range)) {
if (!traversed.has(n.loc)) {
// However, `Node#loc` may not be shared.
const locs = ctx.getConvertLocation({
start: n.range[0],
end: n.range[1],
});
applyLocs(n, locs);
traversed.add(n.loc);
}
}
else {
const start = n.range[0] + offset;
const end = n.range[1] + offset;
const locs = ctx.getConvertLocation({ start, end });
applyLocs(n, locs);
traversed.add(n.range);
traversed.add(n.loc);
}
},
leaveNode: Function.prototype,
});
for (const t of tokens) {
const start = t.range[0] + offset;
const end = t.range[1] + offset;
const locs = ctx.getConvertLocation({ start, end });
applyLocs(t, locs);
}
for (const t of comments) {
const start = t.range[0] + offset;
const end = t.range[1] + offset;
const locs = ctx.getConvertLocation({ start, end });
applyLocs(t, locs);
}
}
/**
* applyLocs
*/
function applyLocs(target, locs) {
target.loc = locs.loc;
target.range = locs.range;
if (typeof target.start === "number") {
delete target.start;
}
if (typeof target.end === "number") {
delete target.end;
}
}

View File

@@ -0,0 +1,116 @@
import type { Comment, Locations, SvelteElement, SvelteHTMLElement, SvelteScriptElement, SvelteSnippetBlock, SvelteStyleElement, Token } from "../ast";
import type ESTree from "estree";
import type * as SvAST from "../parser/svelte-ast-types";
import type * as Compiler from "../parser/svelte-ast-types-for-v5";
import { ScriptLetContext } from "./script-let";
import { LetDirectiveCollections } from "./let-directive-collection";
import { type NormalizedParserOptions } from "../parser/parser-options";
export declare class ScriptsSourceCode {
private raw;
private trimmedRaw;
readonly attrs: Record<string, string | undefined>;
private _appendScriptLets;
separateIndexes: number[];
constructor(script: string, attrs: Record<string, string | undefined>);
getCurrentVirtualCode(): string;
getCurrentVirtualCodeInfo(): {
script: string;
render: string;
rootScope: string;
};
getCurrentVirtualCodeLength(): number;
addLet(letCode: string, kind: "generics" | "snippet" | "render"): {
start: number;
end: number;
};
stripCode(start: number, end: number): void;
}
export type ContextSourceCode = {
template: string;
scripts: ScriptsSourceCode;
};
export declare class Context {
readonly code: string;
readonly parserOptions: NormalizedParserOptions;
readonly sourceCode: ContextSourceCode;
readonly tokens: Token[];
readonly comments: Comment[];
private readonly locs;
private readonly locsMap;
readonly scriptLet: ScriptLetContext;
readonly letDirCollections: LetDirectiveCollections;
readonly slots: Set<SvelteHTMLElement>;
readonly elements: Map<SvelteElement, SvAST.Element | SvAST.InlineComponent | SvAST.Window | SvAST.Document | SvAST.Body | SvAST.Head | SvAST.Title | SvAST.Options | SvAST.SlotTemplate | SvAST.Slot | Compiler.ElementLike>;
readonly snippets: SvelteSnippetBlock[];
private readonly state;
private readonly blocks;
constructor(code: string, parserOptions: NormalizedParserOptions);
getLocFromIndex(index: number): {
line: number;
column: number;
};
getIndexFromLoc(loc: {
line: number;
column: number;
}): number;
/**
* Get the location information of the given node.
* @param node The node.
*/
getConvertLocation(node: {
start: number;
end: number;
} | ESTree.Node): Locations;
addComment(comment: Comment): void;
/**
* Add token to tokens
*/
addToken(type: Token["type"], range: {
start: number;
end: number;
}): Token;
/**
* get text
*/
getText(range: {
start: number;
end: number;
} | ESTree.Node): string;
isTypeScript(): boolean;
stripScriptCode(start: number, end: number): void;
findBlock(element: SvelteScriptElement | SvelteStyleElement | SvelteElement): Block | undefined;
findSelfClosingBlock(element: SvelteElement): SelfClosingBlock | undefined;
}
type Block = {
tag: "script" | "style" | "template";
originalTag: string;
attrs: Compiler.Attribute[];
selfClosing?: false;
contentRange: [number, number];
startTagRange: [number, number];
endTagRange: [number, number];
} | SelfClosingBlock;
type SelfClosingBlock = {
tag: "script" | "style" | "template";
originalTag: string;
attrs: Compiler.Attribute[];
selfClosing: true;
startTagRange: [number, number];
};
export declare class LinesAndColumns {
private readonly lineStartIndices;
constructor(code: string);
getLocFromIndex(index: number): {
line: number;
column: number;
};
getIndexFromLoc(loc: {
line: number;
column: number;
}): number;
/**
* Get the location information of the given indexes.
*/
getLocations(start: number, end: number): Locations;
}
export {};

View File

@@ -0,0 +1,335 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LinesAndColumns = exports.Context = exports.ScriptsSourceCode = void 0;
const script_let_1 = require("./script-let");
const let_directive_collection_1 = require("./let-directive-collection");
const html_1 = require("../parser/html");
const utils_1 = require("../utils");
const parser_options_1 = require("../parser/parser-options");
class ScriptsSourceCode {
constructor(script, attrs) {
this._appendScriptLets = null;
this.separateIndexes = [];
this.raw = script;
this.trimmedRaw = script.trimEnd();
this.attrs = attrs;
this.separateIndexes = [script.length];
}
getCurrentVirtualCode() {
if (this._appendScriptLets == null) {
return this.raw;
}
return (this.trimmedRaw +
this._appendScriptLets.separate +
this._appendScriptLets.beforeSpaces +
this._appendScriptLets.render +
this._appendScriptLets.snippet +
this._appendScriptLets.generics);
}
getCurrentVirtualCodeInfo() {
if (this._appendScriptLets == null) {
return { script: this.raw, render: "", rootScope: "" };
}
return {
script: this.trimmedRaw + this._appendScriptLets.separate,
render: this._appendScriptLets.beforeSpaces + this._appendScriptLets.render,
rootScope: this._appendScriptLets.snippet + this._appendScriptLets.generics,
};
}
getCurrentVirtualCodeLength() {
if (this._appendScriptLets == null) {
return this.raw.length;
}
return (this.trimmedRaw.length +
this._appendScriptLets.separate.length +
this._appendScriptLets.beforeSpaces.length +
this._appendScriptLets.render.length +
this._appendScriptLets.snippet.length +
this._appendScriptLets.generics.length);
}
addLet(letCode, kind) {
if (this._appendScriptLets == null) {
const currentLength = this.trimmedRaw.length;
this.separateIndexes = [currentLength, currentLength + 1];
const after = this.raw.slice(currentLength + 2);
this._appendScriptLets = {
separate: "\n;",
beforeSpaces: after,
render: "",
snippet: "",
generics: "",
};
}
const start = this.getCurrentVirtualCodeLength();
this._appendScriptLets[kind] += letCode;
return {
start,
end: this.getCurrentVirtualCodeLength(),
};
}
stripCode(start, end) {
this.raw =
this.raw.slice(0, start) +
this.raw.slice(start, end).replace(/[^\n\r ]/g, " ") +
this.raw.slice(end);
this.trimmedRaw =
this.trimmedRaw.slice(0, start) +
this.trimmedRaw.slice(start, end).replace(/[^\n\r ]/g, " ") +
this.trimmedRaw.slice(end);
}
}
exports.ScriptsSourceCode = ScriptsSourceCode;
class Context {
constructor(code, parserOptions) {
this.tokens = [];
this.comments = [];
this.locsMap = new Map();
this.letDirCollections = new let_directive_collection_1.LetDirectiveCollections();
this.slots = new Set();
this.elements = new Map();
this.snippets = [];
// ----- States ------
this.state = {};
this.blocks = [];
this.code = code;
this.parserOptions = parserOptions;
this.locs = new LinesAndColumns(code);
const spaces = code.replace(/[^\n\r ]/g, " ");
let templateCode = "";
let scriptCode = "";
const scriptAttrs = {};
let start = 0;
for (const block of extractBlocks(code)) {
if (block.tag === "template") {
if (block.selfClosing) {
continue;
}
const lang = block.attrs.find((attr) => attr.name === "lang");
if (!lang || !Array.isArray(lang.value)) {
continue;
}
const langValue = lang.value[0];
if (!langValue ||
langValue.type !== "Text" ||
langValue.data === "html") {
continue;
}
}
this.blocks.push(block);
if (block.selfClosing) {
// Self-closing blocks are temporarily replaced with `<s---->` or `<t---->` tag
// because the svelte compiler cannot parse self-closing block(script, style) tags.
// It will be restored later in `convertHTMLElement()` processing.
templateCode += `${code.slice(start, block.startTagRange[0] + 2 /* `<` and first letter */)}${"-".repeat(block.tag.length - 1 /* skip first letter */)}${code.slice(block.startTagRange[0] + 1 /* skip `<` */ + block.tag.length, block.startTagRange[1])}`;
scriptCode += spaces.slice(start, block.startTagRange[1]);
start = block.startTagRange[1];
}
else {
templateCode +=
code.slice(start, block.contentRange[0]) +
spaces.slice(block.contentRange[0], block.contentRange[1]);
if (block.tag === "script") {
scriptCode +=
spaces.slice(start, block.contentRange[0]) +
code.slice(...block.contentRange);
for (const attr of block.attrs) {
if (Array.isArray(attr.value)) {
const attrValue = attr.value[0];
scriptAttrs[attr.name] =
attrValue && attrValue.type === "Text"
? attrValue.data
: undefined;
}
}
}
else {
scriptCode += spaces.slice(start, block.contentRange[1]);
}
start = block.contentRange[1];
}
}
templateCode += code.slice(start);
scriptCode += spaces.slice(start);
this.sourceCode = {
template: templateCode,
scripts: new ScriptsSourceCode(scriptCode, scriptAttrs),
};
this.scriptLet = new script_let_1.ScriptLetContext(this);
}
getLocFromIndex(index) {
let loc = this.locsMap.get(index);
if (!loc) {
loc = this.locs.getLocFromIndex(index);
this.locsMap.set(index, loc);
}
return {
line: loc.line,
column: loc.column,
};
}
getIndexFromLoc(loc) {
return this.locs.getIndexFromLoc(loc);
}
/**
* Get the location information of the given node.
* @param node The node.
*/
getConvertLocation(node) {
const { start, end } = node;
return {
range: [start, end],
loc: {
start: this.getLocFromIndex(start),
end: this.getLocFromIndex(end),
},
};
}
addComment(comment) {
this.comments.push(comment);
}
/**
* Add token to tokens
*/
addToken(type, range) {
const token = Object.assign({ type, value: this.getText(range) }, this.getConvertLocation(range));
this.tokens.push(token);
return token;
}
/**
* get text
*/
getText(range) {
return this.code.slice(range.start, range.end);
}
isTypeScript() {
if (this.state.isTypeScript != null) {
return this.state.isTypeScript;
}
const lang = this.sourceCode.scripts.attrs.lang;
return (this.state.isTypeScript = (0, parser_options_1.isTypeScript)(this.parserOptions, lang));
}
stripScriptCode(start, end) {
this.sourceCode.scripts.stripCode(start, end);
}
findBlock(element) {
const tag = element.type === "SvelteScriptElement"
? "script"
: element.type === "SvelteStyleElement"
? "style"
: element.name.name.toLowerCase();
return this.blocks.find((block) => block.tag === tag &&
!block.selfClosing &&
element.range[0] <= block.contentRange[0] &&
block.contentRange[1] <= element.range[1]);
}
findSelfClosingBlock(element) {
return this.blocks.find((block) => Boolean(block.selfClosing &&
element.startTag.range[0] <= block.startTagRange[0] &&
block.startTagRange[1] <= element.startTag.range[1]));
}
}
exports.Context = Context;
/** Extract <script> blocks */
function* extractBlocks(code) {
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;
if (!tag) {
continue;
}
const startTagStart = startTagOpenMatch.index;
let startTagEnd = startTagOpenRe.lastIndex;
const lowerTag = tag.toLowerCase();
let attrs = [];
if (!nextChar.trim()) {
const attrsData = (0, html_1.parseAttributes)(code, startTagOpenRe.lastIndex);
attrs = attrsData.attributes;
startTagEnd = attrsData.index;
if (code[startTagEnd] === "/" && code[startTagEnd + 1] === ">") {
yield {
tag: lowerTag,
originalTag: tag,
attrs,
selfClosing: true,
startTagRange: [startTagStart, startTagEnd + 2],
};
continue;
}
if (code[startTagEnd] === ">") {
startTagEnd++;
}
else {
continue;
}
}
const endTagRe = lowerTag === "script"
? endScriptTagRe
: lowerTag === "style"
? endStyleTagRe
: endTemplateTagRe;
endTagRe.lastIndex = startTagEnd;
const endTagMatch = endTagRe.exec(code);
if (endTagMatch) {
const endTagStart = endTagMatch.index;
const endTagEnd = endTagRe.lastIndex;
yield {
tag: lowerTag,
originalTag: tag,
attrs,
startTagRange: [startTagStart, startTagEnd],
contentRange: [startTagEnd, endTagStart],
endTagRange: [endTagStart, endTagEnd],
};
startTagOpenRe.lastIndex = endTagEnd;
}
}
}
class LinesAndColumns {
constructor(code) {
const len = code.length;
const lineStartIndices = [0];
for (let index = 0; index < len; index++) {
const c = code[index];
if (c === "\r") {
const next = code[index + 1] || "";
if (next === "\n") {
index++;
}
lineStartIndices.push(index + 1);
}
else if (c === "\n") {
lineStartIndices.push(index + 1);
}
}
this.lineStartIndices = lineStartIndices;
}
getLocFromIndex(index) {
const lineNumber = (0, utils_1.sortedLastIndex)(this.lineStartIndices, (target) => target - index);
return {
line: lineNumber,
column: index - this.lineStartIndices[lineNumber - 1],
};
}
getIndexFromLoc(loc) {
const lineStartIndex = this.lineStartIndices[loc.line - 1];
const positionIndex = lineStartIndex + loc.column;
return positionIndex;
}
/**
* Get the location information of the given indexes.
*/
getLocations(start, end) {
return {
range: [start, end],
loc: {
start: this.getLocFromIndex(start),
end: this.getLocFromIndex(end),
},
};
}
}
exports.LinesAndColumns = LinesAndColumns;

View File

@@ -0,0 +1,15 @@
import type { SvelteLetDirective, SvelteName } from "../ast";
import type * as ESTree from "estree";
import type { ScriptLetBlockParam, ScriptLetCallback } from "./script-let";
/** A class that collects pattern nodes for Let directives. */
export declare class LetDirectiveCollection {
private readonly list;
getLetParams(): ScriptLetBlockParam[];
addPattern(pattern: ESTree.Pattern | SvelteName, directive: SvelteLetDirective, typing: string, ...callbacks: ScriptLetCallback<ESTree.Pattern>[]): ScriptLetCallback<ESTree.Pattern>[];
}
export declare class LetDirectiveCollections {
private readonly stack;
beginExtract(): void;
getCollection(): LetDirectiveCollection;
extract(): LetDirectiveCollection;
}

View File

@@ -0,0 +1,41 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LetDirectiveCollections = exports.LetDirectiveCollection = void 0;
/** A class that collects pattern nodes for Let directives. */
class LetDirectiveCollection {
constructor() {
this.list = [];
}
getLetParams() {
return this.list;
}
addPattern(pattern, directive, typing, ...callbacks) {
this.list.push({
node: pattern,
parent: directive,
typing,
callback(node, options) {
for (const callback of callbacks) {
callback(node, options);
}
},
});
return callbacks;
}
}
exports.LetDirectiveCollection = LetDirectiveCollection;
class LetDirectiveCollections {
constructor() {
this.stack = [];
}
beginExtract() {
this.stack.push(new LetDirectiveCollection());
}
getCollection() {
return this.stack[this.stack.length - 1];
}
extract() {
return this.stack.pop();
}
}
exports.LetDirectiveCollections = LetDirectiveCollections;

View File

@@ -0,0 +1,91 @@
import type { ScopeManager, Scope } from "eslint-scope";
import type * as ESTree from "estree";
import type { TSESTree } from "@typescript-eslint/types";
import type { Context } from ".";
import type { Comment, SvelteEachBlock, SvelteIfBlock, SvelteName, SvelteNode, SvelteSnippetBlock, Token } from "../ast";
import type { ESLintExtendedProgram } from "../parser";
export type ScriptLetCallback<E extends ESTree.Node> = (es: E, options: ScriptLetCallbackOption) => void;
export type ScriptLetCallbackOption = {
getScope: (node: ESTree.Node) => Scope;
registerNodeToScope: (node: any, scope: Scope) => void;
scopeManager: ScopeManager;
visitorKeys?: {
[type: string]: string[];
};
};
export type ScriptLetRestoreCallback = (node: ESTree.Node, tokens: Token[], comments: Comment[], options: ScriptLetRestoreCallbackOption) => void;
type ScriptLetRestoreCallbackOption = {
getScope: (node: ESTree.Node) => Scope;
registerNodeToScope: (node: any, scope: Scope) => void;
scopeManager: ScopeManager;
visitorKeys?: {
[type: string]: string[];
};
addPostProcess: (callback: () => void) => void;
};
type StatementNodeType = `${TSESTree.Statement["type"]}`;
type TypeGenHelper = {
generateUniqueId: (base: string) => string;
};
type ObjectShorthandProperty = ESTree.Property & {
key: ESTree.Identifier;
value: ESTree.Identifier;
};
export type ScriptLetBlockParam = {
node: ESTree.Pattern | SvelteName;
parent: SvelteNode;
typing: string;
callback: (node: ESTree.Pattern, options: ScriptLetCallbackOption) => void;
};
/**
* A class that handles script fragments.
* The script fragment AST node remaps and connects to the original directive AST node.
*/
export declare class ScriptLetContext {
private readonly script;
private readonly ctx;
private readonly restoreCallbacks;
private readonly programRestoreCallbacks;
private readonly closeScopeCallbacks;
private readonly unique;
private currentScriptScopeKind;
constructor(ctx: Context);
addExpression<E extends ESTree.Expression>(expression: E | SvelteName, parent: SvelteNode, typing?: string | null, ...callbacks: ScriptLetCallback<E>[]): ScriptLetCallback<E>[];
addExpressionFromRange<E extends ESTree.Expression>(range: [number, number], parent: SvelteNode, typing?: string | null, ...callbacks: ScriptLetCallback<E>[]): ScriptLetCallback<E>[];
addObjectShorthandProperty(identifier: SvelteName, parent: SvelteNode, ...callbacks: ScriptLetCallback<ObjectShorthandProperty>[]): void;
addVariableDeclarator(declarator: ESTree.VariableDeclarator | ESTree.AssignmentExpression, parent: SvelteNode, ...callbacks: ScriptLetCallback<ESTree.VariableDeclarator>[]): ScriptLetCallback<ESTree.VariableDeclarator>[];
addGenericTypeAliasDeclaration(param: TSESTree.TSTypeParameter, callbackId: (id: TSESTree.Identifier, type: TSESTree.TypeNode) => void, callbackDefault: (type: TSESTree.TypeNode) => void): void;
nestIfBlock(expression: ESTree.Expression, ifBlock: SvelteIfBlock, callback: ScriptLetCallback<ESTree.Expression>): void;
nestEachBlock(expression: ESTree.Expression, context: ESTree.Pattern, indexRange: {
start: number;
end: number;
} | null, eachBlock: SvelteEachBlock, callback: (expr: ESTree.Expression, ctx: ESTree.Pattern, index: ESTree.Identifier | null) => void): void;
nestSnippetBlock(id: ESTree.Identifier, closeParentIndex: number, snippetBlock: SvelteSnippetBlock, kind: "snippet" | null, callback: (id: ESTree.Identifier, params: ESTree.Pattern[]) => void): void;
nestBlock(block: SvelteNode, params?: ScriptLetBlockParam[] | ((helper: TypeGenHelper | null) => {
param: ScriptLetBlockParam;
preparationScript?: {
script: string;
nodeType: StatementNodeType;
}[];
})): void;
closeScope(): void;
addProgramRestore(callback: ScriptLetRestoreCallback): void;
private appendScript;
private appendScriptWithoutOffset;
private pushScope;
/**
* Restore AST nodes
*/
restore(result: ESLintExtendedProgram): void;
/**
* Restore AST nodes
*/
private restoreNodes;
/**
* Restore program node
*/
private restoreProgram;
private remapNodes;
private generateUniqueId;
}
export {};

View File

@@ -0,0 +1,816 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ScriptLetContext = void 0;
const common_1 = require("../parser/converts/common");
const scope_1 = require("../scope");
const traverse_1 = require("../traverse");
const unique_1 = require("./unique");
const fix_locations_1 = require("./fix-locations");
/**
* Get node range
*/
function getNodeRange(node) {
let start = null;
let end = null;
if (node.leadingComments) {
start = (0, common_1.getWithLoc)(node.leadingComments[0]).start;
}
if (node.trailingComments) {
end = (0, common_1.getWithLoc)(node.trailingComments[node.trailingComments.length - 1]).end;
}
const loc = "range" in node
? { start: node.range[0], end: node.range[1] }
: (0, common_1.getWithLoc)(node);
return [
start ? Math.min(start, loc.start) : loc.start,
end ? Math.max(end, loc.end) : loc.end,
];
}
/**
* A class that handles script fragments.
* The script fragment AST node remaps and connects to the original directive AST node.
*/
class ScriptLetContext {
constructor(ctx) {
this.restoreCallbacks = [];
this.programRestoreCallbacks = [];
this.closeScopeCallbacks = [];
this.unique = new unique_1.UniqueIdGenerator();
this.currentScriptScopeKind = "render";
this.script = ctx.sourceCode.scripts;
this.ctx = ctx;
}
addExpression(expression, parent, typing, ...callbacks) {
const range = getNodeRange(expression);
return this.addExpressionFromRange(range, parent, typing, ...callbacks);
}
addExpressionFromRange(range, parent, typing, ...callbacks) {
const part = this.ctx.code.slice(...range);
const isTS = typing && this.ctx.isTypeScript();
this.appendScript(`(${part})${isTS ? `as (${typing})` : ""};`, range[0] - 1, this.currentScriptScopeKind, "ExpressionStatement", (st, tokens, comments, result) => {
const exprSt = st;
const tsAs = isTS
? exprSt.expression
: null;
const node = (tsAs === null || tsAs === void 0 ? void 0 : tsAs.expression) || exprSt.expression;
// Process for nodes
for (const callback of callbacks) {
callback(node, result);
}
tokens.shift(); // (
tokens.pop(); // )
tokens.pop(); // ;
if (isTS) {
for (const scope of extractTypeNodeScopes(tsAs.typeAnnotation, result)) {
(0, scope_1.removeScope)(result.scopeManager, scope);
}
this.remapNodes([
{
offset: range[0] - 1,
range,
newNode: node,
},
], tokens, comments, result.visitorKeys);
}
node.parent = parent;
// Disconnect the tree structure.
exprSt.expression = null;
});
return callbacks;
}
addObjectShorthandProperty(identifier, parent, ...callbacks) {
const range = getNodeRange(identifier);
const part = this.ctx.code.slice(...range);
this.appendScript(`({${part}});`, range[0] - 2, this.currentScriptScopeKind, "ExpressionStatement", (st, tokens, _comments, result) => {
const exprSt = st;
const objectExpression = exprSt.expression;
const node = objectExpression.properties[0];
// Process for nodes
for (const callback of callbacks) {
callback(node, result);
}
node.key.parent = parent;
node.value.parent = parent;
tokens.shift(); // (
tokens.shift(); // {
tokens.pop(); // }
tokens.pop(); // )
tokens.pop(); // ;
// Disconnect the tree structure.
exprSt.expression = null;
});
}
addVariableDeclarator(declarator, parent, ...callbacks) {
const range = declarator.type === "VariableDeclarator"
? // As of Svelte v5-next.65, VariableDeclarator nodes do not have location information.
[getNodeRange(declarator.id)[0], getNodeRange(declarator.init)[1]]
: getNodeRange(declarator);
const part = this.ctx.code.slice(...range);
this.appendScript(`const ${part};`, range[0] - 6, this.currentScriptScopeKind, "VariableDeclaration", (st, tokens, _comments, result) => {
const decl = st;
const node = decl.declarations[0];
// Process for nodes
for (const callback of callbacks) {
callback(node, result);
}
const scope = result.getScope(decl);
for (const variable of scope.variables) {
for (const def of variable.defs) {
if (def.parent === decl) {
def.parent = parent;
}
}
}
node.parent = parent;
tokens.shift(); // const
tokens.pop(); // ;
// Disconnect the tree structure.
decl.declarations = [];
});
return callbacks;
}
addGenericTypeAliasDeclaration(param, callbackId, callbackDefault) {
var _a, _b;
const ranges = getTypeParameterRanges(this.ctx.code, param);
let scriptLet = `type ${this.ctx.code.slice(...ranges.idRange)}`;
if (ranges.constraintRange) {
scriptLet += ` = ${this.ctx.code.slice(...ranges.constraintRange)};`;
// |extends|
}
else {
scriptLet += " = unknown;";
}
this.appendScript(scriptLet, ranges.idRange[0] - 5, "generics", "TSTypeAliasDeclaration", (st, tokens, _comments, result) => {
const decl = st;
const id = decl.id;
const typeAnnotation = decl.typeAnnotation;
// Process for nodes
callbackId(id, typeAnnotation);
const scope = result.getScope(decl);
for (const variable of scope.variables) {
for (const def of variable.defs) {
if (def.node === decl) {
def.node = param;
}
}
}
id.parent = param;
typeAnnotation.parent = param;
tokens.shift(); // type
if (ranges.constraintRange) {
const eqToken = tokens[1];
eqToken.type = "Keyword";
eqToken.value = "extends";
eqToken.range[0] = eqToken.range[0] - 1;
eqToken.range[1] = eqToken.range[0] + 7;
tokens.pop(); // ;
}
else {
tokens.pop(); // ;
tokens.pop(); // unknown
tokens.pop(); // =
}
// Disconnect the tree structure.
delete decl.id;
delete decl.typeAnnotation;
delete decl.typeParameters;
});
if (ranges.defaultRange) {
const eqDefaultType = this.ctx.code.slice((_b = (_a = ranges.constraintRange) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : ranges.idRange[1], ranges.defaultRange[1]);
const id = this.generateUniqueId(eqDefaultType);
const scriptLet = `type ${id}${eqDefaultType};`;
this.appendScript(scriptLet, ranges.defaultRange[0] - 5 - id.length - 1, "generics", "TSTypeAliasDeclaration", (st, tokens, _comments, result) => {
const decl = st;
const typeAnnotation = decl.typeAnnotation;
// Process for nodes
callbackDefault(typeAnnotation);
const scope = result.getScope(decl);
(0, scope_1.removeIdentifierVariable)(decl.id, scope);
typeAnnotation.parent = param;
tokens.shift(); // type
tokens.shift(); // ${id}
tokens.pop(); // ;
// Disconnect the tree structure.
delete decl.id;
delete decl.typeAnnotation;
delete decl.typeParameters;
});
}
}
nestIfBlock(expression, ifBlock, callback) {
const range = getNodeRange(expression);
const part = this.ctx.code.slice(...range);
const restore = this.appendScript(`if(${part}){`, range[0] - 3, this.currentScriptScopeKind, "IfStatement", (st, tokens, _comments, result) => {
const ifSt = st;
const node = ifSt.test;
const scope = result.getScope(ifSt.consequent);
// Process for nodes
callback(node, result);
node.parent = ifBlock;
// Process for scope
result.registerNodeToScope(ifBlock, scope);
tokens.shift(); // if
tokens.shift(); // (
tokens.pop(); // )
tokens.pop(); // {
tokens.pop(); // }
// Disconnect the tree structure.
ifSt.test = null;
ifSt.consequent = null;
});
this.pushScope(restore, "}", this.currentScriptScopeKind);
}
nestEachBlock(expression, context, indexRange, eachBlock, callback) {
const exprRange = getNodeRange(expression);
const ctxRange = getNodeRange(context);
let source = "Array.from(";
const exprOffset = source.length;
source += `${this.ctx.code.slice(...exprRange)}).forEach((`;
const ctxOffset = source.length;
source += this.ctx.code.slice(...ctxRange);
let idxOffset = null;
if (indexRange) {
source += ",";
idxOffset = source.length;
source += this.ctx.code.slice(indexRange.start, indexRange.end);
}
source += ")=>{";
const restore = this.appendScript(source, exprRange[0] - exprOffset, this.currentScriptScopeKind, "ExpressionStatement", (st, tokens, comments, result) => {
var _a;
const expSt = st;
const call = expSt.expression;
const fn = call.arguments[0];
const callArrayFrom = call.callee
.object;
const expr = callArrayFrom.arguments[0];
const ctx = fn.params[0];
const idx = ((_a = fn.params[1]) !== null && _a !== void 0 ? _a : null);
const scope = result.getScope(fn.body);
// Process for nodes
callback(expr, ctx, idx);
// Process for scope
result.registerNodeToScope(eachBlock, scope);
for (const v of scope.variables) {
for (const def of v.defs) {
if (def.node === fn) {
def.node = eachBlock;
}
}
}
// remove Array reference
const arrayId = callArrayFrom.callee
.object;
const ref = scope.upper.references.find((r) => r.identifier === arrayId);
if (ref) {
(0, scope_1.removeReference)(ref, scope.upper);
}
expr.parent = eachBlock;
ctx.parent = eachBlock;
if (idx) {
idx.parent = eachBlock;
}
tokens.shift(); // Array
tokens.shift(); // .
tokens.shift(); // from
tokens.shift(); // (
tokens.pop(); // )
tokens.pop(); // =>
tokens.pop(); // {
tokens.pop(); // }
tokens.pop(); // )
tokens.pop(); // ;
const map = [
{
offset: exprOffset,
range: exprRange,
newNode: expr,
},
{
offset: ctxOffset,
range: ctxRange,
newNode: ctx,
},
];
if (indexRange) {
map.push({
offset: idxOffset,
range: [indexRange.start, indexRange.end],
newNode: idx,
});
}
this.remapNodes(map, tokens, comments, result.visitorKeys);
// Disconnect the tree structure.
expSt.expression = null;
});
this.pushScope(restore, "});", this.currentScriptScopeKind);
}
nestSnippetBlock(id, closeParentIndex, snippetBlock,
// If set to null, `currentScriptScopeKind` will be used.
kind, callback) {
const scopeKind = kind || this.currentScriptScopeKind;
const idRange = getNodeRange(id);
const part = this.ctx.code.slice(idRange[0], closeParentIndex + 1);
const restore = this.appendScript(`function ${part}{`, idRange[0] - 9, scopeKind, "FunctionDeclaration", (st, tokens, _comments, result) => {
const fnDecl = st;
const idNode = fnDecl.id;
const params = [...fnDecl.params];
const scope = result.getScope(fnDecl);
// Process for nodes
callback(idNode, params);
idNode.parent = snippetBlock;
for (const param of params) {
param.parent = snippetBlock;
}
// Process for scope
result.registerNodeToScope(snippetBlock, scope);
tokens.shift(); // function
tokens.pop(); // {
tokens.pop(); // }
// Disconnect the tree structure.
fnDecl.id = null;
fnDecl.params = [];
});
this.pushScope(restore, "}", scopeKind);
}
nestBlock(block, params) {
let resolvedParams;
if (typeof params === "function") {
if (this.ctx.isTypeScript()) {
const generatedTypes = params({
generateUniqueId: (base) => this.generateUniqueId(base),
});
resolvedParams = [generatedTypes.param];
if (generatedTypes.preparationScript) {
for (const preparationScript of generatedTypes.preparationScript) {
this.appendScriptWithoutOffset(preparationScript.script, this.currentScriptScopeKind, preparationScript.nodeType, (node, tokens, comments, result) => {
tokens.length = 0;
comments.length = 0;
(0, scope_1.removeAllScopeAndVariableAndReference)(node, result);
});
}
}
}
else {
const generatedTypes = params(null);
resolvedParams = [generatedTypes.param];
}
}
else {
resolvedParams = params;
}
if (!resolvedParams || resolvedParams.length === 0) {
const restore = this.appendScript(`{`, block.range[0], this.currentScriptScopeKind, "BlockStatement", (st, tokens, _comments, result) => {
const blockSt = st;
// Process for scope
const scope = result.getScope(blockSt);
result.registerNodeToScope(block, scope);
tokens.length = 0; // clear tokens
// Disconnect the tree structure.
blockSt.body = null;
});
this.pushScope(restore, "}", this.currentScriptScopeKind);
}
else {
const sortedParams = [...resolvedParams]
.map((d) => {
return Object.assign(Object.assign({}, d), { range: getNodeRange(d.node) });
})
.sort((a, b) => {
const [pA] = a.range;
const [pB] = b.range;
return pA - pB;
});
const maps = [];
let source = "";
for (let index = 0; index < sortedParams.length; index++) {
const param = sortedParams[index];
const range = param.range;
if (source) {
source += ",";
}
const offset = source.length + 1; /* ( */
source += this.ctx.code.slice(...range);
maps.push({
index: maps.length,
offset,
range,
});
if (this.ctx.isTypeScript()) {
source += `: (${param.typing})`;
}
}
const restore = this.appendScript(`(${source})=>{`, maps[0].range[0] - 1, this.currentScriptScopeKind, "ExpressionStatement", (st, tokens, comments, result) => {
const exprSt = st;
const fn = exprSt.expression;
const scope = result.getScope(fn.body);
// Process for nodes
for (let index = 0; index < fn.params.length; index++) {
const p = fn.params[index];
sortedParams[index].callback(p, result);
if (this.ctx.isTypeScript()) {
const typeAnnotation = p.typeAnnotation;
delete p.typeAnnotation;
p.range[1] = typeAnnotation.range[0];
p.loc.end = {
line: typeAnnotation.loc.start.line,
column: typeAnnotation.loc.start.column,
};
(0, scope_1.removeAllScopeAndVariableAndReference)(typeAnnotation, result);
}
p.parent = sortedParams[index].parent;
}
// Process for scope
result.registerNodeToScope(block, scope);
for (const v of scope.variables) {
for (const def of v.defs) {
if (def.node === fn) {
def.node = block;
}
}
}
tokens.shift(); // (
tokens.pop(); // )
tokens.pop(); // =>
tokens.pop(); // {
tokens.pop(); // }
tokens.pop(); // ;
this.remapNodes(maps.map((m) => {
return {
offset: m.offset,
range: m.range,
newNode: fn.params[m.index],
};
}), tokens, comments, result.visitorKeys);
// Disconnect the tree structure.
exprSt.expression = null;
});
this.pushScope(restore, "};", this.currentScriptScopeKind);
}
}
closeScope() {
this.closeScopeCallbacks.pop()();
}
addProgramRestore(callback) {
this.programRestoreCallbacks.push(callback);
}
appendScript(text, offset, kind, nodeType, callback) {
const resultCallback = this.appendScriptWithoutOffset(text, kind, nodeType, (node, tokens, comments, result) => {
(0, fix_locations_1.fixLocations)(node, tokens, comments, offset - resultCallback.start, result.visitorKeys, this.ctx);
callback(node, tokens, comments, result);
});
return resultCallback;
}
appendScriptWithoutOffset(text, kind, nodeType, callback) {
const { start: startOffset, end: endOffset } = this.script.addLet(text, kind);
const restoreCallback = {
start: startOffset,
end: endOffset,
nodeType,
callback,
};
this.restoreCallbacks.push(restoreCallback);
return restoreCallback;
}
pushScope(restoreCallback, closeToken, kind) {
const upper = this.currentScriptScopeKind;
this.currentScriptScopeKind = kind;
this.closeScopeCallbacks.push(() => {
this.script.addLet(closeToken, kind);
this.currentScriptScopeKind = upper;
restoreCallback.end = this.script.getCurrentVirtualCodeLength();
});
}
/**
* Restore AST nodes
*/
restore(result) {
const nodeToScope = getNodeToScope(result.scopeManager);
const postprocessList = [];
const callbackOption = {
getScope,
registerNodeToScope,
scopeManager: result.scopeManager,
visitorKeys: result.visitorKeys,
addPostProcess: (cb) => postprocessList.push(cb),
};
this.restoreNodes(result, callbackOption);
this.restoreProgram(result, callbackOption);
postprocessList.forEach((p) => p());
// Helpers
/** Get scope */
function getScope(node) {
return (0, scope_1.getScopeFromNode)(result.scopeManager, node);
}
/** Register node to scope */
function registerNodeToScope(node, scope) {
// If we replace the `scope.block` at this time,
// the scope restore calculation will not work, so we will replace the `scope.block` later.
postprocessList.push(() => {
var _a, _b;
const beforeBlock = scope.block;
scope.block = node;
for (const variable of [
...scope.variables,
...((_b = (_a = scope.upper) === null || _a === void 0 ? void 0 : _a.variables) !== null && _b !== void 0 ? _b : []),
]) {
for (const def of variable.defs) {
if (def.node === beforeBlock) {
def.node = node;
}
}
}
});
const scopes = nodeToScope.get(node);
if (scopes) {
scopes.push(scope);
}
else {
nodeToScope.set(node, [scope]);
}
}
}
/**
* Restore AST nodes
*/
restoreNodes(result, callbackOption) {
let orderedRestoreCallback = this.restoreCallbacks.shift();
if (!orderedRestoreCallback) {
return;
}
const separateIndexes = this.script.separateIndexes;
const tokens = result.ast.tokens;
const processedTokens = [];
const comments = result.ast.comments;
const processedComments = [];
let tok;
while ((tok = tokens.shift())) {
if (separateIndexes.includes(tok.range[0]) && tok.value === ";") {
break;
}
if (orderedRestoreCallback.start <= tok.range[0]) {
tokens.unshift(tok);
break;
}
processedTokens.push(tok);
}
while ((tok = comments.shift())) {
if (orderedRestoreCallback.start <= tok.range[0]) {
comments.unshift(tok);
break;
}
processedComments.push(tok);
}
const targetNodes = new Map();
const removeStatements = [];
(0, traverse_1.traverseNodes)(result.ast, {
visitorKeys: result.visitorKeys,
enterNode: (node) => {
while (node.range && separateIndexes.includes(node.range[1] - 1)) {
node.range[1]--;
node.loc.end.column--;
}
if (node.loc.end.column < 0) {
node.loc.end = this.ctx.getLocFromIndex(node.range[1]);
}
if (node.parent === result.ast &&
separateIndexes[0] <= node.range[0]) {
removeStatements.push(node);
}
if (!orderedRestoreCallback) {
return;
}
if (orderedRestoreCallback.nodeType === node.type &&
orderedRestoreCallback.start <= node.range[0] &&
node.range[1] <= orderedRestoreCallback.end) {
targetNodes.set(node, orderedRestoreCallback);
orderedRestoreCallback = this.restoreCallbacks.shift();
}
//
},
leaveNode(node) {
const restoreCallback = targetNodes.get(node);
if (!restoreCallback) {
return;
}
const startIndex = {
token: tokens.findIndex((t) => restoreCallback.start <= t.range[0]),
comment: comments.findIndex((t) => restoreCallback.start <= t.range[0]),
};
if (startIndex.comment === -1) {
startIndex.comment = comments.length;
}
const endIndex = {
token: tokens.findIndex((t) => restoreCallback.end < t.range[1], startIndex.token),
comment: comments.findIndex((t) => restoreCallback.end < t.range[1], startIndex.comment),
};
if (endIndex.token === -1) {
endIndex.token = tokens.length;
}
if (endIndex.comment === -1) {
endIndex.comment = comments.length;
}
const targetTokens = tokens.splice(startIndex.token, endIndex.token - startIndex.token);
const targetComments = comments.splice(startIndex.comment, endIndex.comment - startIndex.comment);
restoreCallback.callback(node, targetTokens, targetComments, callbackOption);
processedTokens.push(...targetTokens);
processedComments.push(...targetComments);
},
});
for (const st of removeStatements) {
const index = result.ast.body.indexOf(st);
result.ast.body.splice(index, 1);
}
result.ast.tokens = processedTokens;
result.ast.comments = processedComments;
}
/**
* Restore program node
*/
restoreProgram(result, callbackOption) {
for (const callback of this.programRestoreCallbacks) {
callback(result.ast, result.ast.tokens, result.ast.comments, callbackOption);
}
}
remapNodes(maps, tokens, comments, visitorKeys) {
const targetMaps = [...maps];
const first = targetMaps.shift();
let tokenIndex = 0;
for (; tokenIndex < tokens.length; tokenIndex++) {
const token = tokens[tokenIndex];
if (first.range[1] <= token.range[0]) {
break;
}
}
for (const map of targetMaps) {
const startOffset = map.offset - first.offset + first.range[0];
const endOffset = startOffset + (map.range[1] - map.range[0]);
let removeCount = 0;
let target = tokens[tokenIndex];
while (target && target.range[1] <= startOffset) {
removeCount++;
target = tokens[tokenIndex + removeCount];
}
if (removeCount) {
tokens.splice(tokenIndex, removeCount);
}
const bufferTokens = [];
for (; tokenIndex < tokens.length; tokenIndex++) {
const token = tokens[tokenIndex];
if (endOffset <= token.range[0]) {
break;
}
bufferTokens.push(token);
}
(0, fix_locations_1.fixLocations)(map.newNode, bufferTokens, comments.filter((t) => startOffset <= t.range[0] && t.range[1] <= endOffset), map.range[0] - startOffset, visitorKeys, this.ctx);
}
tokens.splice(tokenIndex);
}
generateUniqueId(base) {
return this.unique.generate(base, this.ctx.code, this.script.getCurrentVirtualCode());
}
}
exports.ScriptLetContext = ScriptLetContext;
function getTypeParameterRanges(code, param) {
var _a;
const idRange = [
param.range[0],
param.constraint || param.default ? param.name.range[1] : param.range[1],
];
let constraintRange = null;
let defaultRange = null;
if (param.constraint) {
constraintRange = [
param.constraint.range[0],
param.default ? param.constraint.range[1] : param.range[1],
];
const index = getTokenIndex(code, (code, index) => code.startsWith("extends", index), idRange[1], param.constraint.range[0]);
if (index != null) {
idRange[1] = index;
constraintRange[0] = index + 7;
}
}
if (param.default) {
defaultRange = [param.default.range[0], param.range[1]];
const index = getTokenIndex(code, (code, index) => code[index] === "=", (_a = constraintRange === null || constraintRange === void 0 ? void 0 : constraintRange[1]) !== null && _a !== void 0 ? _a : idRange[1], param.default.range[0]);
if (index != null) {
(constraintRange !== null && constraintRange !== void 0 ? constraintRange : idRange)[1] = index;
defaultRange[0] = index + 1;
}
}
return { idRange, constraintRange, defaultRange };
}
function getTokenIndex(code, targetToken, start, end) {
let index = start;
while (index < end) {
if (targetToken(code, index)) {
return index;
}
if (code.startsWith("//", index)) {
const lfIndex = code.indexOf("\n", index);
if (lfIndex >= 0) {
index = lfIndex + 1;
continue;
}
return null;
}
if (code.startsWith("/*", index)) {
const endIndex = code.indexOf("*/", index);
if (endIndex >= 0) {
index = endIndex + 2;
continue;
}
return null;
}
index++;
}
return null;
}
/** Get the node to scope map from given scope manager */
function getNodeToScope(scopeManager) {
if ("__nodeToScope" in scopeManager) {
return scopeManager.__nodeToScope;
}
// transform scopeManager
const nodeToScope = new WeakMap();
for (const scope of scopeManager.scopes) {
const scopes = nodeToScope.get(scope.block);
if (scopes) {
scopes.push(scope);
}
else {
nodeToScope.set(scope.block, [scope]);
}
}
scopeManager.acquire = function (node, inner) {
/**
* predicate
*/
function predicate(testScope) {
if (testScope.type === "function" && testScope.functionExpressionScope) {
return false;
}
return true;
}
const scopes = nodeToScope.get(node);
if (!scopes || scopes.length === 0) {
return null;
}
// Heuristic selection from all scopes.
// If you would like to get all scopes, please use ScopeManager#acquireAll.
if (scopes.length === 1) {
return scopes[0];
}
if (inner) {
for (let i = scopes.length - 1; i >= 0; --i) {
const scope = scopes[i];
if (predicate(scope)) {
return scope;
}
}
}
else {
for (let i = 0, iz = scopes.length; i < iz; ++i) {
const scope = scopes[i];
if (predicate(scope)) {
return scope;
}
}
}
return null;
};
return nodeToScope;
}
/** Extract the type scope of the given node. */
function extractTypeNodeScopes(node, result) {
const scopes = new Set();
for (const scope of iterateTypeNodeScopes(node)) {
scopes.add(scope);
}
return scopes;
/** Iterate the type scope of the given node. */
function* iterateTypeNodeScopes(node) {
if (node.type === "TSParenthesizedType") {
// Skip TSParenthesizedType.
yield* iterateTypeNodeScopes(node.typeAnnotation);
}
else if (node.type === "TSConditionalType") {
yield result.getScope(node);
// `falseType` of `TSConditionalType` is sibling scope.
const falseType = node.falseType;
yield* iterateTypeNodeScopes(falseType);
}
else if (node.type === "TSFunctionType" ||
node.type === "TSMappedType" ||
node.type === "TSConstructorType") {
yield result.getScope(node);
}
else {
const typeNode = node;
for (const key of (0, traverse_1.getKeys)(typeNode, result.visitorKeys)) {
for (const child of (0, traverse_1.getNodes)(typeNode, key)) {
yield* iterateTypeNodeScopes(child);
}
}
}
}
}

View File

@@ -0,0 +1,5 @@
export declare class UniqueIdGenerator {
private uniqueIdSeq;
private readonly usedUniqueIds;
generate(base: string, ...texts: string[]): string;
}

View File

@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.UniqueIdGenerator = void 0;
class UniqueIdGenerator {
constructor() {
this.uniqueIdSeq = 1;
this.usedUniqueIds = new Set();
}
generate(base, ...texts) {
const hasId = (id) => this.usedUniqueIds.has(id) || texts.some((t) => t.includes(id));
let candidate = `$_${base.replace(/\W/g, "_")}${this.uniqueIdSeq++}`;
while (hasId(candidate)) {
candidate = `$_${base.replace(/\W/g, "_")}${this.uniqueIdSeq++}`;
}
this.usedUniqueIds.add(candidate);
return candidate;
}
}
exports.UniqueIdGenerator = UniqueIdGenerator;

13
web/node_modules/svelte-eslint-parser/lib/errors.d.ts generated vendored Normal file
View File

@@ -0,0 +1,13 @@
import type { Context } from "./context";
/**
* Svelte parse errors.
*/
export declare class ParseError extends SyntaxError {
index: number;
lineNumber: number;
column: number;
/**
* Initialize this ParseError instance.
*/
constructor(message: string, offset: number, ctx: Context);
}

19
web/node_modules/svelte-eslint-parser/lib/errors.js generated vendored Normal file
View File

@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ParseError = void 0;
/**
* Svelte parse errors.
*/
class ParseError extends SyntaxError {
/**
* Initialize this ParseError instance.
*/
constructor(message, offset, ctx) {
super(message);
this.index = offset;
const loc = ctx.getLocFromIndex(offset);
this.lineNumber = loc.line;
this.column = loc.column;
}
}
exports.ParseError = ParseError;

10
web/node_modules/svelte-eslint-parser/lib/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,10 @@
import * as AST from "./ast";
import { traverseNodes } from "./traverse";
import { ParseError } from "./errors";
export { parseForESLint, type StyleContext, type StyleContextNoStyleElement, type StyleContextParseError, type StyleContextSuccess, type StyleContextUnknownLang, } from "./parser";
export * as meta from "./meta";
export { name } from "./meta";
export type { SvelteConfig } from "./svelte-config";
export { AST, ParseError };
export declare const VisitorKeys: import("eslint").SourceCode.VisitorKeys;
export { traverseNodes };

41
web/node_modules/svelte-eslint-parser/lib/index.js generated vendored Normal file
View File

@@ -0,0 +1,41 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.traverseNodes = exports.VisitorKeys = exports.ParseError = exports.AST = exports.name = exports.meta = exports.parseForESLint = void 0;
const AST = __importStar(require("./ast"));
exports.AST = AST;
const traverse_1 = require("./traverse");
Object.defineProperty(exports, "traverseNodes", { enumerable: true, get: function () { return traverse_1.traverseNodes; } });
const visitor_keys_1 = require("./visitor-keys");
const errors_1 = require("./errors");
Object.defineProperty(exports, "ParseError", { enumerable: true, get: function () { return errors_1.ParseError; } });
var parser_1 = require("./parser");
Object.defineProperty(exports, "parseForESLint", { enumerable: true, get: function () { return parser_1.parseForESLint; } });
exports.meta = __importStar(require("./meta"));
var meta_1 = require("./meta");
Object.defineProperty(exports, "name", { enumerable: true, get: function () { return meta_1.name; } });
// Keys
// eslint-disable-next-line @typescript-eslint/naming-convention -- ignore
exports.VisitorKeys = visitor_keys_1.KEYS;

2
web/node_modules/svelte-eslint-parser/lib/meta.d.ts generated vendored Normal file
View File

@@ -0,0 +1,2 @@
export declare const name = "svelte-eslint-parser";
export declare const version = "0.43.0";

8
web/node_modules/svelte-eslint-parser/lib/meta.js generated vendored Normal file
View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.version = exports.name = void 0;
// IMPORTANT!
// This file has been automatically generated,
// in order to update its content execute "pnpm run build:meta"
exports.name = "svelte-eslint-parser";
exports.version = "0.43.0";

View File

@@ -0,0 +1,19 @@
import type ESTree from "estree";
import type { ScopeManager } from "eslint-scope";
import type { SvelteScriptElement, SvelteSnippetBlock } from "../ast";
import type { NormalizedParserOptions } from "./parser-options";
import type { SvelteParseContext } from "./svelte-parse-context";
/**
* Analyze scope
*/
export declare function analyzeScope(node: ESTree.Node, parserOptions: NormalizedParserOptions): ScopeManager;
/** Analyze reactive scope */
export declare function analyzeReactiveScope(scopeManager: ScopeManager): void;
/**
* Analyze store scope. e.g. $count
*/
export declare function analyzeStoreScope(scopeManager: ScopeManager): void;
/** Transform props exports */
export declare function analyzePropsScope(body: SvelteScriptElement, scopeManager: ScopeManager, svelteParseContext: SvelteParseContext): void;
/** Analyze snippets in component scope */
export declare function analyzeSnippetsScope(snippets: SvelteSnippetBlock[], scopeManager: ScopeManager): void;

View File

@@ -0,0 +1,290 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.analyzeScope = analyzeScope;
exports.analyzeReactiveScope = analyzeReactiveScope;
exports.analyzeStoreScope = analyzeStoreScope;
exports.analyzePropsScope = analyzePropsScope;
exports.analyzeSnippetsScope = analyzeSnippetsScope;
const eslint_scope_1 = require("eslint-scope");
const traverse_1 = require("../traverse");
const scope_1 = require("../scope");
const utils_1 = require("../utils");
/**
* Analyze scope
*/
function analyzeScope(node, parserOptions) {
const ecmaVersion = parserOptions.ecmaVersion || 2020;
const ecmaFeatures = parserOptions.ecmaFeatures || {};
const sourceType = parserOptions.sourceType || "module";
const root = node.type === "Program"
? node
: {
type: "Program",
body: [node],
sourceType,
};
return (0, eslint_scope_1.analyze)(root, {
ignoreEval: true,
nodejsScope: false,
impliedStrict: ecmaFeatures.impliedStrict,
ecmaVersion: typeof ecmaVersion === "number" ? ecmaVersion : 2022,
sourceType,
fallback: traverse_1.getFallbackKeys,
});
}
/** Analyze reactive scope */
function analyzeReactiveScope(scopeManager) {
for (const reference of [...scopeManager.globalScope.through]) {
const parent = reference.writeExpr && getParent(reference.writeExpr);
if ((parent === null || parent === void 0 ? void 0 : parent.type) === "AssignmentExpression") {
const pp = getParent(parent);
if ((pp === null || pp === void 0 ? void 0 : pp.type) === "ExpressionStatement") {
const ppp = getParent(pp);
if ((ppp === null || ppp === void 0 ? void 0 : ppp.type) === "SvelteReactiveStatement" && ppp.label.name === "$") {
const referenceScope = reference.from;
if (referenceScope.type === "module") {
// It is computed
transformComputedVariable(parent, ppp, reference);
continue;
}
}
}
}
}
/** Transform ref to ComputedVariable */
function transformComputedVariable(node, parent, reference) {
const referenceScope = reference.from;
const name = reference.identifier.name;
let variable = referenceScope.set.get(name);
if (!variable) {
variable = new eslint_scope_1.Variable();
variable.scope = referenceScope;
variable.name = name;
(0, utils_1.addElementToSortedArray)(variable.defs, {
type: "ComputedVariable",
node: node,
parent: parent,
name: reference.identifier,
}, (a, b) => a.node.range[0] - b.node.range[0]);
(0, scope_1.addVariable)(referenceScope.variables, variable);
referenceScope.set.set(name, variable);
}
(0, utils_1.addElementToSortedArray)(variable.identifiers, reference.identifier, (a, b) => a.range[0] - b.range[0]);
reference.resolved = variable;
removeReferenceFromThrough(reference, referenceScope);
}
}
/**
* Analyze store scope. e.g. $count
*/
function analyzeStoreScope(scopeManager) {
const moduleScope = scopeManager.scopes.find((scope) => scope.type === "module");
if (!moduleScope) {
return;
}
const toBeMarkAsUsedReferences = [];
for (const reference of [...scopeManager.globalScope.through]) {
if (reference.identifier.name.startsWith("$")) {
const realName = reference.identifier.name.slice(1);
const variable = moduleScope.set.get(realName);
if (variable) {
if (reference.isWriteOnly()) {
// Need mark as used
toBeMarkAsUsedReferences.push(reference);
}
// It does not write directly to the original variable.
// Therefore, this variable is always a reference.
reference.isWrite = () => false;
reference.isWriteOnly = () => false;
reference.isReadWrite = () => false;
reference.isReadOnly = () => true;
reference.isRead = () => true;
(0, scope_1.addReference)(variable.references, reference);
reference.resolved = variable;
removeReferenceFromThrough(reference, moduleScope);
}
}
}
for (const variable of new Set(toBeMarkAsUsedReferences.map((ref) => ref.resolved))) {
if (variable.references.some((ref) => !toBeMarkAsUsedReferences.includes(ref) &&
ref.identifier !== variable.identifiers[0])) {
// It is already used.
continue;
}
// Add the virtual reference for reading.
addVirtualReference(variable.identifiers[0], variable, moduleScope, {
read: true,
}).svelteMarkAsUsed = true;
}
}
/** Transform props exports */
function analyzePropsScope(body, scopeManager, svelteParseContext) {
var _a;
const moduleScope = scopeManager.scopes.find((scope) => scope.type === "module");
if (!moduleScope) {
return;
}
for (const node of body.body) {
if (node.type === "ExportNamedDeclaration") {
// Process for Svelte v4 style props. e.g. `export let x`;
if (node.declaration) {
if (node.declaration.type === "VariableDeclaration") {
for (const decl of node.declaration.declarations) {
for (const pattern of extractPattern(decl.id)) {
if (pattern.type === "Identifier") {
addPropReference(pattern, moduleScope);
}
}
}
}
}
else {
for (const spec of node.specifiers) {
if (spec.local.type !== "Literal") {
addPropReference(spec.local, moduleScope);
}
}
}
}
else if (node.type === "VariableDeclaration") {
if (svelteParseContext.runes) {
// Process for Svelte v5 Runes props. e.g. `let { x = $bindable() } = $props()`;
for (const decl of node.declarations) {
if (((_a = decl.init) === null || _a === void 0 ? void 0 : _a.type) === "CallExpression" &&
decl.init.callee.type === "Identifier" &&
decl.init.callee.name === "$props" &&
decl.id.type === "ObjectPattern") {
for (const pattern of extractPattern(decl.id)) {
if (pattern.type === "AssignmentPattern" &&
pattern.left.type === "Identifier" &&
pattern.right.type === "CallExpression" &&
pattern.right.callee.type === "Identifier" &&
pattern.right.callee.name === "$bindable") {
addPropReference(pattern.left, moduleScope);
}
}
}
}
}
}
}
function* extractPattern(node) {
yield node;
if (node.type === "Identifier") {
return;
}
if (node.type === "ObjectPattern") {
for (const prop of node.properties) {
if (prop.type === "Property") {
yield* extractPattern(prop.value);
}
else {
yield* extractPattern(prop);
}
}
return;
}
if (node.type === "ArrayPattern") {
for (const elem of node.elements) {
if (elem) {
yield* extractPattern(elem);
}
}
return;
}
if (node.type === "AssignmentPattern") {
yield* extractPattern(node.left);
return;
}
if (node.type === "RestElement") {
yield* extractPattern(node.argument);
}
}
/** Add virtual prop reference */
function addPropReference(node, scope) {
for (const variable of scope.variables) {
if (variable.name !== node.name) {
continue;
}
if (variable.references.some((ref) => ref.sveltePropReference)) {
continue;
}
// Add the virtual reference for writing.
const reference = addVirtualReference(Object.assign(Object.assign({}, node), {
// @ts-expect-error -- ignore
parent: body, loc: {
start: Object.assign({}, node.loc.start),
end: Object.assign({}, node.loc.end),
}, range: [...node.range] }), variable, scope, {
write: true,
read: true,
});
reference.sveltePropReference = true;
}
}
}
/** Analyze snippets in component scope */
function analyzeSnippetsScope(snippets, scopeManager) {
for (const snippet of snippets) {
const parent = snippet.parent;
if (parent.type === "SvelteElement" &&
(parent.kind === "component" ||
(parent.kind === "special" && parent.name.name === "svelte:component"))) {
const scope = (0, scope_1.getScopeFromNode)(scopeManager, snippet.id);
const upperScope = scope.upper;
if (!upperScope)
continue;
const variable = upperScope.set.get(snippet.id.name);
if (!variable)
continue;
// Add the virtual reference for reading.
const reference = addVirtualReference(snippet.id, variable, upperScope, {
read: true,
});
reference.svelteSnippetReference = true;
}
}
}
/** Remove reference from through */
function removeReferenceFromThrough(reference, baseScope) {
const variable = reference.resolved;
const name = reference.identifier.name;
let scope = baseScope;
while (scope) {
scope.through = scope.through.filter((ref) => {
if (reference === ref) {
return false;
}
else if (ref.identifier.name === name) {
ref.resolved = variable;
if (!variable.references.includes(ref)) {
(0, scope_1.addReference)(variable.references, ref);
}
return false;
}
return true;
});
scope = scope.upper;
}
}
/**
* Add the virtual reference.
*/
function addVirtualReference(node, variable, scope, readWrite) {
const reference = new eslint_scope_1.Reference();
reference.svelteVirtualReference = true;
reference.from = scope;
reference.identifier = node;
reference.isWrite = () => Boolean(readWrite.write);
reference.isWriteOnly = () => Boolean(readWrite.write) && !readWrite.read;
reference.isRead = () => Boolean(readWrite.read);
reference.isReadOnly = () => Boolean(readWrite.read) && !readWrite.write;
reference.isReadWrite = () => Boolean(readWrite.read && readWrite.write);
(0, scope_1.addReference)(variable.references, reference);
reference.resolved = variable;
return reference;
}
/** Get parent node */
function getParent(node) {
return node.parent;
}

View File

@@ -0,0 +1,36 @@
/** Compatibility for Svelte v4 <-> v5 */
import type ESTree from "estree";
import type * as SvAST from "./svelte-ast-types";
import type * as Compiler from "./svelte-ast-types-for-v5";
export type Child = Compiler.Text | Compiler.Tag | Compiler.ElementLike | Compiler.Block | Compiler.Comment;
type HasChildren = {
children?: SvAST.TemplateNode[];
};
export declare function getFragmentFromRoot(svelteAst: Compiler.Root | SvAST.AstLegacy): SvAST.Fragment | Compiler.Fragment | undefined;
export declare function getInstanceFromRoot(svelteAst: Compiler.Root | SvAST.AstLegacy): SvAST.Script | Compiler.Script | null | undefined;
export declare function getModuleFromRoot(svelteAst: Compiler.Root | SvAST.AstLegacy): SvAST.Script | Compiler.Script | null | undefined;
export declare function getOptionsFromRoot(svelteAst: Compiler.Root | SvAST.AstLegacy): Compiler.SvelteOptionsRaw | null;
export declare function getChildren(fragment: Required<HasChildren> | {
nodes: (Child | SvAST.TemplateNode)[];
}): (SvAST.TemplateNode | Child)[];
export declare function getChildren(fragment: HasChildren | {
nodes: (Child | SvAST.TemplateNode)[];
}): (SvAST.TemplateNode | Child)[] | undefined;
export declare function trimChildren(children: (SvAST.TemplateNode | Child)[]): (SvAST.TemplateNode | Child)[];
export declare function getFragment(element: {
fragment: Compiler.Fragment;
} | Required<HasChildren>): Compiler.Fragment | Required<HasChildren>;
export declare function getFragment(element: {
fragment: Compiler.Fragment;
} | HasChildren): Compiler.Fragment | HasChildren;
export declare function getModifiers(node: SvAST.Directive | SvAST.StyleDirective | Compiler.Directive): string[];
export declare function getTestFromIfBlock(block: SvAST.IfBlock | Compiler.IfBlock): ESTree.Expression;
export declare function getConsequentFromIfBlock(block: SvAST.IfBlock | Compiler.IfBlock): Compiler.Fragment | SvAST.IfBlock;
export declare function getAlternateFromIfBlock(block: SvAST.IfBlock | Compiler.IfBlock): Compiler.Fragment | SvAST.ElseBlock | null;
export declare function getBodyFromEachBlock(block: SvAST.EachBlock | Compiler.EachBlock): Compiler.Fragment | SvAST.EachBlock;
export declare function getFallbackFromEachBlock(block: SvAST.EachBlock | Compiler.EachBlock): Compiler.Fragment | SvAST.ElseBlock | null;
export declare function getPendingFromAwaitBlock(block: SvAST.AwaitBlock | Compiler.AwaitBlock): Compiler.Fragment | SvAST.PendingBlock | null;
export declare function getThenFromAwaitBlock(block: SvAST.AwaitBlock | Compiler.AwaitBlock): Compiler.Fragment | SvAST.ThenBlock | null;
export declare function getCatchFromAwaitBlock(block: SvAST.AwaitBlock | Compiler.AwaitBlock): Compiler.Fragment | SvAST.CatchBlock | null;
export declare function getDeclaratorFromConstTag(node: SvAST.ConstTag | Compiler.ConstTag): ESTree.AssignmentExpression | Compiler.ConstTag["declaration"]["declarations"][0];
export {};

View File

@@ -0,0 +1,167 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getFragmentFromRoot = getFragmentFromRoot;
exports.getInstanceFromRoot = getInstanceFromRoot;
exports.getModuleFromRoot = getModuleFromRoot;
exports.getOptionsFromRoot = getOptionsFromRoot;
exports.getChildren = getChildren;
exports.trimChildren = trimChildren;
exports.getFragment = getFragment;
exports.getModifiers = getModifiers;
exports.getTestFromIfBlock = getTestFromIfBlock;
exports.getConsequentFromIfBlock = getConsequentFromIfBlock;
exports.getAlternateFromIfBlock = getAlternateFromIfBlock;
exports.getBodyFromEachBlock = getBodyFromEachBlock;
exports.getFallbackFromEachBlock = getFallbackFromEachBlock;
exports.getPendingFromAwaitBlock = getPendingFromAwaitBlock;
exports.getThenFromAwaitBlock = getThenFromAwaitBlock;
exports.getCatchFromAwaitBlock = getCatchFromAwaitBlock;
exports.getDeclaratorFromConstTag = getDeclaratorFromConstTag;
// Root
function getFragmentFromRoot(svelteAst) {
var _a;
return ((_a = svelteAst.fragment) !== null && _a !== void 0 ? _a : svelteAst.html);
}
function getInstanceFromRoot(svelteAst) {
return svelteAst.instance;
}
function getModuleFromRoot(svelteAst) {
return svelteAst.module;
}
function getOptionsFromRoot(svelteAst) {
const root = svelteAst;
if (root.options) {
return {
type: "SvelteOptions",
name: "svelte:options",
attributes: root.options.attributes,
fragment: {
type: "Fragment",
nodes: [],
},
start: root.options.start,
end: root.options.end,
};
}
return null;
}
function getChildren(fragment) {
var _a;
return ((_a = fragment.nodes) !== null && _a !== void 0 ? _a : fragment.children);
}
function trimChildren(children) {
if (!startsWithWhitespace(children[0]) &&
!endsWithWhitespace(children[children.length - 1])) {
return children;
}
const nodes = [...children];
while (isWhitespace(nodes[0])) {
nodes.shift();
}
const first = nodes[0];
if (startsWithWhitespace(first)) {
nodes[0] = Object.assign(Object.assign({}, first), { data: first.data.trimStart() });
}
while (isWhitespace(nodes[nodes.length - 1])) {
nodes.pop();
}
const last = nodes[nodes.length - 1];
if (endsWithWhitespace(last)) {
nodes[nodes.length - 1] = Object.assign(Object.assign({}, last), { data: last.data.trimEnd() });
}
return nodes;
function startsWithWhitespace(child) {
if (!child) {
return false;
}
return child.type === "Text" && child.data.trimStart() !== child.data;
}
function endsWithWhitespace(child) {
if (!child) {
return false;
}
return child.type === "Text" && child.data.trimEnd() !== child.data;
}
function isWhitespace(child) {
if (!child) {
return false;
}
return child.type === "Text" && child.data.trim() === "";
}
}
function getFragment(element) {
if (element.fragment) {
return element.fragment;
}
return element;
}
function getModifiers(node) {
var _a;
return (_a = node.modifiers) !== null && _a !== void 0 ? _a : [];
}
// IfBlock
function getTestFromIfBlock(block) {
var _a;
return ((_a = block.expression) !== null && _a !== void 0 ? _a : block.test);
}
function getConsequentFromIfBlock(block) {
var _a;
return (_a = block.consequent) !== null && _a !== void 0 ? _a : block;
}
function getAlternateFromIfBlock(block) {
var _a;
if (block.alternate) {
return block.alternate;
}
return (_a = block.else) !== null && _a !== void 0 ? _a : null;
}
// EachBlock
function getBodyFromEachBlock(block) {
if (block.body) {
return block.body;
}
return block;
}
function getFallbackFromEachBlock(block) {
var _a;
if (block.fallback) {
return block.fallback;
}
return (_a = block.else) !== null && _a !== void 0 ? _a : null;
}
// AwaitBlock
function getPendingFromAwaitBlock(block) {
const pending = block.pending;
if (!pending) {
return null;
}
if (pending.type === "Fragment") {
return pending;
}
return pending.skip ? null : pending;
}
function getThenFromAwaitBlock(block) {
const then = block.then;
if (!then) {
return null;
}
if (then.type === "Fragment") {
return then;
}
return then.skip ? null : then;
}
function getCatchFromAwaitBlock(block) {
const catchFragment = block.catch;
if (!catchFragment) {
return null;
}
if (catchFragment.type === "Fragment") {
return catchFragment;
}
return catchFragment.skip ? null : catchFragment;
}
// ConstTag
function getDeclaratorFromConstTag(node) {
var _a, _b, _c;
return ((_c = (_b = (_a = node.declaration) === null || _a === void 0 ? void 0 : _a.declarations) === null || _b === void 0 ? void 0 : _b[0]) !== null && _c !== void 0 ? _c : node.expression);
}

View File

@@ -0,0 +1,6 @@
import type { SvelteAttribute, SvelteShorthandAttribute, SvelteDirective, SvelteSpreadAttribute, SvelteStartTag, SvelteStyleDirective } from "../../ast";
import type { Context } from "../../context";
import type * as SvAST from "../svelte-ast-types";
import type * as Compiler from "../svelte-ast-types-for-v5";
/** Convert for Attributes */
export declare function convertAttributes(attributes: (SvAST.AttributeOrDirective | Compiler.Attribute | Compiler.SpreadAttribute | Compiler.Directive)[], parent: SvelteStartTag, ctx: Context): IterableIterator<SvelteAttribute | SvelteShorthandAttribute | SvelteSpreadAttribute | SvelteDirective | SvelteStyleDirective>;

View File

@@ -0,0 +1,531 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertAttributes = convertAttributes;
const common_1 = require("./common");
const mustache_1 = require("./mustache");
const text_1 = require("./text");
const errors_1 = require("../../errors");
const svelte_version_1 = require("../svelte-version");
const utils_1 = require("../../utils");
const compat_1 = require("../compat");
/** Convert for Attributes */
function* convertAttributes(attributes, parent, ctx) {
for (const attr of attributes) {
if (attr.type === "Attribute") {
yield convertAttribute(attr, parent, ctx);
continue;
}
if (attr.type === "SpreadAttribute" || attr.type === "Spread") {
yield convertSpreadAttribute(attr, parent, ctx);
continue;
}
if (attr.type === "BindDirective" || attr.type === "Binding") {
yield convertBindingDirective(attr, parent, ctx);
continue;
}
if (attr.type === "OnDirective" || attr.type === "EventHandler") {
yield convertEventHandlerDirective(attr, parent, ctx);
continue;
}
if (attr.type === "ClassDirective" || attr.type === "Class") {
yield convertClassDirective(attr, parent, ctx);
continue;
}
if (attr.type === "StyleDirective") {
yield convertStyleDirective(attr, parent, ctx);
continue;
}
if (attr.type === "TransitionDirective" || attr.type === "Transition") {
yield convertTransitionDirective(attr, parent, ctx);
continue;
}
if (attr.type === "AnimateDirective" || attr.type === "Animation") {
yield convertAnimationDirective(attr, parent, ctx);
continue;
}
if (attr.type === "UseDirective" || attr.type === "Action") {
yield convertActionDirective(attr, parent, ctx);
continue;
}
if (attr.type === "LetDirective") {
yield convertLetDirective(attr, parent, ctx);
continue;
}
if (attr.type === "Let") {
yield convertLetDirective(attr, parent, ctx);
continue;
}
if (attr.type === "Ref") {
throw new errors_1.ParseError("Ref are not supported.", attr.start, ctx);
}
if (attr.type === "Style") {
throw new errors_1.ParseError(`Svelte v3.46.0 is no longer supported. Please use Svelte>=v3.46.1.`, attr.start, ctx);
}
throw new errors_1.ParseError(`Unknown directive or attribute (${attr.type}) are not supported.`, attr.start, ctx);
}
}
/** Convert for Attribute */
function convertAttribute(node, parent, ctx) {
const attribute = Object.assign({ type: "SvelteAttribute", boolean: false, key: null, value: [], parent }, ctx.getConvertLocation(node));
const keyStart = ctx.code.indexOf(node.name, node.start);
const keyRange = { start: keyStart, end: keyStart + node.name.length };
attribute.key = Object.assign({ type: "SvelteName", name: node.name, parent: attribute }, ctx.getConvertLocation(keyRange));
if (node.value === true) {
// Boolean attribute
attribute.boolean = true;
ctx.addToken("HTMLIdentifier", keyRange);
return attribute;
}
const value = node.value;
const shorthand = isAttributeShorthandForSvelteV4(value) ||
isAttributeShorthandForSvelteV5(value);
if (shorthand) {
const key = Object.assign(Object.assign({}, attribute.key), { type: "Identifier" });
const sAttr = {
type: "SvelteShorthandAttribute",
key,
value: key,
parent,
loc: attribute.loc,
range: attribute.range,
};
key.parent = sAttr;
ctx.scriptLet.addObjectShorthandProperty(attribute.key, sAttr, (es) => {
if (
// FIXME: Older parsers may use the same node. In that case, do not replace.
// We will drop support for ESLint v7 in the next major version and remove this branch.
es.key !== es.value) {
sAttr.key = es.key;
}
sAttr.value = es.value;
});
return sAttr;
}
// Not required for shorthands. Therefore, register the token here.
ctx.addToken("HTMLIdentifier", keyRange);
processAttributeValue(value, attribute, parent, ctx);
return attribute;
function isAttributeShorthandForSvelteV4(value) {
var _a;
return Array.isArray(value) && ((_a = value[0]) === null || _a === void 0 ? void 0 : _a.type) === "AttributeShorthand";
}
function isAttributeShorthandForSvelteV5(value) {
return (!Array.isArray(value) &&
value.type === "ExpressionTag" &&
ctx.code[node.start] === "{" &&
ctx.code[node.end - 1] === "}");
}
}
/** Common process attribute value */
function processAttributeValue(nodeValue, attribute, attributeParent, ctx) {
const nodes = (Array.isArray(nodeValue) ? nodeValue : [nodeValue])
.filter((v) => v.type !== "Text" ||
// ignore empty
// https://github.com/sveltejs/svelte/pull/6539
v.start < v.end)
.map((v, index, array) => {
if (v.type === "Text") {
const next = array[index + 1];
if (next && next.start < v.end) {
// Maybe bug in Svelte can cause the completion index to shift.
return Object.assign(Object.assign({}, v), { end: next.start });
}
}
return v;
});
if (nodes.length === 1 &&
(nodes[0].type === "ExpressionTag" || nodes[0].type === "MustacheTag") &&
attribute.type === "SvelteAttribute") {
const typing = buildAttributeType(attributeParent.parent, attribute.key.name, ctx);
const mustache = (0, mustache_1.convertMustacheTag)(nodes[0], attribute, typing, ctx);
attribute.value.push(mustache);
return;
}
for (const v of nodes) {
if (v.type === "Text") {
attribute.value.push((0, text_1.convertTextToLiteral)(v, attribute, ctx));
continue;
}
if (v.type === "ExpressionTag" || v.type === "MustacheTag") {
const mustache = (0, mustache_1.convertMustacheTag)(v, attribute, null, ctx);
attribute.value.push(mustache);
continue;
}
const u = v;
throw new errors_1.ParseError(`Unknown attribute value (${u.type}) are not supported.`, u.start, ctx);
}
}
/** Build attribute type */
function buildAttributeType(element, attrName, ctx) {
if (svelte_version_1.svelteVersion.gte(5) &&
attrName.startsWith("on") &&
(element.type !== "SvelteElement" || element.kind === "html")) {
return buildEventHandlerType(element, attrName.slice(2), ctx);
}
if (element.type !== "SvelteElement" || element.kind !== "component") {
return null;
}
const elementName = ctx.elements.get(element).name;
const componentPropsType = `import('svelte').ComponentProps<typeof ${elementName}>`;
return conditional({
check: `'${attrName}'`,
extends: `infer PROP`,
true: conditional({
check: `PROP`,
extends: `keyof ${componentPropsType}`,
true: `${componentPropsType}[PROP]`,
false: `never`,
}),
false: `never`,
});
/** Generate `C extends E ? T : F` type. */
function conditional(types) {
return `${types.check} extends ${types.extends}?(${types.true}):(${types.false})`;
}
}
/** Convert for Spread */
function convertSpreadAttribute(node, parent, ctx) {
const attribute = Object.assign({ type: "SvelteSpreadAttribute", argument: null, parent }, ctx.getConvertLocation(node));
const spreadStart = ctx.code.indexOf("...", node.start);
ctx.addToken("Punctuator", {
start: spreadStart,
end: spreadStart + 3,
});
ctx.scriptLet.addExpression(node.expression, attribute, null, (es) => {
attribute.argument = es;
});
return attribute;
}
/** Convert for Binding Directive */
function convertBindingDirective(node, parent, ctx) {
const directive = Object.assign({ type: "SvelteDirective", kind: "Binding", key: null, shorthand: false, expression: null, parent }, ctx.getConvertLocation(node));
processDirective(node, directive, ctx, {
processExpression(expression, shorthand) {
directive.shorthand = shorthand;
return ctx.scriptLet.addExpression(expression, directive, null, (es, { getScope }) => {
directive.expression = es;
const scope = getScope(es);
const reference = scope.references.find((ref) => ref.identifier === es);
if (reference) {
// The bind directive does read and write.
reference.isWrite = () => true;
reference.isWriteOnly = () => false;
reference.isReadWrite = () => true;
reference.isReadOnly = () => false;
reference.isRead = () => true;
}
});
},
});
return directive;
}
/** Convert for EventHandler Directive */
function convertEventHandlerDirective(node, parent, ctx) {
const directive = Object.assign({ type: "SvelteDirective", kind: "EventHandler", key: null, expression: null, parent }, ctx.getConvertLocation(node));
const typing = buildEventHandlerType(parent.parent, node.name, ctx);
processDirective(node, directive, ctx, {
processExpression: buildProcessExpressionForExpression(directive, ctx, typing),
});
return directive;
}
/** Build event handler type */
function buildEventHandlerType(element, eventName, ctx) {
const nativeEventHandlerType = `(e:${conditional({
check: `'${eventName}'`,
extends: `infer EVT`,
true: conditional({
check: `EVT`,
extends: `keyof HTMLElementEventMap`,
true: `HTMLElementEventMap[EVT]`,
false: `CustomEvent<any>`,
}),
false: `never`,
})})=>void`;
if (element.type !== "SvelteElement") {
return nativeEventHandlerType;
}
const elementName = ctx.elements.get(element).name;
if (element.kind === "component") {
const componentEventsType = `import('svelte').ComponentEvents<${elementName}>`;
return `(e:${conditional({
check: `0`,
extends: `(1 & ${componentEventsType})`,
// `componentEventsType` is `any`
// `@typescript-eslint/parser` currently cannot parse `*.svelte` import types correctly.
// So if we try to do a correct type parsing, it's argument type will be `any`.
// A workaround is to inject the type directly, as `CustomEvent<any>` is better than `any`.
true: `CustomEvent<any>`,
// `componentEventsType` has an exact type.
false: conditional({
check: `'${eventName}'`,
extends: `infer EVT`,
true: conditional({
check: `EVT`,
extends: `keyof ${componentEventsType}`,
true: `${componentEventsType}[EVT]`,
false: `CustomEvent<any>`,
}),
false: `never`,
}),
})})=>void`;
}
if (element.kind === "special") {
if (elementName === "svelte:component")
return `(e:CustomEvent<any>)=>void`;
return nativeEventHandlerType;
}
const attrName = `on:${eventName}`;
const svelteHTMLElementsType = "import('svelte/elements').SvelteHTMLElements";
return conditional({
check: `'${elementName}'`,
extends: "infer EL",
true: conditional({
check: `EL`,
extends: `keyof ${svelteHTMLElementsType}`,
true: conditional({
check: `'${attrName}'`,
extends: "infer ATTR",
true: conditional({
check: `ATTR`,
extends: `keyof ${svelteHTMLElementsType}[EL]`,
true: `${svelteHTMLElementsType}[EL][ATTR]`,
false: nativeEventHandlerType,
}),
false: `never`,
}),
false: nativeEventHandlerType,
}),
false: `never`,
});
/** Generate `C extends E ? T : F` type. */
function conditional(types) {
return `${types.check} extends ${types.extends}?(${types.true}):(${types.false})`;
}
}
/** Convert for Class Directive */
function convertClassDirective(node, parent, ctx) {
const directive = Object.assign({ type: "SvelteDirective", kind: "Class", key: null, shorthand: false, expression: null, parent }, ctx.getConvertLocation(node));
processDirective(node, directive, ctx, {
processExpression(expression, shorthand) {
directive.shorthand = shorthand;
return ctx.scriptLet.addExpression(expression, directive);
},
});
return directive;
}
/** Convert for Style Directive */
function convertStyleDirective(node, parent, ctx) {
const directive = Object.assign({ type: "SvelteStyleDirective", key: null, shorthand: false, value: [], parent }, ctx.getConvertLocation(node));
processDirectiveKey(node, directive, ctx);
const keyName = directive.key.name;
if (node.value === true) {
const shorthandDirective = directive;
shorthandDirective.shorthand = true;
ctx.scriptLet.addExpression(keyName, shorthandDirective.key, null, (expression) => {
if (expression.type !== "Identifier") {
throw new errors_1.ParseError(`Expected JS identifier or attribute value.`, expression.range[0], ctx);
}
shorthandDirective.key.name = expression;
});
return shorthandDirective;
}
ctx.addToken("HTMLIdentifier", {
start: keyName.range[0],
end: keyName.range[1],
});
processAttributeValue(node.value, directive, parent, ctx);
return directive;
}
/** Convert for Transition Directive */
function convertTransitionDirective(node, parent, ctx) {
const directive = Object.assign({ type: "SvelteDirective", kind: "Transition", intro: node.intro, outro: node.outro, key: null, expression: null, parent }, ctx.getConvertLocation(node));
processDirective(node, directive, ctx, {
processExpression: buildProcessExpressionForExpression(directive, ctx, null),
processName: (name) => ctx.scriptLet.addExpression(name, directive.key, null, buildExpressionTypeChecker(["Identifier"], ctx)),
});
return directive;
}
/** Convert for Animation Directive */
function convertAnimationDirective(node, parent, ctx) {
const directive = Object.assign({ type: "SvelteDirective", kind: "Animation", key: null, expression: null, parent }, ctx.getConvertLocation(node));
processDirective(node, directive, ctx, {
processExpression: buildProcessExpressionForExpression(directive, ctx, null),
processName: (name) => ctx.scriptLet.addExpression(name, directive.key, null, buildExpressionTypeChecker(["Identifier"], ctx)),
});
return directive;
}
/** Convert for Action Directive */
function convertActionDirective(node, parent, ctx) {
const directive = Object.assign({ type: "SvelteDirective", kind: "Action", key: null, expression: null, parent }, ctx.getConvertLocation(node));
processDirective(node, directive, ctx, {
processExpression: buildProcessExpressionForExpression(directive, ctx, `Parameters<typeof ${node.name}>[1]`),
processName: (name) => ctx.scriptLet.addExpression(name, directive.key, null, buildExpressionTypeChecker(["Identifier", "MemberExpression"], ctx)),
});
return directive;
}
/** Convert for Let Directive */
function convertLetDirective(node, parent, ctx) {
const directive = Object.assign({ type: "SvelteDirective", kind: "Let", key: null, expression: null, parent }, ctx.getConvertLocation(node));
processDirective(node, directive, ctx, {
processPattern(pattern) {
return ctx.letDirCollections
.getCollection()
.addPattern(pattern, directive, buildLetDirectiveType(parent.parent, node.name, ctx));
},
processName: node.expression
? undefined
: (name) => {
// shorthand
ctx.letDirCollections
.getCollection()
.addPattern(name, directive, buildLetDirectiveType(parent.parent, node.name, ctx), (es) => {
directive.expression = es;
});
return [];
},
});
return directive;
}
/** Build let directive param type */
function buildLetDirectiveType(element, letName, ctx) {
if (element.type !== "SvelteElement") {
return "any";
}
let slotName = "default";
let componentName;
const svelteNode = ctx.elements.get(element);
const slotAttr = svelteNode.attributes.find((attr) => {
return attr.type === "Attribute" && attr.name === "slot";
});
if (slotAttr) {
if (Array.isArray(slotAttr.value) &&
slotAttr.value.length === 1 &&
slotAttr.value[0].type === "Text") {
slotName = slotAttr.value[0].data;
}
else {
return "any";
}
const parent = findParentComponent(element);
if (parent == null)
return "any";
componentName = ctx.elements.get(parent).name;
}
else {
if (element.kind === "component") {
componentName = svelteNode.name;
}
else {
const parent = findParentComponent(element);
if (parent == null)
return "any";
componentName = ctx.elements.get(parent).name;
}
}
return `${String(componentName)}['$$slot_def'][${JSON.stringify(slotName)}][${JSON.stringify(letName)}]`;
/** Find parent component element */
function findParentComponent(node) {
let parent = node.parent;
while (parent && parent.type !== "SvelteElement") {
parent = parent.parent;
}
if (!parent || parent.kind !== "component") {
return null;
}
return parent;
}
}
/** Common process for directive */
function processDirective(node, directive, ctx, processors) {
processDirectiveKey(node, directive, ctx);
processDirectiveExpression(node, directive, ctx, processors);
}
/** Common process for directive key */
function processDirectiveKey(node, directive, ctx) {
const colonIndex = ctx.code.indexOf(":", directive.range[0]);
ctx.addToken("HTMLIdentifier", {
start: directive.range[0],
end: colonIndex,
});
const nameIndex = ctx.code.indexOf(node.name, colonIndex + 1);
const nameRange = {
start: nameIndex,
end: nameIndex + node.name.length,
};
let keyEndIndex = nameRange.end;
// modifiers
if (ctx.code[nameRange.end] === "|") {
let nextStart = nameRange.end + 1;
let nextEnd = (0, common_1.indexOf)(ctx.code, (c) => c === "=" || c === ">" || c === "/" || c === "|" || !c.trim(), nextStart);
ctx.addToken("HTMLIdentifier", { start: nextStart, end: nextEnd });
while (ctx.code[nextEnd] === "|") {
nextStart = nextEnd + 1;
nextEnd = (0, common_1.indexOf)(ctx.code, (c) => c === "=" || c === ">" || c === "/" || c === "|" || !c.trim(), nextStart);
ctx.addToken("HTMLIdentifier", { start: nextStart, end: nextEnd });
}
keyEndIndex = nextEnd;
}
const key = (directive.key = Object.assign({ type: "SvelteDirectiveKey", name: null, modifiers: (0, compat_1.getModifiers)(node), parent: directive }, ctx.getConvertLocation({ start: node.start, end: keyEndIndex })));
// put name
key.name = Object.assign({ type: "SvelteName", name: node.name, parent: key }, ctx.getConvertLocation(nameRange));
}
/** Common process for directive expression */
function processDirectiveExpression(node, directive, ctx, processors) {
const key = directive.key;
const keyName = key.name;
let shorthand = false;
if (node.expression) {
shorthand =
node.expression.type === "Identifier" &&
node.expression.name === node.name &&
(0, common_1.getWithLoc)(node.expression).start === keyName.range[0];
if (shorthand && (0, common_1.getWithLoc)(node.expression).end !== keyName.range[1]) {
// The identifier location may be incorrect in some edge cases.
// e.g. bind:value=""
(0, common_1.getWithLoc)(node.expression).end = keyName.range[1];
}
if (processors.processExpression) {
processors.processExpression(node.expression, shorthand).push((es) => {
if (node.expression && es.type !== node.expression.type) {
throw new errors_1.ParseError(`Expected ${node.expression.type}, but ${es.type} found.`, es.range[0], ctx);
}
directive.expression = es;
});
}
else {
processors.processPattern(node.expression, shorthand).push((es) => {
directive.expression = es;
});
}
}
if (!shorthand) {
if (processors.processName) {
processors.processName(keyName).push((es) => {
key.name = es;
});
}
else {
ctx.addToken("HTMLIdentifier", {
start: keyName.range[0],
end: keyName.range[1],
});
}
}
}
/** Build processExpression for Expression */
function buildProcessExpressionForExpression(directive, ctx, typing) {
return (expression) => {
if ((0, utils_1.hasTypeInfo)(expression)) {
return ctx.scriptLet.addExpression(expression, directive, null);
}
return ctx.scriptLet.addExpression(expression, directive, typing);
};
}
/** Build expression type checker to script let callbacks */
function buildExpressionTypeChecker(expected, ctx) {
return (node) => {
if (!expected.includes(node.type)) {
throw new errors_1.ParseError(`Expected JS ${expected.join(", or ")}, but ${node.type} found.`, node.range[0], ctx);
}
};
}

View File

@@ -0,0 +1,16 @@
import type * as SvAST from "../svelte-ast-types";
import type * as Compiler from "../svelte-ast-types-for-v5";
import type { SvelteAwaitBlock, SvelteEachBlock, SvelteIfBlock, SvelteIfBlockAlone, SvelteIfBlockElseIf, SvelteKeyBlock, SvelteSnippetBlock } from "../../ast";
import type { Context } from "../../context";
export declare function convertIfBlock(node: SvAST.IfBlock | Compiler.IfBlock, parent: SvelteIfBlock["parent"], ctx: Context): SvelteIfBlockAlone;
export declare function convertIfBlock(node: SvAST.IfBlock | Compiler.IfBlock, parent: SvelteIfBlock["parent"], ctx: Context, elseifContext?: {
start: number;
}): SvelteIfBlockElseIf;
/** Convert for EachBlock */
export declare function convertEachBlock(node: SvAST.EachBlock | Compiler.EachBlock, parent: SvelteEachBlock["parent"], ctx: Context): SvelteEachBlock;
/** Convert for AwaitBlock */
export declare function convertAwaitBlock(node: SvAST.AwaitBlock | Compiler.AwaitBlock, parent: SvelteAwaitBlock["parent"], ctx: Context): SvelteAwaitBlock;
/** Convert for KeyBlock */
export declare function convertKeyBlock(node: SvAST.KeyBlock | Compiler.KeyBlock, parent: SvelteKeyBlock["parent"], ctx: Context): SvelteKeyBlock;
/** Convert for SnippetBlock */
export declare function convertSnippetBlock(node: Compiler.SnippetBlock, parent: SvelteSnippetBlock["parent"], ctx: Context): SvelteSnippetBlock;

View File

@@ -0,0 +1,436 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertIfBlock = convertIfBlock;
exports.convertEachBlock = convertEachBlock;
exports.convertAwaitBlock = convertAwaitBlock;
exports.convertKeyBlock = convertKeyBlock;
exports.convertSnippetBlock = convertSnippetBlock;
const element_1 = require("./element");
const common_1 = require("./common");
const compat_1 = require("../compat");
/** Get start index of block */
function startBlockIndex(code, endIndex, block) {
return (0, common_1.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(block, next);
}
return false;
}, endIndex);
}
function startIndexFromFragment(fragment, getBeforeEndIndex) {
if (fragment.start != null) {
return fragment.start;
}
const children = (0, compat_1.getChildren)(fragment);
return children.length ? children[0].start : getBeforeEndIndex();
}
function endIndexFromFragment(fragment, getBeforeEndIndex) {
if (fragment.end != null) {
return fragment.end;
}
const children = (0, compat_1.getChildren)(fragment);
return children.length
? children[children.length - 1].end
: getBeforeEndIndex();
}
function endIndexFromBlock(fragment, lastExpression, ctx) {
return endIndexFromFragment(fragment, () => {
return ctx.code.indexOf("}", (0, common_1.getWithLoc)(lastExpression).end) + 1;
});
}
/** Convert for IfBlock */
function convertIfBlock(node, parent, ctx, elseifContext) {
var _a;
// {#if expr} {:else} {/if}
// {:else if expr} {/if}
const elseif = Boolean(elseifContext);
const nodeStart = startBlockIndex(ctx.code, (_a = elseifContext === null || elseifContext === void 0 ? void 0 : elseifContext.start) !== null && _a !== void 0 ? _a : node.start, elseif ? ":else" : "#if");
const ifBlock = Object.assign({ type: "SvelteIfBlock", elseif: Boolean(elseif), expression: null, children: [], else: null, parent }, ctx.getConvertLocation({ start: nodeStart, end: node.end }));
const test = (0, compat_1.getTestFromIfBlock)(node);
ctx.scriptLet.nestIfBlock(test, ifBlock, (es) => {
ifBlock.expression = es;
});
const consequent = (0, compat_1.getConsequentFromIfBlock)(node);
ifBlock.children.push(...(0, element_1.convertChildren)({
nodes:
// Adjust for Svelte v5
(0, compat_1.trimChildren)((0, compat_1.getChildren)(consequent)),
}, ifBlock, ctx));
ctx.scriptLet.closeScope();
if (elseif) {
const index = ctx.code.indexOf("if", nodeStart);
ctx.addToken("MustacheKeyword", { start: index, end: index + 2 });
}
extractMustacheBlockTokens(ifBlock, ctx, { startOnly: elseif });
const elseFragment = (0, compat_1.getAlternateFromIfBlock)(node);
if (!elseFragment) {
return ifBlock;
}
const elseStart = startBlockIndexForElse(elseFragment, consequent, test, ctx);
const elseChildren = (0, compat_1.getChildren)(elseFragment);
if (elseChildren.length === 1) {
const c = elseChildren[0];
if (c.type === "IfBlock" && c.elseif) {
const elseBlock = Object.assign({ type: "SvelteElseBlock", elseif: true, children: [], parent: ifBlock }, ctx.getConvertLocation({
start: elseStart,
end: c.end,
}));
ifBlock.else = elseBlock;
const elseIfBlock = convertIfBlock(c, elseBlock, ctx, {
start: elseStart,
});
// adjust loc
elseBlock.range[1] = elseIfBlock.range[1];
elseBlock.loc.end = {
line: elseIfBlock.loc.end.line,
column: elseIfBlock.loc.end.column,
};
elseBlock.children = [elseIfBlock];
return ifBlock;
}
}
const elseBlock = Object.assign({ type: "SvelteElseBlock", elseif: false, children: [], parent: ifBlock }, ctx.getConvertLocation({
start: elseStart,
end: endIndexFromFragment(elseFragment, () => ctx.code.indexOf("}", elseStart + 5) + 1),
}));
ifBlock.else = elseBlock;
ctx.scriptLet.nestBlock(elseBlock);
elseBlock.children.push(...(0, element_1.convertChildren)({
nodes:
// Adjust for Svelte v5
(0, compat_1.trimChildren)(elseChildren),
}, elseBlock, ctx));
ctx.scriptLet.closeScope();
extractMustacheBlockTokens(elseBlock, ctx, { startOnly: true });
return ifBlock;
}
function startBlockIndexForElse(elseFragment, beforeFragment, lastExpression, ctx) {
const elseChildren = (0, compat_1.getChildren)(elseFragment);
if (elseChildren.length > 0) {
const c = elseChildren[0];
if (c.type === "IfBlock" && c.elseif) {
const contentStart = (0, common_1.getWithLoc)((0, compat_1.getTestFromIfBlock)(c)).start;
if (contentStart <= c.start) {
return startBlockIndex(ctx.code, contentStart - 1, ":else");
}
}
return startBlockIndex(ctx.code, c.start, ":else");
}
const beforeEnd = endIndexFromFragment(beforeFragment, () => {
return ctx.code.indexOf("}", (0, common_1.getWithLoc)(lastExpression).end) + 1;
});
return startBlockIndex(ctx.code, beforeEnd, ":else");
}
/** Convert for EachBlock */
function convertEachBlock(node, parent, ctx) {
// {#each expr as item, index (key)} {/each}
const nodeStart = startBlockIndex(ctx.code, node.start, "#each");
const eachBlock = Object.assign({ type: "SvelteEachBlock", expression: null, context: null, index: null, key: null, children: [], else: null, parent }, ctx.getConvertLocation({ start: nodeStart, end: node.end }));
let indexRange = null;
if (node.index) {
const start = ctx.code.indexOf(node.index, (0, common_1.getWithLoc)(node.context).end);
indexRange = {
start,
end: start + node.index.length,
};
}
ctx.scriptLet.nestEachBlock(node.expression, node.context, indexRange, eachBlock, (expression, context, index) => {
eachBlock.expression = expression;
eachBlock.context = context;
eachBlock.index = index;
});
const asStart = ctx.code.indexOf("as", (0, common_1.getWithLoc)(node.expression).end);
ctx.addToken("Keyword", {
start: asStart,
end: asStart + 2,
});
if (node.key) {
ctx.scriptLet.addExpression(node.key, eachBlock, null, (key) => {
eachBlock.key = key;
});
}
const body = (0, compat_1.getBodyFromEachBlock)(node);
eachBlock.children.push(...(0, element_1.convertChildren)({
nodes:
// Adjust for Svelte v5
(0, compat_1.trimChildren)((0, compat_1.getChildren)(body)),
}, eachBlock, ctx));
ctx.scriptLet.closeScope();
extractMustacheBlockTokens(eachBlock, ctx);
const fallbackFragment = (0, compat_1.getFallbackFromEachBlock)(node);
if (!fallbackFragment) {
return eachBlock;
}
const elseStart = startBlockIndexForElse(fallbackFragment, body, node.key || indexRange || node.context, ctx);
const elseBlock = Object.assign({ type: "SvelteElseBlock", elseif: false, children: [], parent: eachBlock }, ctx.getConvertLocation({
start: elseStart,
end: endIndexFromFragment(fallbackFragment, () => elseStart),
}));
eachBlock.else = elseBlock;
ctx.scriptLet.nestBlock(elseBlock);
elseBlock.children.push(...(0, element_1.convertChildren)({
nodes:
// Adjust for Svelte v5
(0, compat_1.trimChildren)((0, compat_1.getChildren)(fallbackFragment)),
}, elseBlock, ctx));
ctx.scriptLet.closeScope();
extractMustacheBlockTokens(elseBlock, ctx, { startOnly: true });
return eachBlock;
}
/** Convert for AwaitBlock */
function convertAwaitBlock(node, parent, ctx) {
const nodeStart = startBlockIndex(ctx.code, node.start, "#await");
const awaitBlock = Object.assign({ type: "SvelteAwaitBlock", expression: null, kind: "await", pending: null, then: null, catch: null, parent }, ctx.getConvertLocation({ start: nodeStart, end: node.end }));
ctx.scriptLet.addExpression(node.expression, awaitBlock, null, (expression) => {
awaitBlock.expression = expression;
});
const pending = (0, compat_1.getPendingFromAwaitBlock)(node);
if (pending) {
const pendingBlock = Object.assign({ type: "SvelteAwaitPendingBlock", children: [], parent: awaitBlock }, ctx.getConvertLocation({
start: awaitBlock.range[0],
end: endIndexFromBlock(pending, node.expression, ctx),
}));
ctx.scriptLet.nestBlock(pendingBlock);
pendingBlock.children.push(...(0, element_1.convertChildren)(pending, pendingBlock, ctx));
awaitBlock.pending = pendingBlock;
ctx.scriptLet.closeScope();
}
const then = (0, compat_1.getThenFromAwaitBlock)(node);
if (then) {
const awaitThen = !pending;
if (awaitThen) {
awaitBlock.kind = "await-then";
}
const thenStart = awaitBlock.pending
? startBlockIndex(ctx.code, node.value
? (0, common_1.getWithLoc)(node.value).start
: startIndexFromFragment(then, () => {
return awaitBlock.pending.range[1];
}), ":then")
: nodeStart;
const thenBlock = Object.assign({ type: "SvelteAwaitThenBlock", awaitThen, value: null, children: [], parent: awaitBlock }, ctx.getConvertLocation({
start: thenStart,
end: endIndexFromFragment(then, () => {
return (ctx.code.indexOf("}", node.value ? (0, common_1.getWithLoc)(node.value).end : thenStart + 5) + 1);
}),
}));
if (node.value) {
const baseParam = {
node: node.value,
parent: thenBlock,
callback(value) {
thenBlock.value = value;
},
typing: "any",
};
ctx.scriptLet.nestBlock(thenBlock, (typeCtx) => {
if (!typeCtx) {
return {
param: baseParam,
};
}
const expression = ctx.getText(node.expression);
if (node.expression.type === "Literal") {
return {
param: Object.assign(Object.assign({}, baseParam), { typing: expression }),
};
}
const idAwaitThenValue = typeCtx.generateUniqueId("AwaitThenValue");
if (node.expression.type === "Identifier" &&
// We cannot use type annotations like `(x: Foo<x>)` if they have the same identifier name.
!hasIdentifierFor(node.expression.name, baseParam.node)) {
return {
preparationScript: [generateAwaitThenValueType(idAwaitThenValue)],
param: Object.assign(Object.assign({}, baseParam), { typing: `${idAwaitThenValue}<(typeof ${expression})>` }),
};
}
const id = typeCtx.generateUniqueId(expression);
return {
preparationScript: [
{
script: `const ${id} = ${expression};`,
nodeType: "VariableDeclaration",
},
generateAwaitThenValueType(idAwaitThenValue),
],
param: Object.assign(Object.assign({}, baseParam), { typing: `${idAwaitThenValue}<(typeof ${id})>` }),
};
});
}
else {
ctx.scriptLet.nestBlock(thenBlock);
}
thenBlock.children.push(...(0, element_1.convertChildren)(then, thenBlock, ctx));
if (awaitBlock.pending) {
extractMustacheBlockTokens(thenBlock, ctx, { startOnly: true });
}
else {
const thenIndex = ctx.code.indexOf("then", (0, common_1.getWithLoc)(node.expression).end);
ctx.addToken("MustacheKeyword", {
start: thenIndex,
end: thenIndex + 4,
});
}
awaitBlock.then = thenBlock;
ctx.scriptLet.closeScope();
}
const catchFragment = (0, compat_1.getCatchFromAwaitBlock)(node);
if (catchFragment) {
const awaitCatch = !pending && !then;
if (awaitCatch) {
awaitBlock.kind = "await-catch";
}
const catchStart = awaitBlock.then || awaitBlock.pending
? startBlockIndex(ctx.code, node.error
? (0, common_1.getWithLoc)(node.error).start
: startIndexFromFragment(catchFragment, () => {
return (awaitBlock.then || awaitBlock.pending).range[1];
}), ":catch")
: nodeStart;
const catchBlock = Object.assign({ type: "SvelteAwaitCatchBlock", awaitCatch, error: null, children: [], parent: awaitBlock }, ctx.getConvertLocation({
start: catchStart,
end: endIndexFromFragment(catchFragment, () => {
return (ctx.code.indexOf("}", node.error ? (0, common_1.getWithLoc)(node.error).end : catchStart + 6) + 1);
}),
}));
if (node.error) {
ctx.scriptLet.nestBlock(catchBlock, [
{
node: node.error,
parent: catchBlock,
typing: "Error",
callback: (error) => {
catchBlock.error = error;
},
},
]);
}
else {
ctx.scriptLet.nestBlock(catchBlock);
}
catchBlock.children.push(...(0, element_1.convertChildren)(catchFragment, catchBlock, ctx));
if (awaitBlock.pending || awaitBlock.then) {
extractMustacheBlockTokens(catchBlock, ctx, { startOnly: true });
}
else {
const catchIndex = ctx.code.indexOf("catch", (0, common_1.getWithLoc)(node.expression).end);
ctx.addToken("MustacheKeyword", {
start: catchIndex,
end: catchIndex + 5,
});
}
awaitBlock.catch = catchBlock;
ctx.scriptLet.closeScope();
}
extractMustacheBlockTokens(awaitBlock, ctx);
return awaitBlock;
}
/** Convert for KeyBlock */
function convertKeyBlock(node, parent, ctx) {
const nodeStart = startBlockIndex(ctx.code, node.start, "#key");
const keyBlock = Object.assign({ type: "SvelteKeyBlock", expression: null, children: [], parent }, ctx.getConvertLocation({ start: nodeStart, end: node.end }));
ctx.scriptLet.addExpression(node.expression, keyBlock, null, (expression) => {
keyBlock.expression = expression;
});
ctx.scriptLet.nestBlock(keyBlock);
keyBlock.children.push(...(0, element_1.convertChildren)({
nodes:
// Adjust for Svelte v5
(0, compat_1.trimChildren)((0, compat_1.getChildren)((0, compat_1.getFragment)(node))),
}, keyBlock, ctx));
ctx.scriptLet.closeScope();
extractMustacheBlockTokens(keyBlock, ctx);
return keyBlock;
}
/** Convert for SnippetBlock */
function convertSnippetBlock(node, parent, ctx) {
var _a;
// {#snippet x(args)}...{/snippet}
const nodeStart = startBlockIndex(ctx.code, node.start, "#snippet");
const snippetBlock = Object.assign({ type: "SvelteSnippetBlock", id: null, params: [], children: [], parent }, ctx.getConvertLocation({ start: nodeStart, end: node.end }));
let beforeClosingParen;
if (node.parameters.length > 0) {
const lastParam = node.parameters[node.parameters.length - 1];
beforeClosingParen = (_a = lastParam.typeAnnotation) !== null && _a !== void 0 ? _a : lastParam;
}
else {
beforeClosingParen = node.expression;
}
const closeParenIndex = ctx.code.indexOf(")", (0, common_1.getWithLoc)(beforeClosingParen).end);
const scopeKind = parent.type === "Program"
? "snippet"
: // use currentScriptScopeKind
null;
ctx.scriptLet.nestSnippetBlock(node.expression, closeParenIndex, snippetBlock, scopeKind, (id, params) => {
snippetBlock.id = id;
snippetBlock.params = params;
});
snippetBlock.children.push(...(0, element_1.convertChildren)({
nodes:
// Adjust for Svelte v5
(0, compat_1.trimChildren)(node.body.nodes),
}, snippetBlock, ctx));
ctx.scriptLet.closeScope();
extractMustacheBlockTokens(snippetBlock, ctx);
ctx.snippets.push(snippetBlock);
return snippetBlock;
}
/** Extract mustache block tokens */
function extractMustacheBlockTokens(node, ctx, option) {
const startSectionNameStart = (0, common_1.indexOf)(ctx.code, (c) => Boolean(c.trim()), node.range[0] + 1);
const startSectionNameEnd = (0, common_1.indexOf)(ctx.code, (c) => c === "}" || !c.trim(), startSectionNameStart + 1);
ctx.addToken("MustacheKeyword", {
start: startSectionNameStart,
end: startSectionNameEnd,
});
if (option === null || option === void 0 ? void 0 : option.startOnly) {
return;
}
const endSectionNameEnd = (0, common_1.lastIndexOf)(ctx.code, (c) => Boolean(c.trim()), node.range[1] - 2) + 1;
const endSectionNameStart = (0, common_1.lastIndexOf)(ctx.code, (c) => c === "{" || c === "/" || !c.trim(), endSectionNameEnd - 1);
ctx.addToken("MustacheKeyword", {
start: endSectionNameStart,
end: endSectionNameEnd,
});
}
/** Generate Awaited like type code */
function generateAwaitThenValueType(id) {
return {
script: `type ${id}<T> = T extends null | undefined
? T
: T extends { then(value: infer F): any }
? F extends (value: infer V, ...args: any) => any
? ${id}<V>
: never
: T;`,
nodeType: "TSTypeAliasDeclaration",
};
}
/** Checks whether the given name identifier is exists or not. */
function hasIdentifierFor(name, node) {
if (node.type === "Identifier") {
return node.name === name;
}
if (node.type === "ObjectPattern") {
return node.properties.some((property) => property.type === "Property"
? hasIdentifierFor(name, property.value)
: hasIdentifierFor(name, property));
}
if (node.type === "ArrayPattern") {
return node.elements.some((element) => element && hasIdentifierFor(name, element));
}
if (node.type === "RestElement") {
return hasIdentifierFor(name, node.argument);
}
if (node.type === "AssignmentPattern") {
return hasIdentifierFor(name, node.left);
}
return false;
}

View File

@@ -0,0 +1,23 @@
import type ESTree from "estree";
/** indexOf */
export declare function indexOf(str: string, search: (c: string, index: number) => boolean, start: number, end?: number): number;
/** lastIndexOf */
export declare function lastIndexOf(str: string, search: (c: string, index: number) => boolean, end: number): number;
export declare function getWithLoc<N extends ESTree.Comment>(node: N): N & {
start: number;
end: number;
};
export declare function getWithLoc<N extends ESTree.Node | {
start: number;
end: number;
}>(node: N): N & {
start: number;
end: number;
};
export declare function getWithLoc<N extends ESTree.Node | {
start: number;
end: number;
}>(node: N | null | undefined): (N & {
start: number;
end: number;
}) | null | undefined;

View File

@@ -0,0 +1,30 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.indexOf = indexOf;
exports.lastIndexOf = lastIndexOf;
exports.getWithLoc = getWithLoc;
/** indexOf */
function indexOf(str, search, start, end) {
const endIndex = end !== null && end !== void 0 ? end : str.length;
for (let index = start; index < endIndex; index++) {
const c = str[index];
if (search(c, index)) {
return index;
}
}
return -1;
}
/** lastIndexOf */
function lastIndexOf(str, search, end) {
for (let index = end; index >= 0; index--) {
const c = str[index];
if (search(c, index)) {
return index;
}
}
return -1;
}
/** Get node with location */
function getWithLoc(node) {
return node;
}

View File

@@ -0,0 +1,6 @@
import type { SvelteConstTag } from "../../ast";
import type { Context } from "../../context";
import type * as SvAST from "../svelte-ast-types";
import type * as Compiler from "../svelte-ast-types-for-v5";
/** Convert for ConstTag */
export declare function convertConstTag(node: SvAST.ConstTag | Compiler.ConstTag, parent: SvelteConstTag["parent"], ctx: Context): SvelteConstTag;

View File

@@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertConstTag = convertConstTag;
const compat_1 = require("../compat");
/** Convert for ConstTag */
function convertConstTag(node, parent, ctx) {
const mustache = Object.assign({ type: "SvelteConstTag", declaration: null, parent }, ctx.getConvertLocation(node));
ctx.scriptLet.addVariableDeclarator((0, compat_1.getDeclaratorFromConstTag)(node), mustache, (declaration) => {
mustache.declaration = declaration;
});
const atConstStart = ctx.code.indexOf("@const", mustache.range[0]);
ctx.addToken("MustacheKeyword", {
start: atConstStart,
end: atConstStart + 6,
});
return mustache;
}

View File

@@ -0,0 +1,19 @@
import type { SvelteAwaitBlock, SvelteAwaitCatchBlock, SvelteAwaitPendingBlock, SvelteAwaitThenBlock, SvelteConstTag, SvelteDebugTag, SvelteEachBlock, SvelteElement, SvelteElseBlockAlone, SvelteHTMLComment, SvelteIfBlock, SvelteIfBlockAlone, SvelteKeyBlock, SvelteMustacheTag, SvelteProgram, SvelteRenderTag, SvelteScriptElement, SvelteSnippetBlock, SvelteStyleElement, SvelteText } from "../../ast";
import type { Context } from "../../context";
import type * as SvAST from "../svelte-ast-types";
import type * as Compiler from "../svelte-ast-types-for-v5";
import type { Child } from "../compat";
/** Convert for Fragment or Element or ... */
export declare function convertChildren(fragment: {
children?: SvAST.TemplateNode[];
} | Compiler.Fragment | {
nodes: (Child | SvAST.TemplateNode)[];
}, parent: SvelteProgram | SvelteElement | SvelteIfBlock | SvelteElseBlockAlone | SvelteEachBlock | SvelteAwaitPendingBlock | SvelteAwaitThenBlock | SvelteAwaitCatchBlock | SvelteKeyBlock | SvelteSnippetBlock, ctx: Context): IterableIterator<SvelteText | SvelteElement | SvelteMustacheTag | SvelteDebugTag | SvelteConstTag | SvelteRenderTag | SvelteIfBlockAlone | SvelteEachBlock | SvelteAwaitBlock | SvelteKeyBlock | SvelteSnippetBlock | SvelteHTMLComment>;
/** Extract element tag and tokens */
export declare function extractElementTags<E extends SvelteScriptElement | SvelteElement | SvelteStyleElement>(element: E, ctx: Context, options: {
buildNameNode: (openTokenRange: {
start: number;
end: number;
}) => E["name"];
extractAttribute?: boolean;
}): void;

View File

@@ -0,0 +1,572 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertChildren = convertChildren;
exports.extractElementTags = extractElementTags;
const block_1 = require("./block");
const common_1 = require("./common");
const mustache_1 = require("./mustache");
const text_1 = require("./text");
const attr_1 = require("./attr");
const const_1 = require("./const");
const sort_1 = require("../sort");
const __1 = require("../..");
const render_1 = require("./render");
const compat_1 = require("../compat");
/** Convert for Fragment or Element or ... */
function* convertChildren(fragment, parent, ctx) {
const children = (0, compat_1.getChildren)(fragment);
if (!children)
return;
for (const child of children) {
if (child.type === "Comment") {
yield convertComment(child, parent, ctx);
continue;
}
if (child.type === "Text") {
if (!child.data && child.start === child.end) {
continue;
}
yield (0, text_1.convertText)(child, parent, ctx);
continue;
}
if (child.type === "RegularElement") {
yield convertHTMLElement(child, parent, ctx);
continue;
}
if (child.type === "Element") {
if (child.name.includes(":")) {
yield convertSpecialElement(child, parent, ctx);
}
else {
yield convertHTMLElement(child, parent, ctx);
}
continue;
}
if (child.type === "Component") {
yield convertComponentElement(child, parent, ctx);
continue;
}
if (child.type === "InlineComponent") {
if (child.name.includes(":")) {
yield convertSpecialElement(child, parent, ctx);
}
else {
yield convertComponentElement(child, parent, ctx);
}
continue;
}
if (child.type === "SvelteComponent" ||
child.type === "SvelteElement" ||
child.type === "SvelteSelf") {
yield convertSpecialElement(child, parent, ctx);
continue;
}
if (child.type === "SlotElement" || child.type === "Slot") {
yield convertSlotElement(child, parent, ctx);
continue;
}
if (child.type === "ExpressionTag" || child.type === "MustacheTag") {
yield (0, mustache_1.convertMustacheTag)(child, parent, null, ctx);
continue;
}
if (child.type === "HtmlTag" || child.type === "RawMustacheTag") {
yield (0, mustache_1.convertRawMustacheTag)(child, parent, ctx);
continue;
}
if (child.type === "IfBlock") {
// {#if expr} {/if}
yield (0, block_1.convertIfBlock)(child, parent, ctx);
continue;
}
if (child.type === "EachBlock") {
// {#each expr as item, index (key)} {/each}
yield (0, block_1.convertEachBlock)(child, parent, ctx);
continue;
}
if (child.type === "AwaitBlock") {
// {#await promise} {:then number} {:catch error} {/await}
yield (0, block_1.convertAwaitBlock)(child, parent, ctx);
continue;
}
if (child.type === "KeyBlock") {
// {#key expression}...{/key}
yield (0, block_1.convertKeyBlock)(child, parent, ctx);
continue;
}
if (child.type === "SnippetBlock") {
// {#snippet x(args)}...{/snippet}
yield (0, block_1.convertSnippetBlock)(child, parent, ctx);
continue;
}
if (child.type === "SvelteWindow" || child.type === "Window") {
yield convertWindowElement(child, parent, ctx);
continue;
}
if (child.type === "SvelteBody" || child.type === "Body") {
yield convertBodyElement(child, parent, ctx);
continue;
}
if (child.type === "SvelteHead" || child.type === "Head") {
yield convertHeadElement(child, parent, ctx);
continue;
}
if (child.type === "TitleElement" || child.type === "Title") {
yield convertTitleElement(child, parent, ctx);
continue;
}
if (child.type === "SvelteOptions" || child.type === "Options") {
yield convertOptionsElement(child, parent, ctx);
continue;
}
if (child.type === "SvelteFragment" || child.type === "SlotTemplate") {
yield convertSlotTemplateElement(child, parent, ctx);
continue;
}
if (child.type === "DebugTag") {
yield (0, mustache_1.convertDebugTag)(child, parent, ctx);
continue;
}
if (child.type === "ConstTag") {
yield (0, const_1.convertConstTag)(child, parent, ctx);
continue;
}
if (child.type === "RenderTag") {
yield (0, render_1.convertRenderTag)(child, parent, ctx);
continue;
}
if (child.type === "SvelteDocument" || child.type === "Document") {
yield convertDocumentElement(child, parent, ctx);
continue;
}
throw new Error(`Unknown type:${child.type}`);
}
}
/** Extract `let:` directives. */
function extractLetDirectives(fragment) {
const letDirectives = [];
const attributes = [];
for (const attr of fragment.attributes) {
if (attr.type === "LetDirective" || attr.type === "Let") {
letDirectives.push(attr);
}
else {
attributes.push(attr);
}
}
return { letDirectives, attributes };
}
/** Check if children needs a scope. */
function needScopeByChildren(fragment) {
const children = (0, compat_1.getChildren)(fragment);
if (!children)
return false;
for (const child of children) {
if (child.type === "ConstTag") {
return true;
}
if (child.type === "SnippetBlock") {
return true;
}
}
return false;
}
/** Convert for HTML Comment */
function convertComment(node, parent, ctx) {
const comment = Object.assign({ type: "SvelteHTMLComment", value: node.data, parent }, ctx.getConvertLocation(node));
ctx.addToken("HTMLComment", node);
return comment;
}
/** Convert for HTMLElement */
function convertHTMLElement(node, parent, ctx) {
var _a, _b;
const locs = ctx.getConvertLocation(node);
const element = Object.assign({ type: "SvelteElement", kind: "html", name: null, startTag: {
type: "SvelteStartTag",
attributes: [],
selfClosing: false,
parent: null,
range: [locs.range[0], null],
loc: {
start: {
line: locs.loc.start.line,
column: locs.loc.start.column,
},
end: null,
},
}, children: [], endTag: null, parent }, locs);
ctx.elements.set(element, node);
element.startTag.parent = element;
const elementName = node.name;
const { letDirectives, attributes } = extractLetDirectives(node);
const letParams = [];
if (letDirectives.length) {
ctx.letDirCollections.beginExtract();
element.startTag.attributes.push(...(0, attr_1.convertAttributes)(letDirectives, element.startTag, ctx));
letParams.push(...ctx.letDirCollections.extract().getLetParams());
}
const fragment = (0, compat_1.getFragment)(node);
if (!letParams.length && !needScopeByChildren(fragment)) {
element.startTag.attributes.push(...(0, attr_1.convertAttributes)(attributes, element.startTag, ctx));
element.children.push(...convertChildren(fragment, element, ctx));
}
else {
ctx.scriptLet.nestBlock(element, letParams);
element.startTag.attributes.push(...(0, attr_1.convertAttributes)(attributes, element.startTag, ctx));
(0, sort_1.sortNodes)(element.startTag.attributes);
element.children.push(...convertChildren(fragment, element, ctx));
ctx.scriptLet.closeScope();
}
extractElementTags(element, ctx, {
buildNameNode: (openTokenRange) => {
ctx.addToken("HTMLIdentifier", openTokenRange);
const name = Object.assign({ type: "SvelteName", name: elementName, parent: element }, ctx.getConvertLocation(openTokenRange));
return name;
},
});
if (element.name.name === "script" ||
element.name.name === "style" ||
(element.name.name === "template" && ctx.findBlock(element))) {
// Restore the block-like element.
for (const child of element.children) {
if (child.type === "SvelteText") {
child.value = ctx.code.slice(...child.range);
}
}
if (element.name.name === "script") {
ctx.stripScriptCode(element.startTag.range[1], (_b = (_a = element.endTag) === null || _a === void 0 ? void 0 : _a.range[0]) !== null && _b !== void 0 ? _b : element.range[1]);
}
}
if (element.startTag.selfClosing && element.name.name.endsWith("-")) {
// Restore the self-closing block.
const selfClosingBlock = /^[a-z]-+$/iu.test(element.name.name) &&
ctx.findSelfClosingBlock(element);
if (selfClosingBlock) {
element.name.name = selfClosingBlock.originalTag;
}
}
return element;
}
/** Convert for Special element. e.g. <svelte:self> */
function convertSpecialElement(node, parent, ctx) {
const locs = ctx.getConvertLocation(node);
const element = Object.assign({ type: "SvelteElement", kind: "special", name: null, startTag: {
type: "SvelteStartTag",
attributes: [],
selfClosing: false,
parent: null,
range: [locs.range[0], null],
loc: {
start: {
line: locs.loc.start.line,
column: locs.loc.start.column,
},
end: null,
},
}, children: [], endTag: null, parent }, locs);
ctx.elements.set(element, node);
element.startTag.parent = element;
const elementName = node.name;
const { letDirectives, attributes } = extractLetDirectives(node);
const letParams = [];
if (letDirectives.length) {
ctx.letDirCollections.beginExtract();
element.startTag.attributes.push(...(0, attr_1.convertAttributes)(letDirectives, element.startTag, ctx));
letParams.push(...ctx.letDirCollections.extract().getLetParams());
}
const fragment = (0, compat_1.getFragment)(node);
if (!letParams.length && !needScopeByChildren(fragment)) {
element.startTag.attributes.push(...(0, attr_1.convertAttributes)(attributes, element.startTag, ctx));
element.children.push(...convertChildren(fragment, element, ctx));
}
else {
ctx.scriptLet.nestBlock(element, letParams);
element.startTag.attributes.push(...(0, attr_1.convertAttributes)(attributes, element.startTag, ctx));
(0, sort_1.sortNodes)(element.startTag.attributes);
element.children.push(...convertChildren(fragment, element, ctx));
ctx.scriptLet.closeScope();
}
const thisExpression = (node.type === "SvelteComponent" && node.expression) ||
(node.type === "SvelteElement" && node.tag) ||
(node.type === "InlineComponent" &&
elementName === "svelte:component" &&
node.expression) ||
(node.type === "Element" && elementName === "svelte:element" && node.tag);
if (thisExpression) {
processThisAttribute(node, thisExpression, element, ctx);
}
extractElementTags(element, ctx, {
buildNameNode: (openTokenRange) => {
ctx.addToken("HTMLIdentifier", openTokenRange);
const name = Object.assign({ type: "SvelteName", name: elementName, parent: element }, ctx.getConvertLocation(openTokenRange));
return name;
},
});
return element;
}
/** process `this=` */
function processThisAttribute(node, thisValue, element, ctx) {
const startIndex = findStartIndexOfThis(node, ctx);
const eqIndex = ctx.code.indexOf("=", startIndex + 4 /* t,h,i,s */);
let thisNode;
if (typeof thisValue === "string") {
// Svelte v4
// this="..."
thisNode = createSvelteAttribute(startIndex, eqIndex, thisValue);
}
else {
// this={...}
const valueStartIndex = (0, common_1.indexOf)(ctx.code, (c) => Boolean(c.trim()), eqIndex + 1);
if (thisValue.type === "Literal" &&
typeof thisValue.value === "string" &&
ctx.code[valueStartIndex] !== "{") {
thisNode = createSvelteAttribute(startIndex, eqIndex, thisValue.value);
}
else {
thisNode = createSvelteSpecialDirective(startIndex, eqIndex, thisValue);
}
}
const targetIndex = element.startTag.attributes.findIndex((attr) => thisNode.range[1] <= attr.range[0]);
if (targetIndex === -1) {
element.startTag.attributes.push(thisNode);
}
else {
element.startTag.attributes.splice(targetIndex, 0, thisNode);
}
/** Create SvelteAttribute */
function createSvelteAttribute(startIndex, eqIndex, thisValue) {
const valueStartIndex = (0, common_1.indexOf)(ctx.code, (c) => Boolean(c.trim()), eqIndex + 1);
const quote = ctx.code.startsWith(thisValue, valueStartIndex)
? null
: ctx.code[valueStartIndex];
const literalStartIndex = quote
? valueStartIndex + quote.length
: valueStartIndex;
const literalEndIndex = literalStartIndex + thisValue.length;
const endIndex = quote ? literalEndIndex + quote.length : literalEndIndex;
const thisAttr = Object.assign({ type: "SvelteAttribute", key: null, boolean: false, value: [], parent: element.startTag }, ctx.getConvertLocation({ start: startIndex, end: endIndex }));
thisAttr.key = Object.assign({ type: "SvelteName", name: "this", parent: thisAttr }, ctx.getConvertLocation({ start: startIndex, end: eqIndex }));
thisAttr.value.push(Object.assign({ type: "SvelteLiteral", value: thisValue, parent: thisAttr }, ctx.getConvertLocation({
start: literalStartIndex,
end: literalEndIndex,
})));
// this
ctx.addToken("HTMLIdentifier", {
start: startIndex,
end: startIndex + 4,
});
// =
ctx.addToken("Punctuator", {
start: eqIndex,
end: eqIndex + 1,
});
if (quote) {
// "
ctx.addToken("Punctuator", {
start: valueStartIndex,
end: literalStartIndex,
});
}
ctx.addToken("HTMLText", {
start: literalStartIndex,
end: literalEndIndex,
});
if (quote) {
// "
ctx.addToken("Punctuator", {
start: literalEndIndex,
end: endIndex,
});
}
return thisAttr;
}
/** Create SvelteSpecialDirective */
function createSvelteSpecialDirective(startIndex, eqIndex, expression) {
const closeIndex = ctx.code.indexOf("}", (0, common_1.getWithLoc)(expression).end);
const endIndex = (0, common_1.indexOf)(ctx.code, (c) => c === ">" || !c.trim(), closeIndex);
const thisDir = Object.assign({ type: "SvelteSpecialDirective", kind: "this", key: null, expression: null, parent: element.startTag }, ctx.getConvertLocation({ start: startIndex, end: endIndex }));
thisDir.key = Object.assign({ type: "SvelteSpecialDirectiveKey", parent: thisDir }, ctx.getConvertLocation({ start: startIndex, end: eqIndex }));
// this
ctx.addToken("HTMLIdentifier", {
start: startIndex,
end: startIndex + 4,
});
// =
ctx.addToken("Punctuator", {
start: eqIndex,
end: eqIndex + 1,
});
ctx.scriptLet.addExpression(expression, thisDir, null, (es) => {
thisDir.expression = es;
});
return thisDir;
}
}
/** Find the start index of `this` */
function findStartIndexOfThis(node, ctx) {
var _a, _b, _d, _e;
// Get the end index of `svelte:element`
const startIndex = ctx.code.indexOf(node.name, node.start) + node.name.length;
const sortedAttrs = [...node.attributes].sort((a, b) => a.start - b.start);
// Find the start index of `this` from the end index of `svelte:element`.
// However, it only seeks to the start index of the first attribute (or the end index of element node).
let thisIndex = (0, common_1.indexOf)(ctx.code, (_c, index) => ctx.code.startsWith("this", index), startIndex, (_b = (_a = sortedAttrs[0]) === null || _a === void 0 ? void 0 : _a.start) !== null && _b !== void 0 ? _b : node.end);
while (thisIndex < 0) {
if (sortedAttrs.length === 0)
throw new __1.ParseError("Cannot resolved `this` attribute.", thisIndex, ctx);
// Step3: Find the start index of `this` from the end index of attribute.
// However, it only seeks to the start index of the first attribute (or the end index of element node).
const nextStartIndex = sortedAttrs.shift().end;
thisIndex = (0, common_1.indexOf)(ctx.code, (_c, index) => ctx.code.startsWith("this", index), nextStartIndex, (_e = (_d = sortedAttrs[0]) === null || _d === void 0 ? void 0 : _d.start) !== null && _e !== void 0 ? _e : node.end);
}
return thisIndex;
}
/** Convert for ComponentElement */
function convertComponentElement(node, parent, ctx) {
const locs = ctx.getConvertLocation(node);
const element = Object.assign({ type: "SvelteElement", kind: "component", name: null, startTag: {
type: "SvelteStartTag",
attributes: [],
selfClosing: false,
parent: null,
range: [locs.range[0], null],
loc: {
start: {
line: locs.loc.start.line,
column: locs.loc.start.column,
},
end: null,
},
}, children: [], endTag: null, parent }, locs);
ctx.elements.set(element, node);
element.startTag.parent = element;
const elementName = node.name;
const { letDirectives, attributes } = extractLetDirectives(node);
const letParams = [];
if (letDirectives.length) {
ctx.letDirCollections.beginExtract();
element.startTag.attributes.push(...(0, attr_1.convertAttributes)(letDirectives, element.startTag, ctx));
letParams.push(...ctx.letDirCollections.extract().getLetParams());
}
const fragment = (0, compat_1.getFragment)(node);
if (!letParams.length && !needScopeByChildren(fragment)) {
element.startTag.attributes.push(...(0, attr_1.convertAttributes)(attributes, element.startTag, ctx));
element.children.push(...convertChildren(fragment, element, ctx));
}
else {
ctx.scriptLet.nestBlock(element, letParams);
element.startTag.attributes.push(...(0, attr_1.convertAttributes)(attributes, element.startTag, ctx));
(0, sort_1.sortNodes)(element.startTag.attributes);
element.children.push(...convertChildren(fragment, element, ctx));
ctx.scriptLet.closeScope();
}
extractElementTags(element, ctx, {
buildNameNode: (openTokenRange) => {
const chains = elementName.split(".");
const id = chains.shift();
const idRange = {
start: openTokenRange.start,
end: openTokenRange.start + id.length,
};
// ctx.addToken("Identifier", idRange)
const identifier = Object.assign({ type: "Identifier", name: id,
// @ts-expect-error -- ignore
parent: element }, ctx.getConvertLocation(idRange));
let object = identifier;
// eslint-disable-next-line func-style -- var
let esCallback = (es) => {
element.name = es;
};
let start = idRange.end + 1;
for (const name of chains) {
const range = { start, end: start + name.length };
ctx.addToken("HTMLIdentifier", range);
const mem = Object.assign({ type: "SvelteMemberExpressionName", object, property: Object.assign({ type: "SvelteName", name, parent: null }, ctx.getConvertLocation(range)), parent: element }, ctx.getConvertLocation({
start: openTokenRange.start,
end: range.end,
}));
mem.property.parent = mem;
object.parent = mem;
object = mem;
start = range.end + 1;
if (mem.object === identifier) {
esCallback = (es) => {
mem.object = es;
};
}
}
ctx.scriptLet.addExpression(identifier, identifier.parent, null, esCallback);
return object;
},
});
return element;
}
/** Convert for Slot */
function convertSlotElement(node, parent, ctx) {
// Slot translates to SvelteHTMLElement.
const element = convertHTMLElement(node, parent, ctx);
ctx.slots.add(element);
return element;
}
/** Convert for window element. e.g. <svelte:window> */
function convertWindowElement(node, parent, ctx) {
return convertSpecialElement(node, parent, ctx);
}
/** Convert for document element. e.g. <svelte:document> */
function convertDocumentElement(node, parent, ctx) {
return convertSpecialElement(node, parent, ctx);
}
/** Convert for body element. e.g. <svelte:body> */
function convertBodyElement(node, parent, ctx) {
return convertSpecialElement(node, parent, ctx);
}
/** Convert for head element. e.g. <svelte:head> */
function convertHeadElement(node, parent, ctx) {
return convertSpecialElement(node, parent, ctx);
}
/** Convert for title element. e.g. <title> */
function convertTitleElement(node, parent, ctx) {
return convertHTMLElement(node, parent, ctx);
}
/** Convert for options element. e.g. <svelte:options> */
function convertOptionsElement(node, parent, ctx) {
return convertSpecialElement(node, parent, ctx);
}
/** Convert for <svelte:fragment> element. */
function convertSlotTemplateElement(node, parent, ctx) {
return convertSpecialElement(node, parent, ctx);
}
/** Extract element tag and tokens */
function extractElementTags(element, ctx, options) {
var _a, _b;
const startTagNameEnd = (0, common_1.indexOf)(ctx.code, (c) => c === "/" || c === ">" || !c.trim(), element.range[0] + 1);
const openTokenRange = {
start: element.range[0] + 1,
end: startTagNameEnd,
};
element.name = options.buildNameNode(openTokenRange);
const startTagEnd = ctx.code.indexOf(">", (_b = (_a = element.startTag.attributes[element.startTag.attributes.length - 1]) === null || _a === void 0 ? void 0 : _a.range[1]) !== null && _b !== void 0 ? _b : openTokenRange.end) + 1;
element.startTag.range[1] = startTagEnd;
element.startTag.loc.end = ctx.getLocFromIndex(startTagEnd);
if (ctx.code[element.range[1] - 1] !== ">") {
// Have not end tag
return;
}
if (ctx.code[element.range[1] - 2] === "/") {
// self close
element.startTag.selfClosing = true;
return;
}
const endTagOpen = ctx.code.lastIndexOf("<", element.range[1] - 1);
if (endTagOpen <= startTagEnd - 1) {
// void element
return;
}
const endTagNameStart = endTagOpen + 2;
const endTagNameEnd = (0, common_1.indexOf)(ctx.code, (c) => c === ">" || !c.trim(), endTagNameStart);
const endTagClose = ctx.code.indexOf(">", endTagNameEnd);
element.endTag = Object.assign({ type: "SvelteEndTag", parent: element }, ctx.getConvertLocation({ start: endTagOpen, end: endTagClose + 1 }));
ctx.addToken("HTMLIdentifier", {
start: endTagNameStart,
end: endTagNameEnd,
});
}

View File

@@ -0,0 +1 @@
export { convertSvelteRoot } from "./root";

View File

@@ -0,0 +1,5 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertSvelteRoot = void 0;
var root_1 = require("./root");
Object.defineProperty(exports, "convertSvelteRoot", { enumerable: true, get: function () { return root_1.convertSvelteRoot; } });

View File

@@ -0,0 +1,10 @@
import type { SvelteDebugTag, SvelteMustacheTag, SvelteMustacheTagRaw, SvelteMustacheTagText } from "../../ast";
import type { Context } from "../../context";
import type * as SvAST from "../svelte-ast-types";
import type * as Compiler from "../svelte-ast-types-for-v5";
/** Convert for MustacheTag */
export declare function convertMustacheTag(node: SvAST.MustacheTag | Compiler.ExpressionTag, parent: SvelteMustacheTag["parent"], typing: string | null, ctx: Context): SvelteMustacheTagText;
/** Convert for MustacheTag */
export declare function convertRawMustacheTag(node: SvAST.RawMustacheTag | Compiler.HtmlTag, parent: SvelteMustacheTag["parent"], ctx: Context): SvelteMustacheTagRaw;
/** Convert for DebugTag */
export declare function convertDebugTag(node: SvAST.DebugTag, parent: SvelteDebugTag["parent"], ctx: Context): SvelteDebugTag;

View File

@@ -0,0 +1,43 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertMustacheTag = convertMustacheTag;
exports.convertRawMustacheTag = convertRawMustacheTag;
exports.convertDebugTag = convertDebugTag;
const utils_1 = require("../../utils");
/** Convert for MustacheTag */
function convertMustacheTag(node, parent, typing, ctx) {
return convertMustacheTag0(node, "text", parent, typing, ctx);
}
/** Convert for MustacheTag */
function convertRawMustacheTag(node, parent, ctx) {
const mustache = convertMustacheTag0(node, "raw", parent, null, ctx);
const atHtmlStart = ctx.code.indexOf("@html", mustache.range[0]);
ctx.addToken("MustacheKeyword", {
start: atHtmlStart,
end: atHtmlStart + 5,
});
return mustache;
}
/** Convert for DebugTag */
function convertDebugTag(node, parent, ctx) {
const mustache = Object.assign({ type: "SvelteDebugTag", identifiers: [], parent }, ctx.getConvertLocation(node));
for (const id of node.identifiers) {
ctx.scriptLet.addExpression(id, mustache, null, (es) => {
mustache.identifiers.push(es);
});
}
const atDebugStart = ctx.code.indexOf("@debug", mustache.range[0]);
ctx.addToken("MustacheKeyword", {
start: atDebugStart,
end: atDebugStart + 6,
});
return mustache;
}
/** Convert to MustacheTag */
function convertMustacheTag0(node, kind, parent, typing, ctx) {
const mustache = Object.assign({ type: "SvelteMustacheTag", kind, expression: null, parent }, ctx.getConvertLocation(node));
ctx.scriptLet.addExpression(node.expression, mustache, (0, utils_1.hasTypeInfo)(node.expression) ? null : typing, (es) => {
mustache.expression = es;
});
return mustache;
}

View File

@@ -0,0 +1,5 @@
import type { SvelteRenderTag } from "../../ast";
import type { Context } from "../../context";
import type * as Compiler from "../svelte-ast-types-for-v5";
/** Convert for RenderTag */
export declare function convertRenderTag(node: Compiler.RenderTag, parent: SvelteRenderTag["parent"], ctx: Context): SvelteRenderTag;

View File

@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertRenderTag = convertRenderTag;
const common_1 = require("./common");
/** Convert for RenderTag */
function convertRenderTag(node, parent, ctx) {
const mustache = Object.assign({ type: "SvelteRenderTag", expression: null, parent }, ctx.getConvertLocation(node));
const callRange = (0, common_1.getWithLoc)(node.expression);
ctx.scriptLet.addExpressionFromRange([callRange.start, callRange.end], mustache, null, (expression) => {
mustache.expression = expression;
mustache.expression.parent = mustache;
});
const atRenderStart = ctx.code.indexOf("@render", mustache.range[0]);
ctx.addToken("MustacheKeyword", {
start: atRenderStart,
end: atRenderStart + 7,
});
return mustache;
}

View File

@@ -0,0 +1,8 @@
import type * as SvAST from "../svelte-ast-types";
import type * as Compiler from "../svelte-ast-types-for-v5";
import type { SvelteProgram } from "../../ast";
import type { Context } from "../../context";
/**
* Convert root
*/
export declare function convertSvelteRoot(svelteAst: Compiler.Root | SvAST.AstLegacy, ctx: Context): SvelteProgram;

View File

@@ -0,0 +1,214 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertSvelteRoot = convertSvelteRoot;
const element_1 = require("./element");
const attr_1 = require("./attr");
const script_1 = require("../script");
const fix_locations_1 = require("../../context/fix-locations");
const compat_1 = require("../compat");
const sort_1 = require("../sort");
/**
* Convert root
*/
function convertSvelteRoot(svelteAst, ctx) {
var _a;
const ast = Object.assign({ type: "Program", body: [], comments: ctx.comments, sourceType: "module", tokens: ctx.tokens, parent: null }, ctx.getConvertLocation({ start: 0, end: ctx.code.length }));
const body = ast.body;
const snippetChildren = [];
const fragment = (0, compat_1.getFragmentFromRoot)(svelteAst);
if (fragment) {
let children = (0, compat_1.getChildren)(fragment);
const options = (0, compat_1.getOptionsFromRoot)(svelteAst);
if (options) {
children = [...children];
if (!children.some((node, idx) => {
if (options.end <= node.start) {
children.splice(idx, 0, options);
return true;
}
return false;
})) {
children.push(options);
}
}
const nonSnippetChildren = [];
for (const child of children) {
if (child.type === "SnippetBlock") {
snippetChildren.push(child);
}
else {
nonSnippetChildren.push(child);
}
}
body.push(...(0, element_1.convertChildren)({ nodes: nonSnippetChildren }, ast, ctx));
}
let script = null;
const instance = (0, compat_1.getInstanceFromRoot)(svelteAst);
if (instance) {
script = Object.assign({ type: "SvelteScriptElement", name: null, startTag: null, body: [], endTag: null, parent: ast }, ctx.getConvertLocation(instance));
extractAttributes(script, ctx);
(0, element_1.extractElementTags)(script, ctx, {
buildNameNode: (openTokenRange) => {
ctx.addToken("HTMLIdentifier", openTokenRange);
const name = Object.assign({ type: "SvelteName", name: "script", parent: script }, ctx.getConvertLocation(openTokenRange));
return name;
},
});
body.push(script);
}
const module = (0, compat_1.getModuleFromRoot)(svelteAst);
if (module) {
const script = Object.assign({ type: "SvelteScriptElement", name: null, startTag: null, body: [], endTag: null, parent: ast }, ctx.getConvertLocation(module));
extractAttributes(script, ctx);
(0, element_1.extractElementTags)(script, ctx, {
buildNameNode: (openTokenRange) => {
ctx.addToken("HTMLIdentifier", openTokenRange);
const name = Object.assign({ type: "SvelteName", name: "script", parent: script }, ctx.getConvertLocation(openTokenRange));
return name;
},
});
body.push(script);
}
if (svelteAst.css) {
const style = Object.assign({ type: "SvelteStyleElement", name: null, startTag: null, children: [], endTag: null, parent: ast }, ctx.getConvertLocation(svelteAst.css));
extractAttributes(style, ctx);
(0, element_1.extractElementTags)(style, ctx, {
buildNameNode: (openTokenRange) => {
ctx.addToken("HTMLIdentifier", openTokenRange);
const name = Object.assign({ type: "SvelteName", name: "style", parent: style }, ctx.getConvertLocation(openTokenRange));
return name;
},
});
if (style.endTag && style.startTag.range[1] < style.endTag.range[0]) {
const contentRange = {
start: style.startTag.range[1],
end: style.endTag.range[0],
};
ctx.addToken("HTMLText", contentRange);
style.children = [
Object.assign({ type: "SvelteText", value: ctx.code.slice(contentRange.start, contentRange.end), parent: style }, ctx.getConvertLocation(contentRange)),
];
}
body.push(style);
}
body.push(...(0, element_1.convertChildren)({ nodes: snippetChildren }, ast, ctx));
if (script && ((_a = ctx.parserOptions.svelteFeatures) === null || _a === void 0 ? void 0 : _a.experimentalGenerics))
convertGenericsAttribute(script, ctx);
// Set the scope of the Program node.
ctx.scriptLet.addProgramRestore((node, _tokens, _comments, { scopeManager, registerNodeToScope, addPostProcess }) => {
const scopes = [];
for (const scope of scopeManager.scopes) {
if (scope.block === node) {
registerNodeToScope(ast, scope);
scopes.push(scope);
}
}
addPostProcess(() => {
// Reverts the node indicated by `block` to the original Program node.
// This state is incorrect, but `eslint-utils`'s `referenceTracker.iterateEsmReferences()` tracks import statements
// from Program nodes set to `block` in global scope. This can only be handled by the original Program node.
scopeManager.globalScope.block = node;
});
});
(0, sort_1.sortNodes)(body);
return ast;
}
/** Extract attrs */
function extractAttributes(element, ctx) {
element.startTag = {
type: "SvelteStartTag",
attributes: [],
selfClosing: false,
parent: element,
range: [element.range[0], null],
loc: {
start: {
line: element.loc.start.line,
column: element.loc.start.column,
},
end: null,
},
};
const block = ctx.findBlock(element);
if (block) {
element.startTag.attributes.push(...(0, attr_1.convertAttributes)(block.attrs, element.startTag, ctx));
}
}
/** Convert generics attribute */
function convertGenericsAttribute(script, ctx) {
const lang = ctx.sourceCode.scripts.attrs.lang;
if (lang !== "ts" && lang !== "typescript") {
return;
}
const genericsAttribute = script.startTag.attributes.find((attr) => {
return (attr.type === "SvelteAttribute" &&
attr.key.name === "generics" &&
attr.value.length === 1 &&
attr.value[0].type === "SvelteLiteral");
});
if (!genericsAttribute) {
return;
}
const value = genericsAttribute.value[0];
const genericValueCode = ctx.code.slice(value.range[0], value.range[1]);
const scriptLet = `void function<${genericValueCode}>(){}`;
let result;
try {
result = (0, script_1.parseScriptWithoutAnalyzeScope)(scriptLet, ctx.sourceCode.scripts.attrs, Object.assign(Object.assign({}, ctx.parserOptions), {
// Without typings
project: null }));
}
catch (_a) {
// ignore
return;
}
delete genericsAttribute.boolean;
delete genericsAttribute.value;
// Remove value token indexes
(0, sort_1.sortNodes)(ctx.tokens);
const firstTokenIndex = ctx.tokens.findIndex((token) => value.range[0] <= token.range[0] && token.range[1] <= value.range[1]);
const lastTokenCount = ctx.tokens
.slice(firstTokenIndex)
.findIndex((token) => value.range[1] <= token.range[0]);
ctx.tokens.splice(firstTokenIndex, lastTokenCount >= 0 ? lastTokenCount : Infinity);
const generics = genericsAttribute;
generics.type = "SvelteGenericsDirective";
generics.params = [];
result.ast.tokens.shift(); // void
result.ast.tokens.shift(); // function
result.ast.tokens.shift(); // <
result.ast.tokens.pop(); // }
result.ast.tokens.pop(); // {
result.ast.tokens.pop(); // )
result.ast.tokens.pop(); // (
result.ast.tokens.pop(); // >
(0, fix_locations_1.fixLocations)(result.ast, result.ast.tokens, result.ast.comments, value.range[0] - 14, result.visitorKeys, ctx);
const { ast } = result;
const statement = ast.body[0];
const rawExpression = statement.expression;
const fnDecl = rawExpression.argument;
const typeParameters = fnDecl
.typeParameters;
const params = typeParameters.params;
// Replace tokens
for (const tokensKey of ["tokens", "comments"]) {
for (const token of result.ast[tokensKey]) {
if (params.every((param) => token.range[1] <= param.range[0] ||
param.range[1] <= token.range[0])) {
ctx[tokensKey].push(token);
}
}
}
for (const param of params) {
param.parent = generics;
generics.params.push(param);
ctx.scriptLet.addGenericTypeAliasDeclaration(param, (id, typeNode) => {
param.name = id;
if (param.constraint) {
param.constraint = typeNode;
}
}, (typeNode) => {
param.default = typeNode;
});
}
}

View File

@@ -0,0 +1,7 @@
import type { SvelteLiteral, SvelteText } from "../../ast";
import type { Context } from "../../context";
import type * as SvAST from "../svelte-ast-types";
/** Convert for Text */
export declare function convertText(node: SvAST.Text, parent: SvelteText["parent"], ctx: Context): SvelteText;
/** Convert for Text to Literal */
export declare function convertTextToLiteral(node: SvAST.Text, parent: SvelteLiteral["parent"], ctx: Context): SvelteLiteral;

View File

@@ -0,0 +1,34 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertText = convertText;
exports.convertTextToLiteral = convertTextToLiteral;
/** Convert for Text */
function convertText(node, parent, ctx) {
const text = Object.assign({ type: "SvelteText", value: node.data, parent }, ctx.getConvertLocation(node));
extractTextTokens(node, ctx);
return text;
}
/** Convert for Text to Literal */
function convertTextToLiteral(node, parent, ctx) {
const text = Object.assign({ type: "SvelteLiteral", value: node.data, parent }, ctx.getConvertLocation(node));
extractTextTokens(node, ctx);
return text;
}
/** Extract tokens */
function extractTextTokens(node, ctx) {
const loc = node;
let start = loc.start;
let word = false;
for (let index = loc.start; index < loc.end; index++) {
if (word !== Boolean(ctx.code[index].trim())) {
if (start < index) {
ctx.addToken("HTMLText", { start, end: index });
}
word = !word;
start = index;
}
}
if (start < loc.end) {
ctx.addToken("HTMLText", { start, end: loc.end });
}
}

View File

@@ -0,0 +1,8 @@
import type { BasicParserObject } from "./parser-object";
/**
* Load `espree` from the loaded ESLint.
* If the loaded ESLint was not found, just returns `require("espree")`.
*/
export declare function getEspree(): BasicParserObject & {
latestEcmaVersion: number;
};

View File

@@ -0,0 +1,55 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getEspree = getEspree;
const module_1 = __importDefault(require("module"));
const path_1 = __importDefault(require("path"));
const createRequire =
// Added in v12.2.0
module_1.default.createRequire ||
// Added in v10.12.0, but deprecated in v12.2.0.
// @ts-expect-error -- old type
module_1.default.createRequireFromPath ||
// Polyfill - This is not executed on the tests on node@>=10.
/* istanbul ignore next */
((modName) => {
const mod = new module_1.default(modName);
mod.filename = modName;
mod.paths = module_1.default._nodeModulePaths(path_1.default.dirname(modName));
mod._compile("module.exports = require;", modName);
return mod.exports;
});
let espreeCache = null;
/** Checks if given path is linter path */
function isLinterPath(p) {
return (
// ESLint 6 and above
p.includes(`eslint${path_1.default.sep}lib${path_1.default.sep}linter${path_1.default.sep}linter.js`) ||
// ESLint 5
p.includes(`eslint${path_1.default.sep}lib${path_1.default.sep}linter.js`));
}
/**
* Load `espree` from the loaded ESLint.
* If the loaded ESLint was not found, just returns `require("espree")`.
*/
function getEspree() {
if (!espreeCache) {
// Lookup the loaded eslint
const linterPath = Object.keys(require.cache || {}).find(isLinterPath);
if (linterPath) {
try {
espreeCache = createRequire(linterPath)("espree");
}
catch (_a) {
// ignore
}
}
if (!espreeCache) {
// eslint-disable-next-line @typescript-eslint/no-require-imports -- ignore
espreeCache = require("espree");
}
}
return espreeCache;
}

View File

@@ -0,0 +1,7 @@
import type { SvelteParseContext } from "./svelte-parse-context";
declare const globalsForSvelte: readonly ["$$slots", "$$props", "$$restProps"];
export declare const globalsForRunes: readonly ["$state", "$derived", "$effect", "$props", "$bindable", "$inspect", "$host"];
type Global = (typeof globalsForSvelte)[number] | (typeof globalsForRunes)[number];
export declare function getGlobalsForSvelte(svelteParseContext: SvelteParseContext): readonly Global[];
export declare function getGlobalsForSvelteScript(svelteParseContext: SvelteParseContext): readonly Global[];
export {};

View File

@@ -0,0 +1,27 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.globalsForRunes = void 0;
exports.getGlobalsForSvelte = getGlobalsForSvelte;
exports.getGlobalsForSvelteScript = getGlobalsForSvelteScript;
const globalsForSvelte = ["$$slots", "$$props", "$$restProps"];
exports.globalsForRunes = [
"$state",
"$derived",
"$effect",
"$props",
"$bindable",
"$inspect",
"$host",
];
function getGlobalsForSvelte(svelteParseContext) {
if (svelteParseContext.runes) {
return [...globalsForSvelte, ...exports.globalsForRunes];
}
return globalsForSvelte;
}
function getGlobalsForSvelteScript(svelteParseContext) {
if (svelteParseContext.runes) {
return exports.globalsForRunes;
}
return [];
}

View File

@@ -0,0 +1,6 @@
import type * as Compiler from "./svelte-ast-types-for-v5";
/** Parse HTML attributes */
export declare function parseAttributes(code: string, startIndex: number): {
attributes: Compiler.Attribute[];
index: number;
};

View File

@@ -0,0 +1,268 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseAttributes = parseAttributes;
const espree_1 = require("./espree");
const RE_IS_SPACE = /^\s$/u;
class State {
constructor(code, index) {
this.code = code;
this.index = index;
}
getCurr() {
return this.code[this.index];
}
skipSpaces() {
while (this.currIsSpace()) {
this.advance();
if (this.eof())
break;
}
}
currIsSpace() {
return RE_IS_SPACE.test(this.getCurr() || "");
}
currIs(expect) {
return this.code.startsWith(expect, this.index);
}
eof() {
return this.index >= this.code.length;
}
eat(expect) {
if (!this.currIs(expect)) {
return null;
}
this.index += expect.length;
return expect;
}
advance() {
this.index++;
return this.getCurr();
}
}
/** Parse HTML attributes */
function parseAttributes(code, startIndex) {
const attributes = [];
const state = new State(code, startIndex);
while (!state.eof()) {
state.skipSpaces();
if (state.currIs(">") || state.currIs("/>") || state.eof())
break;
attributes.push(parseAttribute(state));
}
return { attributes, index: state.index };
}
/** Parse HTML attribute */
function parseAttribute(state) {
const start = state.index;
// parse key
const key = parseAttributeKey(state);
const keyEnd = state.index;
state.skipSpaces();
if (!state.eat("=")) {
return {
type: "Attribute",
name: key,
value: true,
start,
end: keyEnd,
};
}
state.skipSpaces();
if (state.eof()) {
return {
type: "Attribute",
name: key,
value: true,
start,
end: keyEnd,
};
}
// parse value
const value = parseAttributeValue(state);
return {
type: "Attribute",
name: key,
value: [value],
start,
end: state.index,
};
}
/** Parse HTML attribute key */
function parseAttributeKey(state) {
const start = state.index;
while (state.advance()) {
if (state.currIs("=") ||
state.currIs(">") ||
state.currIs("/>") ||
state.currIsSpace()) {
break;
}
}
const end = state.index;
return state.code.slice(start, end);
}
/** Parse HTML attribute value */
function parseAttributeValue(state) {
const start = state.index;
const quote = state.eat('"') || state.eat("'");
const startBk = state.index;
const expression = parseAttributeMustache(state);
if (expression) {
if (!quote || state.eat(quote)) {
const end = state.index;
return {
type: "ExpressionTag",
expression,
start,
end,
};
}
}
state.index = startBk;
if (quote) {
if (state.eof()) {
return {
type: "Text",
data: quote,
raw: quote,
start,
end: state.index,
};
}
let c;
while ((c = state.getCurr())) {
state.advance();
if (c === quote) {
const end = state.index;
const data = state.code.slice(start + 1, end - 1);
return {
type: "Text",
data,
raw: data,
start: start + 1,
end: end - 1,
};
}
}
}
else {
while (state.advance()) {
if (state.currIsSpace() || state.currIs(">") || state.currIs("/>")) {
break;
}
}
}
const end = state.index;
const data = state.code.slice(start, end);
return {
type: "Text",
data,
raw: data,
start,
end,
};
}
/** Parse mustache */
function parseAttributeMustache(state) {
if (!state.eat("{")) {
return null;
}
// parse simple expression
const leadingComments = [];
const startBk = state.index;
state.skipSpaces();
let start = state.index;
while (!state.eof()) {
if (state.eat("//")) {
leadingComments.push(parseInlineComment(state.index - 2));
state.skipSpaces();
start = state.index;
continue;
}
if (state.eat("/*")) {
leadingComments.push(parseBlockComment(state.index - 2));
state.skipSpaces();
start = state.index;
continue;
}
const stringQuote = state.eat('"') || state.eat("'");
if (stringQuote) {
skipString(stringQuote);
state.skipSpaces();
continue;
}
const endCandidate = state.index;
state.skipSpaces();
if (state.eat("}")) {
const end = endCandidate;
try {
const espree = (0, espree_1.getEspree)();
const expression = espree.parse(state.code.slice(start, end), {
ecmaVersion: espree.latestEcmaVersion,
}).body[0].expression;
delete expression.range;
return Object.assign(Object.assign({}, expression), { leadingComments,
start,
end });
}
catch (_a) {
break;
}
}
state.advance();
}
state.index = startBk;
return null;
function parseInlineComment(tokenStart) {
const valueStart = state.index;
let valueEnd = null;
while (!state.eof()) {
if (state.eat("\n")) {
valueEnd = state.index - 1;
break;
}
state.advance();
}
if (valueEnd == null) {
valueEnd = state.index;
}
return {
type: "Line",
value: state.code.slice(valueStart, valueEnd),
start: tokenStart,
end: state.index,
};
}
function parseBlockComment(tokenStart) {
const valueStart = state.index;
let valueEnd = null;
while (!state.eof()) {
if (state.eat("*/")) {
valueEnd = state.index - 2;
break;
}
state.advance();
}
if (valueEnd == null) {
valueEnd = state.index;
}
return {
type: "Block",
value: state.code.slice(valueStart, valueEnd),
start: tokenStart,
end: state.index,
};
}
function skipString(stringQuote) {
while (!state.eof()) {
if (state.eat(stringQuote)) {
break;
}
if (state.eat("\\")) {
// escape
state.advance();
}
state.advance();
}
}
}

View File

@@ -0,0 +1,46 @@
import type { Comment, SvelteProgram, Token } from "../ast";
import type { Program } from "estree";
import type { ScopeManager } from "eslint-scope";
import type * as SvAST from "./svelte-ast-types";
import type * as Compiler from "./svelte-ast-types-for-v5";
import { type StyleContext, type StyleContextNoStyleElement, type StyleContextParseError, type StyleContextSuccess, type StyleContextUnknownLang } from "./style-context";
import { type SvelteParseContext } from "./svelte-parse-context";
export { StyleContext, StyleContextNoStyleElement, StyleContextParseError, StyleContextSuccess, StyleContextUnknownLang, };
export interface ESLintProgram extends Program {
comments: Comment[];
tokens: Token[];
}
/**
* The parsing result of ESLint custom parsers.
*/
export interface ESLintExtendedProgram {
ast: ESLintProgram;
services?: Record<string, any>;
visitorKeys?: {
[type: string]: string[];
};
scopeManager?: ScopeManager;
_virtualScriptCode?: string;
}
type ParseResult = {
ast: SvelteProgram;
services: Record<string, any> & ({
isSvelte: true;
isSvelteScript: false;
getSvelteHtmlAst: () => SvAST.Fragment | Compiler.Fragment;
getStyleContext: () => StyleContext;
svelteParseContext: SvelteParseContext;
} | {
isSvelte: false;
isSvelteScript: true;
svelteParseContext: SvelteParseContext;
});
visitorKeys: {
[type: string]: string[];
};
scopeManager: ScopeManager;
};
/**
* Parse source code
*/
export declare function parseForESLint(code: string, options?: any): ParseResult;

View File

@@ -0,0 +1,185 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseForESLint = parseForESLint;
const visitor_keys_1 = require("../visitor-keys");
const context_1 = require("../context");
const eslint_scope_1 = require("eslint-scope");
const script_1 = require("./script");
const sort_1 = require("./sort");
const template_1 = require("./template");
const analyze_scope_1 = require("./analyze-scope");
const errors_1 = require("../errors");
const typescript_1 = require("./typescript");
const scope_1 = require("../scope");
const style_context_1 = require("./style-context");
const globals_1 = require("./globals");
const parser_options_1 = require("./parser-options");
const compat_1 = require("./compat");
const svelte_parse_context_1 = require("./svelte-parse-context");
const svelte_config_1 = require("../svelte-config");
/**
* Parse source code
*/
function parseForESLint(code, options) {
const svelteConfig = (0, svelte_config_1.resolveSvelteConfigFromOption)(options);
const parserOptions = (0, parser_options_1.normalizeParserOptions)(options);
if ((0, svelte_parse_context_1.isEnableRunes)(svelteConfig, parserOptions) &&
parserOptions.filePath &&
!parserOptions.filePath.endsWith(".svelte") &&
// If no `filePath` is set in ESLint, "<input>" will be specified.
parserOptions.filePath !== "<input>") {
const trimmed = code.trim();
if (!trimmed.startsWith("<") && !trimmed.endsWith(">")) {
const svelteParseContext = (0, svelte_parse_context_1.resolveSvelteParseContextForSvelteScript)(svelteConfig, parserOptions);
return parseAsScript(code, parserOptions, svelteParseContext);
}
}
return parseAsSvelte(code, svelteConfig, parserOptions);
}
/**
* Parse source code as svelte component
*/
function parseAsSvelte(code, svelteConfig, parserOptions) {
const ctx = new context_1.Context(code, parserOptions);
const resultTemplate = (0, template_1.parseTemplate)(ctx.sourceCode.template, ctx, parserOptions);
const svelteParseContext = (0, svelte_parse_context_1.resolveSvelteParseContextForSvelte)(svelteConfig, parserOptions, resultTemplate.svelteAst);
const scripts = ctx.sourceCode.scripts;
const resultScript = ctx.isTypeScript()
? (0, typescript_1.parseTypeScriptInSvelte)(scripts.getCurrentVirtualCodeInfo(), scripts.attrs, parserOptions, { slots: ctx.slots, svelteParseContext })
: (0, script_1.parseScriptInSvelte)(scripts.getCurrentVirtualCode(), scripts.attrs, parserOptions);
ctx.scriptLet.restore(resultScript);
ctx.tokens.push(...resultScript.ast.tokens);
ctx.comments.push(...resultScript.ast.comments);
(0, sort_1.sortNodes)(ctx.comments);
(0, sort_1.sortNodes)(ctx.tokens);
extractTokens(ctx);
(0, analyze_scope_1.analyzeStoreScope)(resultScript.scopeManager);
(0, analyze_scope_1.analyzeReactiveScope)(resultScript.scopeManager);
(0, analyze_scope_1.analyzeStoreScope)(resultScript.scopeManager); // for reactive vars
(0, analyze_scope_1.analyzeSnippetsScope)(ctx.snippets, resultScript.scopeManager);
// Add $$xxx variable
addGlobalVariables(resultScript.scopeManager, (0, globals_1.getGlobalsForSvelte)(svelteParseContext));
const ast = resultTemplate.ast;
const statements = [...resultScript.ast.body];
ast.sourceType = resultScript.ast.sourceType;
const scriptElements = ast.body.filter((b) => b.type === "SvelteScriptElement");
for (let index = 0; index < scriptElements.length; index++) {
const body = scriptElements[index];
let statement = statements[0];
while (statement &&
body.range[0] <= statement.range[0] &&
(statement.range[1] <= body.range[1] ||
index === scriptElements.length - 1)) {
statement.parent = body;
body.body.push(statement);
statements.shift();
statement = statements[0];
}
if (!body.startTag.attributes.some((attr) => attr.type === "SvelteAttribute" &&
attr.key.name === "context" &&
attr.value.length === 1 &&
attr.value[0].type === "SvelteLiteral" &&
attr.value[0].value === "module")) {
(0, analyze_scope_1.analyzePropsScope)(body, resultScript.scopeManager, svelteParseContext);
}
}
if (statements.length) {
throw new errors_1.ParseError("The script is unterminated", statements[0].range[1], ctx);
}
const styleElement = ast.body.find((b) => b.type === "SvelteStyleElement");
let styleContext = null;
resultScript.ast = ast;
resultScript.services = Object.assign(resultScript.services || {}, {
isSvelte: true,
isSvelteScript: false,
getSvelteHtmlAst() {
return (0, compat_1.getFragmentFromRoot)(resultTemplate.svelteAst);
},
getStyleContext() {
if (styleContext === null) {
styleContext = (0, style_context_1.parseStyleContext)(styleElement, ctx);
}
return styleContext;
},
styleNodeLoc: style_context_1.styleNodeLoc,
styleNodeRange: style_context_1.styleNodeRange,
svelteParseContext,
});
resultScript.visitorKeys = Object.assign({}, visitor_keys_1.KEYS, resultScript.visitorKeys);
return resultScript;
}
/**
* Parse source code as script
*/
function parseAsScript(code, parserOptions, svelteParseContext) {
var _a;
const lang = (_a = parserOptions.filePath) === null || _a === void 0 ? void 0 : _a.split(".").pop();
const resultScript = (0, parser_options_1.isTypeScript)(parserOptions, lang)
? (0, typescript_1.parseTypeScript)(code, { lang }, parserOptions, svelteParseContext)
: (0, script_1.parseScript)(code, { lang }, parserOptions);
// Add $$xxx variable
addGlobalVariables(resultScript.scopeManager, (0, globals_1.getGlobalsForSvelteScript)(svelteParseContext));
resultScript.services = Object.assign(resultScript.services || {}, {
isSvelte: false,
isSvelteScript: true,
svelteParseContext,
});
resultScript.visitorKeys = Object.assign({}, visitor_keys_1.KEYS, resultScript.visitorKeys);
return resultScript;
}
function addGlobalVariables(scopeManager, globals) {
const globalScope = scopeManager.globalScope;
for (const globalName of globals) {
if (globalScope.set.has(globalName))
continue;
const variable = new eslint_scope_1.Variable();
variable.name = globalName;
variable.scope = globalScope;
globalScope.variables.push(variable);
globalScope.set.set(globalName, variable);
globalScope.through = globalScope.through.filter((reference) => {
if (reference.identifier.name === globalName) {
// Links the variable and the reference.
// And this reference is removed from `Scope#through`.
reference.resolved = variable;
(0, scope_1.addReference)(variable.references, reference);
return false;
}
return true;
});
}
}
/** Extract tokens */
function extractTokens(ctx) {
const useRanges = (0, sort_1.sortNodes)([...ctx.tokens, ...ctx.comments]).map((t) => t.range);
let range = useRanges.shift();
for (let index = 0; index < ctx.sourceCode.template.length; index++) {
while (range && range[1] <= index) {
range = useRanges.shift();
}
if (range && range[0] <= index) {
index = range[1] - 1;
continue;
}
const c = ctx.sourceCode.template[index];
if (!c.trim()) {
continue;
}
if (isPunctuator(c)) {
ctx.addToken("Punctuator", { start: index, end: index + 1 });
}
else {
// unknown
// It is may be a bug.
ctx.addToken("Identifier", { start: index, end: index + 1 });
}
}
(0, sort_1.sortNodes)(ctx.comments);
(0, sort_1.sortNodes)(ctx.tokens);
/**
* Checks if the given char is punctuator
*/
function isPunctuator(c) {
return /^[^\w$]$/iu.test(c);
}
}

View File

@@ -0,0 +1,34 @@
import type { ESLintExtendedProgram, ESLintProgram } from ".";
import type * as tsESLintParser from "@typescript-eslint/parser";
type TSESLintParser = typeof tsESLintParser;
/**
* The type of basic ESLint custom parser.
* e.g. espree
*/
export type BasicParserObject = {
parse(code: string, options: any): ESLintProgram;
parseForESLint: undefined;
};
/**
* The type of ESLint custom parser enhanced for ESLint.
* e.g. @babel/eslint-parser, @typescript-eslint/parser
*/
export type EnhancedParserObject = {
parseForESLint(code: string, options: any): ESLintExtendedProgram;
parse: undefined;
};
/**
* The type of ESLint (custom) parsers.
*/
export type ParserObject = EnhancedParserObject | BasicParserObject;
/** Checks whether given object is ParserObject */
export declare function isParserObject(value: unknown): value is ParserObject;
/** Checks whether given object is EnhancedParserObject */
export declare function isEnhancedParserObject(value: unknown): value is EnhancedParserObject;
/** Checks whether given object is BasicParserObject */
export declare function isBasicParserObject(value: unknown): value is BasicParserObject;
/** Checks whether given object maybe "@typescript-eslint/parser" */
export declare function maybeTSESLintParserObject(value: unknown): value is TSESLintParser;
/** Checks whether given object is "@typescript-eslint/parser" */
export declare function isTSESLintParserObject(value: unknown): value is TSESLintParser;
export {};

View File

@@ -0,0 +1,42 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isParserObject = isParserObject;
exports.isEnhancedParserObject = isEnhancedParserObject;
exports.isBasicParserObject = isBasicParserObject;
exports.maybeTSESLintParserObject = maybeTSESLintParserObject;
exports.isTSESLintParserObject = isTSESLintParserObject;
/** Checks whether given object is ParserObject */
function isParserObject(value) {
return isEnhancedParserObject(value) || isBasicParserObject(value);
}
/** Checks whether given object is EnhancedParserObject */
function isEnhancedParserObject(value) {
return Boolean(value && typeof value.parseForESLint === "function");
}
/** Checks whether given object is BasicParserObject */
function isBasicParserObject(value) {
return Boolean(value && typeof value.parse === "function");
}
/** Checks whether given object maybe "@typescript-eslint/parser" */
function maybeTSESLintParserObject(value) {
return (isEnhancedParserObject(value) &&
isBasicParserObject(value) &&
typeof value.createProgram === "function" &&
typeof value.clearCaches === "function" &&
typeof value.version === "string");
}
/** Checks whether given object is "@typescript-eslint/parser" */
function isTSESLintParserObject(value) {
if (!isEnhancedParserObject(value))
return false;
try {
const result = value.parseForESLint("", {});
const services = result.services;
return Boolean(services &&
services.esTreeNodeToTSNodeMap &&
services.tsNodeToESTreeNodeMap);
}
catch (_a) {
return false;
}
}

View File

@@ -0,0 +1,29 @@
import { type UserOptionParser } from "./resolve-parser";
export type NormalizedParserOptions = {
parser?: UserOptionParser;
project?: string | string[] | null;
ecmaVersion: number | "latest";
sourceType: "module" | "script";
ecmaFeatures?: {
globalReturn?: boolean | undefined;
impliedStrict?: boolean | undefined;
jsx?: boolean | undefined;
experimentalObjectRestSpread?: boolean | undefined;
[key: string]: any;
};
svelteFeatures?: {
runes?: boolean;
experimentalGenerics?: boolean;
};
loc: boolean;
range: boolean;
raw: boolean;
tokens: boolean;
comment: boolean;
eslintVisitorKeys: boolean;
eslintScopeManager: boolean;
filePath?: string;
};
/** Normalize parserOptions */
export declare function normalizeParserOptions(options: any): NormalizedParserOptions;
export declare function isTypeScript(parserOptions: NormalizedParserOptions, lang: string | undefined): boolean;

View File

@@ -0,0 +1,59 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.normalizeParserOptions = normalizeParserOptions;
exports.isTypeScript = isTypeScript;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const parser_object_1 = require("./parser-object");
const resolve_parser_1 = require("./resolve-parser");
/** Normalize parserOptions */
function normalizeParserOptions(options) {
const parserOptions = Object.assign({ ecmaVersion: 2020, sourceType: "module", loc: true, range: true, raw: true, tokens: true, comment: true, eslintVisitorKeys: true, eslintScopeManager: true }, (options || {}));
parserOptions.sourceType = "module";
if (parserOptions.ecmaVersion <= 5 || parserOptions.ecmaVersion == null) {
parserOptions.ecmaVersion = 2015;
}
return parserOptions;
}
const TS_PARSER_NAMES = [
"@typescript-eslint/parser",
"typescript-eslint-parser-for-extra-files",
];
function isTypeScript(parserOptions, lang) {
var _a;
if (!lang) {
return false;
}
const parserValue = (0, resolve_parser_1.getParserForLang)(lang, parserOptions === null || parserOptions === void 0 ? void 0 : parserOptions.parser);
if (typeof parserValue !== "string") {
return ((0, parser_object_1.maybeTSESLintParserObject)(parserValue) ||
(0, parser_object_1.isTSESLintParserObject)(parserValue));
}
const parserName = parserValue;
if (TS_PARSER_NAMES.includes(parserName)) {
return true;
}
if (TS_PARSER_NAMES.some((nm) => parserName.includes(nm))) {
let targetPath = parserName;
while (targetPath) {
const pkgPath = path_1.default.join(targetPath, "package.json");
if (fs_1.default.existsSync(pkgPath)) {
try {
return TS_PARSER_NAMES.includes((_a = JSON.parse(fs_1.default.readFileSync(pkgPath, "utf-8"))) === null || _a === void 0 ? void 0 : _a.name);
}
catch (_b) {
return false;
}
}
const parent = path_1.default.dirname(targetPath);
if (targetPath === parent) {
break;
}
targetPath = parent;
}
}
return false;
}

View File

@@ -0,0 +1,6 @@
import type { ParserObject } from "./parser-object";
export type UserOptionParser = string | ParserObject | Record<string, string | ParserObject | undefined> | undefined;
/** Get parser for script lang */
export declare function getParserForLang(lang: string | undefined | null, parser: UserOptionParser): string | ParserObject;
/** Get parser */
export declare function getParser(attrs: Record<string, string | undefined>, parser: UserOptionParser): ParserObject;

View File

@@ -0,0 +1,33 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getParserForLang = getParserForLang;
exports.getParser = getParser;
const espree_1 = require("./espree");
const parser_object_1 = require("./parser-object");
/** Get parser for script lang */
function getParserForLang(lang, parser) {
if (parser) {
if (typeof parser === "string" || (0, parser_object_1.isParserObject)(parser)) {
return parser;
}
if (typeof parser === "object") {
const value = parser[lang || "js"];
if (typeof value === "string" || (0, parser_object_1.isParserObject)(value)) {
return value;
}
}
}
return "espree";
}
/** Get parser */
function getParser(attrs, parser) {
const parserValue = getParserForLang(attrs.lang, parser);
if ((0, parser_object_1.isParserObject)(parserValue)) {
return parserValue;
}
if (parserValue !== "espree") {
// eslint-disable-next-line @typescript-eslint/no-require-imports -- ignore
return require(parserValue);
}
return (0, espree_1.getEspree)();
}

View File

@@ -0,0 +1,14 @@
import type { ESLintExtendedProgram } from ".";
import type { NormalizedParserOptions } from "./parser-options";
/**
* Parse for <script>
*/
export declare function parseScriptInSvelte(code: string, attrs: Record<string, string | undefined>, parserOptions: NormalizedParserOptions): ESLintExtendedProgram;
/**
* Parse for script
*/
export declare function parseScript(code: string, attrs: Record<string, string | undefined>, parserOptions: NormalizedParserOptions): ESLintExtendedProgram;
/**
* Parse for script without analyze scope
*/
export declare function parseScriptWithoutAnalyzeScope(code: string, attrs: Record<string, string | undefined>, options: NormalizedParserOptions): ESLintExtendedProgram;

View File

@@ -0,0 +1,63 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseScriptInSvelte = parseScriptInSvelte;
exports.parseScript = parseScript;
exports.parseScriptWithoutAnalyzeScope = parseScriptWithoutAnalyzeScope;
const analyze_scope_1 = require("./analyze-scope");
const traverse_1 = require("../traverse");
const resolve_parser_1 = require("./resolve-parser");
const parser_object_1 = require("./parser-object");
/**
* Parse for <script>
*/
function parseScriptInSvelte(code, attrs, parserOptions) {
const result = parseScript(code, attrs, parserOptions);
(0, traverse_1.traverseNodes)(result.ast, {
visitorKeys: result.visitorKeys,
enterNode(node, parent) {
node.parent = parent;
if (node.type === "LabeledStatement" && node.label.name === "$") {
if ((parent === null || parent === void 0 ? void 0 : parent.type) === "Program") {
// Transform node type
node.type = "SvelteReactiveStatement";
}
}
},
leaveNode() {
//
},
});
return result;
}
/**
* Parse for script
*/
function parseScript(code, attrs, parserOptions) {
const result = parseScriptWithoutAnalyzeScopeFromVCode(code, attrs, parserOptions);
if (!result.scopeManager) {
const scopeManager = (0, analyze_scope_1.analyzeScope)(result.ast, parserOptions);
result.scopeManager = scopeManager;
}
return result;
}
/**
* Parse for script without analyze scope
*/
function parseScriptWithoutAnalyzeScope(code, attrs, options) {
const parser = (0, resolve_parser_1.getParser)(attrs, options.parser);
const result = (0, parser_object_1.isEnhancedParserObject)(parser)
? parser.parseForESLint(code, options)
: parser.parse(code, options);
if ("ast" in result && result.ast != null) {
return result;
}
return { ast: result };
}
/**
* Parse for script without analyze scope
*/
function parseScriptWithoutAnalyzeScopeFromVCode(code, attrs, options) {
const result = parseScriptWithoutAnalyzeScope(code, attrs, options);
result._virtualScriptCode = code;
return result;
}

View File

@@ -0,0 +1,6 @@
/**
* Sort tokens
*/
export declare function sortNodes<T extends {
range: [number, number];
}>(tokens: T[] | null | undefined): T[];

View File

@@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.sortNodes = sortNodes;
/**
* Sort tokens
*/
function sortNodes(tokens) {
if (!tokens) {
return [];
}
return tokens.sort((a, b) => {
if (a.range[0] !== b.range[0]) {
return a.range[0] - b.range[0];
}
return a.range[1] - b.range[1];
});
}

View File

@@ -0,0 +1,33 @@
import type { Node, Root } from "postcss";
import type { Context } from "../context";
import type { SourceLocation, SvelteStyleElement } from "../ast";
export type StyleContext = StyleContextNoStyleElement | StyleContextParseError | StyleContextSuccess | StyleContextUnknownLang;
export interface StyleContextNoStyleElement {
status: "no-style-element";
}
export interface StyleContextParseError {
status: "parse-error";
sourceLang: string;
error: any;
}
export interface StyleContextSuccess {
status: "success";
sourceLang: string;
sourceAst: Root;
}
export interface StyleContextUnknownLang {
status: "unknown-lang";
sourceLang: string;
}
/**
* Extracts style source from a SvelteStyleElement and parses it into a PostCSS AST.
*/
export declare function parseStyleContext(styleElement: SvelteStyleElement | undefined, ctx: Context): StyleContext;
/**
* Extracts a node location (like that of any ESLint node) from a parsed svelte style node.
*/
export declare function styleNodeLoc(node: Node): Partial<SourceLocation>;
/**
* Extracts a node range (like that of any ESLint node) from a parsed svelte style node.
*/
export declare function styleNodeRange(node: Node): [number | undefined, number | undefined];

View File

@@ -0,0 +1,108 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseStyleContext = parseStyleContext;
exports.styleNodeLoc = styleNodeLoc;
exports.styleNodeRange = styleNodeRange;
const postcss_1 = __importDefault(require("postcss"));
const postcss_scss_1 = require("postcss-scss");
/**
* Extracts style source from a SvelteStyleElement and parses it into a PostCSS AST.
*/
function parseStyleContext(styleElement, ctx) {
if (!styleElement || !styleElement.endTag) {
return { status: "no-style-element" };
}
let sourceLang = "css";
for (const attribute of styleElement.startTag.attributes) {
if (attribute.type === "SvelteAttribute" &&
attribute.key.name === "lang" &&
attribute.value.length > 0 &&
attribute.value[0].type === "SvelteLiteral") {
sourceLang = attribute.value[0].value;
}
}
let parseFn, sourceAst;
switch (sourceLang) {
case "css":
parseFn = postcss_1.default.parse;
break;
case "scss":
parseFn = postcss_scss_1.parse;
break;
default:
return { status: "unknown-lang", sourceLang };
}
const styleCode = ctx.code.slice(styleElement.startTag.range[1], styleElement.endTag.range[0]);
try {
sourceAst = parseFn(styleCode, {
from: ctx.parserOptions.filePath,
});
}
catch (error) {
return { status: "parse-error", sourceLang, error };
}
fixPostCSSNodeLocation(sourceAst, styleElement);
sourceAst.walk((node) => fixPostCSSNodeLocation(node, styleElement));
return { status: "success", sourceLang, sourceAst };
}
/**
* Extracts a node location (like that of any ESLint node) from a parsed svelte style node.
*/
function styleNodeLoc(node) {
if (node.source === undefined) {
return {};
}
return {
start: node.source.start !== undefined
? {
line: node.source.start.line,
column: node.source.start.column - 1,
}
: undefined,
end: node.source.end !== undefined
? {
line: node.source.end.line,
column: node.source.end.column,
}
: undefined,
};
}
/**
* Extracts a node range (like that of any ESLint node) from a parsed svelte style node.
*/
function styleNodeRange(node) {
if (node.source === undefined) {
return [undefined, undefined];
}
return [
node.source.start !== undefined ? node.source.start.offset : undefined,
node.source.end !== undefined ? node.source.end.offset + 1 : undefined,
];
}
/**
* Fixes PostCSS AST locations to be relative to the whole file instead of relative to the <style> element.
*/
function fixPostCSSNodeLocation(node, styleElement) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
if (((_b = (_a = node.source) === null || _a === void 0 ? void 0 : _a.start) === null || _b === void 0 ? void 0 : _b.offset) !== undefined) {
node.source.start.offset += styleElement.startTag.range[1];
}
if (((_d = (_c = node.source) === null || _c === void 0 ? void 0 : _c.start) === null || _d === void 0 ? void 0 : _d.line) !== undefined) {
node.source.start.line += styleElement.loc.start.line - 1;
}
if (((_f = (_e = node.source) === null || _e === void 0 ? void 0 : _e.end) === null || _f === void 0 ? void 0 : _f.offset) !== undefined) {
node.source.end.offset += styleElement.startTag.range[1];
}
if (((_h = (_g = node.source) === null || _g === void 0 ? void 0 : _g.end) === null || _h === void 0 ? void 0 : _h.line) !== undefined) {
node.source.end.line += styleElement.loc.start.line - 1;
}
if (((_k = (_j = node.source) === null || _j === void 0 ? void 0 : _j.start) === null || _k === void 0 ? void 0 : _k.line) === styleElement.loc.start.line) {
node.source.start.column += styleElement.startTag.loc.end.column;
}
if (((_m = (_l = node.source) === null || _l === void 0 ? void 0 : _l.end) === null || _m === void 0 ? void 0 : _m.line) === styleElement.loc.start.line) {
node.source.end.column += styleElement.startTag.loc.end.column;
}
}

View File

@@ -0,0 +1,44 @@
import type { AST } from "svelte/compiler";
export type Root = AST.Root;
export type Fragment = AST.Fragment;
export type SvelteOptions = AST.SvelteOptions;
export type Script = AST.Script;
export type Text = AST.Text;
export type ExpressionTag = AST.ExpressionTag;
export type HtmlTag = AST.HtmlTag;
export type ConstTag = AST.ConstTag;
export type DebugTag = AST.DebugTag;
export type RenderTag = AST.RenderTag;
export type Component = AST.Component;
export type TitleElement = AST.TitleElement;
export type SlotElement = AST.SlotElement;
export type RegularElement = AST.RegularElement;
export type SvelteBody = AST.SvelteBody;
export type SvelteComponent = AST.SvelteComponent;
export type SvelteDocument = AST.SvelteDocument;
export type SvelteElement = AST.SvelteElement;
export type SvelteFragment = AST.SvelteFragment;
export type SvelteHead = AST.SvelteHead;
export type SvelteOptionsRaw = AST.SvelteOptionsRaw;
export type SvelteSelf = AST.SvelteSelf;
export type SvelteWindow = AST.SvelteWindow;
export type IfBlock = AST.IfBlock;
export type EachBlock = AST.EachBlock;
export type AwaitBlock = AST.AwaitBlock;
export type KeyBlock = AST.KeyBlock;
export type SnippetBlock = AST.SnippetBlock;
export type Comment = AST.Comment;
export type Attribute = AST.Attribute;
export type SpreadAttribute = AST.SpreadAttribute;
export type AnimateDirective = AST.AnimateDirective;
export type BindDirective = AST.BindDirective;
export type ClassDirective = AST.ClassDirective;
export type LetDirective = AST.LetDirective;
export type OnDirective = AST.OnDirective;
export type StyleDirective = AST.StyleDirective;
export type TransitionDirective = AST.TransitionDirective;
export type UseDirective = AST.UseDirective;
export type Tag = ExpressionTag | HtmlTag | ConstTag | DebugTag | RenderTag;
export type ElementLike = Component | TitleElement | SlotElement | RegularElement | SvelteBody | SvelteComponent | SvelteDocument | SvelteElement | SvelteFragment | SvelteHead | SvelteOptionsRaw | SvelteSelf | SvelteWindow;
export type Block = EachBlock | IfBlock | AwaitBlock | KeyBlock | SnippetBlock;
export type Directive = AnimateDirective | BindDirective | ClassDirective | LetDirective | OnDirective | StyleDirective | TransitionDirective | UseDirective;

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,213 @@
import type ESTree from "estree";
interface BaseNode {
start: number;
end: number;
}
export interface AstLegacy {
html?: Fragment;
css?: Style;
instance?: Script;
module?: Script;
}
export declare type TemplateNode = Text | MustacheTag | RawMustacheTag | DebugTag | ConstTag | Directive | StyleDirective | Element | InlineComponent | Window | Document | Body | Head | Title | Options | SlotTemplate | Slot | Comment | IfBlock | EachBlock | AwaitBlock | KeyBlock;
export interface Fragment extends BaseNode {
type: "Fragment";
children: TemplateNode[];
}
export interface Text extends BaseNode {
type: "Text";
data: string;
}
export interface MustacheTag extends BaseNode {
type: "MustacheTag";
expression: ESTree.Expression;
}
export interface RawMustacheTag extends BaseNode {
type: "RawMustacheTag";
expression: ESTree.Expression;
}
export interface DebugTag extends BaseNode {
type: "DebugTag";
identifiers: ESTree.Identifier[];
}
export interface ConstTag extends BaseNode {
type: "ConstTag";
expression: ESTree.AssignmentExpression;
}
export interface IfBlock extends BaseNode {
type: "IfBlock";
expression: ESTree.Expression;
children: TemplateNode[];
else: ElseBlock | undefined;
elseif: true | undefined;
}
export interface ElseBlock extends BaseNode {
type: "ElseBlock";
children: TemplateNode[] | [IfBlock & {
elseif: true;
}];
}
export interface EachBlock extends BaseNode {
type: "EachBlock";
expression: ESTree.Expression;
context: ESTree.Pattern;
index?: string;
key: ESTree.Expression | undefined;
children: TemplateNode[];
else: ElseBlock | undefined;
}
export interface AwaitBlock extends BaseNode {
type: "AwaitBlock";
expression: ESTree.Expression;
pending: PendingBlock;
value: ESTree.Pattern | null;
then: ThenBlock;
error: ESTree.Pattern | null;
catch: CatchBlock;
}
export interface PendingBlock extends BaseNode {
type: "PendingBlock";
skip: boolean;
children: TemplateNode[];
}
export interface ThenBlock extends BaseNode {
type: "ThenBlock";
skip: boolean;
children: TemplateNode[];
}
export interface CatchBlock extends BaseNode {
type: "CatchBlock";
skip: boolean;
children: TemplateNode[];
}
export interface KeyBlock extends BaseNode {
type: "KeyBlock";
expression: ESTree.Expression;
children: TemplateNode[];
}
export interface BaseElement extends BaseNode {
type: "Element";
name: string;
children: TemplateNode[];
attributes: AttributeOrDirective[];
}
export interface BasicElement extends BaseElement {
tag?: undefined;
}
export interface SvelteElement extends BaseElement {
name: "svelte:element";
tag: ESTree.Expression | string;
}
export type Element = BasicElement | SvelteElement;
export interface BaseInlineComponent extends BaseNode {
type: "InlineComponent";
name: string;
children: TemplateNode[];
attributes: AttributeOrDirective[];
}
export interface Window extends BaseNode {
type: "Window";
name: "svelte:window";
children: TemplateNode[];
attributes: AttributeOrDirective[];
}
export interface Document extends BaseNode {
type: "Document";
name: "svelte:document";
children: TemplateNode[];
attributes: AttributeOrDirective[];
}
export interface Body extends BaseNode {
type: "Body";
name: "svelte:body";
children: TemplateNode[];
attributes: AttributeOrDirective[];
}
export interface Head extends BaseNode {
type: "Head";
name: "svelte:head";
children: TemplateNode[];
attributes: AttributeOrDirective[];
}
export interface Title extends BaseNode {
type: "Title";
name: "title";
children: TemplateNode[];
attributes: AttributeOrDirective[];
}
export interface Options extends BaseNode {
type: "Options";
name: "svelte:options";
children?: TemplateNode[];
attributes: AttributeOrDirective[];
}
export interface SlotTemplate extends BaseNode {
type: "SlotTemplate";
name: "svelte:fragment";
children: TemplateNode[];
attributes: AttributeOrDirective[];
}
export interface BasicInlineComponent extends BaseInlineComponent {
expression?: undefined;
}
export interface InlineSvelteComponent extends BaseInlineComponent {
name: "svelte:component";
expression: ESTree.Expression;
}
export type InlineComponent = BasicInlineComponent | InlineSvelteComponent;
export interface Slot extends BaseNode {
type: "Slot";
name: "slot";
children: TemplateNode[];
attributes: AttributeOrDirective[];
}
export interface Comment extends BaseNode {
type: "Comment";
data: string;
}
export interface Attribute extends BaseNode {
type: "Attribute";
name: string;
value: (Text | MustacheTag)[] | [AttributeShorthand] | true;
}
export interface Spread extends BaseNode {
type: "Spread";
expression: ESTree.Expression;
}
export interface AttributeShorthand extends BaseNode {
type: "AttributeShorthand";
expression: ESTree.Identifier;
}
export type AttributeOrDirective = Attribute | Spread | Directive | StyleDirective;
interface BaseDirective extends BaseNode {
name: string;
modifiers: string[];
}
export interface DirectiveForExpression extends BaseDirective {
type: "Action" | "Animation" | "Binding" | "Class" | "EventHandler" | "Ref";
expression: null | ESTree.Expression;
}
export interface LetDirective extends BaseDirective {
type: "Let";
expression: null | ESTree.Pattern;
}
export interface TransitionDirective extends BaseDirective {
type: "Transition";
intro: boolean;
outro: boolean;
expression: null | ESTree.Expression;
}
export interface StyleDirective extends BaseDirective {
type: "StyleDirective";
value: (Text | MustacheTag)[] | true;
}
export type Directive = DirectiveForExpression | TransitionDirective | LetDirective;
export interface Script extends BaseNode {
type: "Script";
context: string;
children: TemplateNode[];
}
export interface Style extends BaseNode {
type: "Style";
}
export {};

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,20 @@
import type * as Compiler from "./svelte-ast-types-for-v5";
import type * as SvAST from "./svelte-ast-types";
import type { NormalizedParserOptions } from "./parser-options";
import type { SvelteConfig } from "../svelte-config";
/** The context for parsing. */
export type SvelteParseContext = {
/**
* Whether to use Runes mode.
* May be `true` if the user is using Svelte v5.
* Resolved from `svelte.config.js` or `parserOptions`, but may be overridden by `<svelte:options>`.
*/
runes: boolean;
/** The version of "svelte/compiler". */
compilerVersion: string;
/** The result of static analysis of `svelte.config.js`. */
svelteConfig: SvelteConfig | null;
};
export declare function isEnableRunes(svelteConfig: SvelteConfig | null, parserOptions: NormalizedParserOptions): boolean;
export declare function resolveSvelteParseContextForSvelte(svelteConfig: SvelteConfig | null, parserOptions: NormalizedParserOptions, svelteAst: Compiler.Root | SvAST.AstLegacy): SvelteParseContext;
export declare function resolveSvelteParseContextForSvelteScript(svelteConfig: SvelteConfig | null, parserOptions: NormalizedParserOptions): SvelteParseContext;

View File

@@ -0,0 +1,43 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isEnableRunes = isEnableRunes;
exports.resolveSvelteParseContextForSvelte = resolveSvelteParseContextForSvelte;
exports.resolveSvelteParseContextForSvelteScript = resolveSvelteParseContextForSvelteScript;
const svelte_version_1 = require("./svelte-version");
function isEnableRunes(svelteConfig, parserOptions) {
var _a, _b;
if (!svelte_version_1.svelteVersion.gte(5))
return false;
if (((_a = parserOptions.svelteFeatures) === null || _a === void 0 ? void 0 : _a.runes) != null) {
return Boolean(parserOptions.svelteFeatures.runes);
}
if (((_b = svelteConfig === null || svelteConfig === void 0 ? void 0 : svelteConfig.compilerOptions) === null || _b === void 0 ? void 0 : _b.runes) != null) {
return Boolean(svelteConfig.compilerOptions.runes);
}
return true;
}
function resolveSvelteParseContextForSvelte(svelteConfig, parserOptions, svelteAst) {
const svelteOptions = svelteAst.options;
if ((svelteOptions === null || svelteOptions === void 0 ? void 0 : svelteOptions.runes) != null) {
return {
runes: svelteOptions.runes,
compilerVersion: svelte_version_1.compilerVersion,
svelteConfig,
};
}
return {
runes: isEnableRunes(svelteConfig, parserOptions),
compilerVersion: svelte_version_1.compilerVersion,
svelteConfig,
};
}
function resolveSvelteParseContextForSvelteScript(svelteConfig, parserOptions) {
return resolveSvelteParseContext(svelteConfig, parserOptions);
}
function resolveSvelteParseContext(svelteConfig, parserOptions) {
return {
runes: isEnableRunes(svelteConfig, parserOptions),
compilerVersion: svelte_version_1.compilerVersion,
svelteConfig,
};
}

View File

@@ -0,0 +1,5 @@
import { VERSION as compilerVersion } from "svelte/compiler";
export { compilerVersion };
export declare const svelteVersion: {
gte(v: number): boolean;
};

View File

@@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.svelteVersion = exports.compilerVersion = void 0;
const compiler_1 = require("svelte/compiler");
Object.defineProperty(exports, "compilerVersion", { enumerable: true, get: function () { return compiler_1.VERSION; } });
const verStrings = compiler_1.VERSION.split(".");
exports.svelteVersion = {
gte(v) {
return Number(verStrings[0]) >= v;
},
};

View File

@@ -0,0 +1,12 @@
import type * as Compiler from "./svelte-ast-types-for-v5";
import type * as SvAST from "./svelte-ast-types";
import type { Context } from "../context";
import type { SvelteProgram } from "../ast";
import type { NormalizedParserOptions } from "./parser-options";
/**
* Parse for template
*/
export declare function parseTemplate(code: string, ctx: Context, parserOptions: NormalizedParserOptions): {
ast: SvelteProgram;
svelteAst: Compiler.Root | SvAST.AstLegacy;
};

View File

@@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseTemplate = parseTemplate;
const compiler_1 = require("svelte/compiler");
const index_1 = require("./converts/index");
const __1 = require("..");
const svelte_version_1 = require("./svelte-version");
/**
* Parse for template
*/
function parseTemplate(code, ctx, parserOptions) {
try {
const options = Object.assign({ filename: parserOptions.filePath }, (svelte_version_1.svelteVersion.gte(5) ? { modern: true } : {}));
const svelteAst = (0, compiler_1.parse)(code, options);
const ast = (0, index_1.convertSvelteRoot)(svelteAst, ctx);
return {
ast,
svelteAst,
};
}
catch (e) {
if (typeof e.pos === "number") {
const err = new __1.ParseError(e.message, e.pos, ctx);
err.svelteCompilerError = e;
throw err;
}
throw e;
}
}

View File

@@ -0,0 +1,24 @@
import { VirtualTypeScriptContext } from "../context";
import type { SvelteHTMLElement } from "../../../ast";
import type { NormalizedParserOptions } from "../../parser-options";
import type { SvelteParseContext } from "../../svelte-parse-context";
export type AnalyzeTypeScriptContext = {
slots: Set<SvelteHTMLElement>;
svelteParseContext: SvelteParseContext;
};
/**
* Analyze TypeScript source code in <script>.
* Generate virtual code to provide correct type information for Svelte store reference names, scopes, and runes.
* See https://github.com/sveltejs/svelte-eslint-parser/blob/main/docs/internal-mechanism.md#scope-types
*/
export declare function analyzeTypeScriptInSvelte(code: {
script: string;
rootScope: string;
render: string;
}, attrs: Record<string, string | undefined>, parserOptions: NormalizedParserOptions, context: AnalyzeTypeScriptContext): VirtualTypeScriptContext;
/**
* Analyze TypeScript source code.
* Generate virtual code to provide correct type information for Svelte runes.
* See https://github.com/sveltejs/svelte-eslint-parser/blob/main/docs/internal-mechanism.md#scope-types
*/
export declare function analyzeTypeScript(code: string, attrs: Record<string, string | undefined>, parserOptions: NormalizedParserOptions, svelteParseContext: SvelteParseContext): VirtualTypeScriptContext;

View File

@@ -0,0 +1,739 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.analyzeTypeScriptInSvelte = analyzeTypeScriptInSvelte;
exports.analyzeTypeScript = analyzeTypeScript;
const scope_1 = require("../../../scope");
const utils_1 = require("../../../utils");
const script_1 = require("../../script");
const context_1 = require("../context");
const set_parent_1 = require("../set-parent");
const globals_1 = require("../../globals");
/**
* Analyze TypeScript source code in <script>.
* Generate virtual code to provide correct type information for Svelte store reference names, scopes, and runes.
* See https://github.com/sveltejs/svelte-eslint-parser/blob/main/docs/internal-mechanism.md#scope-types
*/
function analyzeTypeScriptInSvelte(code, attrs, parserOptions, context) {
const ctx = new context_1.VirtualTypeScriptContext(code.script + code.render + code.rootScope);
ctx.appendOriginal(/^\s*/u.exec(code.script)[0].length);
const result = (0, script_1.parseScriptWithoutAnalyzeScope)(code.script + code.render + code.rootScope, attrs, Object.assign(Object.assign({}, parserOptions), {
// Without typings
project: null }));
ctx._beforeResult = result;
analyzeStoreReferenceNames(result, context.svelteParseContext, ctx);
analyzeDollarDollarVariables(result, ctx, context.svelteParseContext, context.slots);
analyzeRuneVariables(result, ctx, context.svelteParseContext);
applyTransforms([
...analyzeReactiveScopes(result),
...analyzeDollarDerivedScopes(result, context.svelteParseContext),
], ctx);
analyzeRenderScopes(code, ctx);
ctx.appendOriginalToEnd();
return ctx;
}
/**
* Analyze TypeScript source code.
* Generate virtual code to provide correct type information for Svelte runes.
* See https://github.com/sveltejs/svelte-eslint-parser/blob/main/docs/internal-mechanism.md#scope-types
*/
function analyzeTypeScript(code, attrs, parserOptions, svelteParseContext) {
const ctx = new context_1.VirtualTypeScriptContext(code);
ctx.appendOriginal(/^\s*/u.exec(code)[0].length);
const result = (0, script_1.parseScriptWithoutAnalyzeScope)(code, attrs, Object.assign(Object.assign({}, parserOptions), {
// Without typings
project: null }));
ctx._beforeResult = result;
analyzeRuneVariables(result, ctx, svelteParseContext);
applyTransforms([...analyzeDollarDerivedScopes(result, svelteParseContext)], ctx);
ctx.appendOriginalToEnd();
return ctx;
}
/**
* Analyze the store reference names.
* Insert type definitions code to provide correct type information for variables that begin with `$`.
*/
function analyzeStoreReferenceNames(result, svelteParseContext, ctx) {
const globals = (0, globals_1.getGlobalsForSvelte)(svelteParseContext);
const scopeManager = result.scopeManager;
const programScope = (0, scope_1.getProgramScope)(scopeManager);
const maybeStoreRefNames = new Set();
for (const reference of scopeManager.globalScope.through) {
if (
// Begin with `$`.
reference.identifier.name.startsWith("$") &&
// Ignore globals
!globals.includes(reference.identifier.name) &&
// Ignore if it is already defined.
!programScope.set.has(reference.identifier.name)) {
maybeStoreRefNames.add(reference.identifier.name);
}
}
if (maybeStoreRefNames.size) {
const storeValueTypeName = ctx.generateUniqueId("StoreValueType");
ctx.appendVirtualScript(`type ${storeValueTypeName}<T> = T extends null | undefined
? T
: T extends object & { subscribe(run: infer F, ...args: any): any }
? F extends (value: infer V, ...args: any) => any
? V
: never
: T;`);
ctx.restoreContext.addRestoreStatementProcess((node, result) => {
if (node.type !== "TSTypeAliasDeclaration" ||
node.id.name !== storeValueTypeName) {
return false;
}
const program = result.ast;
program.body.splice(program.body.indexOf(node), 1);
const scopeManager = result.scopeManager;
// Remove `type` scope
(0, scope_1.removeAllScopeAndVariableAndReference)(node, {
visitorKeys: result.visitorKeys,
scopeManager,
});
return true;
});
for (const nm of maybeStoreRefNames) {
const realName = nm.slice(1);
ctx.appendVirtualScript(`declare let ${nm}: ${storeValueTypeName}<typeof ${realName}>;`);
ctx.restoreContext.addRestoreStatementProcess((node, result) => {
if (node.type !== "VariableDeclaration" ||
!node.declare ||
node.declarations.length !== 1 ||
node.declarations[0].id.type !== "Identifier" ||
node.declarations[0].id.name !== nm) {
return false;
}
const program = result.ast;
program.body.splice(program.body.indexOf(node), 1);
const scopeManager = result.scopeManager;
// Remove `declare` variable
(0, scope_1.removeAllScopeAndVariableAndReference)(node, {
visitorKeys: result.visitorKeys,
scopeManager,
});
return true;
});
}
}
}
/**
* Analyze `$$slots`, `$$props`, and `$$restProps` .
* Insert type definitions code to provide correct type information for `$$slots`, `$$props`, and `$$restProps`.
*/
function analyzeDollarDollarVariables(result, ctx, svelteParseContext, slots) {
const globals = (0, globals_1.getGlobalsForSvelte)(svelteParseContext);
const scopeManager = result.scopeManager;
for (const globalName of globals) {
if (!scopeManager.globalScope.through.some((reference) => reference.identifier.name === globalName)) {
continue;
}
switch (globalName) {
case "$$props":
appendDeclareVirtualScript(globalName, `{ [index: string]: any }`);
break;
case "$$restProps":
appendDeclareVirtualScript(globalName, `{ [index: string]: any }`);
break;
case "$$slots": {
const nameTypes = new Set();
for (const slot of slots) {
const nameAttr = slot.startTag.attributes.find((attr) => attr.type === "SvelteAttribute" && attr.key.name === "name");
if (!nameAttr || nameAttr.value.length === 0) {
nameTypes.add('"default"');
continue;
}
if (nameAttr.value.length === 1) {
const value = nameAttr.value[0];
if (value.type === "SvelteLiteral") {
nameTypes.add(JSON.stringify(value.value));
}
else {
nameTypes.add("string");
}
continue;
}
nameTypes.add(`\`${nameAttr.value
.map((value) => value.type === "SvelteLiteral"
? value.value.replace(/([$`])/gu, "\\$1")
: "${string}")
.join("")}\``);
}
appendDeclareVirtualScript(globalName, `Record<${nameTypes.size > 0 ? [...nameTypes].join(" | ") : "any"}, boolean>`);
break;
}
case "$state":
case "$derived":
case "$effect":
case "$props":
case "$bindable":
case "$inspect":
case "$host":
// Processed by `analyzeRuneVariables`.
break;
default: {
const _ = globalName;
throw Error(`Unknown global: ${_}`);
}
}
}
/** Append declare virtual script */
function appendDeclareVirtualScript(name, type) {
ctx.appendVirtualScript(`declare let ${name}: ${type};`);
ctx.restoreContext.addRestoreStatementProcess((node, result) => {
if (node.type !== "VariableDeclaration" ||
!node.declare ||
node.declarations.length !== 1 ||
node.declarations[0].id.type !== "Identifier" ||
node.declarations[0].id.name !== name) {
return false;
}
const program = result.ast;
program.body.splice(program.body.indexOf(node), 1);
const scopeManager = result.scopeManager;
// Remove `declare` variable
(0, scope_1.removeAllScopeAndVariableAndReference)(node, {
visitorKeys: result.visitorKeys,
scopeManager,
});
return true;
});
}
}
/**
* Analyze Runes.
* Insert type definitions code to provide correct type information for Runes.
*/
function analyzeRuneVariables(result, ctx, svelteParseContext) {
if (!svelteParseContext.runes)
return;
const scopeManager = result.scopeManager;
for (const globalName of globals_1.globalsForRunes) {
if (!scopeManager.globalScope.through.some((reference) => reference.identifier.name === globalName)) {
continue;
}
switch (globalName) {
// See https://github.com/sveltejs/svelte/blob/41b5cd6f5daae3970a9927e062f42b6b62440d16/packages/svelte/types/index.d.ts#L2299
case "$state": {
appendDeclareFunctionVirtualScripts(globalName, [
"<T>(initial: T): T",
"<T>(): T | undefined",
]);
appendDeclareNamespaceVirtualScripts(globalName, [
"export function raw<T>(initial: T): T;",
"export function raw<T>(): T | undefined;",
]);
appendDeclareNamespaceVirtualScripts(globalName, [
"export function snapshot<T>(state: T): T;",
]);
break;
}
// See https://github.com/sveltejs/svelte/blob/41b5cd6f5daae3970a9927e062f42b6b62440d16/packages/svelte/types/index.d.ts#L2453
case "$derived": {
appendDeclareFunctionVirtualScripts(globalName, [
"<T>(expression: T): T",
]);
appendDeclareNamespaceVirtualScripts(globalName, [
"export function by<T>(fn: () => T): T;",
]);
break;
}
// See https://github.com/sveltejs/svelte/blob/41b5cd6f5daae3970a9927e062f42b6b62440d16/packages/svelte/types/index.d.ts#L2513
case "$effect": {
appendDeclareFunctionVirtualScripts(globalName, [
"(fn: () => void | (() => void)): void",
]);
appendDeclareNamespaceVirtualScripts(globalName, [
"export function pre(fn: () => void | (() => void)): void;",
"export function tracking(): boolean;",
"export function root(fn: () => void | (() => void)): () => void;",
]);
break;
}
// See https://github.com/sveltejs/svelte/blob/41b5cd6f5daae3970a9927e062f42b6b62440d16/packages/svelte/types/index.d.ts#L2615
case "$props": {
// Use type parameters to avoid `@typescript-eslint/no-unsafe-assignment` errors.
appendDeclareFunctionVirtualScripts(globalName, ["<T>(): T"]);
break;
}
// See https://github.com/sveltejs/svelte/blob/41b5cd6f5daae3970a9927e062f42b6b62440d16/packages/svelte/types/index.d.ts#L2626
case "$bindable": {
appendDeclareFunctionVirtualScripts(globalName, [
"<T>(fallback?: T): T",
]);
break;
}
// See https://github.com/sveltejs/svelte/blob/41b5cd6f5daae3970a9927e062f42b6b62440d16/packages/svelte/types/index.d.ts#L2646
case "$inspect": {
appendDeclareFunctionVirtualScripts(globalName, [
`<T extends any[]>(...values: T): { with: (fn: (type: 'init' | 'update', ...values: T) => void) => void }`,
]);
break;
}
// See https://github.com/sveltejs/svelte/blob/41b5cd6f5daae3970a9927e062f42b6b62440d16/packages/svelte/types/index.d.ts#L2669
case "$host": {
appendDeclareFunctionVirtualScripts(globalName, [
`<El extends HTMLElement = HTMLElement>(): El`,
]);
break;
}
default: {
const _ = globalName;
throw Error(`Unknown global: ${_}`);
}
}
}
/** Append declare virtual script */
function appendDeclareFunctionVirtualScripts(name, types) {
for (const type of types) {
ctx.appendVirtualScript(`declare function ${name}${type};`);
ctx.restoreContext.addRestoreStatementProcess((node, result) => {
var _a;
if (node.type !== "TSDeclareFunction" ||
!node.declare ||
((_a = node.id) === null || _a === void 0 ? void 0 : _a.type) !== "Identifier" ||
node.id.name !== name) {
return false;
}
const program = result.ast;
program.body.splice(program.body.indexOf(node), 1);
const scopeManager = result.scopeManager;
// Remove `declare` variable
(0, scope_1.removeAllScopeAndVariableAndReference)(node, {
visitorKeys: result.visitorKeys,
scopeManager,
});
return true;
});
}
}
function appendDeclareNamespaceVirtualScripts(name, scripts) {
for (const script of scripts) {
ctx.appendVirtualScript(`declare namespace ${name} { ${script} }`);
ctx.restoreContext.addRestoreStatementProcess((node, result) => {
var _a;
if (node.type !== "TSModuleDeclaration" ||
!node.declare ||
((_a = node.id) === null || _a === void 0 ? void 0 : _a.type) !== "Identifier" ||
node.id.name !== name) {
return false;
}
const program = result.ast;
program.body.splice(program.body.indexOf(node), 1);
const scopeManager = result.scopeManager;
// Remove `declare` variable
(0, scope_1.removeAllScopeAndVariableAndReference)(node, {
visitorKeys: result.visitorKeys,
scopeManager,
});
return true;
});
}
}
}
/**
* Analyze the reactive scopes.
* Transform source code to provide the correct type information in the `$:` statements.
*/
function* analyzeReactiveScopes(result) {
const scopeManager = result.scopeManager;
const throughIds = scopeManager.globalScope.through.map((reference) => reference.identifier);
for (const statement of result.ast.body) {
if (statement.type === "LabeledStatement" && statement.label.name === "$") {
if (statement.body.type === "ExpressionStatement" &&
statement.body.expression.type === "AssignmentExpression" &&
statement.body.expression.operator === "=" &&
// Must be a pattern that can be used in the LHS of variable declarations.
// https://github.com/sveltejs/svelte-eslint-parser/issues/213
(statement.body.expression.left.type === "Identifier" ||
statement.body.expression.left.type === "ArrayPattern" ||
statement.body.expression.left.type === "ObjectPattern")) {
const left = statement.body.expression.left;
if (throughIds.some((id) => left.range[0] <= id.range[0] && id.range[1] <= left.range[1])) {
const node = statement;
const expression = statement.body.expression;
yield {
node,
transform: (ctx) => transformForDeclareReactiveVar(node, left, expression, result.ast.tokens, ctx),
};
continue;
}
}
yield {
node: statement,
transform: (ctx) => transformForReactiveStatement(statement, ctx),
};
}
}
}
/**
* Analyze the $derived scopes.
* Transform source code to provide the correct type information in the `$derived(...)` expression.
*/
function* analyzeDollarDerivedScopes(result, svelteParseContext) {
var _a;
if (!svelteParseContext.runes)
return;
const scopeManager = result.scopeManager;
const derivedReferences = scopeManager.globalScope.through.filter((reference) => reference.identifier.name === "$derived");
if (!derivedReferences.length) {
return;
}
(0, set_parent_1.setParent)(result);
for (const ref of derivedReferences) {
const derived = ref.identifier;
if (derived.parent.type === "CallExpression" &&
derived.parent.callee === derived &&
((_a = derived.parent.arguments[0]) === null || _a === void 0 ? void 0 : _a.type) !== "SpreadElement") {
const node = derived.parent;
yield {
node,
transform: (ctx) => transformForDollarDerived(node, ctx),
};
}
}
}
/**
* Analyze the render scopes.
* Transform source code to provide the correct type information in the HTML templates.
*/
function analyzeRenderScopes(code, ctx) {
ctx.appendOriginal(code.script.length);
const renderFunctionName = ctx.generateUniqueId("render");
ctx.appendVirtualScript(`function ${renderFunctionName}(){`);
ctx.appendOriginal(code.script.length + code.render.length);
ctx.appendVirtualScript(`}`);
ctx.restoreContext.addRestoreStatementProcess((node, result) => {
if (node.type !== "FunctionDeclaration" ||
node.id.name !== renderFunctionName) {
return false;
}
const program = result.ast;
program.body.splice(program.body.indexOf(node), 1, ...node.body.body);
for (const body of node.body.body) {
body.parent = program;
}
const scopeManager = result.scopeManager;
removeFunctionScope(node, scopeManager);
return true;
});
}
/**
* Applies the given transforms.
* Note that intersecting transformations are not applied.
*/
function applyTransforms(transforms, ctx) {
transforms.sort((a, b) => a.node.range[0] - b.node.range[0]);
let offset = 0;
for (const transform of transforms) {
const range = transform.node.range;
if (offset <= range[0]) {
transform.transform(ctx);
}
offset = range[1];
}
}
/**
* Transform for `$: id = ...` to `$: let id = ...`
*/
function transformForDeclareReactiveVar(statement, id, expression, tokens, ctx) {
// e.g.
// From:
// $: id = x + y;
//
// To:
// $: let id = fn()
// function fn () { let tmp; return (tmp = x + y); }
//
//
// From:
// $: ({id} = foo);
//
// To:
// $: let {id} = fn()
// function fn () { let tmp; return (tmp = foo); }
/**
* The opening paren tokens for
* `$: ({id} = foo);`
* ^
*/
const openParens = [];
/**
* The equal token for
* `$: ({id} = foo);`
* ^
*/
let eq = null;
/**
* The closing paren tokens for
* `$: ({id} = foo);`
* ^
*/
const closeParens = [];
/**
* The closing paren token for
* `$: id = (foo);`
* ^
*/
let expressionCloseParen = null;
const startIndex = (0, utils_1.sortedLastIndex)(tokens, (target) => target.range[0] - statement.range[0]);
for (let index = startIndex; index < tokens.length; index++) {
const token = tokens[index];
if (statement.range[1] <= token.range[0]) {
break;
}
if (token.range[1] <= statement.range[0]) {
continue;
}
if (token.value === "(" && token.range[1] <= expression.range[0]) {
openParens.push(token);
}
if (token.value === "=" &&
expression.left.range[1] <= token.range[0] &&
token.range[1] <= expression.right.range[0]) {
eq = token;
}
if (token.value === ")") {
if (expression.range[1] <= token.range[0]) {
closeParens.push(token);
}
else if (expression.right.range[1] <= token.range[0]) {
expressionCloseParen = token;
}
}
}
const functionId = ctx.generateUniqueId("reactiveVariableScopeFunction");
const tmpVarId = ctx.generateUniqueId("tmpVar");
for (const token of openParens) {
ctx.appendOriginal(token.range[0]);
ctx.skipOriginalOffset(token.range[1] - token.range[0]);
}
ctx.appendOriginal(expression.range[0]);
ctx.skipUntilOriginalOffset(id.range[0]);
ctx.appendVirtualScript("let ");
ctx.appendOriginal(eq ? eq.range[1] : expression.right.range[0]);
ctx.appendVirtualScript(`${functionId}();\nfunction ${functionId}(){let ${tmpVarId};return (${tmpVarId} = `);
ctx.appendOriginal(expression.right.range[1]);
ctx.appendVirtualScript(`)`);
for (const token of closeParens) {
ctx.appendOriginal(token.range[0]);
ctx.skipOriginalOffset(token.range[1] - token.range[0]);
}
ctx.appendOriginal(statement.range[1]);
ctx.appendVirtualScript(`}`);
ctx.restoreContext.addRestoreStatementProcess((node, result) => {
var _a;
if (node.type !== "SvelteReactiveStatement") {
return false;
}
const reactiveStatement = node;
if (reactiveStatement.body.type !== "VariableDeclaration" ||
reactiveStatement.body.kind !== "let" ||
reactiveStatement.body.declarations.length !== 1) {
return false;
}
const [idDecl] = reactiveStatement.body.declarations;
if (idDecl.type !== "VariableDeclarator" ||
idDecl.id.type !== id.type ||
((_a = idDecl.init) === null || _a === void 0 ? void 0 : _a.type) !== "CallExpression" ||
idDecl.init.callee.type !== "Identifier" ||
idDecl.init.callee.name !== functionId) {
return false;
}
const program = result.ast;
const nextIndex = program.body.indexOf(node) + 1;
const fnDecl = program.body[nextIndex];
if (!fnDecl ||
fnDecl.type !== "FunctionDeclaration" ||
fnDecl.id.name !== functionId ||
fnDecl.body.body.length !== 2 ||
fnDecl.body.body[0].type !== "VariableDeclaration" ||
fnDecl.body.body[1].type !== "ReturnStatement") {
return false;
}
const tmpVarDeclaration = fnDecl.body.body[0];
if (tmpVarDeclaration.declarations.length !== 1 ||
tmpVarDeclaration.declarations[0].type !== "VariableDeclarator") {
return false;
}
const tempVarDeclId = tmpVarDeclaration.declarations[0].id;
if (tempVarDeclId.type !== "Identifier" ||
tempVarDeclId.name !== tmpVarId) {
return false;
}
const returnStatement = fnDecl.body.body[1];
const assignment = returnStatement.argument;
if ((assignment === null || assignment === void 0 ? void 0 : assignment.type) !== "AssignmentExpression" ||
assignment.left.type !== "Identifier" ||
assignment.right.type !== expression.right.type) {
return false;
}
const tempLeft = assignment.left;
// Remove function declaration
program.body.splice(nextIndex, 1);
// Restore expression statement
assignment.left = idDecl.id;
assignment.loc = {
start: idDecl.id.loc.start,
end: expressionCloseParen
? expressionCloseParen.loc.end
: assignment.right.loc.end,
};
assignment.range = [
idDecl.id.range[0],
expressionCloseParen
? expressionCloseParen.range[1]
: assignment.right.range[1],
];
idDecl.id.parent = assignment;
const newBody = {
type: "ExpressionStatement",
expression: assignment,
directive: undefined,
loc: statement.body.loc,
range: statement.body.range,
parent: reactiveStatement,
};
assignment.parent = newBody;
reactiveStatement.body = newBody;
// Restore statement end location
reactiveStatement.range[1] = returnStatement.range[1];
reactiveStatement.loc.end.line = returnStatement.loc.end.line;
reactiveStatement.loc.end.column = returnStatement.loc.end.column;
// Restore tokens
(0, utils_1.addElementsToSortedArray)(program.tokens, [...openParens, ...closeParens], (a, b) => a.range[0] - b.range[0]);
const scopeManager = result.scopeManager;
(0, scope_1.removeAllScopeAndVariableAndReference)(tmpVarDeclaration, {
visitorKeys: result.visitorKeys,
scopeManager,
});
removeFunctionScope(fnDecl, scopeManager);
const scope = (0, scope_1.getProgramScope)(scopeManager);
for (const reference of (0, scope_1.getAllReferences)(idDecl.id, scope)) {
reference.writeExpr = assignment.right;
}
(0, scope_1.removeIdentifierReference)(tempLeft, scope);
(0, scope_1.removeIdentifierVariable)(tempVarDeclId, scope);
(0, scope_1.removeIdentifierReference)(idDecl.init.callee, scope);
(0, scope_1.removeIdentifierVariable)(idDecl.id, scope);
return true;
});
}
/**
* Transform for `$: ...` to `$: function foo(){...}`
*/
function transformForReactiveStatement(statement, ctx) {
const functionId = ctx.generateUniqueId("reactiveStatementScopeFunction");
const originalBody = statement.body;
ctx.appendOriginal(originalBody.range[0]);
ctx.appendVirtualScript(`function ${functionId}(){`);
ctx.appendOriginal(originalBody.range[1]);
ctx.appendVirtualScript(`}`);
ctx.appendOriginal(statement.range[1]);
ctx.restoreContext.addRestoreStatementProcess((node, result) => {
if (node.type !== "SvelteReactiveStatement") {
return false;
}
const reactiveStatement = node;
const body = reactiveStatement.body;
if (body.type !== "FunctionDeclaration" || body.id.name !== functionId) {
return false;
}
reactiveStatement.body = body.body.body[0];
reactiveStatement.body.parent = reactiveStatement;
const scopeManager = result.scopeManager;
removeFunctionScope(body, scopeManager);
return true;
});
}
/**
* Transform for `$derived(expr)` to `$derived((()=>{ return fn(); function fn () { return expr } })())`
*/
function transformForDollarDerived(derivedCall, ctx) {
const functionId = ctx.generateUniqueId("$derivedArgument");
const expression = derivedCall.arguments[0];
ctx.appendOriginal(expression.range[0]);
ctx.appendVirtualScript(`(()=>{return ${functionId}();function ${functionId}(){return `);
ctx.appendOriginal(expression.range[1]);
ctx.appendVirtualScript(`}})()`);
ctx.restoreContext.addRestoreExpressionProcess({
target: "CallExpression",
restore: (node, result) => {
var _a;
if (node.callee.type !== "Identifier" ||
node.callee.name !== "$derived") {
return false;
}
const arg = node.arguments[0];
if (!arg ||
arg.type !== "CallExpression" ||
arg.arguments.length !== 0 ||
arg.callee.type !== "ArrowFunctionExpression" ||
arg.callee.body.type !== "BlockStatement" ||
arg.callee.body.body.length !== 2 ||
arg.callee.body.body[0].type !== "ReturnStatement" ||
((_a = arg.callee.body.body[0].argument) === null || _a === void 0 ? void 0 : _a.type) !== "CallExpression" ||
arg.callee.body.body[0].argument.callee.type !== "Identifier" ||
arg.callee.body.body[0].argument.callee.name !== functionId ||
arg.callee.body.body[1].type !== "FunctionDeclaration" ||
arg.callee.body.body[1].id.name !== functionId) {
return false;
}
const fnNode = arg.callee.body.body[1];
if (fnNode.body.body.length !== 1 ||
fnNode.body.body[0].type !== "ReturnStatement" ||
!fnNode.body.body[0].argument) {
return false;
}
const expr = fnNode.body.body[0].argument;
node.arguments[0] = expr;
expr.parent = node;
const scopeManager = result.scopeManager;
removeFunctionScope(arg.callee.body.body[1], scopeManager);
(0, scope_1.removeIdentifierReference)(arg.callee.body.body[0].argument.callee, scopeManager.acquire(arg.callee));
removeFunctionScope(arg.callee, scopeManager);
return true;
},
});
}
/** Remove function scope and marge child scopes to upper scope */
function removeFunctionScope(node, scopeManager) {
const scope = scopeManager.acquire(node);
const upper = scope.upper;
// Remove render function variable
if (node.id) {
(0, scope_1.removeIdentifierVariable)(node.id, upper);
(0, scope_1.removeIdentifierReference)(node.id, upper);
}
(0, scope_1.replaceScope)(scopeManager, scope, scope.childScopes);
// Marge scope
// * marge variables
for (const variable of scope.variables) {
if (variable.name === "arguments" && variable.defs.length === 0) {
continue;
}
const upperVariable = upper.set.get(variable.name);
if (upperVariable) {
(0, utils_1.addElementsToSortedArray)(upperVariable.identifiers, variable.identifiers, (a, b) => a.range[0] - b.range[0]);
(0, utils_1.addElementsToSortedArray)(upperVariable.defs, variable.defs, (a, b) => a.node.range[0] - b.node.range[0]);
(0, scope_1.addAllReferences)(upperVariable.references, variable.references);
}
else {
upper.set.set(variable.name, variable);
(0, scope_1.addVariable)(upper.variables, variable);
variable.scope = upper;
}
for (const reference of variable.references) {
if (reference.from === scope) {
reference.from = upper;
}
reference.resolved = upperVariable || variable;
}
}
// * marge references
(0, scope_1.addAllReferences)(upper.references, scope.references);
for (const reference of scope.references) {
if (reference.from === scope) {
reference.from = upper;
}
}
}

View File

@@ -0,0 +1,21 @@
import { RestoreContext } from "./restore";
import type { TSESParseForESLintResult } from "./types";
/**
* Context for virtual TypeScript code.
* See https://github.com/sveltejs/svelte-eslint-parser/blob/main/docs/internal-mechanism.md#scope-types
*/
export declare class VirtualTypeScriptContext {
private readonly originalCode;
readonly restoreContext: RestoreContext;
script: string;
private consumedIndex;
private readonly unique;
_beforeResult: TSESParseForESLintResult | null;
constructor(code: string);
skipOriginalOffset(offset: number): void;
skipUntilOriginalOffset(offset: number): void;
appendOriginalToEnd(): void;
appendOriginal(index: number): void;
appendVirtualScript(virtualFragment: string): void;
generateUniqueId(base: string): string;
}

View File

@@ -0,0 +1,48 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.VirtualTypeScriptContext = void 0;
const unique_1 = require("../../context/unique");
const restore_1 = require("./restore");
/**
* Context for virtual TypeScript code.
* See https://github.com/sveltejs/svelte-eslint-parser/blob/main/docs/internal-mechanism.md#scope-types
*/
class VirtualTypeScriptContext {
constructor(code) {
this.script = "";
this.consumedIndex = 0;
this.unique = new unique_1.UniqueIdGenerator();
this._beforeResult = null;
this.originalCode = code;
this.restoreContext = new restore_1.RestoreContext(code);
}
skipOriginalOffset(offset) {
this.consumedIndex += offset;
}
skipUntilOriginalOffset(offset) {
this.consumedIndex = Math.max(offset, this.consumedIndex);
}
appendOriginalToEnd() {
this.appendOriginal(this.originalCode.length);
}
appendOriginal(index) {
if (this.consumedIndex >= index) {
return;
}
this.restoreContext.addOffset({
original: this.consumedIndex,
dist: this.script.length,
});
this.script += this.originalCode.slice(this.consumedIndex, index);
this.consumedIndex = index;
}
appendVirtualScript(virtualFragment) {
const start = this.script.length;
this.script += virtualFragment;
this.restoreContext.addVirtualFragmentRange(start, this.script.length);
}
generateUniqueId(base) {
return this.unique.generate(base, this.originalCode, this.script);
}
}
exports.VirtualTypeScriptContext = VirtualTypeScriptContext;

View File

@@ -0,0 +1,16 @@
import type { ESLintExtendedProgram } from "..";
import type { NormalizedParserOptions } from "../parser-options";
import type { SvelteParseContext } from "../svelte-parse-context";
import type { AnalyzeTypeScriptContext } from "./analyze";
/**
* Parse for TypeScript in <script>
*/
export declare function parseTypeScriptInSvelte(code: {
script: string;
render: string;
rootScope: string;
}, attrs: Record<string, string | undefined>, parserOptions: NormalizedParserOptions, context: AnalyzeTypeScriptContext): ESLintExtendedProgram;
/**
* Parse for TypeScript
*/
export declare function parseTypeScript(code: string, attrs: Record<string, string | undefined>, parserOptions: NormalizedParserOptions, svelteParseContext: SvelteParseContext): ESLintExtendedProgram;

View File

@@ -0,0 +1,26 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseTypeScriptInSvelte = parseTypeScriptInSvelte;
exports.parseTypeScript = parseTypeScript;
const script_1 = require("../script");
const analyze_1 = require("./analyze");
const set_parent_1 = require("./set-parent");
/**
* Parse for TypeScript in <script>
*/
function parseTypeScriptInSvelte(code, attrs, parserOptions, context) {
const tsCtx = (0, analyze_1.analyzeTypeScriptInSvelte)(code, attrs, parserOptions, context);
const result = (0, script_1.parseScriptInSvelte)(tsCtx.script, attrs, parserOptions);
tsCtx.restoreContext.restore(result);
return result;
}
/**
* Parse for TypeScript
*/
function parseTypeScript(code, attrs, parserOptions, svelteParseContext) {
const tsCtx = (0, analyze_1.analyzeTypeScript)(code, attrs, parserOptions, svelteParseContext);
const result = (0, script_1.parseScript)(tsCtx.script, attrs, parserOptions);
(0, set_parent_1.setParent)(result);
tsCtx.restoreContext.restore(result);
return result;
}

View File

@@ -0,0 +1,49 @@
import type { TSESTree } from "@typescript-eslint/types";
import type { TSESParseForESLintResult } from "./types";
/**
* A function that restores the statement.
* @param node The node to restore.
* @param result The result of parsing.
* @returns
* If `false`, it indicates that the specified node was not processed.
*
* If `true`, it indicates that the specified node was processed for processing.
* This process will no longer be called.
*/
type RestoreStatementProcess = (node: TSESTree.Statement, result: TSESParseForESLintResult) => boolean;
/**
* A function that restores the expression.
* @param node The node to restore.
* @param result The result of parsing.
* @returns
* If `false`, it indicates that the specified node was not processed.
*
* If `true`, it indicates that the specified node was processed for processing.
* This process will no longer be called.
*/
type RestoreExpressionProcess<T extends TSESTree.Expression> = {
target: T["type"];
restore: (node: T, result: TSESParseForESLintResult) => boolean;
};
export declare class RestoreContext {
private readonly originalLocs;
private readonly offsets;
private readonly virtualFragments;
private readonly restoreStatementProcesses;
private readonly restoreExpressionProcesses;
constructor(code: string);
addRestoreStatementProcess(process: RestoreStatementProcess): void;
addRestoreExpressionProcess<T extends TSESTree.Expression>(process: RestoreExpressionProcess<T>): void;
addOffset(offset: {
original: number;
dist: number;
}): void;
addVirtualFragmentRange(start: number, end: number): void;
/**
* Restore AST nodes
*/
restore(result: TSESParseForESLintResult): void;
private remapLocation;
private getRemapRange;
}
export {};

View File

@@ -0,0 +1,168 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RestoreContext = void 0;
const traverse_1 = require("../../traverse");
const context_1 = require("../../context");
class RestoreContext {
constructor(code) {
this.offsets = [];
this.virtualFragments = [];
this.restoreStatementProcesses = [];
this.restoreExpressionProcesses = [];
this.originalLocs = new context_1.LinesAndColumns(code);
}
addRestoreStatementProcess(process) {
this.restoreStatementProcesses.push(process);
}
addRestoreExpressionProcess(process) {
this.restoreExpressionProcesses.push(process);
}
addOffset(offset) {
this.offsets.push(offset);
}
addVirtualFragmentRange(start, end) {
const peek = this.virtualFragments[this.virtualFragments.length - 1];
if (peek && peek.end === start) {
peek.end = end;
return;
}
this.virtualFragments.push({ start, end });
}
/**
* Restore AST nodes
*/
restore(result) {
var _a, _b;
remapLocations(result, {
remapLocation: (n) => this.remapLocation(n),
removeToken: (token) => this.virtualFragments.some((f) => f.start <= token.range[0] && token.range[1] <= f.end),
});
restoreStatements(result, this.restoreStatementProcesses);
restoreExpressions(result, this.restoreExpressionProcesses);
// Adjust program node location
const firstOffset = Math.min(...[result.ast.body[0], (_a = result.ast.tokens) === null || _a === void 0 ? void 0 : _a[0], (_b = result.ast.comments) === null || _b === void 0 ? void 0 : _b[0]]
.filter((t) => Boolean(t))
.map((t) => t.range[0]));
if (firstOffset < result.ast.range[0]) {
result.ast.range[0] = firstOffset;
result.ast.loc.start = this.originalLocs.getLocFromIndex(firstOffset);
}
}
remapLocation(node) {
let [start, end] = node.range;
const startFragment = this.virtualFragments.find((f) => f.start <= start && start < f.end);
if (startFragment) {
start = startFragment.end;
}
const endFragment = this.virtualFragments.find((f) => f.start < end && end <= f.end);
if (endFragment) {
end = endFragment.start;
if (startFragment === endFragment) {
start = startFragment.start;
}
}
if (end < start) {
const w = start;
start = end;
end = w;
}
const locs = this.originalLocs.getLocations(...this.getRemapRange(start, end));
node.loc = locs.loc;
node.range = locs.range;
if (node.start != null) {
delete node.start;
}
if (node.end != null) {
delete node.end;
}
}
getRemapRange(start, end) {
if (!this.offsets.length) {
return [start, end];
}
let lastStart = this.offsets[0];
let lastEnd = this.offsets[0];
for (const offset of this.offsets) {
if (offset.dist <= start) {
lastStart = offset;
}
if (offset.dist < end) {
lastEnd = offset;
}
else {
break;
}
}
const remapStart = lastStart.original + (start - lastStart.dist);
const remapEnd = lastEnd.original + (end - lastEnd.dist);
return [remapStart, remapEnd];
}
}
exports.RestoreContext = RestoreContext;
/** Restore locations */
function remapLocations(result, { remapLocation, removeToken, }) {
const traversed = new Map();
// remap locations
(0, traverse_1.traverseNodes)(result.ast, {
visitorKeys: result.visitorKeys,
enterNode: (node, parent) => {
node.parent = parent;
if (!traversed.has(node)) {
traversed.set(node, parent);
remapLocation(node);
}
},
leaveNode: (_node) => {
// noop
},
});
const tokens = [];
for (const token of result.ast.tokens || []) {
if (removeToken(token)) {
continue;
}
remapLocation(token);
tokens.push(token);
}
result.ast.tokens = tokens;
for (const token of result.ast.comments || []) {
remapLocation(token);
}
}
/** Restore statement nodes */
function restoreStatements(result, restoreStatementProcesses) {
const restoreStatementProcessesSet = new Set(restoreStatementProcesses);
for (const node of [...result.ast.body]) {
if (!restoreStatementProcessesSet.size) {
break;
}
for (const proc of restoreStatementProcessesSet) {
if (proc(node, result)) {
restoreStatementProcessesSet.delete(proc);
break;
}
}
}
}
/** Restore expression nodes */
function restoreExpressions(result, restoreExpressionProcesses) {
if (restoreExpressionProcesses.length === 0)
return;
const restoreExpressionProcessesSet = new Set(restoreExpressionProcesses);
(0, traverse_1.traverseNodes)(result.ast, {
visitorKeys: result.visitorKeys,
enterNode(node) {
for (const proc of restoreExpressionProcessesSet) {
if (proc.target === node.type) {
if (proc.restore(node, result)) {
restoreExpressionProcessesSet.delete(proc);
}
break;
}
}
},
leaveNode() {
/* noop */
},
});
}

View File

@@ -0,0 +1,3 @@
import type { ESLintExtendedProgram } from "..";
import type { TSESParseForESLintResult } from "./types";
export declare function setParent(result: ESLintExtendedProgram | TSESParseForESLintResult): void;

View File

@@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setParent = setParent;
const __1 = require("../..");
function setParent(result) {
if (result.ast.body.some((node) => node.parent)) {
return;
}
(0, __1.traverseNodes)(result.ast, {
visitorKeys: result.visitorKeys,
enterNode(node, parent) {
node.parent = parent;
},
leaveNode() {
// noop
},
});
}

View File

@@ -0,0 +1,2 @@
import type { parseForESLint } from "@typescript-eslint/parser";
export type TSESParseForESLintResult = ReturnType<typeof parseForESLint>;

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,53 @@
import type { ScopeManager, Scope, Reference, Variable } from "eslint-scope";
import type * as ESTree from "estree";
import type { TSESTree } from "@typescript-eslint/types";
/** Remove all scope, variable, and reference */
export declare function removeAllScopeAndVariableAndReference(target: ESTree.Node | TSESTree.Node, info: {
visitorKeys?: {
[type: string]: string[];
} | {
readonly [type: string]: readonly string[] | undefined;
};
scopeManager: ScopeManager;
}): void;
/**
* Gets the scope for the current node
*/
export declare function getScopeFromNode(scopeManager: ScopeManager, currentNode: ESTree.Node): Scope;
/**
* Find the variable of a given identifier.
*/
export declare function findVariable(scopeManager: ScopeManager, node: ESTree.Identifier): Variable | null;
/**
* Gets the scope for the Program node
*/
export declare function getProgramScope(scopeManager: ScopeManager): Scope;
/** Remove variable */
export declare function removeIdentifierVariable(node: ESTree.Pattern | TSESTree.BindingName | TSESTree.RestElement | TSESTree.DestructuringPattern, scope: Scope): void;
/** Get all references */
export declare function getAllReferences(node: ESTree.Pattern | TSESTree.BindingName | TSESTree.RestElement | TSESTree.DestructuringPattern, scope: Scope): Iterable<Reference>;
/** Remove reference */
export declare function removeIdentifierReference(node: ESTree.Identifier, scope: Scope): boolean;
/** Remove reference */
export declare function removeReference(reference: Reference, baseScope: Scope): void;
/** Remove scope */
export declare function removeScope(scopeManager: ScopeManager, scope: Scope): void;
/** Replace scope */
export declare function replaceScope(scopeManager: ScopeManager, scope: Scope, newChildScopes?: Scope[]): void;
/**
* Add variable to array
*/
export declare function addVariable(list: Variable[], variable: Variable): void;
/**
* Add reference to array
*/
export declare function addReference(list: Reference[], reference: Reference): void;
/**
* Add all references to array
*/
export declare function addAllReferences(list: Reference[], elements: Reference[]): void;
/**
* Simplify scope data.
* @deprecated For Debug
*/
export declare function simplifyScope(scope: Scope): unknown;

View File

@@ -0,0 +1,383 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.removeAllScopeAndVariableAndReference = removeAllScopeAndVariableAndReference;
exports.getScopeFromNode = getScopeFromNode;
exports.findVariable = findVariable;
exports.getProgramScope = getProgramScope;
exports.removeIdentifierVariable = removeIdentifierVariable;
exports.getAllReferences = getAllReferences;
exports.removeIdentifierReference = removeIdentifierReference;
exports.removeReference = removeReference;
exports.removeScope = removeScope;
exports.replaceScope = replaceScope;
exports.addVariable = addVariable;
exports.addReference = addReference;
exports.addAllReferences = addAllReferences;
exports.simplifyScope = simplifyScope;
const traverse_1 = require("../traverse");
const utils_1 = require("../utils");
/** Remove all scope, variable, and reference */
function removeAllScopeAndVariableAndReference(target, info) {
const targetScopes = new Set();
(0, traverse_1.traverseNodes)(target, {
visitorKeys: info.visitorKeys,
enterNode(node) {
const scope = info.scopeManager.acquire(node);
if (scope) {
targetScopes.add(scope);
return;
}
if (node.type === "Identifier") {
let scope = getScopeFromNode(info.scopeManager, node);
while (scope &&
scope.block.type !== "Program" &&
target.range[0] <= scope.block.range[0] &&
scope.block.range[1] <= target.range[1]) {
scope = scope.upper;
}
if (targetScopes.has(scope)) {
return;
}
removeIdentifierVariable(node, scope);
removeIdentifierReference(node, scope);
}
},
leaveNode() {
// noop
},
});
for (const scope of targetScopes) {
removeScope(info.scopeManager, scope);
}
}
/**
* Gets the scope for the current node
*/
function getScopeFromNode(scopeManager, currentNode) {
let node = currentNode;
for (; node; node = node.parent || null) {
const scope = scopeManager.acquire(node, false);
if (scope) {
if (scope.type === "function-expression-name") {
return scope.childScopes[0];
}
if (scope.type === "global" &&
node.type === "Program" &&
node.sourceType === "module") {
return scope.childScopes.find((s) => s.type === "module") || scope;
}
return scope;
}
}
const global = scopeManager.globalScope;
return global;
}
/**
* Find the variable of a given identifier.
*/
function findVariable(scopeManager, node) {
let scope = getScopeFromNode(scopeManager, node);
while (scope != null) {
const variable = scope.set.get(node.name);
if (variable != null) {
return variable;
}
scope = scope.upper;
}
return null;
}
/**
* Gets the scope for the Program node
*/
function getProgramScope(scopeManager) {
const globalScope = scopeManager.globalScope;
return (globalScope.childScopes.find((s) => s.type === "module") || globalScope);
}
/** Remove variable */
function removeIdentifierVariable(node, scope) {
if (node.type === "ObjectPattern") {
for (const prop of node.properties) {
if (prop.type === "Property") {
removeIdentifierVariable(prop.value, scope);
}
else if (prop.type === "RestElement") {
removeIdentifierVariable(prop, scope);
}
}
return;
}
if (node.type === "ArrayPattern") {
for (const element of node.elements) {
if (!element)
continue;
removeIdentifierVariable(element, scope);
}
return;
}
if (node.type === "AssignmentPattern") {
removeIdentifierVariable(node.left, scope);
return;
}
if (node.type === "RestElement") {
removeIdentifierVariable(node.argument, scope);
return;
}
if (node.type === "MemberExpression") {
return;
}
if (node.type !== "Identifier") {
return;
}
for (let varIndex = 0; varIndex < scope.variables.length; varIndex++) {
const variable = scope.variables[varIndex];
const defIndex = variable.defs.findIndex((def) => def.name === node);
if (defIndex < 0) {
continue;
}
variable.defs.splice(defIndex, 1);
if (variable.defs.length === 0) {
// Remove variable
referencesToThrough(variable.references, scope);
variable.references.forEach((r) => {
if (r.init)
r.init = false;
r.resolved = null;
});
scope.variables.splice(varIndex, 1);
const name = node.name;
if (variable === scope.set.get(name)) {
scope.set.delete(name);
}
}
else {
const idIndex = variable.identifiers.indexOf(node);
if (idIndex >= 0) {
variable.identifiers.splice(idIndex, 1);
}
}
return;
}
}
/** Get all references */
function* getAllReferences(node, scope) {
if (node.type === "ObjectPattern") {
for (const prop of node.properties) {
if (prop.type === "Property") {
yield* getAllReferences(prop.value, scope);
}
else if (prop.type === "RestElement") {
yield* getAllReferences(prop, scope);
}
}
return;
}
if (node.type === "ArrayPattern") {
for (const element of node.elements) {
if (!element)
continue;
yield* getAllReferences(element, scope);
}
return;
}
if (node.type === "AssignmentPattern") {
yield* getAllReferences(node.left, scope);
return;
}
if (node.type === "RestElement") {
yield* getAllReferences(node.argument, scope);
return;
}
if (node.type === "MemberExpression") {
return;
}
if (node.type !== "Identifier") {
return;
}
const ref = scope.references.find((ref) => ref.identifier === node);
if (ref)
yield ref;
}
/** Remove reference */
function removeIdentifierReference(node, scope) {
const reference = scope.references.find((ref) => ref.identifier === node);
if (reference) {
removeReference(reference, scope);
return true;
}
const location = node.range[0];
const pendingScopes = [];
for (const childScope of scope.childScopes) {
const range = childScope.block.range;
if (range[0] <= location && location < range[1]) {
if (removeIdentifierReference(node, childScope)) {
return true;
}
}
else {
pendingScopes.push(childScope);
}
}
for (const childScope of pendingScopes) {
if (removeIdentifierReference(node, childScope)) {
return true;
}
}
return false;
}
/** Remove reference */
function removeReference(reference, baseScope) {
if (reference.resolved) {
if (reference.resolved.defs.some((d) => d.name === reference.identifier)) {
// remove var
const varIndex = baseScope.variables.indexOf(reference.resolved);
if (varIndex >= 0) {
baseScope.variables.splice(varIndex, 1);
}
const name = reference.identifier.name;
if (reference.resolved === baseScope.set.get(name)) {
baseScope.set.delete(name);
}
}
else {
const refIndex = reference.resolved.references.indexOf(reference);
if (refIndex >= 0) {
reference.resolved.references.splice(refIndex, 1);
}
}
}
let scope = baseScope;
while (scope) {
const refIndex = scope.references.indexOf(reference);
if (refIndex >= 0) {
scope.references.splice(refIndex, 1);
}
const throughIndex = scope.through.indexOf(reference);
if (throughIndex >= 0) {
scope.through.splice(throughIndex, 1);
}
scope = scope.upper;
}
}
/** Move reference to through */
function referencesToThrough(references, baseScope) {
let scope = baseScope;
while (scope) {
addAllReferences(scope.through, references);
scope = scope.upper;
}
}
/** Remove scope */
function removeScope(scopeManager, scope) {
while (scope.childScopes[0]) {
removeScope(scopeManager, scope.childScopes[0]);
}
while (scope.references[0]) {
removeReference(scope.references[0], scope);
}
const upper = scope.upper;
if (upper) {
const index = upper.childScopes.indexOf(scope);
if (index >= 0) {
upper.childScopes.splice(index, 1);
}
}
const index = scopeManager.scopes.indexOf(scope);
if (index >= 0) {
scopeManager.scopes.splice(index, 1);
}
}
/** Replace scope */
function replaceScope(scopeManager, scope, newChildScopes = []) {
// remove scope from scopeManager
scopeManager.scopes = scopeManager.scopes.filter((s) => s !== scope);
const upper = scope.upper;
if (upper) {
// remove scope from upper and marge childScopes
upper.childScopes.splice(upper.childScopes.indexOf(scope), 1, ...newChildScopes);
for (const child of newChildScopes) {
child.upper = upper;
replaceVariableScope(child, scope);
}
}
/** Replace variableScope */
function replaceVariableScope(child, replaceTarget) {
if (child.variableScope === replaceTarget) {
child.variableScope = child.upper.variableScope;
for (const c of child.childScopes) {
replaceVariableScope(c, replaceTarget);
}
}
}
}
/**
* Add variable to array
*/
function addVariable(list, variable) {
(0, utils_1.addElementToSortedArray)(list, variable, (a, b) => {
const idA = getFirstId(a);
const idB = getFirstId(b);
return idA.range[0] - idB.range[0];
});
/** Get first id from give variable */
function getFirstId(v) {
var _a, _b;
return v.identifiers[0] || ((_a = v.defs[0]) === null || _a === void 0 ? void 0 : _a.name) || ((_b = v.references[0]) === null || _b === void 0 ? void 0 : _b.identifier);
}
}
/**
* Add reference to array
*/
function addReference(list, reference) {
(0, utils_1.addElementToSortedArray)(list, reference, (a, b) => a.identifier.range[0] - b.identifier.range[0]);
}
/**
* Add all references to array
*/
function addAllReferences(list, elements) {
(0, utils_1.addElementsToSortedArray)(list, elements, (a, b) => a.identifier.range[0] - b.identifier.range[0]);
}
/**
* Simplify scope data.
* @deprecated For Debug
*/
function simplifyScope(scope) {
return {
type: scope.type,
childScopes: scope.childScopes.map(simplifyScope),
block: {
type: scope.block.type,
loc: JSON.stringify(scope.block.loc),
},
variables: scope.type === "global" ? null : simplifyVariables(scope.variables),
references: scope.references.map(simplifyReference),
through: scope.through.map(simplifyReference),
get original() {
return scope;
},
};
}
/**
* Simplify variables data.
* @deprecated For Debug
*/
function simplifyVariables(variables) {
return Object.fromEntries(variables.map((v) => {
var _a;
return [
v.name,
{
loc: JSON.stringify((_a = v.defs[0]) === null || _a === void 0 ? void 0 : _a.node.loc),
},
];
}));
}
/**
* Simplify reference data.
* @deprecated For Debug
*/
function simplifyReference(reference) {
return {
name: reference.identifier.name,
loc: JSON.stringify(reference.identifier.loc),
};
}

View File

@@ -0,0 +1,79 @@
import type * as Compiler from "svelte/compiler";
export type SvelteConfig = {
compilerOptions?: Compiler.CompileOptions;
extensions?: string[];
kit?: KitConfig;
preprocess?: unknown;
vitePlugin?: unknown;
onwarn?: (warning: Compiler.Warning, defaultHandler: (warning: Compiler.Warning) => void) => void;
[key: string]: unknown;
};
interface KitConfig {
adapter?: unknown;
alias?: Record<string, string>;
appDir?: string;
csp?: {
mode?: "hash" | "nonce" | "auto";
directives?: unknown;
reportOnly?: unknown;
};
csrf?: {
checkOrigin?: boolean;
};
embedded?: boolean;
env?: {
dir?: string;
publicPrefix?: string;
privatePrefix?: string;
};
files?: {
assets?: string;
hooks?: {
client?: string;
server?: string;
universal?: string;
};
lib?: string;
params?: string;
routes?: string;
serviceWorker?: string;
appTemplate?: string;
errorTemplate?: string;
};
inlineStyleThreshold?: number;
moduleExtensions?: string[];
outDir?: string;
output?: {
preloadStrategy?: "modulepreload" | "preload-js" | "preload-mjs";
};
paths?: {
assets?: "" | `http://${string}` | `https://${string}`;
base?: "" | `/${string}`;
relative?: boolean;
};
prerender?: {
concurrency?: number;
crawl?: boolean;
entries?: ("*" | `/${string}`)[];
handleHttpError?: unknown;
handleMissingId?: unknown;
handleEntryGeneratorMismatch?: unknown;
origin?: string;
};
serviceWorker?: {
register?: boolean;
files?(filepath: string): boolean;
};
typescript?: {
config?: (config: Record<string, any>) => Record<string, any> | void;
};
version?: {
name?: string;
pollInterval?: number;
};
}
/**
* Resolves svelte.config.
*/
export declare function resolveSvelteConfigFromOption(options: any): SvelteConfig | null;
export {};

View File

@@ -0,0 +1,68 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveSvelteConfigFromOption = resolveSvelteConfigFromOption;
const path_1 = __importDefault(require("path"));
const fs_1 = __importDefault(require("fs"));
const parser_1 = require("./parser");
const caches = new Map();
/**
* Resolves svelte.config.
*/
function resolveSvelteConfigFromOption(options) {
if (options === null || options === void 0 ? void 0 : options.svelteConfig) {
return options.svelteConfig;
}
return resolveSvelteConfig(options === null || options === void 0 ? void 0 : options.filePath);
}
/**
* Resolves `svelte.config.js`.
* It searches the parent directories of the given file to find `svelte.config.js`,
* and returns the static analysis result for it.
*/
function resolveSvelteConfig(filePath) {
let cwd = filePath && fs_1.default.existsSync(filePath) ? path_1.default.dirname(filePath) : null;
if (cwd == null) {
if (typeof process === "undefined")
return null;
cwd = process.cwd();
}
const configFilePath = findConfigFilePath(cwd);
if (!configFilePath)
return null;
if (caches.has(configFilePath)) {
return caches.get(configFilePath) || null;
}
const code = fs_1.default.readFileSync(configFilePath, "utf8");
const config = (0, parser_1.parseConfig)(code);
caches.set(configFilePath, config);
return config;
}
/**
* Searches from the current working directory up until finding the config filename.
* @param {string} cwd The current working directory to search from.
* @returns {string|undefined} The file if found or `undefined` if not.
*/
function findConfigFilePath(cwd) {
let directory = path_1.default.resolve(cwd);
const { root } = path_1.default.parse(directory);
const stopAt = path_1.default.resolve(directory, root);
while (directory !== stopAt) {
const target = path_1.default.resolve(directory, "svelte.config.js");
const stat = fs_1.default.existsSync(target)
? fs_1.default.statSync(target, {
throwIfNoEntry: false,
})
: null;
if (stat === null || stat === void 0 ? void 0 : stat.isFile()) {
return target;
}
const next = path_1.default.dirname(directory);
if (next === directory)
break;
directory = next;
}
return null;
}

View File

@@ -0,0 +1,2 @@
import type { SvelteConfig } from ".";
export declare function parseConfig(code: string): SvelteConfig | null;

View File

@@ -0,0 +1,280 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseConfig = parseConfig;
const traverse_1 = require("../traverse");
const espree_1 = require("../parser/espree");
const eslint_scope_1 = require("eslint-scope");
const scope_1 = require("../scope");
function parseConfig(code) {
const espree = (0, espree_1.getEspree)();
const ast = espree.parse(code, {
range: true,
loc: true,
ecmaVersion: espree.latestEcmaVersion,
sourceType: "module",
});
// Set parent nodes.
(0, traverse_1.traverseNodes)(ast, {
enterNode(node, parent) {
node.parent = parent;
},
leaveNode() {
/* do nothing */
},
});
// Analyze scopes.
const scopeManager = (0, eslint_scope_1.analyze)(ast, {
ignoreEval: true,
nodejsScope: false,
ecmaVersion: espree.latestEcmaVersion,
sourceType: "module",
fallback: traverse_1.getFallbackKeys,
});
return parseAst(ast, scopeManager);
}
function parseAst(ast, scopeManager) {
const edd = ast.body.find((node) => node.type === "ExportDefaultDeclaration");
if (!edd)
return {};
const decl = edd.declaration;
if (decl.type === "ClassDeclaration" || decl.type === "FunctionDeclaration")
return {};
return parseSvelteConfigExpression(decl, scopeManager);
}
function parseSvelteConfigExpression(node, scopeManager) {
var _a, _b;
const evaluated = evaluateExpression(node, scopeManager);
if ((evaluated === null || evaluated === void 0 ? void 0 : evaluated.type) !== 1 /* EvaluatedType.object */)
return {};
const result = {};
// Returns only known properties.
const compilerOptions = evaluated.getProperty("compilerOptions");
if ((compilerOptions === null || compilerOptions === void 0 ? void 0 : compilerOptions.type) === 1 /* EvaluatedType.object */) {
result.compilerOptions = {};
const runes = (_a = compilerOptions.getProperty("runes")) === null || _a === void 0 ? void 0 : _a.getStatic();
if ((runes === null || runes === void 0 ? void 0 : runes.value) != null) {
result.compilerOptions.runes = Boolean(runes.value);
}
}
const kit = evaluated.getProperty("kit");
if ((kit === null || kit === void 0 ? void 0 : kit.type) === 1 /* EvaluatedType.object */) {
result.kit = {};
const files = (_b = kit.getProperty("files")) === null || _b === void 0 ? void 0 : _b.getStatic();
if ((files === null || files === void 0 ? void 0 : files.value) != null)
result.kit.files = files.value;
}
return result;
}
class EvaluatedLiteral {
constructor(value) {
this.type = 0 /* EvaluatedType.literal */;
this.value = value;
}
getStatic() {
return this;
}
}
/** Evaluating an object expression. */
class EvaluatedObject {
constructor(node, parseExpression) {
this.type = 1 /* EvaluatedType.object */;
this.cached = new Map();
this.node = node;
this.parseExpression = parseExpression;
}
/** Gets the evaluated value of the property with the given name. */
getProperty(key) {
return this.withCache(key, () => {
let unknown = false;
for (const prop of [...this.node.properties].reverse()) {
if (prop.type === "Property") {
const name = this.getKey(prop);
if (name === key)
return this.parseExpression(prop.value);
if (name == null)
unknown = true;
}
else if (prop.type === "SpreadElement") {
const evaluated = this.parseExpression(prop.argument);
if ((evaluated === null || evaluated === void 0 ? void 0 : evaluated.type) === 1 /* EvaluatedType.object */) {
const value = evaluated.getProperty(key);
if (value)
return value;
}
unknown = true;
}
}
return unknown ? null : new EvaluatedLiteral(undefined);
});
}
getStatic() {
var _a, _b;
const object = {};
for (const prop of this.node.properties) {
if (prop.type === "Property") {
const name = this.getKey(prop);
if (name == null)
return null;
const evaluated = (_a = this.withCache(name, () => this.parseExpression(prop.value))) === null || _a === void 0 ? void 0 : _a.getStatic();
if (!evaluated)
return null;
object[name] = evaluated.value;
}
else if (prop.type === "SpreadElement") {
const evaluated = (_b = this.parseExpression(prop.argument)) === null || _b === void 0 ? void 0 : _b.getStatic();
if (!evaluated)
return null;
Object.assign(object, evaluated.value);
}
}
return { value: object };
}
withCache(key, parse) {
if (this.cached.has(key))
return this.cached.get(key) || null;
const evaluated = parse();
this.cached.set(key, evaluated);
return evaluated;
}
getKey(node) {
var _a;
if (!node.computed && node.key.type === "Identifier")
return node.key.name;
const evaluatedKey = (_a = this.parseExpression(node.key)) === null || _a === void 0 ? void 0 : _a.getStatic();
if (evaluatedKey)
return String(evaluatedKey.value);
return null;
}
}
function evaluateExpression(node, scopeManager) {
const tracked = new Map();
return parseExpression(node);
function parseExpression(node) {
if (node.type === "Literal") {
return new EvaluatedLiteral(node.value);
}
if (node.type === "Identifier") {
return parseIdentifier(node);
}
if (node.type === "ObjectExpression") {
return new EvaluatedObject(node, parseExpression);
}
return null;
}
function parseIdentifier(node) {
const defs = getIdentifierDefinitions(node);
if (defs.length !== 1) {
if (defs.length === 0 && node.name === "undefined")
return new EvaluatedLiteral(undefined);
return null;
}
const def = defs[0];
if (def.type !== "Variable")
return null;
if (def.parent.kind !== "const" || !def.node.init)
return null;
const evaluated = parseExpression(def.node.init);
if (!evaluated)
return null;
const assigns = parsePatternAssign(def.name, def.node.id);
let result = evaluated;
while (assigns.length) {
const assign = assigns.shift();
if (assign.type === "member") {
if (result.type !== 1 /* EvaluatedType.object */)
return null;
const next = result.getProperty(assign.name);
if (!next)
return null;
result = next;
}
else if (assign.type === "assignment") {
if (result.type === 0 /* EvaluatedType.literal */ &&
result.value === undefined) {
const next = parseExpression(assign.node.right);
if (!next)
return null;
result = next;
}
}
}
return result;
}
function getIdentifierDefinitions(node) {
var _a, _b;
if (tracked.has(node))
return tracked.get(node);
tracked.set(node, []);
const defs = (_a = (0, scope_1.findVariable)(scopeManager, node)) === null || _a === void 0 ? void 0 : _a.defs;
if (!defs)
return [];
tracked.set(node, defs);
if (defs.length !== 1) {
const def = defs[0];
if (def.type === "Variable" &&
def.parent.kind === "const" &&
def.node.id.type === "Identifier" &&
((_b = def.node.init) === null || _b === void 0 ? void 0 : _b.type) === "Identifier") {
const newDef = getIdentifierDefinitions(def.node.init);
tracked.set(node, newDef);
return newDef;
}
}
return defs;
}
}
/**
* Returns the assignment path.
* For example,
* `let {a: {target}} = {}`
* -> `[{type: "member", name: 'a'}, {type: "member", name: 'target'}]`.
* `let {a: {target} = foo} = {}`
* -> `[{type: "member", name: 'a'}, {type: "assignment"}, {type: "member", name: 'target'}]`.
*/
function parsePatternAssign(node, root) {
return parse(root) || [];
function parse(target) {
if (node === target) {
return [];
}
if (target.type === "Identifier") {
return null;
}
if (target.type === "AssignmentPattern") {
const left = parse(target.left);
if (!left)
return null;
return [{ type: "assignment", node: target }, ...left];
}
if (target.type === "ObjectPattern") {
for (const prop of target.properties) {
if (prop.type === "Property") {
const name = !prop.computed && prop.key.type === "Identifier"
? prop.key.name
: prop.key.type === "Literal"
? String(prop.key.value)
: null;
if (!name)
continue;
const value = parse(prop.value);
if (!value)
return null;
return [{ type: "member", name }, ...value];
}
}
return null;
}
if (target.type === "ArrayPattern") {
for (const [index, element] of target.elements.entries()) {
if (!element)
continue;
const value = parse(element);
if (!value)
return null;
return [{ type: "member", name: String(index) }, ...value];
}
return null;
}
return null;
}
}

Some files were not shown because too many files have changed in this diff Show More