sveast 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +47 -1
- package/README.md +146 -2
- package/index.d.ts +1156 -0
- package/index.js +64 -0
- package/package.json +27 -3
- package/index.ts +0 -1
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026 Eric Liu
|
|
3
|
+
Copyright (c) 2026-present Eric Liu
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -19,3 +19,49 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
The parser is adapted from Svelte's (https://github.com/sveltejs/svelte),
|
|
26
|
+
and its type declarations include Svelte's AST types. The published bundle
|
|
27
|
+
includes acorn (https://github.com/acornjs/acorn), and the type
|
|
28
|
+
declarations include @types/estree
|
|
29
|
+
(https://github.com/DefinitelyTyped/DefinitelyTyped). All are MIT licensed:
|
|
30
|
+
|
|
31
|
+
Svelte
|
|
32
|
+
|
|
33
|
+
Copyright (c) 2016-2025 Svelte Contributors
|
|
34
|
+
(https://github.com/sveltejs/svelte/graphs/contributors)
|
|
35
|
+
|
|
36
|
+
acorn
|
|
37
|
+
|
|
38
|
+
Copyright (C) 2012-2022 by various contributors (see AUTHORS)
|
|
39
|
+
|
|
40
|
+
@types/estree
|
|
41
|
+
|
|
42
|
+
Copyright (c) Microsoft Corporation.
|
|
43
|
+
|
|
44
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
45
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
46
|
+
in the Software without restriction, including without limitation the rights
|
|
47
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
48
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
49
|
+
furnished to do so, subject to the following conditions:
|
|
50
|
+
|
|
51
|
+
The above copyright notice and this permission notice shall be included in all
|
|
52
|
+
copies or substantial portions of the Software.
|
|
53
|
+
|
|
54
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
55
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
56
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
57
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
58
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
59
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
60
|
+
SOFTWARE.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
The named character reference table in the bundle is generated from the
|
|
65
|
+
HTML Living Standard's entities.json (https://html.spec.whatwg.org/),
|
|
66
|
+
Copyright WHATWG (Apple, Google, Mozilla, Microsoft), licensed under
|
|
67
|
+
CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/).
|
package/README.md
CHANGED
|
@@ -1,5 +1,149 @@
|
|
|
1
1
|
# sveast
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Zero-dependency Svelte 5 parser. svelte/compiler's exact AST, 2× faster.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Parse `.svelte` components, including `<script lang="ts">`, into the same AST, and the same errors, as `svelte/compiler`'s `parse(source, { modern: true })`. sveast bundles acorn and its own TypeScript plugin, so it has no dependencies, and it runs anywhere: no Node APIs.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
bun i sveast
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { parse } from "sveast";
|
|
13
|
+
|
|
14
|
+
const ast = parse(`<script>let name = "world";</script>\n<h1>Hello {name}!</h1>`);
|
|
15
|
+
|
|
16
|
+
ast.instance?.content.body; // [VariableDeclaration]
|
|
17
|
+
ast.fragment.nodes; // [Text, RegularElement]
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Migrating from svelte/compiler
|
|
21
|
+
|
|
22
|
+
sveast is a drop-in for `parse` in most tools: change the import, and pass `loc: true` if you read line and column numbers.
|
|
23
|
+
|
|
24
|
+
```diff
|
|
25
|
+
- import { type AST, parse } from "svelte/compiler";
|
|
26
|
+
+ import { type AST, parse } from "sveast";
|
|
27
|
+
|
|
28
|
+
- const ast = parse(source, { modern: true });
|
|
29
|
+
+ const ast = parse(source, { loc: true });
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
| | svelte/compiler | sveast |
|
|
33
|
+
|:---|:---|:---|
|
|
34
|
+
| `loc`, `name_loc` | Always | With `loc: true`; otherwise only `start`/`end` offsets, for a faster parse and a smaller AST |
|
|
35
|
+
| Errors | `CompileError` | `ParseError`: same `code`, `message`, `position`, `start`, `end` and `frame`; no `filename` |
|
|
36
|
+
| AST formats | Modern, legacy (`modern: false`), error-tolerant (`loose`) | Modern |
|
|
37
|
+
| Scope | Parsing, `parseCss`, analysis, compilation | Parsing (`parse`, `parseModule`) |
|
|
38
|
+
| TypeScript-only errors | Reported, e.g. modifier order or initializers in ambient contexts | Not reported: 108 of the 2,449 TypeScript conformance tests acorn-typescript rejects still parse |
|
|
39
|
+
|
|
40
|
+
## API
|
|
41
|
+
|
|
42
|
+
### `parse(source, options?) => AST.Root`
|
|
43
|
+
|
|
44
|
+
| Option | Description |
|
|
45
|
+
|:---|:---|
|
|
46
|
+
| `loc` | Add `loc` (line and column) to script and expression nodes, and `name_loc` to elements, attributes and directives, as svelte does. Default `false`: they cost time and memory, and most tools only need `start`/`end`. |
|
|
47
|
+
| `css` | Parse `<style>` into rules and selectors. With `false`, `css` keeps its `start`, `end` and `content`, but `children` and `comments` are empty and CSS syntax errors aren't reported. Default `true`. |
|
|
48
|
+
|
|
49
|
+
With `loc: true`, the result equals svelte's. With the defaults, it's svelte's without `loc` and `name_loc`.
|
|
50
|
+
|
|
51
|
+
A syntax error throws a `ParseError` with svelte's `code` (e.g. `"block_unclosed"`), `message`, `position` (`[start, end]` offsets), `start`/`end` (`{ line, column, character }`) and `frame` (the source around the error).
|
|
52
|
+
|
|
53
|
+
### `parseModule(source, options?) => Program`
|
|
54
|
+
|
|
55
|
+
Parses a JavaScript or TypeScript module, such as a `.ts` file a component imports, the way a component's `<script>` is parsed: estree plus TypeScript nodes, with comments attached as `leadingComments`/`trailingComments`. Options: `typescript` and `loc`, both default `false`.
|
|
56
|
+
|
|
57
|
+
### Types
|
|
58
|
+
|
|
59
|
+
`AST` is svelte's `AST` namespace (`AST.Root`, `AST.RegularElement`, `AST.CSS.Rule`, ...), with `name_loc`, a comment's `loc`, and `Root.instance`/`module` made optional, since they can be absent. `ParseOptions` is exported too.
|
|
60
|
+
|
|
61
|
+
## Recipes
|
|
62
|
+
|
|
63
|
+
**List the components a file renders**, e.g. to build a dependency graph:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { parse } from "sveast";
|
|
67
|
+
|
|
68
|
+
function componentsUsed(source: string): string[] {
|
|
69
|
+
const names = new Set<string>();
|
|
70
|
+
const visit = (node: unknown): void => {
|
|
71
|
+
if (!node || typeof node !== "object") return;
|
|
72
|
+
const { type, name } = node as { type?: string; name?: string };
|
|
73
|
+
if (type === "Component" && name) names.add(name);
|
|
74
|
+
for (const child of Object.values(node)) visit(child);
|
|
75
|
+
};
|
|
76
|
+
visit(parse(source, { css: false }).fragment);
|
|
77
|
+
return [...names]; // ["Button", "Modal.Root"]
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Read the props a runes component declares:**
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import { parse } from "sveast";
|
|
85
|
+
|
|
86
|
+
function propNames(source: string): string[] {
|
|
87
|
+
for (const statement of parse(source).instance?.content.body ?? []) {
|
|
88
|
+
if (statement.type !== "VariableDeclaration") continue;
|
|
89
|
+
for (const { id, init } of statement.declarations) {
|
|
90
|
+
const isProps =
|
|
91
|
+
init?.type === "CallExpression" &&
|
|
92
|
+
init.callee.type === "Identifier" &&
|
|
93
|
+
init.callee.name === "$props";
|
|
94
|
+
if (isProps && id.type === "ObjectPattern") {
|
|
95
|
+
return id.properties.flatMap((p) =>
|
|
96
|
+
p.type === "Property" && p.key.type === "Identifier" ? [p.key.name] : [],
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Report syntax errors**, e.g. in a pre-commit check, with svelte's own messages:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import { ParseError, parse } from "sveast";
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
parse(source, { css: false });
|
|
112
|
+
} catch (error) {
|
|
113
|
+
if (!(error instanceof ParseError)) throw error;
|
|
114
|
+
console.error(`${file}:${error.start?.line}:${error.start?.column} ${error.code}\n${error.frame}`);
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Get a component's styles without parsing them:**
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
import { parse } from "sveast";
|
|
122
|
+
|
|
123
|
+
const css = parse(source, { css: false }).css?.content.styles ?? "";
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Features
|
|
127
|
+
|
|
128
|
+
- **svelte/compiler parity.** Tested on over 12,000 components: carbon-components-svelte, svelte's own test suite, bits-ui, shadcn-svelte, skeleton, flowbite-svelte, svelte.dev, immich, SvelteKit, melt-ui, layerchart and paneforge. Every one either parses to svelte's AST, `loc` included, or throws svelte's error with the same code, message, position and frame. CI runs 442 of them, the smallest set that covers every parser line, AST shape and error code the full set does, and a differential fuzzer compares the two parsers on mutated components.
|
|
129
|
+
- **TypeScript without acorn-typescript.** The built-in plugin produces the same AST as `@sveltejs/acorn-typescript`, key order and `loc` included, on 6,604 real-world modules and on all but 2 of the 9,586 TypeScript conformance tests that acorn-typescript parses. It rejects the same redeclarations, and supports decorators.
|
|
130
|
+
- **About 2.4× faster than svelte/compiler** (2.2–3.0× depending on the input), and its ASTs retain 47% less memory without `loc`.
|
|
131
|
+
- **Fast to load.** A fresh process imports sveast in about 5 ms, against about 45 ms for `svelte/compiler`.
|
|
132
|
+
- **Zero dependencies,** types included. 64 kB gzipped, acorn included.
|
|
133
|
+
|
|
134
|
+
## Benchmarks
|
|
135
|
+
|
|
136
|
+
Apple M2, medians of warm calls. Each task parses every file in the set once. The corpus is `tests/corpus`: all of carbon-components-svelte plus svelte's and sveld's test inputs.
|
|
137
|
+
|
|
138
|
+
| Input | sveast | sveast, `loc: true` | svelte/compiler |
|
|
139
|
+
|:---|:---|:---|:---|
|
|
140
|
+
| Corpus, 400 components, 1.5 MB | **64.7 ms** | 84.0 ms | 155 ms (2.4×) |
|
|
141
|
+
| Carbon's 5 largest components, 318 kB | **13.8 ms** | | 31.3 ms (2.3×) |
|
|
142
|
+
| `lang="ts"` components, 22 files | **0.79 ms** | | 2.40 ms (3.0×) |
|
|
143
|
+
|
|
144
|
+
| Input | sveast's TypeScript plugin | acorn-typescript |
|
|
145
|
+
|:---|:---|:---|
|
|
146
|
+
| Carbon's 98 `.d.ts` modules | **3.19 ms** | 8.07 ms (2.5×) |
|
|
147
|
+
| The corpus's `lang="ts"` scripts on their own | **0.58 ms** | 1.29 ms (2.2×) |
|
|
148
|
+
|
|
149
|
+
In a fresh process, importing the parser takes 5.3 ms with sveast and 44.7 ms with `svelte/compiler`, and a first parse of the whole corpus takes 65 ms against 161 ms. Keeping ten parses of the corpus alive retains 154 MB with sveast, 268 MB with `loc: true`, and 293 MB with `svelte/compiler`.
|