sveast 0.9.5 → 0.9.7

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
@@ -25,7 +25,8 @@ ast.fragment.nodes; // [Text, RegularElement]
25
25
  | [`parseSections`](#parsesectionssource-options--astroot) | A component's AST without the markup: its `<script>`s, `<style>` and `<svelte:options>` | `sveast` |
26
26
  | [`parseModule`](#parsemodulesource-options--program) | A `.js` or `.ts` module's AST, the same as a component's `<script>` | `sveast` |
27
27
  | [`parseImportsExports`](#parseimportsexportssource-options--moduledeclaration) | A module's `import` and `export` statements, without parsing the rest | `sveast` |
28
- | [`lexImportsExports`](#leximportsexportssource--lexedstatement) | Their offsets, sources and names, without loading a parser | `sveast/lexer` |
28
+ | [`lexImportsExports`](#leximportsexportssource-options--lexedstatement) | Their offsets, sources and names, without loading a parser | `sveast/lexer` |
29
+ | [`lexDynamicImports`](#lexdynamicimportssource--lexeddynamicimport) | A module's `import(…)`, `require(…)` and `import.meta.glob(…)`, with their offsets and sources, without loading a parser | `sveast/lexer` |
29
30
  | [`lexComponent`](#lexcomponentsource--lexedcomponent) | A component's `<script>`s, `<style>` and `<svelte:options>`, with their attributes and offsets, without loading a parser | `sveast/lexer` |
30
31
  | [`lexStrings`](#lexstringssource--lexedstring) | A module's string and template literals, with their offsets and values, without loading a parser | `sveast/lexer` |
31
32
  | [`createParser`, `createModuleParser`](#smaller-bundles-sveastcore-and-sveastmodule) | The parsers without TypeScript, named HTML entities or the template parser, for a smaller bundle | `sveast/core`, `sveast/module` |
@@ -35,6 +36,7 @@ ast.fragment.nodes; // [Text, RegularElement]
35
36
  | [`children`, `forEachChild`](#childrennode-keys--node-and-foreachchildnode-callback-keys--void) | A node's children, cached or without allocating | `sveast`, `sveast/walk` |
36
37
  | [`isReference`](#isreferencenode-parent--boolean), [`extractIdentifiers`](#extractidentifierspattern--identifier) | Which identifiers are references, and which ones a pattern declares | `sveast`, `sveast/walk` |
37
38
  | [`analyzeScopes`](#analyzescopesast--scopes) | Which declaration each name refers to, and every read and write of it | `sveast`, `sveast/scope` |
39
+ | [`a11yWarnings`](#a11ywarningsast-options--a11ywarning) | svelte's accessibility warnings, without compiling | `sveast/a11y` |
38
40
  | [`createLocator`, `locate`](#createlocatorsource--offset--position) | Lines and columns for the nodes you report, without parsing with `loc` | `sveast`, `sveast/walk` |
39
41
 
40
42
  ## Migrating from svelte/compiler
@@ -145,7 +147,7 @@ Parses a JavaScript or TypeScript module, such as a `.ts` file a component impor
145
147
 
146
148
  ### `parseImportsExports(source, options?) => ModuleDeclaration[]`
147
149
 
148
- 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: `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" { ... }`.
150
+ 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: `ImportDeclaration`, `ExportNamedDeclaration`, `ExportDefaultDeclaration` and `ExportAllDeclaration`, and, in TypeScript, `TSImportEqualsDeclaration`, `TSExportAssignment` and `TSNamespaceExportDeclaration`. `import(...)` and `import.meta` are expressions, so they aren't returned; [`lexDynamicImports`](#lexdynamicimportssource--lexeddynamicimport) finds them. Unlike a regex, it never matches an `import` in a comment, a string, a template or a nested block such as `declare module "a" { ... }`.
149
151
 
150
152
  It stops after the last `import` or `export` that could start a statement, so the code after a component's imports is never read: on Carbon's component scripts, it takes a third of `parseModule`'s time, and about a seventh with `localExports: false`. A syntax error in a statement it returns throws a `ParseError`, including a name exported twice; errors elsewhere aren't reported.
151
153
 
@@ -164,12 +166,14 @@ function dependencies(source: string, typescript = false): string[] {
164
166
  }
165
167
  ```
166
168
 
167
- ### `lexImportsExports(source) => LexedStatement[]`
169
+ ### `lexImportsExports(source, options?) => LexedStatement[]`
168
170
 
169
171
  From `sveast/lexer`, and also `sveast`. 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, source and names, but not the AST. `sveast/lexer` loads no parser, so a fresh process imports it in 1.4 ms, and on Carbon's component scripts it takes 40% of the time `parseImportsExports` takes with `localExports: false`.
170
172
 
171
173
  It finds the statements `parseImportsExports` returns with `localExports: false`, with the same offsets, sources and names. It doesn't check syntax and never throws: a statement it can't read, such as `import { a from "a"` or `import a = require("a")`, has `source: null` and no specifiers, and ends where it stopped reading. `import type` and `type` before a name are always read.
172
174
 
175
+ With `exportLists: true`, it also returns each top-level `export { a, b as c }` without `from`, e.g. to follow a binding through an `index.js` that imports it and exports it again: `source` is `null`, and each specifier has kind `local`, with `local` the binding in this module. Those are the `ExportNamedDeclaration`s without a declaration that `parseImportsExports` returns with `localExports: true`; `export const a`, `export function b` and `export default` still aren't returned. A module that ends with an export list is read to its end.
176
+
173
177
  ```ts
174
178
  import { lexImportsExports } from "sveast/lexer";
175
179
 
@@ -181,7 +185,7 @@ for (const statement of lexImportsExports(source)) {
181
185
  statement.typeOnly; // `import type …` or `export type …`
182
186
  statement.specifiers;
183
187
  // import: { kind: "default" | "namespace" | "named", imported, local, typeOnly, start, end }
184
- // export: { kind: "all" | "namespace" | "named", local, exported, typeOnly, start, end }
188
+ // export: { kind: "all" | "namespace" | "named" | "local", local, exported, typeOnly, start, end }
185
189
  }
186
190
  ```
187
191
 
@@ -191,9 +195,29 @@ for (const statement of lexImportsExports(source)) {
191
195
  | `* as a` | `namespace`, `imported: "*"`, `local: "a"` | `namespace`, `local: "*"`, `exported: "a"` |
192
196
  | `*` | | `all`, `local: "*"`, `exported: null` |
193
197
  | `b as c`, `"b-c" as d` | `named`, `imported: "b"`, `local: "c"` | `named`, `local: "b"`, `exported: "c"` |
198
+ | `b as c` in `export { … }`, with `exportLists` | | `local`, `local: "b"`, `exported: "c"` |
194
199
 
195
200
  Names are as the module sees them: escapes decoded, and a string name 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 `*`.
196
201
 
202
+ ### `lexDynamicImports(source) => LexedDynamicImport[]`
203
+
204
+ From `sveast/lexer`, and also `sveast`. A module's `import(…)` and `require(…)` calls and Vite's `import.meta.glob(…)`, at any depth, in source order, read without parsing, e.g. to follow the lazily loaded part of a module graph. Unlike a regular expression, it never matches in a comment, a string, a template or a regular expression, and it tells a literal argument from a computed one. `lexImportsExports` finds the static imports; together they're a module's dependencies.
205
+
206
+ ```ts
207
+ import { lexDynamicImports } from "sveast/lexer";
208
+
209
+ for (const call of lexDynamicImports(source)) {
210
+ call.kind; // "import" | "require" | "glob"
211
+ call.start; // offset of `import` or `require`
212
+ call.end; // after the `)`
213
+ call.source; // { value: "./Chart.svelte", start, end } (quotes or backticks included), or null
214
+ }
215
+ ```
216
+
217
+ `source` is the first argument if it's a string or a template without `${…}` and it's followed by `)` or `,`, as in `import("a", { with: { type: "json" } })`; it's `null` for a computed argument such as `` import(`./${name}.js`) `` or `import.meta.glob(["a", "b"])`, and for a call without arguments. Type arguments are skipped, as in `import.meta.glob<Module>("./*.ts")`.
218
+
219
+ Where `parseModule` accepts the module, the calls are its `ImportExpression`s, its calls of an identifier `require`, `new require(…)` included, and of `import.meta.glob`, and in TypeScript also its `import("a")` types, as in `typeof import("a")`, and the `require("a")` of `import a = require("a")`. A method named `import` or `require`, such as `{ import() {} }`, isn't a call, and neither are `a.require(…)`, `require?.(…)` or `(require)(…)`. It doesn't check syntax and never throws. It matched `parseModule` on all 33,001 modules and component scripts it accepts from the projects under [Parity and performance](#parity-and-performance), svelte's tests and TypeScript's conformance tests.
220
+
197
221
  ### `lexComponent(source) => LexedComponent`
198
222
 
199
223
  From `sveast/lexer`, and also `sveast`. A component's top-level `<script>`, `<script module>`, `<style>` and `<svelte:options>`, with their attributes and the offsets of their tags and content, found without parsing: e.g. to read a component's scripts and their imports, its language or its options, or to rewrite a script, on a path that can't afford a parser. On the benchmark corpus it's about 50× faster than `parse`.
@@ -405,6 +429,27 @@ The markup's scopes are svelte's:
405
429
 
406
430
  Names in TypeScript's types aren't references, and `type` and `interface` declare nothing; it skips them with `valueVisitorKeys`. Enums, enum members and namespaces are declared. It doesn't change the AST, and takes less than half as long as parsing it.
407
431
 
432
+ ### `a11yWarnings(ast, options?) => A11yWarning[]`
433
+
434
+ From `sveast/a11y`. The `a11y_…` warnings svelte's compiler gives for a component, with the same `code`, `message` and offsets, and without the ones a `<!-- svelte-ignore -->` comment before an element, or one of its ancestors, silences. For a linter that already has the AST: on Carbon's 2,202 components it takes 60 ms, against 735 ms for `compile(source, { generate: false })`. It loads neither acorn nor the parser.
435
+
436
+ | Option | Default | Description |
437
+ |:---|:---|:---|
438
+ | `runes` | `false` | Whether the component compiles in runes mode, as [`isRunesMode`](#isrunesmodesource--boolean) says. In runes mode, `svelte-ignore` takes only svelte 5's codes, separated by commas; in legacy mode, svelte 4's `a11y-…` codes too, as svelte does. |
439
+
440
+ ```ts
441
+ import { isRunesMode, parse } from "sveast";
442
+ import { a11yWarnings } from "sveast/a11y";
443
+
444
+ for (const warning of a11yWarnings(parse(source), { runes: isRunesMode(source) })) {
445
+ warning.code; // "a11y_missing_attribute"
446
+ warning.message; // "`<img>` element should have an alt attribute\nhttps://svelte.dev/e/a11y_missing_attribute"
447
+ warning.start; // offsets of the element or attribute
448
+ }
449
+ ```
450
+
451
+ The ARIA tables come from svelte's, which read aria-query and axobject-query, generated into the package, so it has no dependencies. On svelte's tests, carbon-components-svelte, bits-ui, shadcn-svelte, skeleton, flowbite-svelte, svelte.dev, immich and SvelteKit, it gives the warnings `compile` gives for all 13,276 components both accept, 899 warnings, and for 13,163 with their `svelte-ignore` comments and common a11y attributes removed, 2,185 warnings.
452
+
408
453
  ### `createLocator(source) => (offset) => Position`
409
454
 
410
455
  A function from an offset to its `{ line, column }`, line from 1 and column from 0, as `loc: true` gives them: for a tool that parses without `loc`, which is faster, and needs lines only for what it reports. Lines are found on the first call, and each lookup starts its search at the line found last, so lookups in source order are fastest. Lines end at `\n`, as in the markup's `loc` and `ParseError`; acorn's `loc` in scripts and modules also ends them at a lone `\r`, U+2028 and U+2029. `parse` drops a leading byte order mark, so pass the source without one.