yuku-parser 0.5.14 → 0.5.16

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 (5) hide show
  1. package/README.md +76 -25
  2. package/decode.js +390 -71
  3. package/index.d.ts +67 -52
  4. package/index.js +0 -10
  5. 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,24 +55,26 @@ 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
 
60
64
  ## Resolving offsets to `(line, column)`
61
65
 
62
- Nodes, comments, and diagnostics carry `start`/`end` offsets. To turn an offset into a `{ line, column }` pair, use `locOf` with `lineStarts` from the parse result:
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:
63
67
 
64
68
  ```ts
65
- import { parse, locOf } from "yuku-parser";
69
+ import { parse } from "yuku-parser";
66
70
 
67
- const { program, lineStarts } = parse(source);
68
- const { line, column } = locOf(lineStarts, program.body[0].start);
71
+ const result = parse(source);
72
+ const { line, column } = result.locOf(result.program.body[0].start);
69
73
  ```
70
74
 
71
- 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, well over 10M ops/s even on million-line files, so it's safe to call per node during a walk.
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.
72
78
 
73
79
  ## Walking the AST
74
80
 
@@ -104,6 +110,7 @@ const result = parse(source, {
104
110
  preserveParens: true,
105
111
  allowReturnOutsideFunction: false,
106
112
  semanticErrors: false,
113
+ attachComments: false,
107
114
  });
108
115
  ```
109
116
 
@@ -114,6 +121,7 @@ const result = parse(source, {
114
121
  | `preserveParens` | `true`, `false` | `true` | Keep `ParenthesizedExpression` nodes in the AST. When false, parentheses are stripped and only the inner expression is kept. |
115
122
  | `allowReturnOutsideFunction` | `true`, `false` | `false` | Allow `return` statements outside of functions, at the top level. |
116
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). |
117
125
 
118
126
  ## Result
119
127
 
@@ -122,13 +130,13 @@ const result = parse(source, {
122
130
  ```ts
123
131
  interface ParseResult {
124
132
  program: Program;
125
- comments: Comment[];
126
- lineStarts: number[];
127
133
  diagnostics: Diagnostic[];
134
+ lineStarts: number[];
135
+ locOf(offset: number): { line: number; column: number };
128
136
  }
129
137
  ```
130
138
 
131
- 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.
132
140
 
133
141
  ### Diagnostics
134
142
 
@@ -153,22 +161,65 @@ const result = parse(`let x = 1; let x = 2;`, { semanticErrors: true });
153
161
 
154
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.
155
163
 
156
- ### Comments
164
+ ## Comments
157
165
 
158
- Every entry in `result.comments` describes one source comment:
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
+ ```
181
+
182
+ Each comment is:
159
183
 
160
184
  ```ts
161
185
  interface Comment {
162
186
  type: "Line" | "Block";
163
- precededByNewline: boolean;
164
- followedByNewline: boolean;
165
- value: string; // text without the surrounding delimiters
166
- start: number; // byte offset
167
- end: number; // byte offset
187
+ position: "before" | "after" | "inside";
188
+ sameLine: boolean;
189
+ value: string; // body without delimiters
168
190
  }
169
191
  ```
170
192
 
171
- `precededByNewline` and `followedByNewline` capture line layout, useful for tools that need to reconstruct source positioning.
193
+ `position` tells you where the comment sits relative to its host:
194
+
195
+ - `"before"`: leading the host node.
196
+ - `"after"`: trailing the host node.
197
+ - `"inside"`: interior to an otherwise empty host, like `function f() { /* hi */ }`.
198
+
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.
221
+
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.
172
223
 
173
224
  ## License
174
225
 
package/decode.js CHANGED
@@ -13,7 +13,6 @@ const PROPERTY_KINDS = ["init", "get", "set"];
13
13
  const METHOD_KINDS = ["constructor", "method", "get", "set"];
14
14
  const FUNCTION_TYPES = ["FunctionDeclaration", "FunctionExpression", "TSDeclareFunction", "TSEmptyBodyFunctionExpression"];
15
15
  const CLASS_TYPES = ["ClassDeclaration", "ClassExpression"];
16
- const COMMENT_TYPES = ["Line", "Block"];
17
16
  const SEVERITY = ["error", "warning", "hint", "info"];
18
17
  const IMPORT_EXPORT_KINDS = ["value", "type"];
19
18
  const ACCESSIBILITY = [null, "public", "private", "protected"];
@@ -34,7 +33,9 @@ function buildPosMap(src, byteLen, startByte) {
34
33
  while (i < len) {
35
34
  if (i + 16 <= len) {
36
35
  let allAscii = true;
37
- 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
+ }
38
39
  if (allAscii) {
39
40
  for (let k = 0; k < 16; k++) m[bp + k] = u16p + k;
40
41
  bp += 16; u16p += 16; i += 16;
@@ -45,8 +46,14 @@ function buildPosMap(src, byteLen, startByte) {
45
46
  m[bp] = u16p;
46
47
  if (cu < 0x80) { bp++; u16p++; i++; }
47
48
  else if (cu < 0x800) { m[bp + 1] = u16p + 1; bp += 2; u16p++; i++; }
48
- else if (cu < 0xD800 || cu >= 0xE000) { m[bp + 1] = u16p + 1; m[bp + 2] = u16p + 1; bp += 3; u16p++; i++; }
49
- 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
+ }
50
57
  }
51
58
  m[byteLen - startByte] = u16p;
52
59
  return m;
@@ -57,14 +64,26 @@ function decode(buffer, source) {
57
64
  const _u32 = new Int32Array(buffer, 0, aLen >> 2);
58
65
  const _src = source;
59
66
  const _srcLen = _u32[3];
60
- const nodeCount = _u32[0], extraCount = _u32[1], spLen = _u32[2];
61
- const commentCount = _u32[4], lineStartsCount = _u32[5], diagCount = _u32[6], progIdx = _u32[7];
62
- 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);
63
77
  const _firstNa = _u32[9];
64
78
  const _nodesOff = 40;
65
79
  const eOff = _nodesOff + nodeCount * 48;
66
80
  const _extraBase = eOff >> 2;
67
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;
68
87
  function _poolDecode(s, e) {
69
88
  const a = _spOff + s - _srcLen, b = _spOff + e - _srcLen;
70
89
  let hasEd = false;
@@ -74,9 +93,23 @@ function decode(buffer, source) {
74
93
  for (let i = a; i < b; ) {
75
94
  const c = _u8[i];
76
95
  if (c < 0x80) { r += String.fromCharCode(c); i++; }
77
- else if (c < 0xE0) { r += String.fromCharCode(((c & 0x1F) << 6) | (_u8[i+1] & 0x3F)); i += 2; }
78
- else if (c < 0xF0) { r += String.fromCharCode(((c & 0x0F) << 12) | ((_u8[i+1] & 0x3F) << 6) | (_u8[i+2] & 0x3F)); i += 3; }
79
- 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
+ }
80
113
  }
81
114
  return r;
82
115
  }
@@ -106,7 +139,10 @@ function decode(buffer, source) {
106
139
  }
107
140
  function nodeArrHoles(s, len) {
108
141
  const r = new Array(len);
109
- 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
+ }
110
146
  return r;
111
147
  }
112
148
  function fnParams(idx) {
@@ -119,19 +155,77 @@ function decode(buffer, source) {
119
155
  if (rest !== NULL) p.push(node(rest));
120
156
  return p;
121
157
  }
122
- function node(i) {
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
+ }
174
+ function nodeWithComments(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 (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) {
123
186
  const o = _nodesOff + i * 48;
124
187
  const tag = _u8[o];
125
188
  const flags = _u8[o + 2] | (_u8[o + 3] << 8);
126
189
  const f0 = _u8[o + 4] | (_u8[o + 5] << 8);
127
190
  const b = o >> 2;
128
- 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];
129
195
  const start = _p(_u32[b + 10]), end = _p(_u32[b + 11]);
130
196
  switch (tag) {
131
197
  case 0: return { type: "SequenceExpression", start, end, expressions: nodeArr(f1, f0) };
132
198
  case 1: return { type: "ParenthesizedExpression", start, end, expression: f1 !== NULL ? node(f1) : null };
133
- 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; }
134
- 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
+ }
135
229
  case 4: return { type: "BlockStatement", start, end, body: nodeArr(f1, f0) };
136
230
  case 5: return { type: "BlockStatement", start, end, body: nodeArr(f1, f0) };
137
231
  case 6: return { params: fnParams(i) };
@@ -139,7 +233,11 @@ function decode(buffer, source) {
139
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] };
140
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] };
141
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 };
142
- 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
+ };
143
241
  case 12: return { type: "UpdateExpression", start, end, argument: f1 !== NULL ? node(f1) : null, operator: UPDATE_OPS[flags & 1], prefix: !!(flags & 2) };
144
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] };
145
243
  case 14: return { type: "ArrayExpression", start, end, elements: nodeArrHoles(f1, f0) };
@@ -155,26 +253,133 @@ function decode(buffer, source) {
155
253
  case 24: return { type: "YieldExpression", start, end, argument: f1 !== NULL ? node(f1) : null, delegate: !!(flags & 1) };
156
254
  case 25: return { type: "MetaProperty", start, end, meta: f1 !== NULL ? node(f1) : null, property: f2 !== NULL ? node(f2) : null };
157
255
  case 26: return { type: "Decorator", start, end, expression: f1 !== NULL ? node(f1) : null };
158
- 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
+ }
159
273
  case 28: return { type: "ClassBody", start, end, body: nodeArr(f1, f0) };
160
- 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; }
161
- 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
+ }
162
317
  case 31: return { type: "StaticBlock", start, end, body: nodeArr(f1, f0) };
163
318
  case 32: return { type: "Super", start, end };
164
- case 33: return { type: "Literal", start, end, value: str(f1, f2), raw: _src.slice(start, end) };
165
- 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 }; }
166
- 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() }; }
167
- 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
+ }
168
351
  case 37: return { type: "Literal", start, end, value: null, raw: "null" };
169
352
  case 38: return { type: "ThisExpression", start, end };
170
- 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
+ }
171
363
  case 40: return { type: "TemplateLiteral", start, end, quasis: nodeArr(f1, f0), expressions: nodeArr(f2, f3) };
172
- 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 }; }
173
- 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; }
174
379
  case 43: return { type: "PrivateIdentifier", start, end, name: str(f1, f2) };
175
- 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; }
176
- case 45: { const r = { type: "Identifier", start, end, name: str(f1, f2) }; if (_isTs) { r.decorators = []; r.optional = false; r.typeAnnotation = null; } return r; }
177
- 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; }
178
383
  case 47: { const r = { type: "ExpressionStatement", start, end, expression: f1 !== NULL ? node(f1) : null }; if (_isTs) { r.directive = null; } return r; }
179
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 };
180
385
  case 49: return { type: "SwitchStatement", start, end, discriminant: f1 !== NULL ? node(f1) : null, cases: nodeArr(f2, f0) };
@@ -196,13 +401,53 @@ function decode(buffer, source) {
196
401
  case 65: return { type: "EmptyStatement", start, end };
197
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; }
198
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; }
199
- 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
+ };
200
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; }
201
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; }
202
- 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; }
203
- 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; }
204
- 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; }
205
- 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
+ };
206
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 };
207
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; }
208
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; }
@@ -252,15 +497,54 @@ function decode(buffer, source) {
252
497
  case 121: return { type: "TSInferType", start, end, typeParameter: f1 !== NULL ? node(f1) : null };
253
498
  case 122: return { type: "TSTypeOperator", start, end, operator: TS_TYPE_OPERATORS[flags & 3], typeAnnotation: f1 !== NULL ? node(f1) : null };
254
499
  case 123: return { type: "TSParenthesizedType", start, end, typeAnnotation: f1 !== NULL ? node(f1) : null };
255
- case 124: return { type: "TSFunctionType", start, end, typeParameters: f1 !== NULL ? node(f1) : null, params: f2 !== NULL ? fnParams(f2) : [], returnType: f3 !== NULL ? node(f3) : null };
256
- 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
+ };
257
513
  case 126: return { type: "TSTypePredicate", start, end, parameterName: f1 !== NULL ? node(f1) : null, typeAnnotation: f2 !== NULL ? node(f2) : null, asserts: !!(flags & 1) };
258
514
  case 127: return { type: "TSTypeLiteral", start, end, members: nodeArr(f1, f0) };
259
- 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
+ };
260
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; }
261
- 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 };
262
- case 131: return { type: "TSCallSignatureDeclaration", start, end, typeParameters: f1 !== NULL ? node(f1) : null, params: f2 !== NULL ? fnParams(f2) : [], returnType: f3 !== NULL ? node(f3) : null };
263
- 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
+ };
264
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; }
265
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) };
266
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) };
@@ -270,11 +554,32 @@ function decode(buffer, source) {
270
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) };
271
555
  case 140: return { type: "TSEnumBody", start, end, members: nodeArr(f1, f0) };
272
556
  case 141: return { type: "TSEnumMember", start, end, id: f1 !== NULL ? node(f1) : null, initializer: f2 !== NULL ? node(f2) : null, computed: !!(flags & 1) };
273
- 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
+ }
274
568
  case 143: return { type: "TSModuleBlock", start, end, body: nodeArr(f1, f0) };
275
- 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
+ };
276
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; }
277
- 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
+ };
278
583
  case 147: return { type: "TSAsExpression", start, end, expression: f1 !== NULL ? node(f1) : null, typeAnnotation: f2 !== NULL ? node(f2) : null };
279
584
  case 148: return { type: "TSSatisfiesExpression", start, end, expression: f1 !== NULL ? node(f1) : null, typeAnnotation: f2 !== NULL ? node(f2) : null };
280
585
  case 149: return { type: "TSTypeAssertion", start, end, typeAnnotation: f1 !== NULL ? node(f1) : null, expression: f2 !== NULL ? node(f2) : null };
@@ -297,34 +602,22 @@ function decode(buffer, source) {
297
602
  case 166: return { type: "JSXSpreadAttribute", start, end, argument: f1 !== NULL ? node(f1) : null };
298
603
  case 167: return { type: "JSXExpressionContainer", start, end, expression: f1 !== NULL ? node(f1) : null };
299
604
  case 168: return { type: "JSXEmptyExpression", start, end };
300
- 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
+ }
301
609
  case 170: return { type: "JSXSpreadChild", start, end, expression: f1 !== NULL ? node(f1) : null };
302
610
  }
303
611
  }
304
- const cOff = _spOff + spLen;
305
- const lsOff = cOff + commentCount * 20;
612
+ const node = _attachComments ? nodeWithComments : _decode;
613
+ const lsOff = _cOff + commentCount * 12;
306
614
  const dOff = lsOff + lineStartsCount * 4;
307
- const dv = new DataView(buffer);
308
- function _decodeComments() {
309
- const out = new Array(commentCount);
310
- for (let j = 0; j < commentCount; j++) {
311
- const o = cOff + j * 20;
312
- const flags = _u8[o + 1];
313
- out[j] = {
314
- type: COMMENT_TYPES[_u8[o + 0]],
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
615
  function _decodeLineStarts() {
325
616
  const out = new Array(lineStartsCount);
326
617
  if (_firstNa >= _srcLen) {
327
- for (let j = 0; j < lineStartsCount; j++) out[j] = dv.getUint32(lsOff + j * 4, true);
618
+ for (let j = 0; j < lineStartsCount; j++) {
619
+ out[j] = dv.getUint32(lsOff + j * 4, true);
620
+ }
328
621
  return out;
329
622
  }
330
623
  if (pm === null) pm = buildPosMap(_src, _srcLen, _firstNa);
@@ -345,25 +638,51 @@ function decode(buffer, source) {
345
638
  const msg = _td.decode(_u8.subarray(dp, dp + ml)); dp += ml;
346
639
  const hh = _u8[dp]; dp++;
347
640
  let help = null;
348
- if (hh) { const hl = dv.getUint32(dp, true); dp += 4; help = _td.decode(_u8.subarray(dp, dp + hl)); dp += hl; }
641
+ if (hh) {
642
+ const hl = dv.getUint32(dp, true); dp += 4;
643
+ help = _td.decode(_u8.subarray(dp, dp + hl)); dp += hl;
644
+ }
349
645
  const lc = dv.getUint32(dp, true); dp += 4;
350
646
  const labels = new Array(lc);
351
647
  for (let k = 0; k < lc; k++) {
352
648
  const ls = _p(dv.getUint32(dp, true)); dp += 4;
353
649
  const le = _p(dv.getUint32(dp, true)); dp += 4;
354
650
  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;
651
+ labels[k] = {
652
+ start: ls,
653
+ end: le,
654
+ message: _td.decode(_u8.subarray(dp, dp + lml)),
655
+ };
656
+ dp += lml;
356
657
  }
357
658
  out[j] = { severity: sev, message: msg, start: ds, end: de, help, labels };
358
659
  }
359
660
  return out;
360
661
  }
361
- let _program, _comments, _lineStarts, _diagnostics;
662
+ let _program, _lineStarts, _diagnostics;
663
+ function _getLineStarts() {
664
+ if (_lineStarts === undefined) _lineStarts = _decodeLineStarts();
665
+ return _lineStarts;
666
+ }
362
667
  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()); },
668
+ get program() {
669
+ return _program !== undefined ? _program : (_program = node(progIdx));
670
+ },
671
+ get diagnostics() {
672
+ return _diagnostics !== undefined
673
+ ? _diagnostics
674
+ : (_diagnostics = _decodeDiagnostics());
675
+ },
676
+ get lineStarts() { return _getLineStarts(); },
677
+ locOf(offset) {
678
+ const ls = _getLineStarts();
679
+ let lo = 0, hi = ls.length;
680
+ while (lo < hi) {
681
+ const mid = (lo + hi) >>> 1;
682
+ if (ls[mid] <= offset) lo = mid + 1; else hi = mid;
683
+ }
684
+ return { line: lo, column: offset - ls[lo - 1] };
685
+ },
367
686
  };
368
687
  }
369
688
  export { decode };
package/index.d.ts CHANGED
@@ -40,24 +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
- /** A source code comment. */
54
+ /**
55
+ * Position of a {@link Comment} relative to its host node.
56
+ *
57
+ * - `before`: leading the host.
58
+ * - `after`: trailing the host.
59
+ * - `inside`: interior to an otherwise empty host.
60
+ */
61
+ type CommentPosition = "before" | "after" | "inside";
62
+
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
+ */
49
70
  interface Comment {
50
71
  type: CommentType;
51
- /** True when a line terminator immediately precedes this comment. */
52
- precededByNewline: boolean;
53
- /** True when a line terminator immediately follows this comment. */
54
- followedByNewline: boolean;
72
+ position: CommentPosition;
73
+ sameLine: boolean;
55
74
  /** Comment text without the delimiters. */
56
75
  value: string;
57
- /** Byte offset. */
58
- start: number;
59
- /** Byte offset. */
60
- end: number;
61
76
  }
62
77
 
63
78
  /** A labeled source span attached to a {@link Diagnostic}. */
@@ -104,12 +119,19 @@ interface SourceLocation {
104
119
  interface ParseResult {
105
120
  /** Root ESTree/TypeScript-ESTree AST node. */
106
121
  program: Program;
107
- /** All comments in source order. */
108
- comments: Comment[];
109
- /** Byte offset where each source line begins. Index 0 is always 0. */
110
- lineStarts: number[];
111
122
  /** Syntax diagnostics, and semantic diagnostics when {@link ParseOptions.semanticErrors} is enabled. */
112
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;
113
135
  }
114
136
 
115
137
  /**
@@ -136,25 +158,16 @@ export function langFromPath(path: string): SourceLang;
136
158
  */
137
159
  export function sourceTypeFromPath(path: string): SourceType;
138
160
 
139
- /**
140
- * Resolves a `{ line, column }` {@link SourceLocation} for an offset, using
141
- * {@link ParseResult.lineStarts}. Runs in O(log n).
142
- *
143
- * Lines are 1-based; columns are 0-based. The unit of `offset` (UTF-16 code
144
- * units in JS) must match the unit of `lineStarts`. Both come from the same
145
- * {@link ParseResult}, so this is automatic.
146
- *
147
- * @example
148
- * const { program, lineStarts } = parse(source);
149
- * locOf(lineStarts, program.body[0].start); // => { line, column }
150
- */
151
- export function locOf(lineStarts: number[], offset: number): SourceLocation;
152
-
153
161
  // AST node types
154
162
 
155
163
  interface BaseNode {
156
164
  start: number;
157
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[];
158
171
  }
159
172
 
160
173
  type Span = BaseNode;
@@ -241,39 +254,37 @@ type PropertyDefinitionType = "PropertyDefinition" | "TSAbstractPropertyDefiniti
241
254
 
242
255
  type AccessorPropertyType = "AccessorProperty" | "TSAbstractAccessorProperty";
243
256
 
244
- interface IdentifierName extends BaseNode {
245
- type: "Identifier";
246
- name: string;
247
- decorators?: [];
248
- optional?: false;
249
- typeAnnotation?: null;
250
- }
251
-
252
- interface IdentifierReference extends BaseNode {
253
- type: "Identifier";
254
- name: string;
255
- decorators?: [];
256
- optional?: false;
257
- typeAnnotation?: null;
258
- }
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";
259
269
 
260
- interface BindingIdentifier extends BaseNode {
270
+ interface Identifier extends BaseNode {
261
271
  type: "Identifier";
262
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;
263
279
  decorators?: Decorator[];
264
280
  optional?: boolean;
265
281
  typeAnnotation?: TSTypeAnnotation | null;
266
282
  }
267
283
 
268
- interface LabelIdentifier extends BaseNode {
269
- type: "Identifier";
270
- name: string;
271
- decorators?: [];
272
- optional?: false;
273
- typeAnnotation?: null;
274
- }
275
-
276
- type Identifier = IdentifierName | IdentifierReference | BindingIdentifier | LabelIdentifier;
284
+ type IdentifierName = Identifier;
285
+ type IdentifierReference = Identifier;
286
+ type BindingIdentifier = Identifier;
287
+ type LabelIdentifier = Identifier;
277
288
 
278
289
  interface PrivateIdentifier extends BaseNode {
279
290
  type: "PrivateIdentifier";
@@ -1686,6 +1697,7 @@ type Node =
1686
1697
  | Directive
1687
1698
  | ObjectProperty
1688
1699
  | BindingProperty
1700
+ | SpreadElement
1689
1701
  | PrivateIdentifier
1690
1702
  | TemplateElement
1691
1703
  | VariableDeclarator
@@ -1704,6 +1716,7 @@ type Node =
1704
1716
  | TSAbstractAccessorProperty
1705
1717
  | StaticBlock
1706
1718
  | Decorator
1719
+ | TSEmptyBodyFunctionExpression
1707
1720
  | ImportSpecifier
1708
1721
  | ImportDefaultSpecifier
1709
1722
  | ImportNamespaceSpecifier
@@ -1749,6 +1762,7 @@ export type {
1749
1762
  ParseResult,
1750
1763
  Comment,
1751
1764
  CommentType,
1765
+ CommentPosition,
1752
1766
  Diagnostic,
1753
1767
  DiagnosticLabel,
1754
1768
  DiagnosticSeverity,
@@ -1780,6 +1794,7 @@ export type {
1780
1794
  ModuleExportName,
1781
1795
  PropertyKey,
1782
1796
  Identifier,
1797
+ IdentifierKind,
1783
1798
  IdentifierName,
1784
1799
  IdentifierReference,
1785
1800
  BindingIdentifier,
package/index.js CHANGED
@@ -16,13 +16,3 @@ export function langFromPath(path) {
16
16
  export function sourceTypeFromPath(path) {
17
17
  return path.endsWith(".cjs") || path.endsWith(".cts") ? "script" : "module";
18
18
  }
19
-
20
- export function locOf(lineStarts, offset) {
21
- let lo = 0, hi = lineStarts.length;
22
- while (lo < hi) {
23
- const mid = (lo + hi) >>> 1;
24
- if (lineStarts[mid] <= offset) lo = mid + 1;
25
- else hi = mid;
26
- }
27
- return { line: lo, column: offset - lineStarts[lo - 1] };
28
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yuku-parser",
3
- "version": "0.5.14",
3
+ "version": "0.5.16",
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.14",
21
- "@yuku-parser/binding-linux-arm64-gnu": "0.5.14",
22
- "@yuku-parser/binding-linux-arm-gnu": "0.5.14",
23
- "@yuku-parser/binding-linux-x64-musl": "0.5.14",
24
- "@yuku-parser/binding-linux-arm64-musl": "0.5.14",
25
- "@yuku-parser/binding-linux-arm-musl": "0.5.14",
26
- "@yuku-parser/binding-darwin-x64": "0.5.14",
27
- "@yuku-parser/binding-darwin-arm64": "0.5.14",
28
- "@yuku-parser/binding-win32-x64": "0.5.14",
29
- "@yuku-parser/binding-win32-arm64": "0.5.14",
30
- "@yuku-parser/binding-freebsd-x64": "0.5.14"
20
+ "@yuku-parser/binding-linux-x64-gnu": "0.5.16",
21
+ "@yuku-parser/binding-linux-arm64-gnu": "0.5.16",
22
+ "@yuku-parser/binding-linux-arm-gnu": "0.5.16",
23
+ "@yuku-parser/binding-linux-x64-musl": "0.5.16",
24
+ "@yuku-parser/binding-linux-arm64-musl": "0.5.16",
25
+ "@yuku-parser/binding-linux-arm-musl": "0.5.16",
26
+ "@yuku-parser/binding-darwin-x64": "0.5.16",
27
+ "@yuku-parser/binding-darwin-arm64": "0.5.16",
28
+ "@yuku-parser/binding-win32-x64": "0.5.16",
29
+ "@yuku-parser/binding-win32-arm64": "0.5.16",
30
+ "@yuku-parser/binding-freebsd-x64": "0.5.16"
31
31
  },
32
32
  "keywords": [
33
33
  "acorn",