oxc-parser 0.73.0 → 0.74.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oxc-parser",
3
- "version": "0.73.0",
3
+ "version": "0.74.0",
4
4
  "main": "index.js",
5
5
  "browser": "wasm.mjs",
6
6
  "engines": {
@@ -34,26 +34,32 @@
34
34
  "generated/deserialize/js.js",
35
35
  "generated/deserialize/ts.js",
36
36
  "generated/deserialize/lazy.js",
37
- "raw-transfer/index.js",
37
+ "generated/deserialize/lazy-types.js",
38
+ "generated/deserialize/lazy-visit.js",
39
+ "raw-transfer/common.js",
38
40
  "raw-transfer/eager.js",
39
- "raw-transfer/lazy.js"
41
+ "raw-transfer/lazy.js",
42
+ "raw-transfer/lazy-common.js",
43
+ "raw-transfer/node-array.js",
44
+ "raw-transfer/supported.js",
45
+ "raw-transfer/visitor.js"
40
46
  ],
41
47
  "publishConfig": {
42
48
  "registry": "https://registry.npmjs.org/",
43
49
  "access": "public"
44
50
  },
45
51
  "dependencies": {
46
- "@oxc-project/types": "^0.73.0"
52
+ "@oxc-project/types": "^0.74.0"
47
53
  },
48
54
  "devDependencies": {
49
55
  "@codspeed/vitest-plugin": "^4.0.0",
50
56
  "@napi-rs/wasm-runtime": "^0.2.7",
51
- "@vitest/browser": "3.2.2",
57
+ "@vitest/browser": "3.2.4",
52
58
  "esbuild": "^0.25.0",
53
59
  "playwright": "^1.51.0",
54
60
  "tinypool": "^1.1.0",
55
61
  "typescript": "5.8.3",
56
- "vitest": "3.2.2"
62
+ "vitest": "3.2.4"
57
63
  },
58
64
  "napi": {
59
65
  "binaryName": "parser",
@@ -72,6 +78,7 @@
72
78
  "riscv64gc-unknown-linux-gnu",
73
79
  "x86_64-apple-darwin",
74
80
  "aarch64-apple-darwin",
81
+ "aarch64-linux-android",
75
82
  "wasm32-wasip1-threads"
76
83
  ],
77
84
  "wasm": {
@@ -82,20 +89,21 @@
82
89
  "dtsHeaderFile": "header.js"
83
90
  },
84
91
  "optionalDependencies": {
85
- "@oxc-parser/binding-win32-x64-msvc": "0.73.0",
86
- "@oxc-parser/binding-win32-arm64-msvc": "0.73.0",
87
- "@oxc-parser/binding-linux-x64-gnu": "0.73.0",
88
- "@oxc-parser/binding-linux-x64-musl": "0.73.0",
89
- "@oxc-parser/binding-freebsd-x64": "0.73.0",
90
- "@oxc-parser/binding-linux-arm64-gnu": "0.73.0",
91
- "@oxc-parser/binding-linux-arm64-musl": "0.73.0",
92
- "@oxc-parser/binding-linux-arm-gnueabihf": "0.73.0",
93
- "@oxc-parser/binding-linux-arm-musleabihf": "0.73.0",
94
- "@oxc-parser/binding-linux-s390x-gnu": "0.73.0",
95
- "@oxc-parser/binding-linux-riscv64-gnu": "0.73.0",
96
- "@oxc-parser/binding-darwin-x64": "0.73.0",
97
- "@oxc-parser/binding-darwin-arm64": "0.73.0",
98
- "@oxc-parser/binding-wasm32-wasi": "0.73.0"
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"
99
107
  },
100
108
  "scripts": {
101
109
  "build-dev": "napi build --platform --js bindings.js",
@@ -1,10 +1,14 @@
1
1
  'use strict';
2
2
 
3
3
  const os = require('node:os');
4
- const bindings = require('../bindings.js');
4
+ const rawTransferSupported = require('./supported.js');
5
+ const {
6
+ parseSyncRaw: parseSyncRawBinding,
7
+ parseAsyncRaw: parseAsyncRawBinding,
8
+ getBufferOffset,
9
+ } = require('../bindings.js');
5
10
 
6
11
  module.exports = {
7
- rawTransferSupported,
8
12
  parseSyncRawImpl,
9
13
  parseAsyncRawImpl,
10
14
  prepareRaw,
@@ -12,19 +16,24 @@ module.exports = {
12
16
  returnBufferToCache,
13
17
  };
14
18
 
15
- // Import `eager.js` and `lazy.js` after the exports above, because of circular dependencies
16
- const { parseSyncRaw, parseAsyncRaw } = require('./eager.js');
17
- module.exports.parseSyncRaw = parseSyncRaw;
18
- module.exports.parseAsyncRaw = parseAsyncRaw;
19
-
20
- const { parseSyncLazy, parseAsyncLazy } = require('./lazy.js');
21
- module.exports.parseSyncLazy = parseSyncLazy;
22
- module.exports.parseAsyncLazy = parseAsyncLazy;
23
-
24
- function parseSyncRawImpl(filename, sourceText, options, deserialize) {
25
- const { buffer, sourceByteLen, options: optionsAmended } = prepareRaw(sourceText, options);
26
- bindings.parseSyncRaw(filename, buffer, sourceByteLen, optionsAmended);
27
- return deserialize(buffer, sourceText, sourceByteLen);
19
+ /**
20
+ * Parse JS/TS source synchronously on current thread using raw transfer.
21
+ *
22
+ * Convert the buffer returned by Rust to a JS object with provided `convert` function.
23
+ *
24
+ * This function contains logic shared by both `parseSyncRaw` and `parseSyncLazy`.
25
+ *
26
+ * @param {string} filename - Filename
27
+ * @param {string} sourceText - Source text of file
28
+ * @param {Object|undefined} options - Parsing options
29
+ * @param {function} convert - Function to convert the buffer returned from Rust into a JS object
30
+ * @returns {Object} - The return value of `convert`
31
+ * @throws {Error} - If raw transfer is not supported on this platform
32
+ */
33
+ function parseSyncRawImpl(filename, sourceText, options, convert) {
34
+ const { buffer, sourceByteLen } = prepareRaw(sourceText);
35
+ parseSyncRawBinding(filename, buffer, sourceByteLen, options);
36
+ return convert(buffer, sourceText, sourceByteLen);
28
37
  }
29
38
 
30
39
  // User should not schedule more async tasks than there are available CPUs, as it hurts performance,
@@ -65,7 +74,23 @@ function parseSyncRawImpl(filename, sourceText, options, deserialize) {
65
74
  let availableCores = os.availableParallelism ? os.availableParallelism() : os.cpus().length;
66
75
  const queue = [];
67
76
 
68
- async function parseAsyncRawImpl(filename, sourceText, options, deserialize) {
77
+ /**
78
+ * Parse JS/TS source asynchronously using raw transfer.
79
+ *
80
+ * Convert the buffer returned by Rust to a JS object with provided `convert` function.
81
+ *
82
+ * Queues up parsing operations if more calls than number of CPU cores (see above).
83
+ *
84
+ * This function contains logic shared by both `parseAsyncRaw` and `parseAsyncLazy`.
85
+ *
86
+ * @param {string} filename - Filename
87
+ * @param {string} sourceText - Source text of file
88
+ * @param {Object|undefined} options - Parsing options
89
+ * @param {function} convert - Function to convert the buffer returned from Rust into a JS object
90
+ * @returns {Object} - The return value of `convert`
91
+ * @throws {Error} - If raw transfer is not supported on this platform
92
+ */
93
+ async function parseAsyncRawImpl(filename, sourceText, options, convert) {
69
94
  // Wait for a free CPU core if all CPUs are currently busy.
70
95
  //
71
96
  // Note: `availableCores` is NOT decremented if have to wait in the queue first,
@@ -87,9 +112,9 @@ async function parseAsyncRawImpl(filename, sourceText, options, deserialize) {
87
112
  }
88
113
 
89
114
  // Parse
90
- const { buffer, sourceByteLen, options: optionsAmended } = prepareRaw(sourceText, options);
91
- await bindings.parseAsyncRaw(filename, buffer, sourceByteLen, optionsAmended);
92
- const ret = deserialize(buffer, sourceText, sourceByteLen);
115
+ const { buffer, sourceByteLen } = prepareRaw(sourceText);
116
+ await parseAsyncRawBinding(filename, buffer, sourceByteLen, options);
117
+ const data = convert(buffer, sourceText, sourceByteLen);
93
118
 
94
119
  // Free the CPU core
95
120
  if (queue.length > 0) {
@@ -102,7 +127,7 @@ async function parseAsyncRawImpl(filename, sourceText, options, deserialize) {
102
127
  availableCores++;
103
128
  }
104
129
 
105
- return ret;
130
+ return data;
106
131
  }
107
132
 
108
133
  const ONE_GIB = 1 << 30,
@@ -140,22 +165,29 @@ const ONE_GIB = 1 << 30,
140
165
  // point creating a new buffer, when one already exists.
141
166
  const CLEAR_BUFFERS_TIMEOUT = 10_000; // 10 seconds
142
167
  const buffers = [], oldBuffers = [];
143
-
144
- let encoder = null, clearBuffersTimeout = null;
145
-
146
- // Get a buffer (from cache if possible), copy source text into it, and amend options object
147
- function prepareRaw(sourceText, options) {
168
+ let clearBuffersTimeout = null;
169
+
170
+ const textEncoder = new TextEncoder();
171
+
172
+ /**
173
+ * Get a buffer (from cache if possible), and copy source text into it.
174
+ *
175
+ * @param {string} sourceText - Source text of file
176
+ * @returns {Object} - Object of form `{ buffer, sourceByteLen }`.
177
+ * - `buffer`: `Uint8Array` containing the AST in raw form.
178
+ * - `sourceByteLen`: Length of source text in UTF-8 bytes
179
+ * (which may not be equal to `sourceText.length` if source contains non-ASCII characters).
180
+ * @throws {Error} - If raw transfer is not supported on this platform
181
+ */
182
+ function prepareRaw(sourceText) {
148
183
  if (!rawTransferSupported()) {
149
184
  throw new Error(
150
- '`experimentalRawTransfer` option is not supported on 32-bit or big-endian systems, ' +
151
- 'versions of NodeJS prior to v22.0.0, versions of Deno prior to v2.0.0, and other runtimes',
185
+ '`experimentalRawTransfer` and `experimentalLazy` options are not supported ' +
186
+ 'on 32-bit or big-endian systems, versions of NodeJS prior to v22.0.0, ' +
187
+ 'versions of Deno prior to v2.0.0, or other runtimes',
152
188
  );
153
189
  }
154
190
 
155
- // Delete `experimentalRawTransfer` and `experimentalLazy` options
156
- let _;
157
- ({ experimentalRawTransfer: _, experimentalLazy: _, ...options } = options);
158
-
159
191
  // Cancel timeout for clearing buffers
160
192
  if (clearBuffersTimeout !== null) {
161
193
  clearTimeout(clearBuffersTimeout);
@@ -176,29 +208,36 @@ function prepareRaw(sourceText, options) {
176
208
  // Reuse existing buffer, or create a new one
177
209
  const buffer = buffers.length > 0 ? buffers.pop() : createBuffer();
178
210
 
179
- // Get/create `TextEncoder`
180
- if (encoder === null) encoder = new TextEncoder();
181
-
182
211
  // Write source into start of buffer.
183
212
  // `TextEncoder` cannot write into a `Uint8Array` larger than 1 GiB,
184
213
  // so create a view into buffer of this size to write into.
185
214
  const sourceBuffer = new Uint8Array(buffer.buffer, buffer.byteOffset, ONE_GIB);
186
- const { read, written: sourceByteLen } = encoder.encodeInto(sourceText, sourceBuffer);
215
+ const { read, written: sourceByteLen } = textEncoder.encodeInto(sourceText, sourceBuffer);
187
216
  if (read !== sourceText.length) throw new Error('Failed to write source text into buffer');
188
217
 
189
- return { buffer, sourceByteLen, options };
218
+ return { buffer, sourceByteLen };
190
219
  }
191
220
 
192
- // Get if AST should be parsed as JS or TS.
193
- // Rust side sets a `bool` in this position in buffer which is `true` if TS.
221
+ /**
222
+ * Get if AST should be parsed as JS or TS.
223
+ * Rust side sets a `bool` in this position in buffer which is `true` if TS.
224
+ *
225
+ * @param {Uint8Array} buffer - Buffer containing AST in raw form
226
+ * @returns {boolean} - `true` if AST is JS, `false` if TS
227
+ */
194
228
  function isJsAst(buffer) {
195
229
  // 2147483636 = (2 * 1024 * 1024 * 1024) - 12
196
230
  // i.e. 12 bytes from end of 2 GiB buffer
197
231
  return buffer[2147483636] === 0;
198
232
  }
199
233
 
200
- // Return buffer to cache, to be reused.
201
- // Set a timer to clear buffers.
234
+ /**
235
+ * Return buffer to cache, to be reused.
236
+ * Set a timer to clear buffers.
237
+ *
238
+ * @param {Uint8Array} buffer - Buffer
239
+ * @returns {undefined}
240
+ */
202
241
  function returnBufferToCache(buffer) {
203
242
  buffers.push(buffer);
204
243
 
@@ -207,8 +246,12 @@ function returnBufferToCache(buffer) {
207
246
  clearBuffersTimeout.unref();
208
247
  }
209
248
 
210
- // Downgrade buffers in tier 1 cache (`buffers`) to tier 2 (`oldBuffers`),
211
- // so they can be garbage collected
249
+ /**
250
+ * Downgrade buffers in tier 1 cache (`buffers`) to tier 2 (`oldBuffers`)
251
+ * so they can be garbage collected.
252
+ *
253
+ * @returns {undefined}
254
+ */
212
255
  function clearBuffersCache() {
213
256
  clearBuffersTimeout = null;
214
257
 
@@ -218,66 +261,26 @@ function clearBuffersCache() {
218
261
  buffers.length = 0;
219
262
  }
220
263
 
221
- // Create a `Uint8Array` which is 2 GiB in size, with its start aligned on 4 GiB.
222
- //
223
- // Achieve this by creating a 6 GiB `ArrayBuffer`, getting the offset within it that's aligned to 4 GiB,
224
- // chopping off that number of bytes from the start, and shortening to 2 GiB.
225
- //
226
- // It's always possible to obtain a 2 GiB slice aligned on 4 GiB within a 6 GiB buffer,
227
- // no matter how the 6 GiB buffer is aligned.
228
- //
229
- // Note: On systems with virtual memory, this only consumes 6 GiB of *virtual* memory.
230
- // It does not consume physical memory until data is actually written to the `Uint8Array`.
231
- // Physical memory consumed corresponds to the quantity of data actually written.
264
+ /**
265
+ * Create a `Uint8Array` which is 2 GiB in size, with its start aligned on 4 GiB.
266
+ *
267
+ * Achieve this by creating a 6 GiB `ArrayBuffer`, getting the offset within it that's aligned to 4 GiB,
268
+ * chopping off that number of bytes from the start, and shortening to 2 GiB.
269
+ *
270
+ * It's always possible to obtain a 2 GiB slice aligned on 4 GiB within a 6 GiB buffer,
271
+ * no matter how the 6 GiB buffer is aligned.
272
+ *
273
+ * Note: On systems with virtual memory, this only consumes 6 GiB of *virtual* memory.
274
+ * It does not consume physical memory until data is actually written to the `Uint8Array`.
275
+ * Physical memory consumed corresponds to the quantity of data actually written.
276
+ *
277
+ * @returns {Uint8Array} - Buffer
278
+ */
232
279
  function createBuffer() {
233
280
  const arrayBuffer = new ArrayBuffer(SIX_GIB);
234
- const offset = bindings.getBufferOffset(new Uint8Array(arrayBuffer));
281
+ const offset = getBufferOffset(new Uint8Array(arrayBuffer));
235
282
  const buffer = new Uint8Array(arrayBuffer, offset, TWO_GIB);
236
283
  buffer.uint32 = new Uint32Array(arrayBuffer, offset, TWO_GIB / 4);
237
284
  buffer.float64 = new Float64Array(arrayBuffer, offset, TWO_GIB / 8);
238
285
  return buffer;
239
286
  }
240
-
241
- let rawTransferIsSupported = null;
242
-
243
- // Returns `true` if `experimentalRawTransfer` is option is supported.
244
- //
245
- // Raw transfer is only supported on 64-bit little-endian systems,
246
- // and NodeJS >= v22.0.0 or Deno >= v2.0.0.
247
- //
248
- // Versions of NodeJS prior to v22.0.0 do not support creating an `ArrayBuffer` larger than 4 GiB.
249
- // Bun (as at v1.2.4) also does not support creating an `ArrayBuffer` larger than 4 GiB.
250
- // Support on Deno v1 is unknown and it's EOL, so treating Deno before v2.0.0 as unsupported.
251
- function rawTransferSupported() {
252
- if (rawTransferIsSupported === null) {
253
- rawTransferIsSupported = rawTransferRuntimeSupported() && bindings.rawTransferSupported();
254
- }
255
- return rawTransferIsSupported;
256
- }
257
-
258
- // Checks copied from:
259
- // https://github.com/unjs/std-env/blob/ab15595debec9e9115a9c1d31bc7597a8e71dbfd/src/runtimes.ts
260
- // MIT license: https://github.com/unjs/std-env/blob/ab15595debec9e9115a9c1d31bc7597a8e71dbfd/LICENCE
261
- function rawTransferRuntimeSupported() {
262
- let global;
263
- try {
264
- global = globalThis;
265
- } catch (e) {
266
- return false;
267
- }
268
-
269
- const isBun = !!global.Bun || !!global.process?.versions?.bun;
270
- if (isBun) return false;
271
-
272
- const isDeno = !!global.Deno;
273
- if (isDeno) {
274
- const match = Deno.version?.deno?.match(/^(\d+)\./);
275
- return !!match && match[1] * 1 >= 2;
276
- }
277
-
278
- const isNode = global.process?.release?.name === 'node';
279
- if (!isNode) return false;
280
-
281
- const match = process.version?.match(/^v(\d+)\./);
282
- return !!match && match[1] * 1 >= 22;
283
- }
@@ -1,20 +1,59 @@
1
1
  'use strict';
2
2
 
3
- const { parseSyncRawImpl, parseAsyncRawImpl, isJsAst, returnBufferToCache } = require('./index.js');
3
+ const { parseSyncRawImpl, parseAsyncRawImpl, isJsAst, returnBufferToCache } = require('./common.js');
4
4
 
5
5
  module.exports = { parseSyncRaw, parseAsyncRaw };
6
6
 
7
+ /**
8
+ * Parse JS/TS source synchronously on current thread, using raw transfer to speed up deserialization.
9
+ *
10
+ * @param {string} filename - Filename
11
+ * @param {string} sourceText - Source text of file
12
+ * @param {Object} options - Parsing options
13
+ * @returns {Object} - Object with property getters for `program`, `module`, `comments`, and `errors`
14
+ * @throws {Error} - If raw transfer is not supported on this platform
15
+ */
7
16
  function parseSyncRaw(filename, sourceText, options) {
17
+ let _;
18
+ ({ experimentalRawTransfer: _, ...options } = options);
8
19
  return parseSyncRawImpl(filename, sourceText, options, deserialize);
9
20
  }
10
21
 
22
+ /**
23
+ * Parse JS/TS source asynchronously, using raw transfer to speed up deserialization.
24
+ *
25
+ * Note that not all of the workload can happen on a separate thread.
26
+ * Parsing on Rust side does happen in a separate thread, but deserialization of the AST to JS objects
27
+ * has to happen on current thread. This synchronous deserialization work typically outweighs
28
+ * the asynchronous parsing by a factor of around 3.
29
+ *
30
+ * i.e. the majority of the workload cannot be parallelized by using this method.
31
+ *
32
+ * Generally `parseSyncRaw` is preferable to use as it does not have the overhead of spawning a thread.
33
+ * If you need to parallelize parsing multiple files, it is recommended to use worker threads.
34
+ *
35
+ * @param {string} filename - Filename
36
+ * @param {string} sourceText - Source text of file
37
+ * @param {Object} options - Parsing options
38
+ * @returns {Object} - Object with property getters for `program`, `module`, `comments`, and `errors`
39
+ * @throws {Error} - If raw transfer is not supported on this platform
40
+ */
11
41
  function parseAsyncRaw(filename, sourceText, options) {
42
+ let _;
43
+ ({ experimentalRawTransfer: _, ...options } = options);
12
44
  return parseAsyncRawImpl(filename, sourceText, options, deserialize);
13
45
  }
14
46
 
15
47
  let deserializeJS = null, deserializeTS = null;
16
48
 
17
- // Deserialize whole AST from buffer eagerly
49
+ /**
50
+ * Deserialize whole AST from buffer.
51
+ *
52
+ * @param {Uint8Array} buffer - Buffer containing AST in raw form
53
+ * @param {string} sourceText - Source for the file
54
+ * @param {number} sourceByteLen - Length of source text in UTF-8 bytes
55
+ * @returns {Object} - Object with property getters for `program`, `module`, `comments`, and `errors`
56
+ */
18
57
  function deserialize(buffer, sourceText, sourceByteLen) {
19
58
  // Lazy load deserializer, and deserialize buffer to JS objects
20
59
  let data;
@@ -38,7 +77,7 @@ function deserialize(buffer, sourceText, sourceByteLen) {
38
77
  returnBufferToCache(buffer);
39
78
 
40
79
  // We cannot lazily deserialize in the getters, because the buffer might be re-used to parse
41
- // another file before the getter is called.
80
+ // another file before the getter is called
42
81
  return {
43
82
  get program() {
44
83
  return data.program;
@@ -0,0 +1,15 @@
1
+ 'use strict';
2
+
3
+ // Unique token which is not exposed publicly.
4
+ // Used to prevent user calling class constructors.
5
+ const TOKEN = {};
6
+
7
+ /**
8
+ * Throw error when restricted class constructor is called by user code.
9
+ * @throws {Error}
10
+ */
11
+ function constructorError() {
12
+ throw new Error('Constructor is for internal use only');
13
+ }
14
+
15
+ module.exports = { TOKEN, constructorError };
@@ -1,14 +1,74 @@
1
1
  'use strict';
2
2
 
3
- const { parseSyncRawImpl, parseAsyncRawImpl, returnBufferToCache } = require('./index.js');
3
+ const { parseSyncRawImpl, parseAsyncRawImpl, returnBufferToCache } = require('./common.js'),
4
+ { TOKEN } = require('./lazy-common.js'),
5
+ constructLazyData = require('../generated/deserialize/lazy.js').construct,
6
+ walkProgram = require('../generated/deserialize/lazy-visit.js'),
7
+ { Visitor, getVisitorsArr } = require('./visitor.js');
4
8
 
5
- module.exports = { parseSyncLazy, parseAsyncLazy };
9
+ module.exports = { parseSyncLazy, parseAsyncLazy, Visitor };
6
10
 
11
+ /**
12
+ * Parse JS/TS source synchronously on current thread.
13
+ *
14
+ * The data in buffer is not deserialized. Is deserialized to JS objects lazily, when accessing the
15
+ * properties of objects.
16
+ *
17
+ * e.g. `program` in returned object is an instance of `Program` class, with getters for `start`, `end`,
18
+ * `body` etc.
19
+ *
20
+ * Returned object contains a `visit` function which can be used to visit the AST with a `Visitor`
21
+ * (`Visitor` class can be obtained by calling `experimentalGetLazyVisitor()`).
22
+ *
23
+ * Returned object contains a `dispose` method. When finished with this AST, it's advisable to call
24
+ * `dispose`, to return the buffer to the cache, so it can be reused.
25
+ * Garbage collector should do this anyway at some point, but on an unpredictable schedule,
26
+ * so it's preferable to call `dispose` manually, to ensure the buffer can be reused immediately.
27
+ *
28
+ * @param {string} filename - Filename
29
+ * @param {string} sourceText - Source text of file
30
+ * @param {Object} options - Parsing options
31
+ * @returns {Object} - Object with property getters for `program`, `module`, `comments`, and `errors`,
32
+ * and `dispose` and `visit` methods
33
+ * @throws {Error} - If raw transfer is not supported on this platform
34
+ */
7
35
  function parseSyncLazy(filename, sourceText, options) {
36
+ let _;
37
+ ({ experimentalLazy: _, ...options } = options);
8
38
  return parseSyncRawImpl(filename, sourceText, options, construct);
9
39
  }
10
40
 
41
+ /**
42
+ * Parse JS/TS source asynchronously on a separate thread.
43
+ *
44
+ * The data in buffer is not deserialized. Is deserialized to JS objects lazily, when accessing the
45
+ * properties of objects.
46
+ *
47
+ * e.g. `program` in returned object is an instance of `Program` class, with getters for `start`, `end`,
48
+ * `body` etc.
49
+ *
50
+ * Because this function does not deserialize the AST, unlike `parseAsyncRaw`, very little work happens
51
+ * on current thread in this function. Deserialization work only occurs when properties of the objects
52
+ * are accessed.
53
+ *
54
+ * Returned object contains a `visit` function which can be used to visit the AST with a `Visitor`
55
+ * (`Visitor` class can be obtained by calling `experimentalGetLazyVisitor()`).
56
+ *
57
+ * Returned object contains a `dispose` method. When finished with this AST, it's advisable to call
58
+ * `dispose`, to return the buffer to the cache, so it can be reused.
59
+ * Garbage collector should do this anyway at some point, but on an unpredictable schedule,
60
+ * so it's preferable to call `dispose` manually, to ensure the buffer can be reused immediately.
61
+ *
62
+ * @param {string} filename - Filename
63
+ * @param {string} sourceText - Source text of file
64
+ * @param {Object} options - Parsing options
65
+ * @returns {Object} - Object with property getters for `program`, `module`, `comments`, and `errors`,
66
+ * and `dispose` and `visit` methods
67
+ * @throws {Error} - If raw transfer is not supported on this platform
68
+ */
11
69
  function parseAsyncLazy(filename, sourceText, options) {
70
+ let _;
71
+ ({ experimentalLazy: _, ...options } = options);
12
72
  return parseAsyncRawImpl(filename, sourceText, options, construct);
13
73
  }
14
74
 
@@ -23,15 +83,18 @@ const bufferRecycleRegistry = typeof FinalizationRegistry === 'undefined'
23
83
  ? null
24
84
  : new FinalizationRegistry(returnBufferToCache);
25
85
 
26
- let constructLazyData = null, TOKEN;
27
-
28
- // Get an object with getters which lazy deserialize AST from buffer
86
+ /**
87
+ * Get an object with getters which lazy deserialize AST and other data from buffer.
88
+ *
89
+ * Object also includes `dispose` and `visit` functions.
90
+ *
91
+ * @param {Uint8Array} buffer - Buffer containing AST in raw form
92
+ * @param {string} sourceText - Source for the file
93
+ * @param {number} sourceByteLen - Length of source text in UTF-8 bytes
94
+ * @returns {Object} - Object with property getters for `program`, `module`, `comments`, and `errors`,
95
+ * and `dispose` and `visit` methods
96
+ */
29
97
  function construct(buffer, sourceText, sourceLen) {
30
- // Lazy load deserializer, and get `TOKEN` to store in `ast` objects
31
- if (constructLazyData === null) {
32
- ({ construct: constructLazyData, TOKEN } = require('../generated/deserialize/lazy.js'));
33
- }
34
-
35
98
  // Create AST object
36
99
  const sourceIsAscii = sourceText.length === sourceLen;
37
100
  const ast = { buffer, sourceText, sourceLen, sourceIsAscii, nodes: new Map(), token: TOKEN };
@@ -57,21 +120,32 @@ function construct(buffer, sourceText, sourceLen) {
57
120
  return data.errors;
58
121
  },
59
122
  dispose: dispose.bind(null, ast),
123
+ visit(visitor) {
124
+ // (2 * 1024 * 1024 * 1024 - 16) >> 2
125
+ const metadataPos32 = 536870908;
126
+ const pos = buffer.uint32[metadataPos32];
127
+ walkProgram(pos, ast, getVisitorsArr(visitor));
128
+ },
60
129
  };
61
130
  }
62
131
 
63
- // Dispose of this AST.
64
- //
65
- // After calling this method, trying to read any nodes from this AST may cause an error.
66
- //
67
- // Buffer is returned to the cache to be reused.
68
- //
69
- // The buffer would be returned to the cache anyway, once all nodes of the AST are garbage collected,
70
- // but calling `dispose` is preferable, as it will happen immediately.
71
- // Otherwise, garbage collector may take time to collect the `ast` object, and new buffers may be created
72
- // in the meantime, when we could have reused this one.
132
+ /**
133
+ * Dispose of this AST.
134
+ *
135
+ * After calling this method, trying to read any nodes from this AST may cause an error.
136
+ *
137
+ * Buffer is returned to the cache to be reused.
138
+ *
139
+ * The buffer would be returned to the cache anyway, once all nodes of the AST are garbage collected,
140
+ * but calling `dispose` is preferable, as it will happen immediately.
141
+ * Otherwise, garbage collector may take time to collect the `ast` object, and new buffers may be created
142
+ * in the meantime, when we could have reused this one.
143
+ *
144
+ * @param {Object} ast - AST object containing buffer etc
145
+ * @returns {undefined}
146
+ */
73
147
  function dispose(ast) {
74
- // Return buffer to cache to be reused
148
+ // Return buffer to cache, to be reused
75
149
  returnBufferToCache(ast.buffer);
76
150
 
77
151
  // Remove connection between `ast` and the buffer