yuku-ast 0.1.7 → 0.6.8

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
@@ -1,146 +1,129 @@
1
1
  # yuku-ast
2
2
 
3
- A fast, fully typed AST toolkit for [`yuku-parser`](https://www.npmjs.com/package/yuku-parser):
4
- node builders, type guards, and identifier validators.
3
+ A typed, mutating AST walker and syntactic utilities for JavaScript and TypeScript ESTree trees, powered by [Yuku](https://github.com/yuku-toolchain/yuku).
5
4
 
6
- | Import | Provides |
7
- | --------------------- | --------------------------------------- |
8
- | `yuku-ast` | `b` (node builders), `is` (type guards) |
9
- | `yuku-ast/utils` | node utilities such as `nameOf` |
10
- | `yuku-ast/identifier` | identifier and reserved-word validators |
11
-
12
- ```ts
13
- import { parse } from "yuku-parser";
14
- import { b, is } from "yuku-ast";
15
-
16
- const { program } = parse("const greet = 1;");
17
-
18
- const decl = program.body[0];
19
- if (is.VariableDeclaration(decl)) {
20
- // `decl` is narrowed; swap the initializer for a freshly built node.
21
- decl.declarations[0].init = b.numericLiteral(42);
22
- }
23
- ```
5
+ Works with any ESTree / TypeScript-ESTree AST. Traversal order is driven by tables generated from the [yuku-parser](https://www.npmjs.com/package/yuku-parser) AST definition, so it can never drift from the parser, and there is no runtime key discovery.
24
6
 
25
7
  ## Install
26
8
 
27
- ```sh
28
- bun add yuku-ast
9
+ ```bash
10
+ npm install yuku-ast
29
11
  ```
30
12
 
31
- ## Type guards (`is`)
13
+ ## Walking
32
14
 
33
- A guard for every node `type`, the alias families, and the variants that share a
34
- `type` string. All accept `null` / `undefined` and return `false`.
15
+ Handlers are keyed by node `type`, by alias group, or the universal `enter` / `leave`. Every handler receives the exact node type.
35
16
 
36
17
  ```ts
37
- import { is } from "yuku-ast";
38
-
39
- is.CallExpression(node); // every concrete type
40
- is.Expression(node); // families
41
- is.StringLiteral(node); // Literal variants
42
- is.StaticMemberExpression(node); // MemberExpression variants
43
-
44
- is.Identifier(node, "this"); // an Identifier with that exact name
45
- is.oneOf(node, ["CallExpression", "NewExpression"]); // any of these types
18
+ import { parse } from "yuku-parser";
19
+ import { walk } from "yuku-ast";
20
+
21
+ const { program } = parse(source);
22
+
23
+ walk(program, {
24
+ Identifier(node) {
25
+ console.log(node.name);
26
+ },
27
+ CallExpression: {
28
+ enter(node, ctx) {},
29
+ leave(node, ctx) {},
30
+ },
31
+ Function(node) {
32
+ // fires for function declarations, expressions, and arrows
33
+ },
34
+ enter(node) {},
35
+ });
46
36
  ```
47
37
 
48
- `is.oneOf(node, types)` narrows to the union of the listed types, and
49
- `is.Identifier(node, name?)` narrows to `Identifier`. A matching guard narrows the
50
- node for typed field access:
38
+ Aliases: `Expression`, `Statement`, `Declaration`, `ModuleDeclaration`, `Function`, `Class`, `Method`, `Loop`, `Pattern`, `JSX`, `TSType`. Per node the order is universal `enter`, alias enters, the typed enter, children, then the mirror for leave.
51
39
 
52
- ```ts
53
- const element = arrayExpression.elements[0];
54
- if (is.Identifier(element)) {
55
- element.name; // `element` is narrowed to Identifier
56
- }
57
- ```
40
+ The context exposes the position (`ctx.parent`, `ctx.key`, `ctx.index`, `ctx.ancestors()`), flow control (`ctx.skip()`, `ctx.stop()`), and in-place mutation: `ctx.replace(node)` continues into the replacement, `ctx.remove()` skips the removed subtree, `ctx.insertBefore(node)` inserts a sibling without visiting it, `ctx.insertAfter(node)` inserts one the walk visits. An optional third argument threads state to every handler as `ctx.state`.
58
41
 
59
- Aliases: `Expression`, `Statement`, `Declaration`, `ModuleDeclaration`,
60
- `Function`, `Class`, `Method`, `Loop`, `Pattern`, `JSX`, `TSType`.
42
+ `walkAsync` is the async counterpart: identical traversal and mutation semantics, every handler awaited before the walk moves on.
61
43
 
62
- ## Reading names (`nameOf`)
44
+ ## Builders
63
45
 
64
- `nameOf(node)` returns the static name a node denotes, an `Identifier`'s `name`
65
- or a string `Literal`'s `value`, and `null` for anything else (including `null`
66
- or `undefined`). It reads the common `Identifier | StringLiteral` slots, such as
67
- a `ModuleExportName` or a static property or member key, in one call.
46
+ One constructor per node type, its fields derived from the node type itself, so a builder can never drift from the AST. Spans default to 0, which `ctx.replace` fills from the replaced node.
68
47
 
69
48
  ```ts
70
- import { nameOf } from "yuku-ast/utils";
49
+ import { b } from "yuku-ast";
50
+
51
+ b.Identifier({ name: "x" });
52
+ b.CallExpression({ callee: b.Identifier({ name: "f" }), arguments: [], optional: false });
53
+ ```
71
54
 
72
- // `export { x as y }` and `export { x as "z" }` both resolve.
73
- nameOf(specifier.exported); // "y", "z"
55
+ ## Guards
74
56
 
75
- // A static (non-computed) property key, else null.
76
- if (!property.computed) nameOf(property.key);
57
+ ```ts
58
+ import { is } from "yuku-ast";
59
+
60
+ is.CallExpression(node);
61
+ is.Identifier(node, "require");
62
+ is.oneOf(node, ["FunctionDeclaration", "ClassDeclaration"]);
63
+ is.Expression(node);
64
+ is.StringLiteral(node);
65
+ is.StaticMemberExpression(node);
66
+ is.Directive(node);
77
67
  ```
78
68
 
79
- ## Builders (`b`)
69
+ One guard per concrete node type, per alias group, and for the common shapes ESTree folds into one type: literal kinds, member expression kinds, and directives. Every guard accepts `null` and `undefined` and narrows.
80
70
 
81
- A builder for every node type. Required fields are positional, the rest go in a
82
- trailing options object. Synthetic nodes get a zero span.
71
+ ## Modules
83
72
 
84
73
  ```ts
85
- import { b } from "yuku-ast";
74
+ import { collectImports, collectExports } from "yuku-ast";
75
+
76
+ for (const record of collectImports(program)) {
77
+ record.source; // "./m"
78
+ record.local; // the local binding name
79
+ record.imported; // "default", "*", or the export name
80
+ record.typeOnly; // import type / import { type x }
81
+ record.phase; // "source" | "defer" | null
82
+ }
86
83
 
87
- b.identifier("x");
88
- b.callExpression(b.identifier("f"), [b.numericLiteral(1)]);
89
- b.arrowFunctionExpression([b.identifier("a")], b.identifier("a"));
90
- b.variableDeclaration("const", [b.variableDeclarator(b.identifier("x"), b.numericLiteral(0))]);
84
+ for (const record of collectExports(program)) {
85
+ record.exported; // the exported name, null for bare export *
86
+ record.local; // the backing local name, when there is one
87
+ record.source; // the re-export specifier, when there is one
88
+ record.typeOnly;
89
+ }
91
90
  ```
92
91
 
93
- When you assign a synthetic node in place of an existing one, copy the original's
94
- `start` / `end` first so [`yuku-codegen`](https://www.npmjs.com/package/yuku-codegen)
95
- source maps still point back to the original input.
92
+ Declaration forms expand to one record per bound name, destructuring included. The per-declaration forms `collectImportDeclaration` and `collectExportDeclaration` return the records of a single statement, composing with a walk:
96
93
 
97
- ## Build and print
94
+ ```ts
95
+ walk(program, {
96
+ ImportDeclaration(node) {
97
+ records.push(...collectImportDeclaration(node));
98
+ },
99
+ });
100
+ ```
98
101
 
99
- Pair it with [`yuku-codegen`](https://www.npmjs.com/package/yuku-codegen) to print
100
- a built tree back to source.
102
+ ## Utilities
101
103
 
102
104
  ```ts
103
- import { print } from "yuku-codegen";
104
- import { b } from "yuku-ast";
105
-
106
- const program = b.program([
107
- b.variableDeclaration("const", [b.variableDeclarator(b.identifier("x"), b.numericLiteral(42))]),
108
- b.expressionStatement(
109
- b.callExpression(b.memberExpression(b.identifier("console"), b.identifier("log")), [
110
- b.identifier("x"),
111
- ]),
112
- ),
113
- ]);
114
-
115
- console.log(print(program).code);
116
- // const x = 42;
117
- // console.log(x);
105
+ import {
106
+ nameOf, // Identifier name or string Literal value
107
+ literalValue, // string | number | boolean | bigint | RegExp | null
108
+ unwrap, // strips parens and erased TS assertion wrappers
109
+ isCallOf, // isCallOf(node, "require")
110
+ bindingIdentifiers, // every binding Identifier a pattern introduces
111
+ findAll, // findAll(program, "CallExpression")
112
+ } from "yuku-ast";
118
113
  ```
119
114
 
120
115
  ## Identifiers
121
116
 
122
- Identifier and reserved-word helpers for raw strings and code points.
123
-
124
117
  ```ts
125
- import { isIdentifierName, isValidIdentifier } from "yuku-ast/identifier";
118
+ import { isValidIdentifier, isIdentifierName, isKeyword } from "yuku-ast";
126
119
 
127
- isIdentifierName("π"); // true - a well-formed IdentifierName
128
- isIdentifierName("class"); // true - purely syntactic, keywords included
129
- isValidIdentifier("class"); // false - rejects reserved words (use for bindings)
130
- isValidIdentifier("π"); // true
120
+ isValidIdentifier("foo"); // true
121
+ isValidIdentifier("class"); // false, reserved
122
+ isIdentifierName("class"); // true, syntactically an IdentifierName
131
123
  ```
132
124
 
133
- | Function | Checks |
134
- | ------------------------------------------------ | ------------------------------------------------------------------------- |
135
- | `isIdentifierName(name)` | `name` is a syntactically valid `IdentifierName` |
136
- | `isValidIdentifier(name, reserved?)` | a valid binding name; rejects reserved words unless `reserved` is `false` |
137
- | `isIdentifierStart(cp)` / `isIdentifierChar(cp)` | a code point may start / continue an identifier |
138
- | `isKeyword(word)` | `word` is a core grammar keyword (`if`, `class`, …) |
139
- | `isReservedWord(word, inModule?)` | reserved everywhere (`enum`; `await` in modules) |
140
- | `isStrictReservedWord(word, inModule?)` | also reserved in strict mode (`let`, `yield`, …) |
141
- | `isStrictBindOnlyReservedWord(word)` | `eval` / `arguments` |
142
- | `isStrictBindReservedWord(word, inModule?)` | strict reserved, plus `eval` / `arguments` |
125
+ Plus `isIdentifierStart`, `isIdentifierChar`, `isReservedWord`, `isStrictReservedWord`, `isStrictBindReservedWord`, `isStrictBindOnlyReservedWord`.
143
126
 
144
- ## License
127
+ ## Semantic analysis
145
128
 
146
- MIT
129
+ [`yuku-analyzer`](https://www.npmjs.com/package/yuku-analyzer) builds on this walker and adds full semantics: scopes, symbols, resolved references, closure analysis, and cross-file module linking, computed natively. Its `module.walk` carries the semantic model in context (`ctx.scope`, `ctx.symbol`, `ctx.reference`).