sveast 0.5.0 → 0.7.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/README.md CHANGED
@@ -34,7 +34,7 @@ sveast is a drop-in for `parse` in most tools: change the import, and pass `loc:
34
34
  | `loc`, `name_loc` | Always | With `loc: true`; otherwise only `start`/`end` offsets, for a faster parse and a smaller AST |
35
35
  | Errors | `CompileError` | `ParseError`: same `code`, `message`, `position`, `start`, `end` and `frame`; no `filename`; `reason`, the message without its link |
36
36
  | AST formats | Modern, legacy (`modern: false`), error-tolerant (`loose`) | Modern |
37
- | Scope | Parsing, `parseCss`, analysis, compilation | Parsing (`parse`, `parseModule`, `isValidType`), and walking the AST (`walk`, `visitorKeys`) |
37
+ | Scope | Parsing, `parseCss`, analysis, compilation | Parsing (`parse`, `parseModule`, `parseImportsExports`, `isValidType`), reading imports without a parser (`lexImportsExports`), walking the AST (`walk`, `visitorKeys`), and finding references and bindings (`isReference`, `extractIdentifiers`) |
38
38
  | Non-ASCII identifiers | acorn's tables, Unicode 17 | The engine's own Unicode data, which is smaller to ship: the same as acorn's in Node 24 and Bun; an engine on another Unicode version differs on the letters added in between |
39
39
  | 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 |
40
40
 
@@ -57,20 +57,75 @@ A syntax error throws a `ParseError` with svelte's `code` (e.g. `"block_unclosed
57
57
 
58
58
  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`, and `comments`, default `true`; with `false`, no node has `leadingComments` or `trailingComments`.
59
59
 
60
- ### `createParser(support?) => { parse, parseModule }` from `sveast/core`
60
+ ### `parseImportsExports(source, options?) => ModuleDeclaration[]`
61
61
 
62
- `parse` and `parseModule` without the two parts most tools can do without: the TypeScript plugin and the table of HTML's named character references. Together they're about a third of a bundle: `parse` and `parseModule` from `sveast` minify to 57.3 kB gzipped, and from `createParser()` to 38.7 kB. Pass back the parts you need; with both, the parsers are the same as `sveast`'s.
62
+ A module's top-level `import` and `export` statements, without parsing the rest, e.g. to rewrite a component's imports or follow a module graph. Each node is the one `parseModule(source, { comments: false })` has for that statement, so `start`, `end`, `specifiers` (with `imported`, `local` and `importKind`) and `source` are all there: `ImportDeclaration`, `ExportNamedDeclaration`, `ExportDefaultDeclaration` and `ExportAllDeclaration`, and, in TypeScript, `TSImportEqualsDeclaration`, `TSExportAssignment` and `TSNamespaceExportDeclaration`. `import(...)` and `import.meta` are expressions, so they aren't returned. Unlike a regex, it never matches an `import` in a comment, a string, a template or a nested block such as `declare module "a" { ... }`.
63
+
64
+ The rest is skipped by a tokenizer that only tracks strings, comments, templates, regular expressions and brackets, and only the statements it finds are parsed. It stops after the last `import` or `export` word that could start a statement, so the code after a component's imports is never read. A syntax error in one of them throws `parseModule`'s `ParseError`, including a name exported twice; errors elsewhere aren't reported.
65
+
66
+ | Option | Description |
67
+ |:---|:---|
68
+ | `typescript` | Parse TypeScript: `import type`, `export type`, `import a = require("a")`. Default `false`. |
69
+ | `localExports` | Return the exports of the module's own bindings, such as `export let a`, `export function b() {}`, `export { c }` and `export default d`, which are parsed in full. With `false`, only imports and the exports that have a `from` (`export * from "e"`, `export { f } from "f"`, `export import`) are returned, and the rest is skipped. Default `true`. |
70
+
71
+ On Carbon's 327 component scripts, mostly JSDoc-documented `export let` props, it takes 10.7 ms against `parseModule`'s 33.8 ms, and 4.5 ms with `localExports: false`; the largest script, 63 kB, takes 0.37 ms against 2.7 ms. It checks the TypeScript conformance tests, kit, immich and bits-ui against `parseModule`, with an `import` inserted after every top-level statement: the same nodes on all 24,354 modules.
72
+
73
+ ```ts
74
+ import { parseImportsExports } from "sveast";
75
+
76
+ function dependencies(source: string, typescript = false): string[] {
77
+ return parseImportsExports(source, { typescript, localExports: false }).flatMap(
78
+ (node) => ("source" in node && node.source ? [node.source.value] : []),
79
+ );
80
+ }
81
+ ```
82
+
83
+ ### `lexImportsExports(source) => LexedStatement[]` from `sveast/lexer`
84
+
85
+ A module's top-level `import` statements and `export … from` re-exports, read by hand instead of parsed: for a tool on a hot or eagerly loaded path, such as a preprocessor that rewrites a component's imports, that needs each statement's offsets, its source and its names, and not the AST. `dist/lexer.js` loads neither acorn nor the template parser, and minifies to 3.2 kB gzipped; a fresh Node process imports it in 1.4 ms, against 7.0 ms for `parseImportsExports`. It's also exported from `sveast`.
86
+
87
+ It finds statements the way `parseImportsExports` does with `localExports: false`, skipping comments, strings, templates, `import(...)`, `import.meta` and nested blocks, and stops after the last `import` or `export` that could start one. `export let`, `export function`, `export { a }` and other exports of the module's own bindings aren't returned. On every statement `parseImportsExports` accepts, the offsets, sources and names are the same: the tests check it on the corpus, on mutated modules and on generated statements, and `scripts/compare-imports.ts` on 8,749 files from other Svelte projects. It doesn't check syntax and never throws. A statement it can't read, such as `import { a from "a"` or TypeScript's `import a = require("a")`, has `source: null` and no specifiers and ends where it stopped reading, so leave it as written. `import type` and `type` before a name are always read, as with `typescript: true`.
88
+
89
+ ```ts
90
+ import { lexImportsExports } from "sveast/lexer";
91
+
92
+ for (const statement of lexImportsExports(source)) {
93
+ statement.kind; // "import" | "export"
94
+ statement.start; // offset of `import` or `export`
95
+ statement.end; // after the `;`, or the last token without one
96
+ statement.source; // { value: "carbon-components-svelte", start, end } (quotes included), or null
97
+ statement.typeOnly; // `import type …` or `export type …`
98
+ statement.specifiers;
99
+ // import: { kind: "default" | "namespace" | "named", imported, local, typeOnly, start, end }
100
+ // export: { kind: "all" | "namespace" | "named", local, exported, typeOnly, start, end }
101
+ }
102
+ ```
103
+
104
+ | Specifier | `import` | `export … from` |
105
+ |:---|:---|:---|
106
+ | `a` in `import a` | `default`, `imported: "default"`, `local: "a"` | |
107
+ | `* as a` | `namespace`, `imported: "*"`, `local: "a"` | `namespace`, `local: "*"`, `exported: "a"` |
108
+ | `*` | | `all`, `local: "*"`, `exported: null` |
109
+ | `b as c`, `"b-c" as d` | `named`, `imported: "b"`, `local: "c"` | `named`, `local: "b"`, `exported: "c"` |
110
+
111
+ Names are as the module sees them: escapes decoded, and a string name's value without its quotes. A specifier's `typeOnly` is also `true` in an `import type` or `export type`. Its `start` is that of `type` before the name, if any, and `end` is after the last name; for `export *`, after the `*`.
112
+
113
+ On Carbon's 327 component scripts it takes about 40% of the time `parseImportsExports` with `localExports: false` takes, and on the largest, 63 kB, about an eighth.
114
+
115
+ ### `createParser(support?) => { parse, parseModule, parseImportsExports }` from `sveast/core`
116
+
117
+ `parse`, `parseModule` and `parseImportsExports` without the two parts most tools can do without: the TypeScript plugin and the table of HTML's named character references. Together they're about a third of a bundle: `parse` and `parseModule` from `sveast` minify to 57.3 kB gzipped, and from `createParser()` to 40.3 kB. Pass back the parts you need; with both, the parsers are the same as `sveast`'s.
63
118
 
64
119
  ```ts
65
120
  import { createParser } from "sveast/core";
66
121
  import { typescript } from "sveast/typescript";
67
122
 
68
- const { parse, parseModule } = createParser({ typescript });
123
+ const { parse, parseModule, parseImportsExports } = createParser({ typescript });
69
124
  ```
70
125
 
71
126
  | Support | Without it |
72
127
  |:---|:---|
73
- | `typescript` from `sveast/typescript` (+8.8 kB gzipped) | A component with `<script lang="ts">`, or `parseModule` with `typescript: true`, throws an `Error`, not a `ParseError`: a missing import, not a syntax error, and never a JavaScript AST of TypeScript |
128
+ | `typescript` from `sveast/typescript` (+8.8 kB gzipped) | A component with `<script lang="ts">`, or `parseModule` or `parseImportsExports` with `typescript: true`, throws an `Error`, not a `ParseError`: a missing import, not a syntax error, and never a JavaScript AST of TypeScript |
74
129
  | `entities` from `sveast/entities` (+10.0 kB gzipped) | Text and attribute values decode numeric references, such as `&#169;`, and `&amp;`, `&apos;`, `&gt;`, `&lt;` and `&quot;`, but leave other names, such as `&copy;`, as written, so `Text.data` differs from svelte's where the source uses them |
75
130
 
76
131
  `sveast/core` also exports `ParseError`, the same class as `sveast`'s, and the types.
@@ -83,15 +138,56 @@ A `//` comment runs to the end of the line, so `string // the size` is a valid t
83
138
 
84
139
  ### `walk(node, visitor) => void`
85
140
 
86
- Visits `node` and every node under it, depth-first and in source order, calling `visitor.enter(node, parent, key, index)` before a node's children and `visitor.leave(node, parent, key, index)` after them. `parent[key]` is the node, or `parent[key][index]` when the field is an array; all three are `null` for the node you pass. Return `false` from `enter` to skip the node's children; `leave` is still called, so a stack you push in `enter` and pop in `leave` stays balanced. Return `STOP` from `enter` or `leave` to end the walk there: no more `enter` or `leave` calls, not even `leave` on the node's ancestors, e.g. to find the first use of a rune. Return another node from `enter` to put it in this one's place: `walk` writes it to `parent[key]` (or `parent[key][index]`), then calls `enter` on it and visits its children instead, and `leave` gets the replacement. So `return node.expression` on each `TSAsExpression` strips `a as B as C` down to `a`, one layer per `enter`. The node you pass to `walk` can't be replaced. Checking `node.type` narrows `node`.
141
+ Visits `node` and every node under it, depth-first and in source order, calling `visitor.enter(node, parent, key, index)` before a node's children and `visitor.leave(node, parent, key, index)` after them. `parent[key]` is the node, or `parent[key][index]` when the field is an array; all three are `null` for the node you pass. Return `SKIP` from `enter` to skip the node's children; `leave` is still called, so a stack you push in `enter` and pop in `leave` stays balanced. Return `STOP` from `enter` or `leave` to end the walk there: no more `enter` or `leave` calls, not even `leave` on the node's ancestors, e.g. to find the first use of a rune. Return another node from `enter` to put it in this one's place: `walk` writes it to `parent[key]` (or `parent[key][index]`), then calls `enter` on it and visits its children instead, and `leave` gets the replacement. So `return node.expression` on each `TSAsExpression` strips `a as B as C` down to `a`, one layer per `enter`. The node you pass to `walk` can't be replaced. Any other return value, `false` included, is ignored, so an expression-bodied `enter: (node) => node.type === "Component" && names.add(node.name)` still visits every node. Checking `node.type` narrows `node`.
87
142
 
88
143
  It works on any node the parsers return: a component's `Root`, a `Fragment`, an expression, a `parseModule` program, a `<style>`'s rules. It only reads the fields that hold child nodes, so it never descends into `loc`, comments or strings, and it throws on a node type it doesn't know. Two exceptions to source order: a component's sections are visited in scope order, `module`, `instance`, `fragment`, then `css`; and a template literal's `quasis` come before its `expressions`. `Root.options` isn't a node, so `<svelte:options>`'s attributes aren't visited; read them from `ast.options?.attributes`. acorn shares one `Identifier` between both names of `import { a }`, so it's visited once as `imported` and once as `local`, and likewise as `local` and `exported` in `export { a }`.
89
144
 
90
145
  `visitorKeys` is the table `walk` reads: the fields of each node type that hold child nodes, in source order, e.g. `visitorKeys.IfBlock` is `["test", "consequent", "alternate"]`. Use it with another walker, or to write your own.
91
146
 
147
+ ### `isReference(node, parent) => boolean`
148
+
149
+ Whether `node` is an `Identifier` that names a variable, function, class, import or other binding, where it's declared or where it's used, given the `parent` `walk` passes. It's the check svelte's analyzer makes, from the [`is-reference`](https://github.com/Rich-Harris/is-reference) package: `false` for a property or method name (`b` in `a.b`, `{ b: a }` and `class { b() {} }`, unless computed), the renamed side of an import or export (`b` in `import { b as a }` and `export { a as b }`), and labels. Unlike `is-reference`, it's also `false` for `import` and `new` in `import.meta` and `new.target`, an import attribute's key, and `b` in `export * as b from`, and for any node that isn't an `Identifier`.
150
+
151
+ TypeScript nodes hold types, so an `Identifier` whose parent is a TypeScript node isn't a reference (`B` in `let a: B`, `typeof b` in a type, interface and type alias names), except where it's a value: the expression of `as`, `satisfies`, `<T>a`, `a!`, `a<T>` and `export =`, an enum member's initializer, a parameter property (`a` in `constructor(private a)`), an enum's name, and the name and target of `import a = b`. Names of namespaces and of functions without a body (overloads, `declare function`) aren't references.
152
+
153
+ acorn shares one `Identifier` between both names of `import { a }` and `export { a }`, and `walk` visits it twice, so it's a reference on both visits.
154
+
155
+ ```ts
156
+ import { isReference, parseModule, walk } from "sveast";
157
+
158
+ const used = new Set<string>();
159
+ walk(parseModule("const a = b.c({ d: e });"), {
160
+ enter(node, parent) {
161
+ if (node.type === "Identifier" && isReference(node, parent)) used.add(node.name);
162
+ },
163
+ });
164
+ used; // Set { "a", "b", "e" }
165
+ ```
166
+
167
+ ### `extractIdentifiers(pattern) => Identifier[]`
168
+
169
+ The `Identifier` nodes a pattern binds, in source order, as svelte's `extract_identifiers` returns them: `a`, `c`, `d`, `e` and `f` in `let { a, b: [c, ...d], e = 1, ...f } = x`. Property keys, defaults and type annotations aren't included. It returns nodes rather than names, so you keep their positions; `.map((node) => node.name)` for the names.
170
+
171
+ It takes any binding or assignment target: a declarator's `id`, a function's params, including a parameter property (`a` in `constructor(private a)`), a catch clause's `param`, an each block's `context`, a snippet's `parameters`, or an assignment's `left`. A member expression binds nothing, so `[a.b, c] = x` yields only `c`, as in svelte. On an assignment's `left`, `a!`, `a as T`, `a satisfies T` and `<T>a` yield `a`.
172
+
173
+ ```ts
174
+ import { extractIdentifiers, parseModule } from "sveast";
175
+
176
+ const [declaration] = parseModule("let { a, b: [c] } = x;").body;
177
+ if (declaration.type === "VariableDeclaration") {
178
+ declaration.declarations.flatMap((d) => extractIdentifiers(d.id)).map((node) => node.name); // ["a", "c"]
179
+ }
180
+ ```
181
+
182
+ `walk`, `SKIP`, `STOP`, `visitorKeys`, `Visitor`, `isReference`, `extractIdentifiers` and the types are also exported from `sveast/walk`, which loads neither acorn nor the parser: for code that walks ASTs it gets from elsewhere, such as a cache, and must not pay for loading the parser. They're the same values as `sveast`'s.
183
+
184
+ ```ts
185
+ import { STOP, walk } from "sveast/walk";
186
+ ```
187
+
92
188
  ### Types
93
189
 
94
- `AST` is svelte's `AST` namespace (`AST.Root`, `AST.RegularElement`, `AST.CSS.Rule`, ...), corrected to match what the parser returns: `name_loc` and a comment's `loc` are optional, `Root.instance`/`module` are absent rather than `null` when there's no such `<script>`, `Root.js` is declared, and every directive has `modifiers`. `ParseOptions` and `ParseModuleOptions` are exported too.
190
+ `AST` is svelte's `AST` namespace (`AST.Root`, `AST.RegularElement`, `AST.CSS.Rule`, ...), corrected to match what the parser returns: `name_loc` and a comment's `loc` are optional, `Root.instance`/`module` are absent rather than `null` when there's no such `<script>`, `Root.js` is declared, and every directive has `modifiers`. `ParseOptions`, `ParseModuleOptions` and `ParseImportsExportsOptions` are exported too, and `lexImportsExports`'s `LexedStatement` (`LexedImport` or `LexedExport`), `LexedImportSpecifier`, `LexedExportSpecifier` and `LexedSource`.
95
191
 
96
192
  The estree node types are exported as well (`Program`, `Node`, `Statement`, `Expression`, `Identifier`, ...), so you don't need `@types/estree`. They're estree's, plus what the parser adds: `start`/`end` on every node, and the TypeScript plugin's nodes (`TSInterfaceDeclaration`, `TSTypeAnnotation`, `TSTypeReference`, ...; `TSNode` is their union) and fields (`typeAnnotation`, `typeParameters`, `typeArguments`, `returnType`, `importKind`/`exportKind`, ...). The TypeScript nodes are in the `Statement`, `Declaration` and `Expression` unions, so checking `node.type` narrows to them. Where the grammar only allows a string, such as an import's `source`, the type is `StringLiteral`, a `Literal` whose `value` is a `string`.
97
193
 
@@ -204,7 +300,7 @@ const css = parse(source, { css: false }).css?.content.styles ?? "";
204
300
  - **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.
205
301
  - **About 2.4× faster than svelte/compiler** (2.2–3.0× depending on the input), and its ASTs retain 47% less memory without `loc`.
206
302
  - **Fast to load.** A fresh process imports sveast in about 5 ms, against about 45 ms for `svelte/compiler`.
207
- - **Zero dependencies,** types included. 64 kB gzipped, acorn included.
303
+ - **Zero dependencies,** types included. 67 kB gzipped, acorn included.
208
304
 
209
305
  ## Benchmarks
210
306
 
package/core.d.ts CHANGED
@@ -21,13 +21,16 @@ export interface Parser {
21
21
  parse(source: string, options?: ParseOptions): AST.Root;
22
22
  /** `parseModule` from `sveast`, with only what the parser was created with. */
23
23
  parseModule(source: string, options?: ParseModuleOptions): Program;
24
+ /** `parseImportsExports` from `sveast`, with only what the parser was created with. */
25
+ parseImportsExports(source: string, options?: ParseImportsExportsOptions): ModuleDeclaration[];
24
26
  }
25
27
 
26
28
  /**
27
- * Creates `parse` and `parseModule` that bundle only what's passed in:
28
- * the TypeScript plugin and the table of HTML's named character
29
- * references are about a third of sveast's size, and a bundler leaves
30
- * out whichever isn't imported. With both, they're the same as `sveast`'s.
29
+ * Creates `parse`, `parseModule` and `parseImportsExports` that bundle
30
+ * only what's passed in: the TypeScript plugin and the table of HTML's
31
+ * named character references are about a third of sveast's size, and a
32
+ * bundler leaves out whichever isn't imported. With both, they're the
33
+ * same as `sveast`'s.
31
34
  *
32
35
  * ```ts
33
36
  * import { createParser } from "sveast/core";
@@ -87,6 +90,25 @@ export interface ParseModuleOptions {
87
90
  comments?: boolean;
88
91
  }
89
92
 
93
+ /** Options for {@link parseImportsExports}. */
94
+ export interface ParseImportsExportsOptions {
95
+ /**
96
+ * Parse TypeScript, e.g. `import type`, `export type` and
97
+ * `import a = require("a")`. Default `false`.
98
+ */
99
+ typescript?: boolean;
100
+ /**
101
+ * Return the `export` statements that export the module's own bindings,
102
+ * such as `export let a`, `export function b() {}`, `export { c }` and
103
+ * `export default d`. With `false`, only imports and the exports that
104
+ * have a `from`, such as `export * from "e"` and `export { f } from "f"`,
105
+ * are returned, and the rest is skipped like other code, which is faster
106
+ * when most statements are exported, as in a component's props.
107
+ * Default `true`.
108
+ */
109
+ localExports?: boolean;
110
+ }
111
+
90
112
  /** TypeScript support for {@link createParser}, from `sveast/typescript`. */
91
113
  export interface TypeScriptSupport {
92
114
  readonly support: "typescript";
package/core.js CHANGED
@@ -1 +1 @@
1
- import{d}from"./shared-7q7hyetp.js";import"./shared-cx7c0gk3.js";import{e}from"./shared-t6kshmn4.js";import{Ee}from"./shared-c2k7d7rg.js";import{m}from"./shared-kqcn291j.js";import{_e,Pe}from"./shared-da2hc1fx.js";function u(p={}){let o=_e(p.typescript),s={typescript:o,entityNames:Pe(p.entities)??Ee};return{parse:(t,r)=>d(t,r,s),parseModule:(t,r={})=>m(t,r,o)}}export{e as ParseError,u as createParser};
1
+ import{b}from"./shared-gyydaea2.js";import"./shared-as9can03.js";import{e}from"./shared-pyjcz2f8.js";import{Re}from"./shared-g16dt48m.js";import"./shared-pmkdz7sc.js";import{v}from"./shared-b37zemjd.js";import{S}from"./shared-qnfwr59k.js";import{qe,De}from"./shared-pt7hcxxy.js";function u(o={}){let p=qe(o.typescript),s={typescript:p,entityNames:De(o.entities)??Re};return{parse:(t,r)=>b(t,r,s),parseModule:(t,r={})=>v(t,r,p),parseImportsExports:(t,r={})=>S(t,r,p)}}export{e as ParseError,u as createParser};
package/entities.js CHANGED
@@ -1 +1 @@
1
- import"./shared-c2k7d7rg.js";import{S}from"./shared-p7c7qbej.js";import{Ce}from"./shared-da2hc1fx.js";var p=Ce(S);export{p as entities};
1
+ import"./shared-g16dt48m.js";import{C}from"./shared-1xjsv3h6.js";import{$e}from"./shared-pt7hcxxy.js";var p=$e(C);export{p as entities};
package/index.d.ts CHANGED
@@ -15,6 +15,86 @@ export declare function isValidType(text: string, options?: {
15
15
  inline?: boolean;
16
16
  }): boolean;
17
17
 
18
+ /** A module specifier: the string after `from`, or after `import` in `import "a"`. */
19
+ export interface LexedSource {
20
+ /** The string's value, with escapes decoded. */
21
+ value: string;
22
+ /** The offset of the opening quote. */
23
+ start: number;
24
+ /** The offset after the closing quote. */
25
+ end: number;
26
+ }
27
+
28
+ /** A name an `import` binds. */
29
+ export interface LexedImportSpecifier {
30
+ /** `"default"` for `a` in `import a from`, `"namespace"` for `* as a`, `"named"` for a name in braces. */
31
+ kind: "default" | "namespace" | "named";
32
+ /** The name in the imported module: `"default"`, `"*"` for a namespace, or the name before `as`, with a string's value for `"a-b" as c`. */
33
+ imported: string;
34
+ /** The name it's bound to in this module. */
35
+ local: string;
36
+ /** Whether it's only a type: in `import type`, or with `type` before it. */
37
+ typeOnly: boolean;
38
+ /** The offset of the name, of `*`, or of `type` before the name. */
39
+ start: number;
40
+ /** The offset after the local name. */
41
+ end: number;
42
+ }
43
+
44
+ /** A name an `export … from` re-exports. */
45
+ export interface LexedExportSpecifier {
46
+ /** `"all"` for `export *`, `"namespace"` for `export * as a`, `"named"` for a name in braces. */
47
+ kind: "all" | "namespace" | "named";
48
+ /** The name in the other module: `"*"` for `all` and `namespace`, or the name before `as`, with a string's value for `"a-b" as c`. */
49
+ local: string;
50
+ /** The name it's exported as, or `null` for `export *`. */
51
+ exported: string | null;
52
+ /** Whether it's only a type: in `export type`, or with `type` before it. */
53
+ typeOnly: boolean;
54
+ /** The offset of the name, of `*`, or of `type` before the name. */
55
+ start: number;
56
+ /** The offset after the exported name, or after `*` for `export *`. */
57
+ end: number;
58
+ }
59
+
60
+ interface LexedStatementBase {
61
+ /** The offset of `import` or `export`. */
62
+ start: number;
63
+ /** The offset after the statement's `;`, or its last token without one. */
64
+ end: number;
65
+ /** The module it imports from, or `null` for a statement the lexer doesn't recognize. */
66
+ source: LexedSource | null;
67
+ /** Whether it's `import type` or `export type`. */
68
+ typeOnly: boolean;
69
+ }
70
+
71
+ /** An `import` statement. */
72
+ export interface LexedImport extends LexedStatementBase {
73
+ kind: "import";
74
+ specifiers: LexedImportSpecifier[];
75
+ }
76
+
77
+ /** An `export … from` statement. */
78
+ export interface LexedExport extends LexedStatementBase {
79
+ kind: "export";
80
+ specifiers: LexedExportSpecifier[];
81
+ }
82
+
83
+ export type LexedStatement = LexedImport | LexedExport;
84
+
85
+ /**
86
+ * A module's top-level `import` statements and `export … from`
87
+ * re-exports, read without a parser: each statement's offsets, its
88
+ * source and the names it imports or re-exports. It finds statements as
89
+ * {@link parseImportsExports} does with `localExports: false`, and on
90
+ * every statement that accepts, gives the same offsets, sources and names,
91
+ * but it doesn't check syntax and never throws. A statement it can't read,
92
+ * including TypeScript's `import a = require("a")`, is returned with
93
+ * `source: null` and no specifiers, ending where it stopped reading.
94
+ * TypeScript's `type` forms are always read.
95
+ */
96
+ export declare function lexImportsExports(source: string): LexedStatement[];
97
+
18
98
  /**
19
99
  * Options for {@link parse}. The defaults give svelte's own output, minus
20
100
  * `loc` and `name_loc`.
@@ -64,6 +144,25 @@ export interface ParseModuleOptions {
64
144
  comments?: boolean;
65
145
  }
66
146
 
147
+ /** Options for {@link parseImportsExports}. */
148
+ export interface ParseImportsExportsOptions {
149
+ /**
150
+ * Parse TypeScript, e.g. `import type`, `export type` and
151
+ * `import a = require("a")`. Default `false`.
152
+ */
153
+ typescript?: boolean;
154
+ /**
155
+ * Return the `export` statements that export the module's own bindings,
156
+ * such as `export let a`, `export function b() {}`, `export { c }` and
157
+ * `export default d`. With `false`, only imports and the exports that
158
+ * have a `from`, such as `export * from "e"` and `export { f } from "f"`,
159
+ * are returned, and the rest is skipped like other code, which is faster
160
+ * when most statements are exported, as in a component's props.
161
+ * Default `true`.
162
+ */
163
+ localExports?: boolean;
164
+ }
165
+
67
166
  /**
68
167
  * Parses a Svelte component into svelte's modern AST, the same as
69
168
  * `parse(source, { modern: true })` from svelte/compiler. From svelte's
@@ -1697,6 +1796,21 @@ export interface TSInstantiationExpression extends BaseNode {
1697
1796
  typeArguments: TSTypeParameterInstantiation;
1698
1797
  }
1699
1798
 
1799
+ /**
1800
+ * Parses only a module's top-level `import` and `export` statements, e.g.
1801
+ * to rewrite or follow its imports, and skips the rest without building
1802
+ * it. Each node is the one {@link parseModule} returns for the statement
1803
+ * with `comments: false`: `ImportDeclaration`, `ExportNamedDeclaration`,
1804
+ * `ExportDefaultDeclaration` and `ExportAllDeclaration`, plus, with
1805
+ * `typescript: true`, `TSImportEqualsDeclaration`, `TSExportAssignment`
1806
+ * and `TSNamespaceExportDeclaration`. `import(...)` and `import.meta`
1807
+ * aren't statements, so they aren't returned.
1808
+ *
1809
+ * Throws a {@link ParseError} (`js_parse_error`) on a syntax error in one
1810
+ * of those statements. Syntax errors in the rest aren't reported.
1811
+ */
1812
+ export declare function parseImportsExports(source: string, options?: ParseImportsExportsOptions): ModuleDeclaration[];
1813
+
1700
1814
  /**
1701
1815
  * Parses a JavaScript or TypeScript module, e.g. a `.ts` file a component
1702
1816
  * imports, into the same kind of AST as a component's `<script>`:
@@ -1954,14 +2068,22 @@ export declare const visitorKeys: {
1954
2068
  */
1955
2069
  export declare const STOP: unique symbol;
1956
2070
 
2071
+ /**
2072
+ * Return it from `enter` to skip the node's children; `leave` is still
2073
+ * called on the node.
2074
+ */
2075
+ export declare const SKIP: unique symbol;
2076
+
1957
2077
  /** The callbacks {@link walk} calls for each node. */
1958
2078
  export interface Visitor {
1959
2079
  /**
1960
- * Called before the node's children. Return `false` to skip them; `leave`
1961
- * is still called, so the two stay paired. Return {@link STOP} to end the
1962
- * walk. Return another node to put it in this one's place: `walk` writes it
1963
- * to `parent[key]` (or `parent[key][index]`), then calls `enter` on it and
1964
- * visits its children instead.
2080
+ * Called before the node's children. Return {@link SKIP} to skip them;
2081
+ * `leave` is still called, so the two stay paired. Return {@link STOP} to
2082
+ * end the walk. Return another node to put it in this one's place: `walk`
2083
+ * writes it to `parent[key]` (or `parent[key][index]`), then calls `enter`
2084
+ * on it and visits its children instead. Any other value, `false`
2085
+ * included, is ignored, so `(node) => node.type === "Component" &&
2086
+ * names.add(node.name)` visits every node.
1965
2087
  */
1966
2088
  enter?(node: AST.SvelteNode, parent: AST.SvelteNode | null, key: string | null, index: number | null): unknown;
1967
2089
  /** Called after the node's children. Return {@link STOP} to end the walk. */
@@ -1975,3 +2097,29 @@ export interface Visitor {
1975
2097
  * array; all three are `null` for `node` itself.
1976
2098
  */
1977
2099
  export declare function walk(node: AST.SvelteNode, visitor: Visitor): void;
2100
+
2101
+ /**
2102
+ * The `Identifier` nodes a pattern binds, in source order: `a`, `c`, `d`,
2103
+ * `e` and `f` in `{ a, b: [c, ...d], e = 1, ...f }`. Not the property
2104
+ * keys, defaults or type annotations. A member expression, which an
2105
+ * assignment's pattern can hold, as in `[a.b] = c`, binds nothing, so it
2106
+ * yields no identifiers, as in svelte's `extract_identifiers`. Also takes
2107
+ * a function's params as they are, parameter properties included, and an
2108
+ * assignment's `left`, where `a!`, `a as T`, `a satisfies T` and `<T>a`
2109
+ * yield `a`.
2110
+ */
2111
+ export declare function extractIdentifiers(pattern: Pattern | TSParameterProperty | AssignmentExpression["left"]): Identifier[];
2112
+
2113
+ /**
2114
+ * Whether `node` is an `Identifier` that names a binding in the value
2115
+ * namespace, where it's declared or where it's used, given its `parent`
2116
+ * from `walk`. `false` for a property, method or label name, the
2117
+ * renamed side of an import or export specifier, `import.meta`'s and
2118
+ * `new.target`'s names, an import attribute's key, and every name in a
2119
+ * type. The check svelte's analyzer makes, from the `is-reference` package,
2120
+ * plus TypeScript: an `Identifier` whose parent is a TypeScript node is a
2121
+ * reference only as the expression of `as`, `satisfies`, `<T>x`, `x!`,
2122
+ * `f<T>` or `export =`, an enum member's initializer, a parameter
2123
+ * property, an enum's name, or the name or target of `import a = b`.
2124
+ */
2125
+ export declare function isReference(node: AST.SvelteNode, parent: AST.SvelteNode | null): boolean;
package/index.js CHANGED
@@ -1 +1 @@
1
- import{isValidType as o}from"./is-valid-type.js";import{parse as p}from"./parse.js";import{ParseError as y,parseModule as i}from"./parse-module.js";import{STOP as f,visitorKeys as m,walk as x}from"./walk.js";export{y as ParseError,f as STOP,o as isValidType,p as parse,i as parseModule,m as visitorKeys,x as walk};
1
+ import{isValidType as t}from"./is-valid-type.js";import{lexImportsExports as p}from"./lexer.js";import{parse as x}from"./parse.js";import{parseImportsExports as m}from"./parse-imports-exports.js";import{ParseError as a,parseModule as y}from"./parse-module.js";import{extractIdentifiers as l,isReference as n,SKIP as I,STOP as S,visitorKeys as c,walk as E}from"./walk.js";export{a as ParseError,I as SKIP,S as STOP,l as extractIdentifiers,n as isReference,t as isValidType,p as lexImportsExports,x as parse,m as parseImportsExports,y as parseModule,c as visitorKeys,E as walk};
package/is-valid-type.js CHANGED
@@ -1 +1 @@
1
- import{t}from"./shared-cx7c0gk3.js";import{y}from"./shared-xw9zc4rz.js";var a=new Set("abstract accessor arguments as asserts async await break case catch class const continue debugger declare default delete do else enum eval export extends false finally for function get global if implements import in infer instanceof interface intrinsic is keyof let module namespace new null of out override package private protected public readonly return satisfies set static super switch this throw true try type typeof unique var void while with yield".split(" ")),c=new Set("any bigint boolean false never null number object string symbol true undefined unknown void".split(" ")),d=new Set("get set new readonly static public private protected".split(" "));function u(e){return e>=97&&e<=122||e>=65&&e<=90||e===95||e===36}function o(e){return e>=48&&e<=57}function h(e){return u(e)||o(e)}class l{text;pos=0;constructor(e){this.text=e}readAll(){if(!this.type())return!1;return this.peek(),this.pos===this.text.length}peek(){let e=this.text,s=this.pos,r=e.charCodeAt(s);while(r===32||r===9)r=e.charCodeAt(++s);return this.pos=s,r}eat(e){if(this.peek()!==e.charCodeAt(0))return!1;return this.pos++,!0}word(){if(!u(this.peek()))return;let e=this.pos,s=e+1;while(h(this.text.charCodeAt(s)))s++;return this.pos=s,this.text.slice(e,s)}name(){let e=this.word();return e!==void 0&&!a.has(e)}string(){let e=this.peek();if(e!==34&&e!==39)return!1;let s=this.text;for(let r=this.pos+1;r<s.length;r++){let i=s.charCodeAt(r);if(i===e)return this.pos=r+1,!0;if(i===92||i===10||i===13||i===8232||i===8233)return!1}return!1}number(){let e=this.text,s=this.pos;if(e.charCodeAt(s)===45)s++;let r=s;while(o(e.charCodeAt(s)))s++;if(s===r||s-r>1&&e.charCodeAt(r)===48)return!1;if(e.charCodeAt(s)===46){let n=++s;while(o(e.charCodeAt(s)))s++;if(s===n)return!1}let i=e.charCodeAt(s);if(h(i)||i===46)return!1;return this.pos=s,!0}type(){if(this.peek()===40){let e=this.pos;if(this.functionType())return!0;this.pos=e}do do if(!this.operatorType())return!1;while(this.eat("&"));while(this.eat("|"));return!0}operatorType(){let e=this.pos;if(this.word()==="keyof")return this.operatorType();if(this.pos=e,!this.primaryType())return!1;while(this.eat("[")){if(this.eat("]"))continue;if(!this.type()||!this.eat("]"))return!1}return!0}primaryType(){let e=this.peek();if(e===40)return this.pos++,this.type()&&this.eat(")");if(e===123)return this.objectType();if(e===91)return this.tupleType();if(e===34||e===39)return this.string();if(e===45||o(e))return this.number();let s=this.word();if(s===void 0)return!1;if(s==="typeof")return this.typeQuery();if(s==="import")return this.importType(!0);if(c.has(s)){let r=this.peek();return r!==46&&r!==60}if(a.has(s))return!1;while(this.eat("."))if(this.word()===void 0)return!1;return!this.eat("<")||this.typeArguments()}typeArguments(){do if(!this.type())return!1;while(this.eat(","));return this.eat(">")}typeQuery(){let e=this.pos;if(this.word()==="import")return this.importType(!1);this.pos=e;do if(!this.name())return!1;while(this.eat("."));return!0}importType(e){if(!this.eat("(")||!this.string()||!this.eat(")"))return!1;while(this.eat("."))if(!this.name())return!1;return!e||!this.eat("<")||this.typeArguments()}objectType(){this.pos++;while(!this.eat("}")){if(!this.typeMember())return!1;if(!this.eat(";")&&!this.eat(",")&&this.peek()!==125)return!1}return!0}typeMember(){let e=this.peek();if(this.eat("[")){let s=this.peek();if(s===34||s===39){if(!this.string()||!this.eat("]"))return!1}else return this.name()&&this.eat(":")&&this.type()&&this.eat("]")&&this.eat(":")&&this.type()}else if(e===34||e===39){if(!this.string())return!1}else{let s=this.word();if(s===void 0||d.has(s))return!1}return this.eat("?"),this.eat(":")&&this.type()}tupleType(){if(this.pos++,this.eat("]"))return!0;do if(!this.type())return!1;while(this.eat(","));return this.eat("]")}functionType(){if(this.pos++,!this.eat(")")){let e=new Set;for(;;){this.peek();let s=this.text.startsWith("...",this.pos);if(s)this.pos+=3;let r=this.word();if(r===void 0||a.has(r)||e.has(r))return!1;if(e.add(r),!s)this.eat("?");if(!this.eat(":")||!this.type())return!1;if(s||!this.eat(","))break}if(!this.eat(")"))return!1}if(this.peek(),!this.text.startsWith("=>",this.pos))return!1;return this.pos+=2,this.type()}}function f(e){return new l(e).readAll()}var m=t.extend(y);function S(e,s={}){return f(e)||b(e,s.inline??!1)}function b(e,s){let r=!1,i=new m(s?{ecmaVersion:16,sourceType:"module",onComment:(n,T,A,p)=>{if(!n&&p===e.length)r=!0}}:{ecmaVersion:16,sourceType:"module"},e);try{return i.parseWholeType(),!r}catch(n){if(n instanceof SyntaxError)return!1;throw n}}export{S as isValidType,b as parsesAsType};
1
+ import{t}from"./shared-as9can03.js";import{T}from"./shared-yr6e6h72.js";var a=new Set("abstract accessor arguments as asserts async await break case catch class const continue debugger declare default delete do else enum eval export extends false finally for function get global if implements import in infer instanceof interface intrinsic is keyof let module namespace new null of out override package private protected public readonly return satisfies set static super switch this throw true try type typeof unique var void while with yield".split(" ")),c=new Set("any bigint boolean false never null number object string symbol true undefined unknown void".split(" ")),d=new Set("get set new readonly static public private protected".split(" "));function u(e){return e>=97&&e<=122||e>=65&&e<=90||e===95||e===36}function o(e){return e>=48&&e<=57}function h(e){return u(e)||o(e)}class l{text;pos=0;constructor(e){this.text=e}readAll(){if(!this.type())return!1;return this.peek(),this.pos===this.text.length}peek(){let e=this.text,s=this.pos,r=e.charCodeAt(s);while(r===32||r===9)r=e.charCodeAt(++s);return this.pos=s,r}eat(e){if(this.peek()!==e.charCodeAt(0))return!1;return this.pos++,!0}word(){if(!u(this.peek()))return;let e=this.pos,s=e+1;while(h(this.text.charCodeAt(s)))s++;return this.pos=s,this.text.slice(e,s)}name(){let e=this.word();return e!==void 0&&!a.has(e)}string(){let e=this.peek();if(e!==34&&e!==39)return!1;let s=this.text;for(let r=this.pos+1;r<s.length;r++){let i=s.charCodeAt(r);if(i===e)return this.pos=r+1,!0;if(i===92||i===10||i===13||i===8232||i===8233)return!1}return!1}number(){let e=this.text,s=this.pos;if(e.charCodeAt(s)===45)s++;let r=s;while(o(e.charCodeAt(s)))s++;if(s===r||s-r>1&&e.charCodeAt(r)===48)return!1;if(e.charCodeAt(s)===46){let n=++s;while(o(e.charCodeAt(s)))s++;if(s===n)return!1}let i=e.charCodeAt(s);if(h(i)||i===46)return!1;return this.pos=s,!0}type(){if(this.peek()===40){let e=this.pos;if(this.functionType())return!0;this.pos=e}do do if(!this.operatorType())return!1;while(this.eat("&"));while(this.eat("|"));return!0}operatorType(){let e=this.pos;if(this.word()==="keyof")return this.operatorType();if(this.pos=e,!this.primaryType())return!1;while(this.eat("[")){if(this.eat("]"))continue;if(!this.type()||!this.eat("]"))return!1}return!0}primaryType(){let e=this.peek();if(e===40)return this.pos++,this.type()&&this.eat(")");if(e===123)return this.objectType();if(e===91)return this.tupleType();if(e===34||e===39)return this.string();if(e===45||o(e))return this.number();let s=this.word();if(s===void 0)return!1;if(s==="typeof")return this.typeQuery();if(s==="import")return this.importType(!0);if(c.has(s)){let r=this.peek();return r!==46&&r!==60}if(a.has(s))return!1;while(this.eat("."))if(this.word()===void 0)return!1;return!this.eat("<")||this.typeArguments()}typeArguments(){do if(!this.type())return!1;while(this.eat(","));return this.eat(">")}typeQuery(){let e=this.pos;if(this.word()==="import")return this.importType(!1);this.pos=e;do if(!this.name())return!1;while(this.eat("."));return!0}importType(e){if(!this.eat("(")||!this.string()||!this.eat(")"))return!1;while(this.eat("."))if(!this.name())return!1;return!e||!this.eat("<")||this.typeArguments()}objectType(){this.pos++;while(!this.eat("}")){if(!this.typeMember())return!1;if(!this.eat(";")&&!this.eat(",")&&this.peek()!==125)return!1}return!0}typeMember(){let e=this.peek();if(this.eat("[")){let s=this.peek();if(s===34||s===39){if(!this.string()||!this.eat("]"))return!1}else return this.name()&&this.eat(":")&&this.type()&&this.eat("]")&&this.eat(":")&&this.type()}else if(e===34||e===39){if(!this.string())return!1}else{let s=this.word();if(s===void 0||d.has(s))return!1}return this.eat("?"),this.eat(":")&&this.type()}tupleType(){if(this.pos++,this.eat("]"))return!0;do if(!this.type())return!1;while(this.eat(","));return this.eat("]")}functionType(){if(this.pos++,!this.eat(")")){let e=new Set;for(;;){this.peek();let s=this.text.startsWith("...",this.pos);if(s)this.pos+=3;let r=this.word();if(r===void 0||a.has(r)||e.has(r))return!1;if(e.add(r),!s)this.eat("?");if(!this.eat(":")||!this.type())return!1;if(s||!this.eat(","))break}if(!this.eat(")"))return!1}if(this.peek(),!this.text.startsWith("=>",this.pos))return!1;return this.pos+=2,this.type()}}function f(e){return new l(e).readAll()}var y=t.extend(T);function S(e,s={}){return f(e)||m(e,s.inline??!1)}function m(e,s){let r=!1,i=new y(s?{ecmaVersion:16,sourceType:"module",onComment:(n,b,A,p)=>{if(!n&&p===e.length)r=!0}}:{ecmaVersion:16,sourceType:"module"},e);try{return i.parseWholeType(),!r}catch(n){if(n instanceof SyntaxError)return!1;throw n}}export{S as isValidType,m as parsesAsType};
package/lexer.d.ts ADDED
@@ -0,0 +1,79 @@
1
+ /** A module specifier: the string after `from`, or after `import` in `import "a"`. */
2
+ export interface LexedSource {
3
+ /** The string's value, with escapes decoded. */
4
+ value: string;
5
+ /** The offset of the opening quote. */
6
+ start: number;
7
+ /** The offset after the closing quote. */
8
+ end: number;
9
+ }
10
+
11
+ /** A name an `import` binds. */
12
+ export interface LexedImportSpecifier {
13
+ /** `"default"` for `a` in `import a from`, `"namespace"` for `* as a`, `"named"` for a name in braces. */
14
+ kind: "default" | "namespace" | "named";
15
+ /** The name in the imported module: `"default"`, `"*"` for a namespace, or the name before `as`, with a string's value for `"a-b" as c`. */
16
+ imported: string;
17
+ /** The name it's bound to in this module. */
18
+ local: string;
19
+ /** Whether it's only a type: in `import type`, or with `type` before it. */
20
+ typeOnly: boolean;
21
+ /** The offset of the name, of `*`, or of `type` before the name. */
22
+ start: number;
23
+ /** The offset after the local name. */
24
+ end: number;
25
+ }
26
+
27
+ /** A name an `export … from` re-exports. */
28
+ export interface LexedExportSpecifier {
29
+ /** `"all"` for `export *`, `"namespace"` for `export * as a`, `"named"` for a name in braces. */
30
+ kind: "all" | "namespace" | "named";
31
+ /** The name in the other module: `"*"` for `all` and `namespace`, or the name before `as`, with a string's value for `"a-b" as c`. */
32
+ local: string;
33
+ /** The name it's exported as, or `null` for `export *`. */
34
+ exported: string | null;
35
+ /** Whether it's only a type: in `export type`, or with `type` before it. */
36
+ typeOnly: boolean;
37
+ /** The offset of the name, of `*`, or of `type` before the name. */
38
+ start: number;
39
+ /** The offset after the exported name, or after `*` for `export *`. */
40
+ end: number;
41
+ }
42
+
43
+ interface LexedStatementBase {
44
+ /** The offset of `import` or `export`. */
45
+ start: number;
46
+ /** The offset after the statement's `;`, or its last token without one. */
47
+ end: number;
48
+ /** The module it imports from, or `null` for a statement the lexer doesn't recognize. */
49
+ source: LexedSource | null;
50
+ /** Whether it's `import type` or `export type`. */
51
+ typeOnly: boolean;
52
+ }
53
+
54
+ /** An `import` statement. */
55
+ export interface LexedImport extends LexedStatementBase {
56
+ kind: "import";
57
+ specifiers: LexedImportSpecifier[];
58
+ }
59
+
60
+ /** An `export … from` statement. */
61
+ export interface LexedExport extends LexedStatementBase {
62
+ kind: "export";
63
+ specifiers: LexedExportSpecifier[];
64
+ }
65
+
66
+ export type LexedStatement = LexedImport | LexedExport;
67
+
68
+ /**
69
+ * A module's top-level `import` statements and `export … from`
70
+ * re-exports, read without a parser: each statement's offsets, its
71
+ * source and the names it imports or re-exports. It finds statements as
72
+ * {@link parseImportsExports} does with `localExports: false`, and on
73
+ * every statement that accepts, gives the same offsets, sources and names,
74
+ * but it doesn't check syntax and never throws. A statement it can't read,
75
+ * including TypeScript's `import a = require("a")`, is returned with
76
+ * `source: null` and no specifiers, ending where it stopped reading.
77
+ * TypeScript's `type` forms are always read.
78
+ */
79
+ export declare function lexImportsExports(source: string): LexedStatement[];
package/lexer.js ADDED
@@ -0,0 +1,2 @@
1
+ import{r,k,h,p,y}from"./shared-pmkdz7sc.js";function g(e){let t=new m(e),i=[];return k(e,!1,(n,s)=>{let a=t.statement(s);return i.push(a),a.end}),i}class m{source;pos=0;end=0;escapeEnd=0;constructor(e){this.source=e}statement(e){this.pos=this.end=e+6;let t=this.source.charCodeAt(e)===105?{kind:"import",start:e,end:0,source:null,typeOnly:!1,specifiers:[]}:{kind:"export",start:e,end:0,source:null,typeOnly:!1,specifiers:[]};if(!(t.kind==="import"?this.import(t):this.export(t)))t.source=null,t.specifiers=[];return t.end=this.end,t}import(e){let t=this.string();if(t!==null)return e.source=t,this.finish();if(this.isWord("type")){let a=this.peek(4);if(a===123||a===42||c(a)&&!this.isWordAfter(4,"from"))e.typeOnly=!0,this.advance(4)}let{specifiers:i,typeOnly:n}=e,s=this.at();if(s!==123&&s!==42){let a=this.pos,o=this.name();if(o===null)return!1;if(this.at()===61)return this.importEquals();if(i.push({kind:"default",imported:"default",local:o,typeOnly:n,start:a,end:this.end}),!this.eat(44))return this.from(e);s=this.at()}if(s===42){let a=this.pos;if(this.advance(1),!this.eatWord("as"))return!1;let o=this.name();if(o===null)return!1;return i.push({kind:"namespace",imported:"*",local:o,typeOnly:n,start:a,end:this.end}),this.from(e)}if(!this.eat(123))return!1;while(!this.eat(125)){let a=this.skip(),o=this.typeModifier()||n,u=this.name(),d=u??this.string()?.value;if(d===void 0)return!1;let l=u;if(this.eatWord("as"))l=this.name();if(l===null)return!1;if(i.push({kind:"named",imported:d,local:l,typeOnly:o,start:a,end:this.end}),!this.eat(44)&&this.at()!==125)return!1}return this.from(e)}export(e){if(this.eatWord("import")){if(this.isWord("type")&&c(this.peek(4))&&!this.isWordAfter(4,"from"))e.typeOnly=!0,this.advance(4);return this.name()!==null&&this.at()===61&&this.importEquals()}if(this.isWord("type")){let n=this.peek(4);if(n===123||n===42)e.typeOnly=!0,this.advance(4)}let{specifiers:t,typeOnly:i}=e;if(this.at()===42){let n=this.pos;this.advance(1);let s=null;if(this.eatWord("as")){if(s=this.moduleExportName(),s===null)return!1}return t.push({kind:s===null?"all":"namespace",local:"*",exported:s,typeOnly:i,start:n,end:this.end}),this.from(e)}if(!this.eat(123))return!1;while(!this.eat(125)){let n=this.skip(),s=this.typeModifier()||i,a=this.moduleExportName();if(a===null)return!1;let o=a;if(this.eatWord("as"))o=this.moduleExportName();if(o===null)return!1;if(t.push({kind:"named",local:a,exported:o,typeOnly:s,start:n,end:this.end}),!this.eat(44)&&this.at()!==125)return!1}return this.from(e)}typeModifier(){if(!this.isWord("type"))return!1;let e=this.peek(4);if(e===34||e===39||c(e)&&!this.isWordAfter(4,"as"))return this.advance(4),!0;return!1}importEquals(){if(this.advance(1),this.isWord("require")&&this.peek(7)===40){if(this.advance(7),this.eat(40),this.string()===null||!this.eat(41))return!1}else do if(this.name()===null)return!1;while(this.eat(46));return this.eat(59),!0}from(e){if(!this.eatWord("from"))return!1;return e.source=this.string(),e.source!==null&&this.finish()}finish(){let e=this.isWord("with")?4:0;if(this.isWord("assert")&&!x(this.source,this.end,this.pos))e=6;if(e!==0&&this.peek(e)===123){this.advance(e),this.eat(123);while(!this.eat(125))if(this.string()===null&&this.name()===null&&!this.eat(58)&&!this.eat(44))return!1}return this.eat(59),!0}skip(){return this.pos=p(this.source,this.pos),this.pos}at(){return this.source.charCodeAt(this.skip())}advance(e){this.pos+=e,this.end=this.pos}peek(e){return this.source.charCodeAt(p(this.source,this.pos+e))}isWordAfter(e,t){return y(this.source,p(this.source,this.pos+e),t)}eat(e){if(this.at()!==e)return!1;return this.advance(1),!0}isWord(e){return y(this.source,this.skip(),e)}eatWord(e){if(!this.isWord(e))return!1;return this.advance(e.length),!0}moduleExportName(){return this.name()??this.string()?.value??null}name(){let e=this.source,t=this.skip();if(!r(e.charCodeAt(t))||h(e.charCodeAt(t)))return null;let i="",n=t,s=t;while(s<e.length){let a=e.charCodeAt(s);if(a===92){if(e.charCodeAt(s+1)!==117)return null;let o=this.unicodeEscape(s+2);if(o===null)return null;i+=e.slice(n,s)+o,s=n=this.escapeEnd}else if(r(a))s++;else break}return this.advance(s-t),i+e.slice(n,s)}string(){let e=this.source,t=this.skip(),i=e.charCodeAt(t);if(i!==34&&i!==39)return null;let n="",s=t+1;for(let a=s;a<e.length;){let o=e.charCodeAt(a);if(o===i)return this.advance(a+1-t),{value:n+e.slice(s,a),start:t,end:a+1};if(o===10||o===13)return null;if(o===92){let u=this.escape(a+1);if(u===null)return null;n+=e.slice(s,a)+u,a=s=this.escapeEnd}else a++}return null}escape(e){let t=this.source.charCodeAt(e);switch(this.escapeEnd=e+1,t){case 110:return`
2
+ `;case 114:return"\r";case 116:return"\t";case 98:return"\b";case 118:return"\v";case 102:return"\f";case 48:return h(this.source.charCodeAt(e+1))?null:"\x00";case 120:return this.hexEscape(e+1,2);case 117:return this.unicodeEscape(e+1);case 13:if(this.source.charCodeAt(e+1)===10)this.escapeEnd++;return"";case 10:case 8232:case 8233:return"";default:return h(t)||e>=this.source.length?null:String.fromCharCode(t)}}unicodeEscape(e){if(this.source.charCodeAt(e)!==123)return this.hexEscape(e,4);let t=0,i=e+1;for(;this.source.charCodeAt(i)!==125;i++){let n=f(this.source.charCodeAt(i));if(n===-1)return null;if(t=t*16+n,t>1114111)return null}if(i===e+1)return null;return this.escapeEnd=i+1,String.fromCodePoint(t)}hexEscape(e,t){let i=0;for(let n=e;n<e+t;n++){let s=f(this.source.charCodeAt(n));if(s===-1)return null;i=i*16+s}return this.escapeEnd=e+t,String.fromCharCode(i)}}function c(e){return e!==92&&r(e)&&!h(e)}function f(e){if(h(e))return e-48;let t=e|32;return t>=97&&t<=102?t-87:-1}function x(e,t,i){for(let n=t;n<i;n++){let s=e.charCodeAt(n);if(s===10||s===13||s===8232||s===8233)return!0}return!1}export{g as lexImportsExports};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sveast",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "license": "MIT",
5
5
  "description": "Zero-dependency Svelte 5 parser, TypeScript included, with svelte/compiler's exact AST, 2-3x faster.",
6
6
  "author": "Eric Liu (https://github.com/metonym)",
@@ -27,6 +27,16 @@
27
27
  "types": "./entities.d.ts",
28
28
  "import": "./entities.js",
29
29
  "default": "./entities.js"
30
+ },
31
+ "./walk": {
32
+ "types": "./walk.d.ts",
33
+ "import": "./walk.js",
34
+ "default": "./walk.js"
35
+ },
36
+ "./lexer": {
37
+ "types": "./lexer.d.ts",
38
+ "import": "./lexer.js",
39
+ "default": "./lexer.js"
30
40
  }
31
41
  },
32
42
  "sideEffects": false,
@@ -0,0 +1 @@
1
+ import"./shared-as9can03.js";import"./shared-pyjcz2f8.js";import"./shared-pmkdz7sc.js";import"./shared-yr6e6h72.js";import{S}from"./shared-qnfwr59k.js";import{s}from"./shared-mwp38arc.js";function e(r,t={}){return S(r,t,s)}export{e as parseImportsExports};
package/parse-module.js CHANGED
@@ -1 +1 @@
1
- import"./shared-cx7c0gk3.js";import{e}from"./shared-t6kshmn4.js";import"./shared-xw9zc4rz.js";import{m}from"./shared-kqcn291j.js";import{l}from"./shared-xvd609sh.js";function s(r,o={}){return m(r,o,l)}export{e as ParseError,s as parseModule};
1
+ import"./shared-as9can03.js";import{e}from"./shared-pyjcz2f8.js";import"./shared-yr6e6h72.js";import{v}from"./shared-b37zemjd.js";import{s}from"./shared-mwp38arc.js";function i(r,o={}){return v(r,o,s)}export{e as ParseError,i as parseModule};
package/parse.js CHANGED
@@ -1 +1 @@
1
- import{d}from"./shared-7q7hyetp.js";import"./shared-cx7c0gk3.js";import"./shared-t6kshmn4.js";import"./shared-c2k7d7rg.js";import"./shared-xw9zc4rz.js";import{l}from"./shared-xvd609sh.js";import{S}from"./shared-p7c7qbej.js";var o={typescript:l,entityNames:S};function i(t,r){return d(t,r,o)}export{i as parse};
1
+ import{b}from"./shared-gyydaea2.js";import"./shared-as9can03.js";import"./shared-pyjcz2f8.js";import"./shared-g16dt48m.js";import"./shared-yr6e6h72.js";import{s}from"./shared-mwp38arc.js";import{C}from"./shared-1xjsv3h6.js";var o={typescript:s,entityNames:C};function m(t,r){return b(t,r,o)}export{m as parse};