yuku-parser 0.5.11 → 0.5.15

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.
Files changed (4) hide show
  1. package/README.md +81 -36
  2. package/decode.js +392 -74
  3. package/index.d.ts +74 -49
  4. package/package.json +12 -12
package/README.md CHANGED
@@ -15,8 +15,7 @@ import { parse } from "yuku-parser";
15
15
 
16
16
  const result = parse("const x = 1 + 2;");
17
17
 
18
- console.log(result.program); // ESTree / TypeScript-ESTree Program node
19
- console.log(result.comments); // all comments
18
+ console.log(result.program); // ESTree / TypeScript-ESTree Program node
20
19
  console.log(result.diagnostics); // errors and warnings
21
20
  ```
22
21
 
@@ -26,10 +25,15 @@ For JavaScript and JSX, the AST is fully conformant with the [ESTree](https://gi
26
25
 
27
26
  For TypeScript, the AST conforms to the [TypeScript-ESTree](https://www.npmjs.com/package/@typescript-eslint/typescript-estree) format used by `@typescript-eslint`.
28
27
 
29
- The only differences from the base ESTree / TypeScript-ESTree specifications are:
28
+ Yuku also matches [Oxc](https://oxc.rs) for both JS and TS, with two intentional deviations from the base specs:
30
29
 
31
- - Support for Stage 3 [decorators](https://github.com/tc39/proposal-decorators).
32
- - Support for Stage 3 [import defer](https://github.com/tc39/proposal-defer-import-eval) and [import source](https://github.com/tc39/proposal-source-phase-imports). Dynamic forms (`import.defer(...)`, `import.source(...)`) are represented as an `ImportExpression` with a `phase` field set to `"defer"` or `"source"`, following the ESTree convention.
30
+ - **Comments are attached to the AST nodes they belong to** rather than exposed as a separate offset-indexed array. See [Comments](#comments) for why.
31
+ - **`Identifier` carries a `kind` field** (`"reference" | "binding" | "name" | "label" | "this"`) naming its role in the tree. ESTree collapses these distinct roles onto one `Identifier` type, so consumers normally have to walk the parent context to tell them apart. The parser already knows which is which, so it surfaces the answer as a free field.
32
+
33
+ On top of the base specs, the AST also carries:
34
+
35
+ - Stage 3 [decorators](https://github.com/tc39/proposal-decorators).
36
+ - Stage 3 [import defer](https://github.com/tc39/proposal-defer-import-eval) and [import source](https://github.com/tc39/proposal-source-phase-imports). Dynamic forms (`import.defer(...)`, `import.source(...)`) are represented as an `ImportExpression` with a `phase` field set to `"defer"` or `"source"`, following the ESTree convention.
33
37
  - A non-standard `hashbang` field on `Program` for `#!/usr/bin/env node` lines.
34
38
 
35
39
  Any other deviation from Acorn's ESTree or `@typescript-eslint`'s TypeScript-ESTree would be considered a bug.
@@ -51,12 +55,27 @@ Two small helpers are exported for resolving the `lang` and `sourceType` options
51
55
  ```ts
52
56
  import { langFromPath, sourceTypeFromPath } from "yuku-parser";
53
57
 
54
- langFromPath("foo.tsx"); // "tsx"
55
- langFromPath("types.d.ts"); // "dts"
56
- sourceTypeFromPath("foo.cjs"); // "script"
57
- sourceTypeFromPath("foo.mjs"); // "module"
58
+ langFromPath("foo.tsx"); // "tsx"
59
+ langFromPath("types.d.ts"); // "dts"
60
+ sourceTypeFromPath("foo.cjs"); // "script"
61
+ sourceTypeFromPath("foo.mjs"); // "module"
58
62
  ```
59
63
 
64
+ ## Resolving offsets to `(line, column)`
65
+
66
+ Nodes and diagnostics carry `start`/`end` offsets (UTF-16 code units). To turn an offset into a `{ line, column }` pair, call `locOf` on the parse result:
67
+
68
+ ```ts
69
+ import { parse } from "yuku-parser";
70
+
71
+ const result = parse(source);
72
+ const { line, column } = result.locOf(result.program.body[0].start);
73
+ ```
74
+
75
+ Lines are 1-based and columns are 0-based, matching ESTree's `loc` convention. The lookup is an O(log n) binary search: tens of nanoseconds per call, safe to invoke per node during a walk.
76
+
77
+ For bulk work — building your own line/column index, integrating with another source-map or diagnostics library, or skipping the per-call binary search — read `result.lineStarts` directly. It's a sorted array of UTF-16 offsets where each line begins: `lineStarts[i]` is the start of line `i + 1`. Decoded lazily on first access and cached.
78
+
60
79
  ## Walking the AST
61
80
 
62
81
  The AST is standard ESTree, so any ESTree-compatible walker works. For example, with [zimmerframe](https://github.com/sveltejs/zimmerframe):
@@ -91,6 +110,7 @@ const result = parse(source, {
91
110
  preserveParens: true,
92
111
  allowReturnOutsideFunction: false,
93
112
  semanticErrors: false,
113
+ attachComments: false,
94
114
  });
95
115
  ```
96
116
 
@@ -101,6 +121,7 @@ const result = parse(source, {
101
121
  | `preserveParens` | `true`, `false` | `true` | Keep `ParenthesizedExpression` nodes in the AST. When false, parentheses are stripped and only the inner expression is kept. |
102
122
  | `allowReturnOutsideFunction` | `true`, `false` | `false` | Allow `return` statements outside of functions, at the top level. |
103
123
  | `semanticErrors` | `true`, `false` | `false` | Run semantic analysis and report semantic errors alongside syntax errors. |
124
+ | `attachComments` | `true`, `false` | `false` | Collect comments and attach them to host AST nodes. See [Comments](#comments). |
104
125
 
105
126
  ## Result
106
127
 
@@ -109,13 +130,13 @@ const result = parse(source, {
109
130
  ```ts
110
131
  interface ParseResult {
111
132
  program: Program;
112
- comments: Comment[];
113
- lineStarts: number[];
114
133
  diagnostics: Diagnostic[];
134
+ lineStarts: number[];
135
+ locOf(offset: number): { line: number; column: number };
115
136
  }
116
137
  ```
117
138
 
118
- The parser is error-tolerant, an AST is always produced even when diagnostics are present. `lineStarts` records the byte offset of every line in the source, useful for resolving spans to `(line, column)` pairs without rescanning.
139
+ The parser is error-tolerant: an AST is always produced even when diagnostics are present.
119
140
 
120
141
  ### Diagnostics
121
142
 
@@ -140,41 +161,65 @@ const result = parse(`let x = 1; let x = 2;`, { semanticErrors: true });
140
161
 
141
162
  This incurs a very small performance overhead. If your build pipeline already handles semantic validation (e.g. through a linter or type checker), you can leave this off for faster parsing.
142
163
 
143
- ### Comments
164
+ ## Comments
165
+
166
+ Comments are attached to the AST node they sit next to. Enable collection with `attachComments`, then read them off any node:
167
+
168
+ ```js
169
+ const { program } = parse(
170
+ `// header\nfunction foo() {} // trailing`,
171
+ { attachComments: true },
172
+ );
173
+
174
+ const fn = program.body[0];
175
+ for (const c of fn.comments ?? []) {
176
+ console.log(c.position, c.type, c.value);
177
+ }
178
+ // before Line " header"
179
+ // after Line " trailing"
180
+ ```
144
181
 
145
- Every entry in `result.comments` is classified at parse time so consumers can route comments without rescanning the value:
182
+ Each comment is:
146
183
 
147
184
  ```ts
148
185
  interface Comment {
149
186
  type: "Line" | "Block";
150
- kind: CommentKind;
151
- precededByNewline: boolean;
152
- followedByNewline: boolean;
153
- value: string; // text without the surrounding delimiters
154
- start: number; // byte offset
155
- end: number; // byte offset
187
+ position: "before" | "after" | "inside";
188
+ sameLine: boolean;
189
+ value: string; // body without delimiters
156
190
  }
157
-
158
- type CommentKind =
159
- | "normal" // plain comment, no special meaning
160
- | "legal" // /*! ... */ or contains @license / @preserve / @cc_on
161
- | "jsdoc" // /** ... */ block
162
- | "annotation" // /*# ... */ or /*@ ... */ other than tree-shaking
163
- | "pure" // /*#__PURE__*/ or /*@__PURE__*/
164
- | "no_side_effects"; // /*#__NO_SIDE_EFFECTS__*/ or /*@__NO_SIDE_EFFECTS__*/
165
191
  ```
166
192
 
167
- Common patterns:
193
+ `position` tells you where the comment sits relative to its host:
168
194
 
169
- ```js
170
- // Extract legal banners for a sidecar file.
171
- const banners = result.comments.filter((c) => c.kind === "legal");
195
+ - `"before"`: leading the host node.
196
+ - `"after"`: trailing the host node.
197
+ - `"inside"`: interior to an otherwise empty host, like `function f() { /* hi */ }`.
172
198
 
173
- // Find tree-shaking annotations.
174
- const pureMarkers = result.comments.filter((c) => c.kind === "pure");
175
- ```
199
+ `sameLine` is `true` when the comment shares a source line with the host's adjacent edge (host's start for `before`, host's end for `after`). For `inside` it is always `false`.
200
+
201
+ When `attachComments` is disabled (the default), comments are skipped like whitespace and no `comments` field appears on any node.
202
+
203
+ ### Why comments are attached to nodes
204
+
205
+ Comments are part of the source. Any tool that reads or rewrites code needs to know which comments belong with which node, and that link has to survive AST transforms. Yuku puts a single `comments` array on each node, with a `position` field on each comment (`"before"`, `"after"`, `"inside"`). It is the simplest shape that holds up under transforms and stays cheap to decode. Every other approach trades away one of those properties.
206
+
207
+ **Flat offset-indexed array (ESTree default, Oxc).** A separate `comments: [...]` array on the parse result, sibling to the program, each entry pointing at a source range. Fine for read-only analysis. It falls apart the moment you transform the tree.
208
+
209
+ - *Transforms desync the array.* Insert, delete, or move a node and every offset downstream of the edit is wrong. The comment that used to sit between `a` and `b` may now sit inside something unrelated, or hang in empty space between two nodes. Re-syncing means walking the entire array and rewriting offsets on every edit, and even then the meaning of "the comment between these two things" cannot really be preserved by offsets alone.
210
+ - *Codegen cannot trust the offsets.* A printer takes whatever AST it is handed. With offset-based comments, a transformed AST will either print comments in the wrong places or drop them entirely. With attached comments, when you move a node its comments come with it. The printer reads them off `node.comments` and emits them next to the node, no reconciliation needed.
211
+ - *Lookup is awkward.* "Give me the comments belonging to this node" with a flat array is a binary search at best and a linear scan at worst. `node.comments` is O(1) and always exact.
212
+
213
+ **Parser returns a flat list, you attach on the JS side (Acorn).** Acorn's `onComment` option pushes every comment into an array you provide, and that is all it does. To get them onto AST nodes you have to run a separate JS pass like `escodegen.attachComments`, which walks the tree, scans the comment list alongside it, and tags each comment as leading or trailing on the nearest node. That work is unavoidable if you want to transform the tree, and doing it in JavaScript is slow. Yuku does the same attachment in Zig as part of the parse. On the 7.8 MB `typescript.js` (30k+ comments), turning `attachComments` on adds only around 4ms.
214
+
215
+ **Three separate fields per node (Babel).** `leadingComments`, `trailingComments`, and `innerComments`. Same idea as Yuku, but split across three arrays. Two costs come with the split.
216
+
217
+ - *Decode work.* Three fields per node means three slots to check, three potential array allocations, and roughly 3x the work on the JS side for the same information.
218
+ - *Duplication.* A comment that sits between two nodes is the `trailingComments` of one and the `leadingComments` of the other. Transforms have to track both copies and keep them in sync.
219
+
220
+ A single `comments` array with a `position` field carries the same meaning, with one entry per comment and one place to look.
176
221
 
177
- `precededByNewline` and `followedByNewline` capture line layout, useful for tools that need to reconstruct source positioning.
222
+ Even with `attachComments` enabled, Yuku still parses the same file roughly 4x faster than Babel does without comments attached at all. When the flag is off (the default), comments are skipped like whitespace and there is no attachment work at all.
178
223
 
179
224
  ## License
180
225
 
package/decode.js CHANGED
@@ -1,5 +1,7 @@
1
1
  // generated by tools/gen_estree_decoder.zig, do not edit
2
- const NULL = 0xFFFFFFFF;
2
+ // `_u32` is an Int32Array so NodeIndex NULL reads as -1 and fits in
3
+ // an SMI/int32, avoiding heap-number boxing on every field read in JSC.
4
+ const NULL = -1;
3
5
  const _td = new TextDecoder("utf-8", { ignoreBOM: true });
4
6
  const BINARY_OPS = ["==", "!=", "===", "!==", "<", "<=", ">", ">=", "+", "-", "*", "/", "%", "**", "|", "^", "&", "<<", ">>", ">>>", "in", "instanceof"];
5
7
  const LOGICAL_OPS = ["&&", "||", "??"];
@@ -11,8 +13,6 @@ const PROPERTY_KINDS = ["init", "get", "set"];
11
13
  const METHOD_KINDS = ["constructor", "method", "get", "set"];
12
14
  const FUNCTION_TYPES = ["FunctionDeclaration", "FunctionExpression", "TSDeclareFunction", "TSEmptyBodyFunctionExpression"];
13
15
  const CLASS_TYPES = ["ClassDeclaration", "ClassExpression"];
14
- const COMMENT_TYPES = ["Line", "Block"];
15
- const COMMENT_KINDS = ["normal", "legal", "jsdoc", "annotation", "pure", "no_side_effects"];
16
16
  const SEVERITY = ["error", "warning", "hint", "info"];
17
17
  const IMPORT_EXPORT_KINDS = ["value", "type"];
18
18
  const ACCESSIBILITY = [null, "public", "private", "protected"];
@@ -33,7 +33,9 @@ function buildPosMap(src, byteLen, startByte) {
33
33
  while (i < len) {
34
34
  if (i + 16 <= len) {
35
35
  let allAscii = true;
36
- for (let k = 0; k < 16; k++) if (src.charCodeAt(i + k) >= 0x80) { allAscii = false; break; }
36
+ for (let k = 0; k < 16; k++) {
37
+ if (src.charCodeAt(i + k) >= 0x80) { allAscii = false; break; }
38
+ }
37
39
  if (allAscii) {
38
40
  for (let k = 0; k < 16; k++) m[bp + k] = u16p + k;
39
41
  bp += 16; u16p += 16; i += 16;
@@ -44,8 +46,14 @@ function buildPosMap(src, byteLen, startByte) {
44
46
  m[bp] = u16p;
45
47
  if (cu < 0x80) { bp++; u16p++; i++; }
46
48
  else if (cu < 0x800) { m[bp + 1] = u16p + 1; bp += 2; u16p++; i++; }
47
- else if (cu < 0xD800 || cu >= 0xE000) { m[bp + 1] = u16p + 1; m[bp + 2] = u16p + 1; bp += 3; u16p++; i++; }
48
- else { m[bp + 1] = u16p + 1; m[bp + 2] = u16p + 2; m[bp + 3] = u16p + 2; bp += 4; u16p += 2; i += 2; }
49
+ else if (cu < 0xD800 || cu >= 0xE000) {
50
+ m[bp + 1] = u16p + 1; m[bp + 2] = u16p + 1;
51
+ bp += 3; u16p++; i++;
52
+ }
53
+ else {
54
+ m[bp + 1] = u16p + 1; m[bp + 2] = u16p + 2; m[bp + 3] = u16p + 2;
55
+ bp += 4; u16p += 2; i += 2;
56
+ }
49
57
  }
50
58
  m[byteLen - startByte] = u16p;
51
59
  return m;
@@ -53,17 +61,29 @@ function buildPosMap(src, byteLen, startByte) {
53
61
  function decode(buffer, source) {
54
62
  const _u8 = new Uint8Array(buffer);
55
63
  const aLen = (buffer.byteLength >> 2) << 2;
56
- const _u32 = new Uint32Array(buffer, 0, aLen >> 2);
64
+ const _u32 = new Int32Array(buffer, 0, aLen >> 2);
57
65
  const _src = source;
58
66
  const _srcLen = _u32[3];
59
- const nodeCount = _u32[0], extraCount = _u32[1], spLen = _u32[2];
60
- const commentCount = _u32[4], lineStartsCount = _u32[5], diagCount = _u32[6], progIdx = _u32[7];
61
- const _isTs = !!(_u32[8] & 1);
67
+ const nodeCount = _u32[0],
68
+ extraCount = _u32[1],
69
+ spLen = _u32[2];
70
+ const commentCount = _u32[4],
71
+ lineStartsCount = _u32[5],
72
+ diagCount = _u32[6],
73
+ progIdx = _u32[7];
74
+ const _flags = _u32[8];
75
+ const _isTs = !!(_flags & 1);
76
+ const _attachComments = !!(_flags & 2);
62
77
  const _firstNa = _u32[9];
63
78
  const _nodesOff = 40;
64
79
  const eOff = _nodesOff + nodeCount * 48;
65
80
  const _extraBase = eOff >> 2;
66
81
  const _spOff = eOff + extraCount * 4;
82
+ // sections after the variable-length string pool may be at any
83
+ // byte alignment, so reads against them use `dv`, not `_u32`.
84
+ const dv = new DataView(buffer);
85
+ const _coOff = _spOff + spLen;
86
+ const _cOff = _attachComments ? _coOff + (nodeCount + 1) * 4 : _coOff;
67
87
  function _poolDecode(s, e) {
68
88
  const a = _spOff + s - _srcLen, b = _spOff + e - _srcLen;
69
89
  let hasEd = false;
@@ -73,9 +93,23 @@ function decode(buffer, source) {
73
93
  for (let i = a; i < b; ) {
74
94
  const c = _u8[i];
75
95
  if (c < 0x80) { r += String.fromCharCode(c); i++; }
76
- else if (c < 0xE0) { r += String.fromCharCode(((c & 0x1F) << 6) | (_u8[i+1] & 0x3F)); i += 2; }
77
- else if (c < 0xF0) { r += String.fromCharCode(((c & 0x0F) << 12) | ((_u8[i+1] & 0x3F) << 6) | (_u8[i+2] & 0x3F)); i += 3; }
78
- else { r += String.fromCodePoint(((c & 0x07) << 18) | ((_u8[i+1] & 0x3F) << 12) | ((_u8[i+2] & 0x3F) << 6) | (_u8[i+3] & 0x3F)); i += 4; }
96
+ else if (c < 0xE0) {
97
+ r += String.fromCharCode(((c & 0x1F) << 6) | (_u8[i+1] & 0x3F));
98
+ i += 2;
99
+ }
100
+ else if (c < 0xF0) {
101
+ r += String.fromCharCode(
102
+ ((c & 0x0F) << 12) | ((_u8[i+1] & 0x3F) << 6) | (_u8[i+2] & 0x3F)
103
+ );
104
+ i += 3;
105
+ }
106
+ else {
107
+ r += String.fromCodePoint(
108
+ ((c & 0x07) << 18) | ((_u8[i+1] & 0x3F) << 12) |
109
+ ((_u8[i+2] & 0x3F) << 6) | (_u8[i+3] & 0x3F)
110
+ );
111
+ i += 4;
112
+ }
79
113
  }
80
114
  return r;
81
115
  }
@@ -105,7 +139,10 @@ function decode(buffer, source) {
105
139
  }
106
140
  function nodeArrHoles(s, len) {
107
141
  const r = new Array(len);
108
- for (let j = 0; j < len; j++) { const x = _u32[_extraBase + s + j]; r[j] = x !== NULL ? node(x) : null; }
142
+ for (let j = 0; j < len; j++) {
143
+ const x = _u32[_extraBase + s + j];
144
+ r[j] = x !== NULL ? node(x) : null;
145
+ }
109
146
  return r;
110
147
  }
111
148
  function fnParams(idx) {
@@ -118,19 +155,77 @@ function decode(buffer, source) {
118
155
  if (rest !== NULL) p.push(node(rest));
119
156
  return p;
120
157
  }
158
+ function _commentsOf(a, e) {
159
+ const out = new Array(e - a);
160
+ for (let j = a; j < e; j++) {
161
+ const o = _cOff + j * 12;
162
+ const cf = _u8[o + 0];
163
+ const vs = dv.getUint32(o + 4, true),
164
+ ve = dv.getUint32(o + 8, true);
165
+ out[j - a] = {
166
+ type: (cf & 1) ? "Block" : "Line",
167
+ position: ["before", "after", "inside"][(cf >> 1) & 3],
168
+ sameLine: (cf & 8) !== 0,
169
+ value: str(vs, ve),
170
+ };
171
+ }
172
+ return out;
173
+ }
121
174
  function node(i) {
175
+ const r = _decode(i);
176
+ // skip unwrap cases whose inner node already carries its own
177
+ // comments (e.g. formal_parameter returning its pattern)
178
+ if (_attachComments && r && r.type !== undefined && r.comments === undefined) {
179
+ const off = _coOff + i * 4;
180
+ const a = dv.getUint32(off, true), e = dv.getUint32(off + 4, true);
181
+ if (a !== e) r.comments = _commentsOf(a, e);
182
+ }
183
+ return r;
184
+ }
185
+ function _decode(i) {
122
186
  const o = _nodesOff + i * 48;
123
187
  const tag = _u8[o];
124
188
  const flags = _u8[o + 2] | (_u8[o + 3] << 8);
125
189
  const f0 = _u8[o + 4] | (_u8[o + 5] << 8);
126
190
  const b = o >> 2;
127
- const f1 = _u32[b + 2], f2 = _u32[b + 3], f3 = _u32[b + 4], f4 = _u32[b + 5], f5 = _u32[b + 6], f6 = _u32[b + 7], f7 = _u32[b + 8], f8 = _u32[b + 9];
191
+ const f1 = _u32[b + 2], f2 = _u32[b + 3],
192
+ f3 = _u32[b + 4], f4 = _u32[b + 5],
193
+ f5 = _u32[b + 6], f6 = _u32[b + 7],
194
+ f7 = _u32[b + 8], f8 = _u32[b + 9];
128
195
  const start = _p(_u32[b + 10]), end = _p(_u32[b + 11]);
129
196
  switch (tag) {
130
197
  case 0: return { type: "SequenceExpression", start, end, expressions: nodeArr(f1, f0) };
131
198
  case 1: return { type: "ParenthesizedExpression", start, end, expression: f1 !== NULL ? node(f1) : null };
132
- case 2: { const r = { type: "ArrowFunctionExpression", start, end, id: null, generator: false, async: !!(flags & 2), params: f1 !== NULL ? fnParams(f1) : [], body: node(f2), expression: !!(flags & 1) }; if (_isTs) { r.typeParameters = f3 !== NULL ? node(f3) : null; r.returnType = f4 !== NULL ? node(f4) : null; } return r; }
133
- case 3: { const ft = flags & 3; const r = { type: FUNCTION_TYPES[ft], start, end, id: f1 !== NULL ? node(f1) : null, generator: !!(flags & 4), async: !!(flags & 8), params: f2 !== NULL ? fnParams(f2) : [], body: f3 !== NULL ? node(f3) : null, expression: false }; if (_isTs) { r.typeParameters = f4 !== NULL ? node(f4) : null; r.returnType = f5 !== NULL ? node(f5) : null; r.declare = !!(flags & 16); } return r; }
199
+ case 2: {
200
+ const r = {
201
+ type: "ArrowFunctionExpression", start, end,
202
+ id: null, generator: false, async: !!(flags & 2),
203
+ params: f1 !== NULL ? fnParams(f1) : [],
204
+ body: node(f2), expression: !!(flags & 1),
205
+ };
206
+ if (_isTs) {
207
+ r.typeParameters = f3 !== NULL ? node(f3) : null;
208
+ r.returnType = f4 !== NULL ? node(f4) : null;
209
+ }
210
+ return r;
211
+ }
212
+ case 3: {
213
+ const ft = flags & 3;
214
+ const r = {
215
+ type: FUNCTION_TYPES[ft], start, end,
216
+ id: f1 !== NULL ? node(f1) : null,
217
+ generator: !!(flags & 4), async: !!(flags & 8),
218
+ params: f2 !== NULL ? fnParams(f2) : [],
219
+ body: f3 !== NULL ? node(f3) : null,
220
+ expression: false,
221
+ };
222
+ if (_isTs) {
223
+ r.typeParameters = f4 !== NULL ? node(f4) : null;
224
+ r.returnType = f5 !== NULL ? node(f5) : null;
225
+ r.declare = !!(flags & 16);
226
+ }
227
+ return r;
228
+ }
134
229
  case 4: return { type: "BlockStatement", start, end, body: nodeArr(f1, f0) };
135
230
  case 5: return { type: "BlockStatement", start, end, body: nodeArr(f1, f0) };
136
231
  case 6: return { params: fnParams(i) };
@@ -138,7 +233,11 @@ function decode(buffer, source) {
138
233
  case 8: return { type: "BinaryExpression", start, end, left: f1 !== NULL ? node(f1) : null, right: f2 !== NULL ? node(f2) : null, operator: BINARY_OPS[flags & 31] };
139
234
  case 9: return { type: "LogicalExpression", start, end, left: f1 !== NULL ? node(f1) : null, right: f2 !== NULL ? node(f2) : null, operator: LOGICAL_OPS[flags & 3] };
140
235
  case 10: return { type: "ConditionalExpression", start, end, test: f1 !== NULL ? node(f1) : null, consequent: f2 !== NULL ? node(f2) : null, alternate: f3 !== NULL ? node(f3) : null };
141
- case 11: return { type: "UnaryExpression", start, end, operator: UNARY_OPS[flags & 7], prefix: true, argument: f1 !== NULL ? node(f1) : null };
236
+ case 11: return {
237
+ type: "UnaryExpression", start, end,
238
+ operator: UNARY_OPS[flags & 7], prefix: true,
239
+ argument: f1 !== NULL ? node(f1) : null,
240
+ };
142
241
  case 12: return { type: "UpdateExpression", start, end, argument: f1 !== NULL ? node(f1) : null, operator: UPDATE_OPS[flags & 1], prefix: !!(flags & 2) };
143
242
  case 13: return { type: "AssignmentExpression", start, end, left: f1 !== NULL ? node(f1) : null, right: f2 !== NULL ? node(f2) : null, operator: ASSIGNMENT_OPS[flags & 15] };
144
243
  case 14: return { type: "ArrayExpression", start, end, elements: nodeArrHoles(f1, f0) };
@@ -154,26 +253,133 @@ function decode(buffer, source) {
154
253
  case 24: return { type: "YieldExpression", start, end, argument: f1 !== NULL ? node(f1) : null, delegate: !!(flags & 1) };
155
254
  case 25: return { type: "MetaProperty", start, end, meta: f1 !== NULL ? node(f1) : null, property: f2 !== NULL ? node(f2) : null };
156
255
  case 26: return { type: "Decorator", start, end, expression: f1 !== NULL ? node(f1) : null };
157
- case 27: { const r = { type: CLASS_TYPES[flags & 1], start, end, decorators: nodeArr(f1, f0), id: f2 !== NULL ? node(f2) : null, superClass: f3 !== NULL ? node(f3) : null, body: node(f4) }; if (_isTs) { r.typeParameters = f5 !== NULL ? node(f5) : null; r.superTypeArguments = f6 !== NULL ? node(f6) : null; r.implements = nodeArr(f7, f8); r.abstract = !!(flags & 2); r.declare = !!(flags & 4); } return r; }
256
+ case 27: {
257
+ const r = {
258
+ type: CLASS_TYPES[flags & 1], start, end,
259
+ decorators: nodeArr(f1, f0),
260
+ id: f2 !== NULL ? node(f2) : null,
261
+ superClass: f3 !== NULL ? node(f3) : null,
262
+ body: node(f4),
263
+ };
264
+ if (_isTs) {
265
+ r.typeParameters = f5 !== NULL ? node(f5) : null;
266
+ r.superTypeArguments = f6 !== NULL ? node(f6) : null;
267
+ r.implements = nodeArr(f7, f8);
268
+ r.abstract = !!(flags & 2);
269
+ r.declare = !!(flags & 4);
270
+ }
271
+ return r;
272
+ }
158
273
  case 28: return { type: "ClassBody", start, end, body: nodeArr(f1, f0) };
159
- case 29: { const r = { type: "MethodDefinition", start, end, decorators: nodeArr(f1, f0), key: node(f2), value: node(f3), kind: METHOD_KINDS[flags & 3], computed: !!(flags & 4), static: !!(flags & 8) }; if (_isTs) { r.override = !!(flags & 16); r.optional = !!(flags & 32); const _abs = !!(flags & 64); r.accessibility = ACCESSIBILITY[(flags >> 7) & 3]; if (_abs) r.type = "TSAbstractMethodDefinition"; } return r; }
160
- case 30: { const _acc = !!(flags & 4); const r = { type: _acc ? "AccessorProperty" : "PropertyDefinition", start, end, decorators: nodeArr(f1, f0), key: node(f2), value: f3 !== NULL ? node(f3) : null, computed: !!(flags & 1), static: !!(flags & 2) }; if (_isTs) { r.typeAnnotation = f4 !== NULL ? node(f4) : null; r.declare = !!(flags & 8); r.override = !!(flags & 16); r.optional = !!(flags & 32); r.definite = !!(flags & 64); r.readonly = !!(flags & 128); const _abs = !!(flags & 256); r.accessibility = ACCESSIBILITY[(flags >> 9) & 3]; if (_abs) r.type = _acc ? "TSAbstractAccessorProperty" : "TSAbstractPropertyDefinition"; } return r; }
274
+ case 29: {
275
+ const r = {
276
+ type: "MethodDefinition", start, end,
277
+ decorators: nodeArr(f1, f0),
278
+ key: node(f2), value: node(f3),
279
+ kind: METHOD_KINDS[flags & 3],
280
+ computed: !!(flags & 4), static: !!(flags & 8),
281
+ };
282
+ if (_isTs) {
283
+ r.override = !!(flags & 16);
284
+ r.optional = !!(flags & 32);
285
+ const _abs = !!(flags & 64);
286
+ r.accessibility = ACCESSIBILITY[(flags >> 7) & 3];
287
+ if (_abs) r.type = "TSAbstractMethodDefinition";
288
+ }
289
+ return r;
290
+ }
291
+ case 30: {
292
+ const _acc = !!(flags & 4);
293
+ const r = {
294
+ type: _acc ? "AccessorProperty" : "PropertyDefinition",
295
+ start, end,
296
+ decorators: nodeArr(f1, f0),
297
+ key: node(f2),
298
+ value: f3 !== NULL ? node(f3) : null,
299
+ computed: !!(flags & 1), static: !!(flags & 2),
300
+ };
301
+ if (_isTs) {
302
+ r.typeAnnotation = f4 !== NULL ? node(f4) : null;
303
+ r.declare = !!(flags & 8);
304
+ r.override = !!(flags & 16);
305
+ r.optional = !!(flags & 32);
306
+ r.definite = !!(flags & 64);
307
+ r.readonly = !!(flags & 128);
308
+ const _abs = !!(flags & 256);
309
+ r.accessibility = ACCESSIBILITY[(flags >> 9) & 3];
310
+ if (_abs)
311
+ r.type = _acc
312
+ ? "TSAbstractAccessorProperty"
313
+ : "TSAbstractPropertyDefinition";
314
+ }
315
+ return r;
316
+ }
161
317
  case 31: return { type: "StaticBlock", start, end, body: nodeArr(f1, f0) };
162
318
  case 32: return { type: "Super", start, end };
163
- case 33: return { type: "Literal", start, end, value: str(f1, f2), raw: _src.slice(start, end) };
164
- case 34: { const r = _src.slice(start, end); const s = r.indexOf("_") === -1 ? r : r.replace(/_/g, ""); const v = (flags & 3) === 2 && s[1] !== "o" && s[1] !== "O" ? parseInt(s.slice(1), 8) : +s; return { type: "Literal", start, end, value: v === v && isFinite(v) ? v : null, raw: r }; }
165
- case 35: { const r = _src.slice(start, end); const d = str(f1, f2).replace(/_/g, ""); const v = BigInt(d); return { type: "Literal", start, end, value: v, raw: r, bigint: v.toString() }; }
166
- case 36: { const v = !!(flags & 1); return { type: "Literal", start, end, value: v, raw: v ? "true" : "false" }; }
319
+ case 33: return {
320
+ type: "Literal", start, end,
321
+ value: str(f1, f2), raw: _src.slice(start, end),
322
+ };
323
+ case 34: {
324
+ const r = _src.slice(start, end);
325
+ const s = r.indexOf("_") === -1 ? r : r.replace(/_/g, "");
326
+ const v = (flags & 3) === 2 && s[1] !== "o" && s[1] !== "O"
327
+ ? parseInt(s.slice(1), 8)
328
+ : +s;
329
+ return {
330
+ type: "Literal", start, end,
331
+ value: v === v && isFinite(v) ? v : null,
332
+ raw: r,
333
+ };
334
+ }
335
+ case 35: {
336
+ const r = _src.slice(start, end);
337
+ const d = str(f1, f2).replace(/_/g, "");
338
+ const v = BigInt(d);
339
+ return {
340
+ type: "Literal", start, end,
341
+ value: v, raw: r, bigint: v.toString(),
342
+ };
343
+ }
344
+ case 36: {
345
+ const v = !!(flags & 1);
346
+ return {
347
+ type: "Literal", start, end,
348
+ value: v, raw: v ? "true" : "false",
349
+ };
350
+ }
167
351
  case 37: return { type: "Literal", start, end, value: null, raw: "null" };
168
352
  case 38: return { type: "ThisExpression", start, end };
169
- case 39: { const p = str(f1, f2), fl = str(f3, f4); let v = null; try { v = new RegExp(p, fl); } catch {} return { type: "Literal", start, end, value: v, raw: "/" + p + "/" + fl, regex: { pattern: p, flags: fl.split("").sort().join("") } }; }
353
+ case 39: {
354
+ const p = str(f1, f2), fl = str(f3, f4);
355
+ let v = null;
356
+ try { v = new RegExp(p, fl); } catch {}
357
+ return {
358
+ type: "Literal", start, end,
359
+ value: v, raw: "/" + p + "/" + fl,
360
+ regex: { pattern: p, flags: fl.split("").sort().join("") },
361
+ };
362
+ }
170
363
  case 40: return { type: "TemplateLiteral", start, end, quasis: nodeArr(f1, f0), expressions: nodeArr(f2, f3) };
171
- case 41: { const raw = _src.slice(start, end).replace(/\r\n?/g, "\n"); const tl = !!(flags & 1); const s = _isTs ? start - 1 : start; const e = _isTs ? (tl ? end + 1 : end + 2) : end; return { type: "TemplateElement", start: s, end: e, value: { raw, cooked: (flags & 2) ? null : str(f1, f2) }, tail: tl }; }
172
- case 42: { const r = { type: "Identifier", start, end, name: str(f1, f2) }; if (_isTs) { r.decorators = []; r.optional = false; r.typeAnnotation = null; } return r; }
364
+ case 41: {
365
+ const raw = _src.slice(start, end).replace(/\r\n?/g, "\n");
366
+ const tl = !!(flags & 1);
367
+ const s = _isTs ? start - 1 : start;
368
+ const e = _isTs ? (tl ? end + 1 : end + 2) : end;
369
+ return {
370
+ type: "TemplateElement", start: s, end: e,
371
+ value: {
372
+ raw,
373
+ cooked: (flags & 2) ? null : str(f1, f2),
374
+ },
375
+ tail: tl,
376
+ };
377
+ }
378
+ case 42: { const r = { type: "Identifier", start, end, name: str(f1, f2), kind: "reference" }; if (_isTs) { r.decorators = []; r.optional = false; r.typeAnnotation = null; } return r; }
173
379
  case 43: return { type: "PrivateIdentifier", start, end, name: str(f1, f2) };
174
- case 44: { const r = { type: "Identifier", start, end, name: str(f1, f2) }; if (_isTs) { r.decorators = nodeArr(f3, f0); r.typeAnnotation = f4 !== NULL ? node(f4) : null; r.optional = !!(flags & 1); } return r; }
175
- case 45: { const r = { type: "Identifier", start, end, name: str(f1, f2) }; if (_isTs) { r.decorators = []; r.optional = false; r.typeAnnotation = null; } return r; }
176
- case 46: { const r = { type: "Identifier", start, end, name: str(f1, f2) }; if (_isTs) { r.decorators = []; r.optional = false; r.typeAnnotation = null; } return r; }
380
+ case 44: { const r = { type: "Identifier", start, end, name: str(f1, f2), kind: "binding" }; if (_isTs) { r.decorators = nodeArr(f3, f0); r.typeAnnotation = f4 !== NULL ? node(f4) : null; r.optional = !!(flags & 1); } return r; }
381
+ case 45: { const r = { type: "Identifier", start, end, name: str(f1, f2), kind: "name" }; if (_isTs) { r.decorators = []; r.optional = false; r.typeAnnotation = null; } return r; }
382
+ case 46: { const r = { type: "Identifier", start, end, name: str(f1, f2), kind: "label" }; if (_isTs) { r.decorators = []; r.optional = false; r.typeAnnotation = null; } return r; }
177
383
  case 47: { const r = { type: "ExpressionStatement", start, end, expression: f1 !== NULL ? node(f1) : null }; if (_isTs) { r.directive = null; } return r; }
178
384
  case 48: return { type: "IfStatement", start, end, test: f1 !== NULL ? node(f1) : null, consequent: f2 !== NULL ? node(f2) : null, alternate: f3 !== NULL ? node(f3) : null };
179
385
  case 49: return { type: "SwitchStatement", start, end, discriminant: f1 !== NULL ? node(f1) : null, cases: nodeArr(f2, f0) };
@@ -195,13 +401,53 @@ function decode(buffer, source) {
195
401
  case 65: return { type: "EmptyStatement", start, end };
196
402
  case 66: { const r = { type: "VariableDeclaration", start, end, kind: VAR_KINDS[flags & 7], declarations: nodeArr(f1, f0) }; if (_isTs) { r.declare = !!(flags & 8); } return r; }
197
403
  case 67: { const r = { type: "VariableDeclarator", start, end, id: f1 !== NULL ? node(f1) : null, init: f2 !== NULL ? node(f2) : null }; if (_isTs) { r.definite = !!(flags & 1); } return r; }
198
- case 68: return { type: "ExpressionStatement", start, end, expression: node(f1), directive: str(f2, f3) };
404
+ case 68: return {
405
+ type: "ExpressionStatement", start, end,
406
+ expression: node(f1), directive: str(f2, f3),
407
+ };
199
408
  case 69: { const r = { type: "AssignmentPattern", start, end, left: f1 !== NULL ? node(f1) : null, right: f2 !== NULL ? node(f2) : null }; if (_isTs) { r.decorators = nodeArr(f3, f0); r.typeAnnotation = f4 !== NULL ? node(f4) : null; r.optional = !!(flags & 1); } return r; }
200
409
  case 70: { const r = { type: "RestElement", start, end, argument: f1 !== NULL ? node(f1) : null }; if (_isTs) { r.decorators = nodeArr(f2, f0); r.typeAnnotation = f3 !== NULL ? node(f3) : null; r.optional = !!(flags & 1); r.value = null; } return r; }
201
- case 71: { const el = nodeArrHoles(f1, f0); if (f2 !== NULL) el.push(node(f2)); const r = { type: "ArrayPattern", start, end, elements: el }; if (_isTs) { r.decorators = nodeArr(f3, f4); r.optional = !!(flags & 1); r.typeAnnotation = f5 !== NULL ? node(f5) : null; } return r; }
202
- case 72: { const pr = nodeArr(f1, f0); if (f2 !== NULL) pr.push(node(f2)); const r = { type: "ObjectPattern", start, end, properties: pr }; if (_isTs) { r.decorators = nodeArr(f3, f4); r.optional = !!(flags & 1); r.typeAnnotation = f5 !== NULL ? node(f5) : null; } return r; }
203
- case 73: { const r = { type: "Property", start, end, kind: "init", key: node(f1), value: node(f2), method: false, shorthand: !!(flags & 1), computed: !!(flags & 2) }; if (_isTs) { r.optional = false; } return r; }
204
- case 74: return { type: "Program", start, end, sourceType: (flags & 1) ? "module" : "script", hashbang: (flags & 2) ? { type: "Hashbang", start: _p(f2 - 2), end: _p(f3), value: str(f2, f3) } : null, body: nodeArr(f1, f0) };
410
+ case 71: {
411
+ const el = nodeArrHoles(f1, f0);
412
+ if (f2 !== NULL) el.push(node(f2));
413
+ const r = { type: "ArrayPattern", start, end, elements: el };
414
+ if (_isTs) {
415
+ r.decorators = nodeArr(f3, f4);
416
+ r.optional = !!(flags & 1);
417
+ r.typeAnnotation = f5 !== NULL ? node(f5) : null;
418
+ }
419
+ return r;
420
+ }
421
+ case 72: {
422
+ const pr = nodeArr(f1, f0);
423
+ if (f2 !== NULL) pr.push(node(f2));
424
+ const r = { type: "ObjectPattern", start, end, properties: pr };
425
+ if (_isTs) {
426
+ r.decorators = nodeArr(f3, f4);
427
+ r.optional = !!(flags & 1);
428
+ r.typeAnnotation = f5 !== NULL ? node(f5) : null;
429
+ }
430
+ return r;
431
+ }
432
+ case 73: {
433
+ const r = {
434
+ type: "Property", start, end,
435
+ kind: "init",
436
+ key: node(f1), value: node(f2),
437
+ method: false,
438
+ shorthand: !!(flags & 1),
439
+ computed: !!(flags & 2),
440
+ }; if (_isTs) { r.optional = false; } return r; }
441
+ case 74: return {
442
+ type: "Program", start, end,
443
+ sourceType: (flags & 1) ? "module" : "script",
444
+ hashbang: (flags & 2) ? {
445
+ type: "Hashbang",
446
+ start: _p(f2 - 2), end: _p(f3),
447
+ value: str(f2, f3),
448
+ } : null,
449
+ body: nodeArr(f1, f0),
450
+ };
205
451
  case 75: return { type: "ImportExpression", start, end, source: f1 !== NULL ? node(f1) : null, options: f2 !== NULL ? node(f2) : null, phase: (flags & 1) ? ["source", "defer"][(flags >> 1) & 1] : null };
206
452
  case 76: { const r = { type: "ImportDeclaration", start, end, specifiers: nodeArr(f1, f0), source: f2 !== NULL ? node(f2) : null, attributes: nodeArr(f3, f4), phase: (flags & 1) ? ["source", "defer"][(flags >> 1) & 1] : null }; if (_isTs) { r.importKind = IMPORT_EXPORT_KINDS[(flags >> 2) & 1]; } return r; }
207
453
  case 77: { const r = { type: "ImportSpecifier", start, end, imported: f1 !== NULL ? node(f1) : null, local: f2 !== NULL ? node(f2) : null }; if (_isTs) { r.importKind = IMPORT_EXPORT_KINDS[flags & 1]; } return r; }
@@ -251,15 +497,54 @@ function decode(buffer, source) {
251
497
  case 121: return { type: "TSInferType", start, end, typeParameter: f1 !== NULL ? node(f1) : null };
252
498
  case 122: return { type: "TSTypeOperator", start, end, operator: TS_TYPE_OPERATORS[flags & 3], typeAnnotation: f1 !== NULL ? node(f1) : null };
253
499
  case 123: return { type: "TSParenthesizedType", start, end, typeAnnotation: f1 !== NULL ? node(f1) : null };
254
- case 124: return { type: "TSFunctionType", start, end, typeParameters: f1 !== NULL ? node(f1) : null, params: f2 !== NULL ? fnParams(f2) : [], returnType: f3 !== NULL ? node(f3) : null };
255
- case 125: return { type: "TSConstructorType", start, end, abstract: !!(flags & 1), typeParameters: f1 !== NULL ? node(f1) : null, params: f2 !== NULL ? fnParams(f2) : [], returnType: f3 !== NULL ? node(f3) : null };
500
+ case 124: return {
501
+ type: "TSFunctionType", start, end,
502
+ typeParameters: f1 !== NULL ? node(f1) : null,
503
+ params: f2 !== NULL ? fnParams(f2) : [],
504
+ returnType: f3 !== NULL ? node(f3) : null,
505
+ };
506
+ case 125: return {
507
+ type: "TSConstructorType", start, end,
508
+ abstract: !!(flags & 1),
509
+ typeParameters: f1 !== NULL ? node(f1) : null,
510
+ params: f2 !== NULL ? fnParams(f2) : [],
511
+ returnType: f3 !== NULL ? node(f3) : null,
512
+ };
256
513
  case 126: return { type: "TSTypePredicate", start, end, parameterName: f1 !== NULL ? node(f1) : null, typeAnnotation: f2 !== NULL ? node(f2) : null, asserts: !!(flags & 1) };
257
514
  case 127: return { type: "TSTypeLiteral", start, end, members: nodeArr(f1, f0) };
258
- case 128: return { type: "TSMappedType", start, end, key: node(f1), constraint: node(f2), nameType: f3 !== NULL ? node(f3) : null, typeAnnotation: f4 !== NULL ? node(f4) : null, optional: TS_MAPPED_OPTIONAL[(flags >> 0) & 3], readonly: TS_MAPPED_READONLY[(flags >> 2) & 3] };
515
+ case 128: return {
516
+ type: "TSMappedType", start, end,
517
+ key: node(f1),
518
+ constraint: node(f2),
519
+ nameType: f3 !== NULL ? node(f3) : null,
520
+ typeAnnotation: f4 !== NULL ? node(f4) : null,
521
+ optional: TS_MAPPED_OPTIONAL[(flags >> 0) & 3],
522
+ readonly: TS_MAPPED_READONLY[(flags >> 2) & 3],
523
+ };
259
524
  case 129: { const r = { type: "TSPropertySignature", start, end, key: f1 !== NULL ? node(f1) : null, typeAnnotation: f2 !== NULL ? node(f2) : null, computed: !!(flags & 1), optional: !!(flags & 2), readonly: !!(flags & 4) }; if (_isTs) { r.accessibility = null; r.static = false; } return r; }
260
- case 130: return { type: "TSMethodSignature", start, end, key: node(f1), computed: !!(flags & 4), optional: !!(flags & 8), kind: TS_METHOD_SIGNATURE_KINDS[(flags >> 0) & 3], typeParameters: f2 !== NULL ? node(f2) : null, params: f3 !== NULL ? fnParams(f3) : [], returnType: f4 !== NULL ? node(f4) : null, accessibility: null, readonly: false, static: false };
261
- case 131: return { type: "TSCallSignatureDeclaration", start, end, typeParameters: f1 !== NULL ? node(f1) : null, params: f2 !== NULL ? fnParams(f2) : [], returnType: f3 !== NULL ? node(f3) : null };
262
- case 132: return { type: "TSConstructSignatureDeclaration", start, end, typeParameters: f1 !== NULL ? node(f1) : null, params: f2 !== NULL ? fnParams(f2) : [], returnType: f3 !== NULL ? node(f3) : null };
525
+ case 130: return {
526
+ type: "TSMethodSignature", start, end,
527
+ key: node(f1),
528
+ computed: !!(flags & 4),
529
+ optional: !!(flags & 8),
530
+ kind: TS_METHOD_SIGNATURE_KINDS[(flags >> 0) & 3],
531
+ typeParameters: f2 !== NULL ? node(f2) : null,
532
+ params: f3 !== NULL ? fnParams(f3) : [],
533
+ returnType: f4 !== NULL ? node(f4) : null,
534
+ accessibility: null, readonly: false, static: false,
535
+ };
536
+ case 131: return {
537
+ type: "TSCallSignatureDeclaration", start, end,
538
+ typeParameters: f1 !== NULL ? node(f1) : null,
539
+ params: f2 !== NULL ? fnParams(f2) : [],
540
+ returnType: f3 !== NULL ? node(f3) : null,
541
+ };
542
+ case 132: return {
543
+ type: "TSConstructSignatureDeclaration", start, end,
544
+ typeParameters: f1 !== NULL ? node(f1) : null,
545
+ params: f2 !== NULL ? fnParams(f2) : [],
546
+ returnType: f3 !== NULL ? node(f3) : null,
547
+ };
263
548
  case 133: { const r = { type: "TSIndexSignature", start, end, parameters: nodeArr(f1, f0), typeAnnotation: f2 !== NULL ? node(f2) : null, readonly: !!(flags & 1) }; if (_isTs) { r.static = !!(flags & 2); r.accessibility = null; } return r; }
264
549
  case 134: return { type: "TSTypeAliasDeclaration", start, end, id: f1 !== NULL ? node(f1) : null, typeParameters: f2 !== NULL ? node(f2) : null, typeAnnotation: f3 !== NULL ? node(f3) : null, declare: !!(flags & 1) };
265
550
  case 135: return { type: "TSInterfaceDeclaration", start, end, id: f1 !== NULL ? node(f1) : null, typeParameters: f2 !== NULL ? node(f2) : null, extends: nodeArr(f3, f0), body: f4 !== NULL ? node(f4) : null, declare: !!(flags & 1) };
@@ -269,11 +554,32 @@ function decode(buffer, source) {
269
554
  case 139: return { type: "TSEnumDeclaration", start, end, id: f1 !== NULL ? node(f1) : null, body: f2 !== NULL ? node(f2) : null, const: !!(flags & 1), declare: !!(flags & 2) };
270
555
  case 140: return { type: "TSEnumBody", start, end, members: nodeArr(f1, f0) };
271
556
  case 141: return { type: "TSEnumMember", start, end, id: f1 !== NULL ? node(f1) : null, initializer: f2 !== NULL ? node(f2) : null, computed: !!(flags & 1) };
272
- case 142: { const r = { type: "TSModuleDeclaration", start, end, id: node(f1), kind: TS_MODULE_KINDS[(flags >> 0) & 1], declare: !!(flags & 2), global: false }; if (f2 !== NULL) r.body = node(f2); return r; }
557
+ case 142: {
558
+ const r = {
559
+ type: "TSModuleDeclaration", start, end,
560
+ id: node(f1),
561
+ kind: TS_MODULE_KINDS[(flags >> 0) & 1],
562
+ declare: !!(flags & 2),
563
+ global: false,
564
+ };
565
+ if (f2 !== NULL) r.body = node(f2);
566
+ return r;
567
+ }
273
568
  case 143: return { type: "TSModuleBlock", start, end, body: nodeArr(f1, f0) };
274
- case 144: return { type: "TSModuleDeclaration", start, end, id: node(f1), body: node(f2), kind: "global", declare: !!(flags & 1), global: true };
569
+ case 144: return {
570
+ type: "TSModuleDeclaration", start, end,
571
+ id: node(f1), body: node(f2),
572
+ kind: "global",
573
+ declare: !!(flags & 1),
574
+ global: true,
575
+ };
275
576
  case 145: { const r = { type: "TSParameterProperty", start, end, decorators: nodeArr(f1, f0), parameter: f2 !== NULL ? node(f2) : null, override: !!(flags & 1), readonly: !!(flags & 2), accessibility: ACCESSIBILITY[(flags >> 2) & 3] }; if (_isTs) { r.static = false; } return r; }
276
- case 146: return { type: "Identifier", start, end, decorators: [], name: "this", optional: false, typeAnnotation: f1 !== NULL ? node(f1) : null };
577
+ case 146: return {
578
+ type: "Identifier", start, end,
579
+ decorators: [],
580
+ name: "this", kind: "this", optional: false,
581
+ typeAnnotation: f1 !== NULL ? node(f1) : null,
582
+ };
277
583
  case 147: return { type: "TSAsExpression", start, end, expression: f1 !== NULL ? node(f1) : null, typeAnnotation: f2 !== NULL ? node(f2) : null };
278
584
  case 148: return { type: "TSSatisfiesExpression", start, end, expression: f1 !== NULL ? node(f1) : null, typeAnnotation: f2 !== NULL ? node(f2) : null };
279
585
  case 149: return { type: "TSTypeAssertion", start, end, typeAnnotation: f1 !== NULL ? node(f1) : null, expression: f2 !== NULL ? node(f2) : null };
@@ -296,35 +602,21 @@ function decode(buffer, source) {
296
602
  case 166: return { type: "JSXSpreadAttribute", start, end, argument: f1 !== NULL ? node(f1) : null };
297
603
  case 167: return { type: "JSXExpressionContainer", start, end, expression: f1 !== NULL ? node(f1) : null };
298
604
  case 168: return { type: "JSXEmptyExpression", start, end };
299
- case 169: { const t = str(f1, f2); return { type: "JSXText", start, end, value: t, raw: t }; }
605
+ case 169: {
606
+ const t = str(f1, f2);
607
+ return { type: "JSXText", start, end, value: t, raw: t };
608
+ }
300
609
  case 170: return { type: "JSXSpreadChild", start, end, expression: f1 !== NULL ? node(f1) : null };
301
610
  }
302
611
  }
303
- const cOff = _spOff + spLen;
304
- const lsOff = cOff + commentCount * 20;
612
+ const lsOff = _cOff + commentCount * 12;
305
613
  const dOff = lsOff + lineStartsCount * 4;
306
- const dv = new DataView(buffer);
307
- function _decodeComments() {
308
- const out = new Array(commentCount);
309
- for (let j = 0; j < commentCount; j++) {
310
- const o = cOff + j * 20;
311
- const flags = _u8[o + 2];
312
- out[j] = {
313
- type: COMMENT_TYPES[_u8[o + 0]],
314
- kind: COMMENT_KINDS[_u8[o + 1]],
315
- precededByNewline: (flags & 1) !== 0,
316
- followedByNewline: (flags & 2) !== 0,
317
- value: str(dv.getUint32(o + 12, true), dv.getUint32(o + 16, true)),
318
- start: _p(dv.getUint32(o + 4, true)),
319
- end: _p(dv.getUint32(o + 8, true)),
320
- };
321
- }
322
- return out;
323
- }
324
614
  function _decodeLineStarts() {
325
615
  const out = new Array(lineStartsCount);
326
616
  if (_firstNa >= _srcLen) {
327
- for (let j = 0; j < lineStartsCount; j++) out[j] = dv.getUint32(lsOff + j * 4, true);
617
+ for (let j = 0; j < lineStartsCount; j++) {
618
+ out[j] = dv.getUint32(lsOff + j * 4, true);
619
+ }
328
620
  return out;
329
621
  }
330
622
  if (pm === null) pm = buildPosMap(_src, _srcLen, _firstNa);
@@ -345,25 +637,51 @@ function decode(buffer, source) {
345
637
  const msg = _td.decode(_u8.subarray(dp, dp + ml)); dp += ml;
346
638
  const hh = _u8[dp]; dp++;
347
639
  let help = null;
348
- if (hh) { const hl = dv.getUint32(dp, true); dp += 4; help = _td.decode(_u8.subarray(dp, dp + hl)); dp += hl; }
640
+ if (hh) {
641
+ const hl = dv.getUint32(dp, true); dp += 4;
642
+ help = _td.decode(_u8.subarray(dp, dp + hl)); dp += hl;
643
+ }
349
644
  const lc = dv.getUint32(dp, true); dp += 4;
350
645
  const labels = new Array(lc);
351
646
  for (let k = 0; k < lc; k++) {
352
647
  const ls = _p(dv.getUint32(dp, true)); dp += 4;
353
648
  const le = _p(dv.getUint32(dp, true)); dp += 4;
354
649
  const lml = dv.getUint32(dp, true); dp += 4;
355
- labels[k] = { start: ls, end: le, message: _td.decode(_u8.subarray(dp, dp + lml)) }; dp += lml;
650
+ labels[k] = {
651
+ start: ls,
652
+ end: le,
653
+ message: _td.decode(_u8.subarray(dp, dp + lml)),
654
+ };
655
+ dp += lml;
356
656
  }
357
657
  out[j] = { severity: sev, message: msg, start: ds, end: de, help, labels };
358
658
  }
359
659
  return out;
360
660
  }
361
- let _program, _comments, _lineStarts, _diagnostics;
661
+ let _program, _lineStarts, _diagnostics;
662
+ function _getLineStarts() {
663
+ if (_lineStarts === undefined) _lineStarts = _decodeLineStarts();
664
+ return _lineStarts;
665
+ }
362
666
  return {
363
- get program() { return _program !== undefined ? _program : (_program = node(progIdx)); },
364
- get comments() { return _comments !== undefined ? _comments : (_comments = _decodeComments()); },
365
- get lineStarts() { return _lineStarts !== undefined ? _lineStarts : (_lineStarts = _decodeLineStarts()); },
366
- get diagnostics() { return _diagnostics !== undefined ? _diagnostics : (_diagnostics = _decodeDiagnostics()); },
667
+ get program() {
668
+ return _program !== undefined ? _program : (_program = node(progIdx));
669
+ },
670
+ get diagnostics() {
671
+ return _diagnostics !== undefined
672
+ ? _diagnostics
673
+ : (_diagnostics = _decodeDiagnostics());
674
+ },
675
+ get lineStarts() { return _getLineStarts(); },
676
+ locOf(offset) {
677
+ const ls = _getLineStarts();
678
+ let lo = 0, hi = ls.length;
679
+ while (lo < hi) {
680
+ const mid = (lo + hi) >>> 1;
681
+ if (ls[mid] <= offset) lo = mid + 1; else hi = mid;
682
+ }
683
+ return { line: lo, column: offset - ls[lo - 1] };
684
+ },
367
685
  };
368
686
  }
369
687
  export { decode };
package/index.d.ts CHANGED
@@ -40,38 +40,39 @@ interface ParseOptions {
40
40
  * @default false
41
41
  */
42
42
  semanticErrors?: boolean;
43
+ /**
44
+ * Collect comments and attach them to host AST nodes via
45
+ * {@link BaseNode.comments}.
46
+ * @default false
47
+ */
48
+ attachComments?: boolean;
43
49
  }
44
50
 
45
51
  /** Whether a {@link Comment} came from a line or block source comment. */
46
52
  type CommentType = "Line" | "Block";
47
53
 
48
54
  /**
49
- * Semantic classification of a {@link Comment}, computed at parse time so
50
- * consumers can route comments without rescanning their value.
55
+ * Position of a {@link Comment} relative to its host node.
51
56
  *
52
- * - `"normal"`: plain comment with no special meaning.
53
- * - `"legal"`: `/*! ... *\/` or contains `@license`, `@preserve`, or `@cc_on`.
54
- * - `"jsdoc"`: `/** ... *\/` block.
55
- * - `"annotation"`: `/*# ... *\/` or `/*@ ... *\/` other than tree-shaking.
56
- * - `"pure"`: `/*#__PURE__*\/` or `/*@__PURE__*\/`.
57
- * - `"no_side_effects"`: `/*#__NO_SIDE_EFFECTS__*\/` or `/*@__NO_SIDE_EFFECTS__*\/`.
57
+ * - `before`: leading the host.
58
+ * - `after`: trailing the host.
59
+ * - `inside`: interior to an otherwise empty host.
58
60
  */
59
- type CommentKind = "normal" | "legal" | "jsdoc" | "annotation" | "pure" | "no_side_effects";
61
+ type CommentPosition = "before" | "after" | "inside";
60
62
 
61
- /** A source code comment. */
63
+ /**
64
+ * A source code comment attached to a single host AST node.
65
+ *
66
+ * `sameLine` is true when the comment shares a source line with the
67
+ * host's adjacent edge (host's start for `before`, host's end for
68
+ * `after`). For `inside` it is always `false`.
69
+ */
62
70
  interface Comment {
63
71
  type: CommentType;
64
- kind: CommentKind;
65
- /** True when a line terminator immediately precedes this comment. */
66
- precededByNewline: boolean;
67
- /** True when a line terminator immediately follows this comment. */
68
- followedByNewline: boolean;
72
+ position: CommentPosition;
73
+ sameLine: boolean;
69
74
  /** Comment text without the delimiters. */
70
75
  value: string;
71
- /** Byte offset. */
72
- start: number;
73
- /** Byte offset. */
74
- end: number;
75
76
  }
76
77
 
77
78
  /** A labeled source span attached to a {@link Diagnostic}. */
@@ -103,16 +104,34 @@ interface Diagnostic {
103
104
  labels: DiagnosticLabel[];
104
105
  }
105
106
 
107
+ /**
108
+ * A `(line, column)` pair into the source, matching ESTree's `loc` convention.
109
+ * Lines are 1-based; columns are 0-based.
110
+ */
111
+ interface SourceLocation {
112
+ /** 1-based line number. */
113
+ line: number;
114
+ /** 0-based column number within the line. */
115
+ column: number;
116
+ }
117
+
106
118
  /** The result returned by the parser. */
107
119
  interface ParseResult {
108
120
  /** Root ESTree/TypeScript-ESTree AST node. */
109
121
  program: Program;
110
- /** All comments in source order. */
111
- comments: Comment[];
112
- /** Byte offset where each source line begins. Index 0 is always 0. */
113
- lineStarts: number[];
114
122
  /** Syntax diagnostics, and semantic diagnostics when {@link ParseOptions.semanticErrors} is enabled. */
115
123
  diagnostics: Diagnostic[];
124
+ /**
125
+ * Sorted UTF-16 offsets where each line begins. Index `i` is the start of
126
+ * line `i + 1`. Used internally by {@link locOf}, and required by
127
+ * `yuku-codegen` for source maps.
128
+ */
129
+ lineStarts: number[];
130
+ /**
131
+ * Resolves an offset to a `{ line, column }` pair. Lines are
132
+ * 1-based, columns are 0-based, matching ESTree's `loc` convention.
133
+ */
134
+ locOf(offset: number): SourceLocation;
116
135
  }
117
136
 
118
137
  /**
@@ -144,6 +163,11 @@ export function sourceTypeFromPath(path: string): SourceType;
144
163
  interface BaseNode {
145
164
  start: number;
146
165
  end: number;
166
+ /**
167
+ * Comments attached to this node in source order. Present only when
168
+ * {@link ParseOptions.attachComments} is enabled.
169
+ */
170
+ comments?: Comment[];
147
171
  }
148
172
 
149
173
  type Span = BaseNode;
@@ -230,39 +254,37 @@ type PropertyDefinitionType = "PropertyDefinition" | "TSAbstractPropertyDefiniti
230
254
 
231
255
  type AccessorPropertyType = "AccessorProperty" | "TSAbstractAccessorProperty";
232
256
 
233
- interface IdentifierName extends BaseNode {
234
- type: "Identifier";
235
- name: string;
236
- decorators?: [];
237
- optional?: false;
238
- typeAnnotation?: null;
239
- }
240
-
241
- interface IdentifierReference extends BaseNode {
242
- type: "Identifier";
243
- name: string;
244
- decorators?: [];
245
- optional?: false;
246
- typeAnnotation?: null;
247
- }
257
+ /**
258
+ * Role of an `Identifier` within the AST. Yuku exposes the four ESTree-collapsed
259
+ * identifier flavors as a discriminator, plus `"this"` for a TS `this`-parameter
260
+ * (which TS-ESTree also encodes as an `Identifier` with `name: "this"`).
261
+ *
262
+ * - `"reference"`: identifier read as a value (`x` in `x + 1`, `console`).
263
+ * - `"binding"`: identifier being declared (parameters, `let x`, function/class names).
264
+ * - `"name"`: identifier in a non-binding name slot (property/method names like `log` in `console.log`).
265
+ * - `"label"`: loop label (`outer` in `outer: for (...) {}`).
266
+ * - `"this"`: the TS `this`-parameter (`function f(this: T)`).
267
+ */
268
+ type IdentifierKind = "reference" | "binding" | "name" | "label" | "this";
248
269
 
249
- interface BindingIdentifier extends BaseNode {
270
+ interface Identifier extends BaseNode {
250
271
  type: "Identifier";
251
272
  name: string;
273
+ /**
274
+ * ESTree collapses several distinct identifier
275
+ * roles onto one `Identifier` node, `kind` lets you tell them apart without
276
+ * walking the parent context. See {@link IdentifierKind}.
277
+ */
278
+ kind: IdentifierKind;
252
279
  decorators?: Decorator[];
253
280
  optional?: boolean;
254
281
  typeAnnotation?: TSTypeAnnotation | null;
255
282
  }
256
283
 
257
- interface LabelIdentifier extends BaseNode {
258
- type: "Identifier";
259
- name: string;
260
- decorators?: [];
261
- optional?: false;
262
- typeAnnotation?: null;
263
- }
264
-
265
- type Identifier = IdentifierName | IdentifierReference | BindingIdentifier | LabelIdentifier;
284
+ type IdentifierName = Identifier;
285
+ type IdentifierReference = Identifier;
286
+ type BindingIdentifier = Identifier;
287
+ type LabelIdentifier = Identifier;
266
288
 
267
289
  interface PrivateIdentifier extends BaseNode {
268
290
  type: "PrivateIdentifier";
@@ -1693,6 +1715,7 @@ type Node =
1693
1715
  | TSAbstractAccessorProperty
1694
1716
  | StaticBlock
1695
1717
  | Decorator
1718
+ | TSEmptyBodyFunctionExpression
1696
1719
  | ImportSpecifier
1697
1720
  | ImportDefaultSpecifier
1698
1721
  | ImportNamespaceSpecifier
@@ -1738,13 +1761,14 @@ export type {
1738
1761
  ParseResult,
1739
1762
  Comment,
1740
1763
  CommentType,
1741
- CommentKind,
1764
+ CommentPosition,
1742
1765
  Diagnostic,
1743
1766
  DiagnosticLabel,
1744
1767
  DiagnosticSeverity,
1745
1768
  SourceType,
1746
1769
  ModuleKind,
1747
1770
  SourceLang,
1771
+ SourceLocation,
1748
1772
  BaseNode,
1749
1773
  Span,
1750
1774
  Program,
@@ -1769,6 +1793,7 @@ export type {
1769
1793
  ModuleExportName,
1770
1794
  PropertyKey,
1771
1795
  Identifier,
1796
+ IdentifierKind,
1772
1797
  IdentifierName,
1773
1798
  IdentifierReference,
1774
1799
  BindingIdentifier,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yuku-parser",
3
- "version": "0.5.11",
3
+ "version": "0.5.15",
4
4
  "description": "High-performance JavaScript/TypeScript parser",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -17,17 +17,17 @@
17
17
  "decode.js"
18
18
  ],
19
19
  "optionalDependencies": {
20
- "@yuku-parser/binding-linux-x64-gnu": "0.5.11",
21
- "@yuku-parser/binding-linux-arm64-gnu": "0.5.11",
22
- "@yuku-parser/binding-linux-arm-gnu": "0.5.11",
23
- "@yuku-parser/binding-linux-x64-musl": "0.5.11",
24
- "@yuku-parser/binding-linux-arm64-musl": "0.5.11",
25
- "@yuku-parser/binding-linux-arm-musl": "0.5.11",
26
- "@yuku-parser/binding-darwin-x64": "0.5.11",
27
- "@yuku-parser/binding-darwin-arm64": "0.5.11",
28
- "@yuku-parser/binding-win32-x64": "0.5.11",
29
- "@yuku-parser/binding-win32-arm64": "0.5.11",
30
- "@yuku-parser/binding-freebsd-x64": "0.5.11"
20
+ "@yuku-parser/binding-linux-x64-gnu": "0.5.15",
21
+ "@yuku-parser/binding-linux-arm64-gnu": "0.5.15",
22
+ "@yuku-parser/binding-linux-arm-gnu": "0.5.15",
23
+ "@yuku-parser/binding-linux-x64-musl": "0.5.15",
24
+ "@yuku-parser/binding-linux-arm64-musl": "0.5.15",
25
+ "@yuku-parser/binding-linux-arm-musl": "0.5.15",
26
+ "@yuku-parser/binding-darwin-x64": "0.5.15",
27
+ "@yuku-parser/binding-darwin-arm64": "0.5.15",
28
+ "@yuku-parser/binding-win32-x64": "0.5.15",
29
+ "@yuku-parser/binding-win32-arm64": "0.5.15",
30
+ "@yuku-parser/binding-freebsd-x64": "0.5.15"
31
31
  },
32
32
  "keywords": [
33
33
  "acorn",