sveast 0.1.0 → 0.3.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
@@ -32,9 +32,9 @@ sveast is a drop-in for `parse` in most tools: change the import, and pass `loc:
32
32
  | | svelte/compiler | sveast |
33
33
  |:---|:---|:---|
34
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` |
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`) |
37
+ | Scope | Parsing, `parseCss`, analysis, compilation | Parsing (`parse`, `parseModule`, `isValidType`) |
38
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
39
 
40
40
  ## API
@@ -48,15 +48,30 @@ sveast is a drop-in for `parse` in most tools: change the import, and pass `loc:
48
48
 
49
49
  With `loc: true`, the result equals svelte's. With the defaults, it's svelte's without `loc` and `name_loc`.
50
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).
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). `message` is always the reason, a line break, then the link to the error's docs, `https://svelte.dev/e/${code}`. `reason` has the reason on its own, e.g. `"Unexpected token"`.
52
52
 
53
53
  ### `parseModule(source, options?) => Program`
54
54
 
55
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
56
 
57
+ ### `isValidType(text) => boolean`
58
+
59
+ Whether `text` is exactly one TypeScript type, such as a JSDoc `{"sm" | "lg"}` a tool is about to copy into a `.d.ts`. The text is parsed as a type on its own, not wrapped in a statement, so a `;`, a line break or a `}` in it can't end the type and smuggle in a statement: `string; let x = 1` and `{ a: string } }` are `false`. Whitespace and comments around the type are allowed; a `//` comment runs to the end of the line, so check for one before embedding the text ahead of more code on the same line. Results aren't cached; memoize if you check the same text often.
60
+
57
61
  ### Types
58
62
 
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.
63
+ `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` is exported too.
64
+
65
+ 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`.
66
+
67
+ ```ts
68
+ import { parseModule, type TSInterfaceDeclaration } from "sveast";
69
+
70
+ const interfaces: TSInterfaceDeclaration[] = [];
71
+ for (const node of parseModule(source, { typescript: true }).body) {
72
+ if (node.type === "TSInterfaceDeclaration") interfaces.push(node);
73
+ }
74
+ ```
60
75
 
61
76
  ## Recipes
62
77
 
@@ -67,11 +82,18 @@ import { parse } from "sveast";
67
82
 
68
83
  function componentsUsed(source: string): string[] {
69
84
  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);
85
+ const visit = (node: object): void => {
86
+ if (
87
+ "type" in node &&
88
+ node.type === "Component" &&
89
+ "name" in node &&
90
+ typeof node.name === "string"
91
+ ) {
92
+ names.add(node.name);
93
+ }
94
+ for (const child of Object.values(node)) {
95
+ if (typeof child === "object" && child !== null) visit(child);
96
+ }
75
97
  };
76
98
  visit(parse(source, { css: false }).fragment);
77
99
  return [...names]; // ["Button", "Modal.Root"]