oxc-parser 0.73.2 → 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.
- package/generated/deserialize/lazy-types.js +194 -0
- package/generated/deserialize/lazy-visit.js +5457 -0
- package/generated/deserialize/lazy.js +410 -11
- package/index.d.ts +8 -0
- package/index.js +95 -18
- package/package.json +25 -21
- package/raw-transfer/{index.js → common.js} +101 -98
- package/raw-transfer/eager.js +42 -3
- package/raw-transfer/lazy-common.js +4 -1
- package/raw-transfer/lazy.js +95 -21
- package/raw-transfer/node-array.js +127 -92
- package/raw-transfer/supported.js +56 -0
- package/raw-transfer/visitor.js +129 -0
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/index.js
CHANGED
|
@@ -2,13 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
const bindings = require('./bindings.js');
|
|
4
4
|
const { wrap } = require('./wrap.cjs');
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
parseSyncLazy,
|
|
9
|
-
parseAsyncLazy,
|
|
10
|
-
rawTransferSupported,
|
|
11
|
-
} = require('./raw-transfer/index.js');
|
|
5
|
+
const rawTransferSupported = require('./raw-transfer/supported.js');
|
|
6
|
+
|
|
7
|
+
const { parseSync: parseSyncBinding, parseAsync: parseAsyncBinding } = bindings;
|
|
12
8
|
|
|
13
9
|
module.exports.ParseResult = bindings.ParseResult;
|
|
14
10
|
module.exports.ExportExportNameKind = bindings.ExportExportNameKind;
|
|
@@ -18,16 +14,97 @@ module.exports.ImportNameKind = bindings.ImportNameKind;
|
|
|
18
14
|
module.exports.parseWithoutReturn = bindings.parseWithoutReturn;
|
|
19
15
|
module.exports.Severity = bindings.Severity;
|
|
20
16
|
|
|
21
|
-
module.exports.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
};
|
|
17
|
+
module.exports.parseSync = parseSync;
|
|
18
|
+
module.exports.parseAsync = parseAsync;
|
|
19
|
+
module.exports.experimentalGetLazyVisitor = experimentalGetLazyVisitor;
|
|
20
|
+
module.exports.rawTransferSupported = rawTransferSupported;
|
|
26
21
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
22
|
+
// Lazily loaded as needed
|
|
23
|
+
let parseSyncRaw = null,
|
|
24
|
+
parseAsyncRaw,
|
|
25
|
+
parseSyncLazy = null,
|
|
26
|
+
parseAsyncLazy,
|
|
27
|
+
Visitor;
|
|
32
28
|
|
|
33
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Lazy-load code related to raw transfer.
|
|
31
|
+
* @returns {undefined}
|
|
32
|
+
*/
|
|
33
|
+
function loadRawTransfer() {
|
|
34
|
+
if (parseSyncRaw === null) {
|
|
35
|
+
({ parseSyncRaw, parseAsyncRaw } = require('./raw-transfer/eager.js'));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Lazy-load code related to raw transfer lazy deserialization.
|
|
41
|
+
* @returns {undefined}
|
|
42
|
+
*/
|
|
43
|
+
function loadRawTransferLazy() {
|
|
44
|
+
if (parseSyncLazy === null) {
|
|
45
|
+
({ parseSyncLazy, parseAsyncLazy, Visitor } = require('./raw-transfer/lazy.js'));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Parse JS/TS source synchronously on current thread.
|
|
51
|
+
*
|
|
52
|
+
* @param {string} filename - Filename
|
|
53
|
+
* @param {string} sourceText - Source text of file
|
|
54
|
+
* @param {Object|undefined} options - Parsing options
|
|
55
|
+
* @returns {Object} - Object with property getters for `program`, `module`, `comments`, and `errors`
|
|
56
|
+
* @throws {Error} - If `experimentalRawTransfer` or `experimentalLazy` option is enabled,
|
|
57
|
+
* and raw transfer is not supported on this platform
|
|
58
|
+
*/
|
|
59
|
+
function parseSync(filename, sourceText, options) {
|
|
60
|
+
if (options?.experimentalRawTransfer) {
|
|
61
|
+
loadRawTransfer();
|
|
62
|
+
return parseSyncRaw(filename, sourceText, options);
|
|
63
|
+
}
|
|
64
|
+
if (options?.experimentalLazy) {
|
|
65
|
+
loadRawTransferLazy();
|
|
66
|
+
return parseSyncLazy(filename, sourceText, options);
|
|
67
|
+
}
|
|
68
|
+
return wrap(parseSyncBinding(filename, sourceText, options));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Parse JS/TS source asynchronously on a separate thread.
|
|
73
|
+
*
|
|
74
|
+
* Note that not all of the workload can happen on a separate thread.
|
|
75
|
+
* Parsing on Rust side does happen in a separate thread, but deserialization of the AST to JS objects
|
|
76
|
+
* has to happen on current thread. This synchronous deserialization work typically outweighs
|
|
77
|
+
* the asynchronous parsing by a factor of between 3 and 20.
|
|
78
|
+
*
|
|
79
|
+
* i.e. the majority of the workload cannot be parallelized by using this method.
|
|
80
|
+
*
|
|
81
|
+
* Generally `parseSync` is preferable to use as it does not have the overhead of spawning a thread.
|
|
82
|
+
* If you need to parallelize parsing multiple files, it is recommended to use worker threads.
|
|
83
|
+
*
|
|
84
|
+
* @param {string} filename - Filename
|
|
85
|
+
* @param {string} sourceText - Source text of file
|
|
86
|
+
* @param {Object|undefined} options - Parsing options
|
|
87
|
+
* @returns {Object} - Object with property getters for `program`, `module`, `comments`, and `errors`
|
|
88
|
+
* @throws {Error} - If `experimentalRawTransfer` or `experimentalLazy` option is enabled,
|
|
89
|
+
* and raw transfer is not supported on this platform
|
|
90
|
+
*/
|
|
91
|
+
async function parseAsync(filename, sourceText, options) {
|
|
92
|
+
if (options?.experimentalRawTransfer) {
|
|
93
|
+
loadRawTransfer();
|
|
94
|
+
return await parseAsyncRaw(filename, sourceText, options);
|
|
95
|
+
}
|
|
96
|
+
if (options?.experimentalLazy) {
|
|
97
|
+
loadRawTransferLazy();
|
|
98
|
+
return await parseAsyncLazy(filename, sourceText, options);
|
|
99
|
+
}
|
|
100
|
+
return wrap(await parseAsyncBinding(filename, sourceText, options));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Get `Visitor` class to construct visitors with.
|
|
105
|
+
* @returns {function} - `Visitor` class
|
|
106
|
+
*/
|
|
107
|
+
function experimentalGetLazyVisitor() {
|
|
108
|
+
loadRawTransferLazy();
|
|
109
|
+
return Visitor;
|
|
110
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oxc-parser",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.75.0",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"browser": "wasm.mjs",
|
|
6
6
|
"engines": {
|
|
@@ -34,28 +34,32 @@
|
|
|
34
34
|
"generated/deserialize/js.js",
|
|
35
35
|
"generated/deserialize/ts.js",
|
|
36
36
|
"generated/deserialize/lazy.js",
|
|
37
|
-
"
|
|
37
|
+
"generated/deserialize/lazy-types.js",
|
|
38
|
+
"generated/deserialize/lazy-visit.js",
|
|
39
|
+
"raw-transfer/common.js",
|
|
38
40
|
"raw-transfer/eager.js",
|
|
39
41
|
"raw-transfer/lazy.js",
|
|
40
42
|
"raw-transfer/lazy-common.js",
|
|
41
|
-
"raw-transfer/node-array.js"
|
|
43
|
+
"raw-transfer/node-array.js",
|
|
44
|
+
"raw-transfer/supported.js",
|
|
45
|
+
"raw-transfer/visitor.js"
|
|
42
46
|
],
|
|
43
47
|
"publishConfig": {
|
|
44
48
|
"registry": "https://registry.npmjs.org/",
|
|
45
49
|
"access": "public"
|
|
46
50
|
},
|
|
47
51
|
"dependencies": {
|
|
48
|
-
"@oxc-project/types": "^0.
|
|
52
|
+
"@oxc-project/types": "^0.75.0"
|
|
49
53
|
},
|
|
50
54
|
"devDependencies": {
|
|
51
55
|
"@codspeed/vitest-plugin": "^4.0.0",
|
|
52
56
|
"@napi-rs/wasm-runtime": "^0.2.7",
|
|
53
|
-
"@vitest/browser": "3.2.
|
|
57
|
+
"@vitest/browser": "3.2.4",
|
|
54
58
|
"esbuild": "^0.25.0",
|
|
55
59
|
"playwright": "^1.51.0",
|
|
56
60
|
"tinypool": "^1.1.0",
|
|
57
61
|
"typescript": "5.8.3",
|
|
58
|
-
"vitest": "3.2.
|
|
62
|
+
"vitest": "3.2.4"
|
|
59
63
|
},
|
|
60
64
|
"napi": {
|
|
61
65
|
"binaryName": "parser",
|
|
@@ -85,21 +89,21 @@
|
|
|
85
89
|
"dtsHeaderFile": "header.js"
|
|
86
90
|
},
|
|
87
91
|
"optionalDependencies": {
|
|
88
|
-
"@oxc-parser/binding-win32-x64-msvc": "0.
|
|
89
|
-
"@oxc-parser/binding-win32-arm64-msvc": "0.
|
|
90
|
-
"@oxc-parser/binding-linux-x64-gnu": "0.
|
|
91
|
-
"@oxc-parser/binding-linux-x64-musl": "0.
|
|
92
|
-
"@oxc-parser/binding-freebsd-x64": "0.
|
|
93
|
-
"@oxc-parser/binding-linux-arm64-gnu": "0.
|
|
94
|
-
"@oxc-parser/binding-linux-arm64-musl": "0.
|
|
95
|
-
"@oxc-parser/binding-linux-arm-gnueabihf": "0.
|
|
96
|
-
"@oxc-parser/binding-linux-arm-musleabihf": "0.
|
|
97
|
-
"@oxc-parser/binding-linux-s390x-gnu": "0.
|
|
98
|
-
"@oxc-parser/binding-linux-riscv64-gnu": "0.
|
|
99
|
-
"@oxc-parser/binding-darwin-x64": "0.
|
|
100
|
-
"@oxc-parser/binding-darwin-arm64": "0.
|
|
101
|
-
"@oxc-parser/binding-android-arm64": "0.
|
|
102
|
-
"@oxc-parser/binding-wasm32-wasi": "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"
|
|
103
107
|
},
|
|
104
108
|
"scripts": {
|
|
105
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
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
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
|
|
91
|
-
await
|
|
92
|
-
const
|
|
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
|
|
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
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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`
|
|
151
|
-
'
|
|
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 } =
|
|
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
|
|
218
|
+
return { buffer, sourceByteLen };
|
|
190
219
|
}
|
|
191
220
|
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
|
|
201
|
-
|
|
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
|
-
|
|
211
|
-
|
|
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
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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 =
|
|
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
|
-
}
|
package/raw-transfer/eager.js
CHANGED
|
@@ -1,20 +1,59 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { parseSyncRawImpl, parseAsyncRawImpl, isJsAst, returnBufferToCache } = require('./
|
|
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
|
-
|
|
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;
|
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
// Used to prevent user calling class constructors.
|
|
5
5
|
const TOKEN = {};
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Throw error when restricted class constructor is called by user code.
|
|
9
|
+
* @throws {Error}
|
|
10
|
+
*/
|
|
8
11
|
function constructorError() {
|
|
9
12
|
throw new Error('Constructor is for internal use only');
|
|
10
13
|
}
|