yuku-parser 0.5.19 → 0.5.21

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 +12 -4
  2. package/decode.js +10 -30
  3. package/index.d.ts +0 -20
  4. package/package.json +12 -12
package/README.md CHANGED
@@ -25,10 +25,7 @@ For JavaScript and JSX, the AST is fully conformant with the [ESTree](https://gi
25
25
 
26
26
  For TypeScript, the AST conforms to the [TypeScript-ESTree](https://www.npmjs.com/package/@typescript-eslint/typescript-estree) format used by `@typescript-eslint`.
27
27
 
28
- Yuku also matches [Oxc](https://oxc.rs) for both JS and TS, with two intentional deviations from the base specs:
29
-
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.
28
+ Yuku produces exactly the AST that [Oxc](https://oxc.rs) produces, for both JS and TS.
32
29
 
33
30
  On top of the base specs, the AST also carries:
34
31
 
@@ -74,6 +71,16 @@ const { line, column } = result.locOf(result.program.body[0].start);
74
71
 
75
72
  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
73
 
74
+ When resolving many offsets in roughly source order, `locNear` is faster: it scans from a hint line instead of binary searching. Pass the previously returned `line` as the next `hintLine` so each lookup starts near the answer.
75
+
76
+ ```ts
77
+ let hintLine = 1;
78
+ for (const node of nodesInSourceOrder) {
79
+ const { line, column } = result.locNear(node.start, hintLine);
80
+ hintLine = line;
81
+ }
82
+ ```
83
+
77
84
  ## Walking the AST
78
85
 
79
86
  [`yuku-ast`](https://www.npmjs.com/package/yuku-ast) is the companion toolkit built for this AST: a typed walker, node builders (`b`), type guards (`is`), and identifier validators.
@@ -134,6 +141,7 @@ interface ParseResult {
134
141
  diagnostics: Diagnostic[];
135
142
  lineStarts: number[];
136
143
  locOf(offset: number): { line: number; column: number };
144
+ locNear(offset: number, hintLine: number): { line: number; column: number };
137
145
  }
138
146
  ```
139
147
 
package/decode.js CHANGED
@@ -1,6 +1,4 @@
1
1
  // generated by tools/gen_estree_decoder.zig, do not edit
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
2
  const NULL = -1;
5
3
  const _td = new TextDecoder("utf-8", { ignoreBOM: true });
6
4
  const BINARY_OPS = ["==", "!=", "===", "!==", "<", "<=", ">", ">=", "+", "-", "*", "/", "%", "**", "|", "^", "&", "<<", ">>", ">>>", "in", "instanceof"];
@@ -21,11 +19,6 @@ const TS_METHOD_SIGNATURE_KINDS = ["method", "get", "set"];
21
19
  const TS_MODULE_KINDS = ["namespace", "module"];
22
20
  const TS_MAPPED_OPTIONAL = [false, true, "+", "-"];
23
21
  const TS_MAPPED_READONLY = [null, true, "+", "-"];
24
- // builds a partial pos-map covering bytes [startByte, byteLen]. for any
25
- // byte position v >= startByte, m[v - startByte] is the corresponding
26
- // UTF-16 code-unit index into `src`. positions below startByte are an
27
- // identity map (byte position == UTF-16 position because the prefix is
28
- // pure ASCII), so the caller checks `v < startByte` and returns v directly.
29
22
  function buildPosMap(src, byteLen, startByte) {
30
23
  const m = new Uint32Array(byteLen - startByte + 1);
31
24
  const len = src.length;
@@ -80,8 +73,6 @@ function decode(buffer, source) {
80
73
  const eOff = _nodesOff + nodeCount * 48;
81
74
  const _extraBase = eOff >> 2;
82
75
  const _spOff = eOff + extraCount * 4;
83
- // sections after the variable-length string pool may be at any
84
- // byte alignment, so reads against them use `dv`, not `_u32`.
85
76
  const dv = new DataView(buffer);
86
77
  const _aoOff = _spOff + spLen;
87
78
  const _acOff = _attached ? _aoOff + (nodeCount + 1) * 4 : _aoOff;
@@ -115,22 +106,12 @@ function decode(buffer, source) {
115
106
  }
116
107
  return r;
117
108
  }
118
- // `_p` and `str` translate UTF-8 byte offsets into JS UTF-16 indices.
119
- // for any offset below `_firstNa` (the prefix of the source that is pure
120
- // ASCII) the mapping is the identity, so we short-circuit and skip
121
- // building a pos-map. when offsets cross into non-ASCII territory we
122
- // lazily build a pos-map for `[_firstNa, _srcLen]` on first use.
123
- let pm = null;
124
- const _p = v => {
125
- if (v < _firstNa) return v;
126
- if (pm === null) pm = buildPosMap(_src, _srcLen, _firstNa);
127
- return pm[v - _firstNa];
128
- };
109
+ const pm = _firstNa < _srcLen ? buildPosMap(_src, _srcLen, _firstNa) : null;
110
+ const _p = v => v <= _firstNa ? v : pm[v - _firstNa];
129
111
  const str = (s, e) => {
130
112
  if (s === e) return "";
131
113
  if (s >= _srcLen) return _poolDecode(s, e);
132
114
  if (e <= _firstNa) return _src.slice(s, e);
133
- if (pm === null) pm = buildPosMap(_src, _srcLen, _firstNa);
134
115
  const ss = s < _firstNa ? s : pm[s - _firstNa];
135
116
  return _src.slice(ss, pm[e - _firstNa]);
136
117
  };
@@ -175,8 +156,6 @@ function decode(buffer, source) {
175
156
  }
176
157
  function nodeWithComments(i) {
177
158
  const r = _decode(i);
178
- // skip unwrap cases whose inner node already carries its own
179
- // comments (e.g. formal_parameter returning its pattern)
180
159
  if (r && r.type !== undefined && r.comments === undefined) {
181
160
  const off = _aoOff + i * 4;
182
161
  const a = dv.getUint32(off, true), e = dv.getUint32(off + 4, true);
@@ -194,7 +173,9 @@ function decode(buffer, source) {
194
173
  f3 = _u32[b + 4], f4 = _u32[b + 5],
195
174
  f5 = _u32[b + 6], f6 = _u32[b + 7],
196
175
  f7 = _u32[b + 8], f8 = _u32[b + 9];
197
- const start = _p(_u32[b + 10]), end = _p(_u32[b + 11]);
176
+ const _ss = _u32[b + 10], _se = _u32[b + 11];
177
+ const start = _ss <= _firstNa ? _ss : pm[_ss - _firstNa];
178
+ const end = _se <= _firstNa ? _se : pm[_se - _firstNa];
198
179
  switch (tag) {
199
180
  case 0: return { type: "SequenceExpression", start, end, expressions: nodeArr(f1, f0) };
200
181
  case 1: return { type: "ParenthesizedExpression", start, end, expression: f1 !== NULL ? node(f1) : null };
@@ -377,11 +358,11 @@ function decode(buffer, source) {
377
358
  tail: tl,
378
359
  };
379
360
  }
380
- 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; }
361
+ case 42: { const r = { type: "Identifier", start, end, name: str(f1, f2) }; if (_isTs) { r.decorators = []; r.optional = false; r.typeAnnotation = null; } return r; }
381
362
  case 43: return { type: "PrivateIdentifier", start, end, name: str(f1, f2) };
382
- 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; }
383
- 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; }
384
- 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; }
363
+ 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; }
364
+ case 45: { const r = { type: "Identifier", start, end, name: str(f1, f2) }; if (_isTs) { r.decorators = []; r.optional = false; r.typeAnnotation = null; } return r; }
365
+ case 46: { const r = { type: "Identifier", start, end, name: str(f1, f2) }; if (_isTs) { r.decorators = []; r.optional = false; r.typeAnnotation = null; } return r; }
385
366
  case 47: { const r = { type: "ExpressionStatement", start, end, expression: f1 !== NULL ? node(f1) : null }; if (_isTs) { r.directive = null; } return r; }
386
367
  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 };
387
368
  case 49: return { type: "SwitchStatement", start, end, discriminant: f1 !== NULL ? node(f1) : null, cases: nodeArr(f2, f0) };
@@ -579,7 +560,7 @@ function decode(buffer, source) {
579
560
  case 146: return {
580
561
  type: "Identifier", start, end,
581
562
  decorators: [],
582
- name: "this", kind: "this", optional: false,
563
+ name: "this", optional: false,
583
564
  typeAnnotation: f1 !== NULL ? node(f1) : null,
584
565
  };
585
566
  case 147: return { type: "TSAsExpression", start, end, expression: f1 !== NULL ? node(f1) : null, typeAnnotation: f2 !== NULL ? node(f2) : null };
@@ -640,7 +621,6 @@ function decode(buffer, source) {
640
621
  }
641
622
  return out;
642
623
  }
643
- if (pm === null) pm = buildPosMap(_src, _srcLen, _firstNa);
644
624
  for (let j = 0; j < lineStartsCount; j++) {
645
625
  const v = dv.getUint32(lsOff + j * 4, true);
646
626
  out[j] = v < _firstNa ? v : (v >= _srcLen ? pm[pm.length - 1] : pm[v - _firstNa]);
package/index.d.ts CHANGED
@@ -271,28 +271,9 @@ type PropertyDefinitionType = "PropertyDefinition" | "TSAbstractPropertyDefiniti
271
271
 
272
272
  type AccessorPropertyType = "AccessorProperty" | "TSAbstractAccessorProperty";
273
273
 
274
- /**
275
- * Role of an `Identifier` within the AST. Yuku exposes the four ESTree-collapsed
276
- * identifier flavors as a discriminator, plus `"this"` for a TS `this`-parameter
277
- * (which TS-ESTree also encodes as an `Identifier` with `name: "this"`).
278
- *
279
- * - `"reference"`: identifier read as a value (`x` in `x + 1`, `console`).
280
- * - `"binding"`: identifier being declared (parameters, `let x`, function/class names).
281
- * - `"name"`: identifier in a non-binding name slot (property/method names like `log` in `console.log`).
282
- * - `"label"`: loop label (`outer` in `outer: for (...) {}`).
283
- * - `"this"`: the TS `this`-parameter (`function f(this: T)`).
284
- */
285
- type IdentifierKind = "reference" | "binding" | "name" | "label" | "this";
286
-
287
274
  interface Identifier extends BaseNode {
288
275
  type: "Identifier";
289
276
  name: string;
290
- /**
291
- * ESTree collapses several distinct identifier
292
- * roles onto one `Identifier` node, `kind` lets you tell them apart without
293
- * walking the parent context. See {@link IdentifierKind}.
294
- */
295
- kind: IdentifierKind;
296
277
  decorators?: Decorator[];
297
278
  optional?: boolean;
298
279
  typeAnnotation?: TSTypeAnnotation | null;
@@ -1821,7 +1802,6 @@ export type {
1821
1802
  ModuleExportName,
1822
1803
  PropertyKey,
1823
1804
  Identifier,
1824
- IdentifierKind,
1825
1805
  IdentifierName,
1826
1806
  IdentifierReference,
1827
1807
  BindingIdentifier,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yuku-parser",
3
- "version": "0.5.19",
3
+ "version": "0.5.21",
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.19",
21
- "@yuku-parser/binding-linux-arm64-gnu": "0.5.19",
22
- "@yuku-parser/binding-linux-arm-gnu": "0.5.19",
23
- "@yuku-parser/binding-linux-x64-musl": "0.5.19",
24
- "@yuku-parser/binding-linux-arm64-musl": "0.5.19",
25
- "@yuku-parser/binding-linux-arm-musl": "0.5.19",
26
- "@yuku-parser/binding-darwin-x64": "0.5.19",
27
- "@yuku-parser/binding-darwin-arm64": "0.5.19",
28
- "@yuku-parser/binding-win32-x64": "0.5.19",
29
- "@yuku-parser/binding-win32-arm64": "0.5.19",
30
- "@yuku-parser/binding-freebsd-x64": "0.5.19"
20
+ "@yuku-parser/binding-linux-x64-gnu": "0.5.21",
21
+ "@yuku-parser/binding-linux-arm64-gnu": "0.5.21",
22
+ "@yuku-parser/binding-linux-arm-gnu": "0.5.21",
23
+ "@yuku-parser/binding-linux-x64-musl": "0.5.21",
24
+ "@yuku-parser/binding-linux-arm64-musl": "0.5.21",
25
+ "@yuku-parser/binding-linux-arm-musl": "0.5.21",
26
+ "@yuku-parser/binding-darwin-x64": "0.5.21",
27
+ "@yuku-parser/binding-darwin-arm64": "0.5.21",
28
+ "@yuku-parser/binding-win32-x64": "0.5.21",
29
+ "@yuku-parser/binding-win32-arm64": "0.5.21",
30
+ "@yuku-parser/binding-freebsd-x64": "0.5.21"
31
31
  },
32
32
  "keywords": [
33
33
  "acorn",