oxc-parser 0.74.0 → 0.75.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -187,7 +187,8 @@ const NODE_TYPE_IDS_MAP = new Map([
187
187
  ['JSDocNonNullableType', 177],
188
188
  ]);
189
189
 
190
- // Number of AST node types which are leaf nodes
191
- const LEAF_NODES_COUNT = 38;
192
-
193
- module.exports = { NODE_TYPE_IDS_MAP, LEAF_NODES_COUNT };
190
+ module.exports = {
191
+ NODE_TYPE_IDS_MAP,
192
+ NODE_TYPES_COUNT: 178,
193
+ LEAF_NODE_TYPES_COUNT: 38,
194
+ };
package/index.d.ts CHANGED
@@ -189,6 +189,14 @@ export interface ParserOptions {
189
189
  * The type of the file is determined from `lang` option, or extension of provided `filename`.
190
190
  */
191
191
  astType?: 'js' | 'ts'
192
+ /**
193
+ * Controls whether the `range` property is included on AST nodes.
194
+ * The `range` property is a `[number, number]` which indicates the start/end offsets
195
+ * of the node in the file contents.
196
+ *
197
+ * @default false
198
+ */
199
+ range?: boolean
192
200
  /**
193
201
  * Emit `ParenthesizedExpression` and `TSParenthesizedType` in AST.
194
202
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oxc-parser",
3
- "version": "0.74.0",
3
+ "version": "0.75.0",
4
4
  "main": "index.js",
5
5
  "browser": "wasm.mjs",
6
6
  "engines": {
@@ -49,7 +49,7 @@
49
49
  "access": "public"
50
50
  },
51
51
  "dependencies": {
52
- "@oxc-project/types": "^0.74.0"
52
+ "@oxc-project/types": "^0.75.0"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@codspeed/vitest-plugin": "^4.0.0",
@@ -89,21 +89,21 @@
89
89
  "dtsHeaderFile": "header.js"
90
90
  },
91
91
  "optionalDependencies": {
92
- "@oxc-parser/binding-win32-x64-msvc": "0.74.0",
93
- "@oxc-parser/binding-win32-arm64-msvc": "0.74.0",
94
- "@oxc-parser/binding-linux-x64-gnu": "0.74.0",
95
- "@oxc-parser/binding-linux-x64-musl": "0.74.0",
96
- "@oxc-parser/binding-freebsd-x64": "0.74.0",
97
- "@oxc-parser/binding-linux-arm64-gnu": "0.74.0",
98
- "@oxc-parser/binding-linux-arm64-musl": "0.74.0",
99
- "@oxc-parser/binding-linux-arm-gnueabihf": "0.74.0",
100
- "@oxc-parser/binding-linux-arm-musleabihf": "0.74.0",
101
- "@oxc-parser/binding-linux-s390x-gnu": "0.74.0",
102
- "@oxc-parser/binding-linux-riscv64-gnu": "0.74.0",
103
- "@oxc-parser/binding-darwin-x64": "0.74.0",
104
- "@oxc-parser/binding-darwin-arm64": "0.74.0",
105
- "@oxc-parser/binding-android-arm64": "0.74.0",
106
- "@oxc-parser/binding-wasm32-wasi": "0.74.0"
92
+ "@oxc-parser/binding-win32-x64-msvc": "0.75.0",
93
+ "@oxc-parser/binding-win32-arm64-msvc": "0.75.0",
94
+ "@oxc-parser/binding-linux-x64-gnu": "0.75.0",
95
+ "@oxc-parser/binding-linux-x64-musl": "0.75.0",
96
+ "@oxc-parser/binding-freebsd-x64": "0.75.0",
97
+ "@oxc-parser/binding-linux-arm64-gnu": "0.75.0",
98
+ "@oxc-parser/binding-linux-arm64-musl": "0.75.0",
99
+ "@oxc-parser/binding-linux-arm-gnueabihf": "0.75.0",
100
+ "@oxc-parser/binding-linux-arm-musleabihf": "0.75.0",
101
+ "@oxc-parser/binding-linux-s390x-gnu": "0.75.0",
102
+ "@oxc-parser/binding-linux-riscv64-gnu": "0.75.0",
103
+ "@oxc-parser/binding-darwin-x64": "0.75.0",
104
+ "@oxc-parser/binding-darwin-arm64": "0.75.0",
105
+ "@oxc-parser/binding-android-arm64": "0.75.0",
106
+ "@oxc-parser/binding-wasm32-wasi": "0.75.0"
107
107
  },
108
108
  "scripts": {
109
109
  "build-dev": "napi build --platform --js bindings.js",
@@ -2,15 +2,18 @@
2
2
 
3
3
  const { TOKEN, constructorError } = require('./lazy-common.js');
4
4
 
5
- // Mapping from a proxy to the `NodeArray` that it wraps.
6
- // Used by `slice`, `values`, `key` and `elements` methods.
5
+ // Internal symbol to get `NodeArray` from a proxy wrapping a `NodeArray`.
7
6
  //
8
- // TODO: Is there any way to avoid this?
9
- // Seems necessary because `this` in methods is a proxy, so accessing `this.#internal` throws.
10
- const nodeArrays = new WeakMap();
7
+ // Methods of `NodeArray` are called with `this` being the proxy, rather than the `NodeArray` itself.
8
+ // They can "unwrap" the proxy by getting `this[ARRAY]`, and the `get` proxy trap will return
9
+ // the actual `NodeArray`.
10
+ //
11
+ // This symbol is not exported, and it is not actually defined on `NodeArray`s, so user cannot obtain it
12
+ // via `Object.getOwnPropertySymbols` or `Reflect.ownKeys`. Therefore user code cannot unwrap the proxy.
13
+ const ARRAY = Symbol();
11
14
 
12
- // Function to get element from an array. Initialized in class static block below.
13
- let getElement;
15
+ // Functions to get internal properties of a `NodeArray`. Initialized in class static block below.
16
+ let getInternalFromProxy, getLength, getElement;
14
17
 
15
18
  /**
16
19
  * An array of AST nodes where elements are deserialized lazily upon access.
@@ -41,12 +44,9 @@ class NodeArray extends Array {
41
44
  constructor(pos, length, stride, construct, ast) {
42
45
  if (ast?.token !== TOKEN) constructorError();
43
46
 
44
- super(length);
45
- this.#internal = { pos, ast, stride, construct };
46
-
47
- const proxy = new Proxy(this, PROXY_HANDLERS);
48
- nodeArrays.set(proxy, this);
49
- return proxy;
47
+ super();
48
+ this.#internal = { pos, length, ast, stride, construct };
49
+ return new Proxy(this, PROXY_HANDLERS);
50
50
  }
51
51
 
52
52
  // Allow `arr.filter`, `arr.map` etc.
@@ -55,27 +55,19 @@ class NodeArray extends Array {
55
55
  // Override `values` method with a more efficient one that avoids going via proxy for every iteration.
56
56
  // TODO: Benchmark to check that this is actually faster.
57
57
  values() {
58
- // Get actual `NodeArray`. `this` is a proxy.
59
- const arr = nodeArrays.get(this);
60
- return new NodeArrayValuesIterator(arr.#internal, arr.length);
58
+ return new NodeArrayValuesIterator(this);
61
59
  }
62
60
 
63
61
  // Override `keys` method with a more efficient one that avoids going via proxy for every iteration.
64
62
  // TODO: Benchmark to check that this is actually faster.
65
63
  keys() {
66
- // Get actual `NodeArray`. `this` is a proxy.
67
- // TODO: `this.length` would work here.
68
- // Not sure which is more expensive - property lookup via proxy, or `WeakMap` lookup.
69
- const arr = nodeArrays.get(this);
70
- return new NodeArrayKeysIterator(arr.length);
64
+ return new NodeArrayKeysIterator(this);
71
65
  }
72
66
 
73
67
  // Override `entries` method with a more efficient one that avoids going via proxy for every iteration.
74
68
  // TODO: Benchmark to check that this is actually faster.
75
69
  entries() {
76
- // Get actual `NodeArray`. `this` is a proxy.
77
- const arr = nodeArrays.get(this);
78
- return new NodeArrayEntriesIterator(arr.#internal, arr.length);
70
+ return new NodeArrayEntriesIterator(this);
79
71
  }
80
72
 
81
73
  // This method is overwritten with reference to `values` method below.
@@ -91,37 +83,35 @@ class NodeArray extends Array {
91
83
  * @returns {NodeArray} - `NodeArray` containing slice of this one
92
84
  */
93
85
  slice(start, end) {
94
- // Get actual `NodeArray`. `this` is a proxy.
95
- const arr = nodeArrays.get(this);
96
- if (arr === void 0) throw new Error('`slice` called on a value which is not a `NodeArray`');
86
+ const internal = this[ARRAY].#internal,
87
+ { length } = internal;
97
88
 
98
89
  start = toInt(start);
99
90
  if (start < 0) {
100
- start = arr.length + start;
91
+ start = length + start;
101
92
  if (start < 0) start = 0;
102
93
  }
103
94
 
104
95
  if (end === void 0) {
105
- end = arr.length;
96
+ end = length;
106
97
  } else {
107
98
  end = toInt(end);
108
99
  if (end < 0) {
109
- end = arr.length + end;
100
+ end += length;
110
101
  if (end < 0) end = 0;
111
- } else if (end > arr.length) {
112
- end = arr.length;
102
+ } else if (end > length) {
103
+ end = length;
113
104
  }
114
105
  }
115
106
 
116
- let length = end - start;
117
- if (length <= 0 || start >= arr.length) {
107
+ let sliceLength = end - start;
108
+ if (sliceLength <= 0 || start >= length) {
118
109
  start = 0;
119
- length = 0;
110
+ sliceLength = 0;
120
111
  }
121
112
 
122
- const internal = arr.#internal,
123
- { stride } = internal;
124
- return new NodeArray(internal.pos + start * stride, length, stride, internal.construct, internal.ast);
113
+ const { stride } = internal;
114
+ return new NodeArray(internal.pos + start * stride, sliceLength, stride, internal.construct, internal.ast);
125
115
  }
126
116
 
127
117
  // Make `console.log` deserialize all elements.
@@ -132,16 +122,30 @@ class NodeArray extends Array {
132
122
  }
133
123
 
134
124
  static {
125
+ /**
126
+ * Get internal properties of `NodeArray`, given a proxy wrapping a `NodeArray`.
127
+ * @param {Proxy} proxy - Proxy wrapping `NodeArray` object
128
+ * @returns {Object} - Internal properties object
129
+ */
130
+ getInternalFromProxy = proxy => proxy[ARRAY].#internal;
131
+
132
+ /**
133
+ * Get length of `NodeArray`.
134
+ * @param {NodeArray} arr - `NodeArray` object
135
+ * @returns {number} - Array length
136
+ */
137
+ getLength = arr => arr.#internal.length;
138
+
135
139
  /**
136
140
  * Get element of `NodeArray` at index `index`.
137
- * `index` must be in bounds (i.e. `< arr.length`).
138
141
  *
139
142
  * @param {NodeArray} arr - `NodeArray` object
140
143
  * @param {number} index - Index of element to get
141
- * @returns {*} - Element at index `index`
144
+ * @returns {*|undefined} - Element at index `index`, or `undefined` if out of bounds
142
145
  */
143
146
  getElement = (arr, index) => {
144
147
  const internal = arr.#internal;
148
+ if (index >= internal.length) return void 0;
145
149
  return (0, internal.construct)(internal.pos + index * internal.stride, internal.ast);
146
150
  };
147
151
  }
@@ -158,15 +162,15 @@ module.exports = NodeArray;
158
162
  class NodeArrayValuesIterator {
159
163
  #internal;
160
164
 
161
- constructor(arrInternal, length) {
162
- const { ast, pos, stride } = arrInternal || {};
163
- if (ast?.token !== TOKEN) constructorError();
165
+ constructor(proxy) {
166
+ const internal = getInternalFromProxy(proxy),
167
+ { pos, stride } = internal;
164
168
 
165
169
  this.#internal = {
166
170
  pos,
167
- endPos: pos + length * stride,
168
- ast,
169
- construct: arrInternal.construct,
171
+ endPos: pos + internal.length * stride,
172
+ ast: internal.ast,
173
+ construct: internal.construct,
170
174
  stride,
171
175
  };
172
176
  }
@@ -190,10 +194,9 @@ class NodeArrayValuesIterator {
190
194
  class NodeArrayKeysIterator {
191
195
  #internal;
192
196
 
193
- constructor(length) {
194
- // Don't bother gating constructor with `TOKEN` check.
195
- // This iterator doesn't access the buffer, so is harmless.
196
- this.#internal = { index: 0, length };
197
+ constructor(proxy) {
198
+ const internal = getInternalFromProxy(proxy);
199
+ this.#internal = { index: 0, length: internal.length };
197
200
  }
198
201
 
199
202
  next() {
@@ -215,17 +218,16 @@ class NodeArrayKeysIterator {
215
218
  class NodeArrayEntriesIterator {
216
219
  #internal;
217
220
 
218
- constructor(arrInternal, length) {
219
- const { ast } = arrInternal || {};
220
- if (ast?.token !== TOKEN) constructorError();
221
+ constructor(proxy) {
222
+ const internal = getInternalFromProxy(proxy);
221
223
 
222
224
  this.#internal = {
223
225
  index: 0,
224
- length,
225
- pos: arrInternal.pos,
226
- ast,
227
- construct: arrInternal.construct,
228
- stride: arrInternal.stride,
226
+ length: internal.length,
227
+ pos: internal.pos,
228
+ ast: internal.ast,
229
+ construct: internal.construct,
230
+ stride: internal.stride,
229
231
  };
230
232
  }
231
233
 
@@ -256,29 +258,38 @@ const PROXY_HANDLERS = {
256
258
  // Return `true` for indexes which are in bounds.
257
259
  // e.g. `'0' in arr`.
258
260
  has(arr, key) {
259
- if (isIndex(key)) return key * 1 < arr.length;
261
+ const index = toIndex(key);
262
+ if (index !== null) return index < getLength(arr);
260
263
  return Reflect.has(arr, key);
261
264
  },
262
265
 
263
- // Get entries which are in bounds.
266
+ // Get elements and length.
264
267
  get(arr, key) {
265
- if (isIndex(key)) {
266
- key *= 1;
267
- if (key >= arr.length) return void 0;
268
- return getElement(arr, key);
269
- }
268
+ // Methods of `NodeArray` are called with `this` being the proxy, rather than the `NodeArray` itself.
269
+ // They can "unwrap" the proxy by getting `this[ARRAY]`.
270
+ if (key === ARRAY) return arr;
271
+ if (key === 'length') return getLength(arr);
272
+ const index = toIndex(key);
273
+ if (index !== null) return getElement(arr, index);
274
+
270
275
  return Reflect.get(arr, key);
271
276
  },
272
277
 
273
- // Get descriptors which are in bounds.
278
+ // Get descriptors for elements and length.
274
279
  getOwnPropertyDescriptor(arr, key) {
275
- if (isIndex(key)) {
276
- key *= 1;
277
- if (key >= arr.length) return void 0;
280
+ if (key === 'length') {
281
+ // Cannot return `writable: false` unfortunately
282
+ return { value: getLength(arr), writable: true, enumerable: false, configurable: false };
283
+ }
284
+
285
+ const index = toIndex(key);
286
+ if (index !== null) {
287
+ const value = getElement(arr, index);
288
+ if (value === void 0) return void 0;
278
289
  // Cannot return `configurable: false` unfortunately
279
- return { value: getElement(arr, key), writable: false, enumerable: true, configurable: true };
290
+ return { value, writable: false, enumerable: true, configurable: true };
280
291
  }
281
- // Cannot return `writable: false` for `length` property unfortunately
292
+
282
293
  return Reflect.getOwnPropertyDescriptor(arr, key);
283
294
  },
284
295
 
@@ -290,21 +301,22 @@ const PROXY_HANDLERS = {
290
301
  // * `Object.defineProperty(arr, 'length', {value: 0})`.
291
302
  // * Other operations which mutate entries e.g. `arr.push(123)`.
292
303
  defineProperty(arr, key, descriptor) {
293
- if (key === 'length' || isIndex(key)) return false;
304
+ if (key === 'length' || toIndex(key) !== null) return false;
294
305
  return Reflect.defineProperty(arr, key, descriptor);
295
306
  },
296
307
 
297
308
  // Prevent deleting entries.
298
309
  deleteProperty(arr, key) {
299
310
  // Note: `Reflect.deleteProperty(arr, 'length')` already returns `false`
300
- if (isIndex(key)) return false;
311
+ if (toIndex(key) !== null) return false;
301
312
  return Reflect.deleteProperty(arr, key);
302
313
  },
303
314
 
304
315
  // Get keys, including element indexes.
305
316
  ownKeys(arr) {
306
- const keys = [];
307
- for (let i = 0; i < arr.length; i++) {
317
+ const keys = [],
318
+ length = getLength(arr);
319
+ for (let i = 0; i < length; i++) {
308
320
  keys.push(i + '');
309
321
  }
310
322
  keys.push(...Reflect.ownKeys(arr));
@@ -313,16 +325,24 @@ const PROXY_HANDLERS = {
313
325
  };
314
326
 
315
327
  /**
316
- * Check if a key is a valid array index.
328
+ * Convert key to array index, if it is a valid array index.
329
+ *
317
330
  * Only strings comprising a plain integer are valid indexes.
318
331
  * e.g. `"-1"`, `"01"`, `"0xFF"`, `"1e1"`, `"1 "` are not valid indexes.
332
+ * Integers >= 4294967295 are not valid indexes.
319
333
  *
320
- * @param {*} - Key used for property lookup.
321
- * @returns {boolean} - `true` if `key` is a valid array index.
334
+ * @param {string|Symbol} - Key used for property lookup.
335
+ * @returns {number|null} - `key` converted to integer, if it's a valid array index, otherwise `null`.
322
336
  */
323
- function isIndex(key) {
324
- // TODO: Any way to do this without a regex?
325
- return typeof key === 'string' && (key === '0' || INDEX_REGEX.test(key));
337
+ function toIndex(key) {
338
+ if (typeof key === 'string') {
339
+ if (key === '0') return 0;
340
+ if (INDEX_REGEX.test(key)) {
341
+ const index = +key;
342
+ if (index < 4294967295) return index;
343
+ }
344
+ }
345
+ return null;
326
346
  }
327
347
 
328
348
  const INDEX_REGEX = /^[1-9]\d*$/;
@@ -1,8 +1,10 @@
1
1
  'use strict';
2
2
 
3
- const { NODE_TYPE_IDS_MAP, LEAF_NODES_COUNT } = require('../generated/deserialize/lazy-types.js');
4
-
5
- const NODE_TYPES_COUNT = NODE_TYPE_IDS_MAP.size;
3
+ const {
4
+ NODE_TYPE_IDS_MAP,
5
+ NODE_TYPES_COUNT,
6
+ LEAF_NODE_TYPES_COUNT,
7
+ } = require('../generated/deserialize/lazy-types.js');
6
8
 
7
9
  // Getter for private `#visitorsArr` property of `Visitor` class. Initialized in class body below.
8
10
  let getVisitorsArr;
@@ -67,7 +69,7 @@ function createVisitorsArr(visitor) {
67
69
 
68
70
  // Create empty visitors array
69
71
  const visitorsArr = [];
70
- for (let i = 0; i < NODE_TYPES_COUNT; i++) {
72
+ for (let i = NODE_TYPES_COUNT; i !== 0; i--) {
71
73
  visitorsArr.push(null);
72
74
  }
73
75
 
@@ -84,7 +86,7 @@ function createVisitorsArr(visitor) {
84
86
  const typeId = NODE_TYPE_IDS_MAP.get(name);
85
87
  if (typeId === void 0) throw new Error(`Unknown node type '${name}' in \`visitors\` object`);
86
88
 
87
- if (typeId < LEAF_NODES_COUNT) {
89
+ if (typeId < LEAF_NODE_TYPES_COUNT) {
88
90
  // Leaf node. Store just 1 function.
89
91
  const existingVisitFn = visitorsArr[typeId];
90
92
  if (existingVisitFn === null) {