goatlint-parser 0.125.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.
@@ -0,0 +1,365 @@
1
+ import { constructorError, TOKEN } from "./lazy-common.js";
2
+
3
+ // Internal symbol to get `NodeArray` from a proxy wrapping a `NodeArray`.
4
+ //
5
+ // Methods of `NodeArray` are called with `this` being the proxy, rather than the `NodeArray` itself.
6
+ // They can "unwrap" the proxy by getting `this[ARRAY]`, and the `get` proxy trap will return
7
+ // the actual `NodeArray`.
8
+ //
9
+ // This symbol is not exported, and it is not actually defined on `NodeArray`s, so user cannot obtain it
10
+ // via `Object.getOwnPropertySymbols` or `Reflect.ownKeys`. Therefore user code cannot unwrap the proxy.
11
+ const ARRAY = Symbol();
12
+
13
+ // Functions to get internal properties of a `NodeArray`. Initialized in class static block below.
14
+ let getInternalFromProxy, getLength, getElement;
15
+
16
+ /**
17
+ * An array of AST nodes where elements are deserialized lazily upon access.
18
+ *
19
+ * Extends `Array` to make `Array.isArray` return `true` for a `NodeArray`.
20
+ *
21
+ * TODO: Other methods could maybe be more optimal, avoiding going via proxy multiple times
22
+ * e.g. `some`, `indexOf`.
23
+ */
24
+ export class NodeArray extends Array {
25
+ #internal;
26
+
27
+ /**
28
+ * Create a `NodeArray`.
29
+ *
30
+ * Constructor does not actually return a `NodeArray`, but one wrapped in a `Proxy`.
31
+ * The proxy intercepts accesses to elements and lazily deserializes them,
32
+ * and blocks mutation of elements or `length` property.
33
+ *
34
+ * @class
35
+ * @param {number} pos - Buffer position of first element
36
+ * @param {number} length - Number of elements
37
+ * @param {number} stride - Element size in bytes
38
+ * @param {Function} construct - Function to deserialize element
39
+ * @param {Object} ast - AST object
40
+ * @returns {Proxy<NodeArray>} - `NodeArray` wrapped in a `Proxy`
41
+ */
42
+ constructor(pos, length, stride, construct, ast) {
43
+ if (ast?.token !== TOKEN) constructorError();
44
+
45
+ super();
46
+ this.#internal = { pos, length, ast, stride, construct };
47
+ return new Proxy(this, PROXY_HANDLERS);
48
+ }
49
+
50
+ // Allow `arr.filter`, `arr.map` etc.
51
+ static [Symbol.species] = Array;
52
+
53
+ // Override `values` method with a more efficient one that avoids going via proxy for every iteration.
54
+ // TODO: Benchmark to check that this is actually faster.
55
+ values() {
56
+ return new NodeArrayValuesIterator(this);
57
+ }
58
+
59
+ // Override `keys` method with a more efficient one that avoids going via proxy for every iteration.
60
+ // TODO: Benchmark to check that this is actually faster.
61
+ keys() {
62
+ return new NodeArrayKeysIterator(this);
63
+ }
64
+
65
+ // Override `entries` method with a more efficient one that avoids going via proxy for every iteration.
66
+ // TODO: Benchmark to check that this is actually faster.
67
+ entries() {
68
+ return new NodeArrayEntriesIterator(this);
69
+ }
70
+
71
+ // This method is overwritten with reference to `values` method below.
72
+ // Defining dummy method here to prevent the later assignment altering the shape of class prototype.
73
+ [Symbol.iterator]() {}
74
+
75
+ /**
76
+ * Override `slice` method to return a `NodeArray`.
77
+ *
78
+ * @this {NodeArray}
79
+ * @param {*} start - Start of slice
80
+ * @param {*} end - End of slice
81
+ * @returns {NodeArray} - `NodeArray` containing slice of this one
82
+ */
83
+ slice(start, end) {
84
+ const internal = this[ARRAY].#internal,
85
+ { length } = internal;
86
+
87
+ start = toInt(start);
88
+ if (start < 0) {
89
+ start = length + start;
90
+ if (start < 0) start = 0;
91
+ }
92
+
93
+ if (end === void 0) {
94
+ end = length;
95
+ } else {
96
+ end = toInt(end);
97
+ if (end < 0) {
98
+ end += length;
99
+ if (end < 0) end = 0;
100
+ } else if (end > length) {
101
+ end = length;
102
+ }
103
+ }
104
+
105
+ let sliceLength = end - start;
106
+ if (sliceLength <= 0 || start >= length) {
107
+ start = 0;
108
+ sliceLength = 0;
109
+ }
110
+
111
+ const { stride } = internal;
112
+ return new NodeArray(
113
+ internal.pos + start * stride,
114
+ sliceLength,
115
+ stride,
116
+ internal.construct,
117
+ internal.ast,
118
+ );
119
+ }
120
+
121
+ // Make `console.log` deserialize all elements.
122
+ [Symbol.for("nodejs.util.inspect.custom")]() {
123
+ const values = [...this.values()];
124
+ Object.setPrototypeOf(values, DebugNodeArray.prototype);
125
+ return values;
126
+ }
127
+
128
+ static {
129
+ /**
130
+ * Get internal properties of `NodeArray`, given a proxy wrapping a `NodeArray`.
131
+ * @param {Proxy} proxy - Proxy wrapping `NodeArray` object
132
+ * @returns {Object} - Internal properties object
133
+ */
134
+ getInternalFromProxy = (proxy) => proxy[ARRAY].#internal;
135
+
136
+ /**
137
+ * Get length of `NodeArray`.
138
+ * @param {NodeArray} arr - `NodeArray` object
139
+ * @returns {number} - Array length
140
+ */
141
+ getLength = (arr) => arr.#internal.length;
142
+
143
+ /**
144
+ * Get element of `NodeArray` at index `index`.
145
+ *
146
+ * @param {NodeArray} arr - `NodeArray` object
147
+ * @param {number} index - Index of element to get
148
+ * @returns {*} - Element at index `index`, or `undefined` if out of bounds
149
+ */
150
+ getElement = (arr, index) => {
151
+ const internal = arr.#internal;
152
+ if (index >= internal.length) return void 0;
153
+ return (0, internal.construct)(internal.pos + index * internal.stride, internal.ast);
154
+ };
155
+ }
156
+ }
157
+
158
+ // goatlint-disable-next-line typescript/unbound-method
159
+ NodeArray.prototype[Symbol.iterator] = NodeArray.prototype.values;
160
+
161
+ /**
162
+ * Iterator over values of a `NodeArray`.
163
+ * Returned by `values` method, and also used as iterator for `for (const node of nodeArray) {}`.
164
+ */
165
+ class NodeArrayValuesIterator {
166
+ #internal;
167
+
168
+ constructor(proxy) {
169
+ const internal = getInternalFromProxy(proxy),
170
+ { pos, stride } = internal;
171
+
172
+ this.#internal = {
173
+ pos,
174
+ endPos: pos + internal.length * stride,
175
+ ast: internal.ast,
176
+ construct: internal.construct,
177
+ stride,
178
+ };
179
+ }
180
+
181
+ next() {
182
+ const internal = this.#internal,
183
+ { pos } = internal;
184
+ if (pos === internal.endPos) return { done: true, value: null };
185
+ internal.pos = pos + internal.stride;
186
+ return { done: false, value: (0, internal.construct)(pos, internal.ast) };
187
+ }
188
+
189
+ [Symbol.iterator]() {
190
+ return this;
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Iterator over keys of a `NodeArray`. Returned by `keys` method.
196
+ */
197
+ class NodeArrayKeysIterator {
198
+ #internal;
199
+
200
+ constructor(proxy) {
201
+ const internal = getInternalFromProxy(proxy);
202
+ this.#internal = { index: 0, length: internal.length };
203
+ }
204
+
205
+ next() {
206
+ const internal = this.#internal,
207
+ { index } = internal;
208
+ if (index === internal.length) return { done: true, value: null };
209
+ internal.index = index + 1;
210
+ return { done: false, value: index };
211
+ }
212
+
213
+ [Symbol.iterator]() {
214
+ return this;
215
+ }
216
+ }
217
+
218
+ /**
219
+ * Iterator over values of a `NodeArray`. Returned by `entries` method.
220
+ */
221
+ class NodeArrayEntriesIterator {
222
+ #internal;
223
+
224
+ constructor(proxy) {
225
+ const internal = getInternalFromProxy(proxy);
226
+
227
+ this.#internal = {
228
+ index: 0,
229
+ length: internal.length,
230
+ pos: internal.pos,
231
+ ast: internal.ast,
232
+ construct: internal.construct,
233
+ stride: internal.stride,
234
+ };
235
+ }
236
+
237
+ next() {
238
+ const internal = this.#internal,
239
+ { index } = internal;
240
+ if (index === internal.length) return { done: true, value: null };
241
+ internal.index = index + 1;
242
+ return {
243
+ done: false,
244
+ value: [index, (0, internal.construct)(internal.pos + index * internal.stride, internal.ast)],
245
+ };
246
+ }
247
+
248
+ [Symbol.iterator]() {
249
+ return this;
250
+ }
251
+ }
252
+
253
+ // Class used for `[Symbol.for('nodejs.util.inspect.custom')]` method (for `console.log`).
254
+ const DebugNodeArray = class NodeArray extends Array {};
255
+
256
+ // Proxy handlers.
257
+ //
258
+ // Every `NodeArray` returned to user is wrapped in a `Proxy`, using these handlers.
259
+ // They lazily deserialize array elements upon access, and block mutation of array elements / `length`.
260
+ const PROXY_HANDLERS = {
261
+ // Return `true` for indexes which are in bounds.
262
+ // e.g. `'0' in arr`.
263
+ has(arr, key) {
264
+ const index = toIndex(key);
265
+ if (index !== null) return index < getLength(arr);
266
+ return Reflect.has(arr, key);
267
+ },
268
+
269
+ // Get elements and length.
270
+ get(arr, key) {
271
+ // Methods of `NodeArray` are called with `this` being the proxy, rather than the `NodeArray` itself.
272
+ // They can "unwrap" the proxy by getting `this[ARRAY]`.
273
+ if (key === ARRAY) return arr;
274
+ if (key === "length") return getLength(arr);
275
+ const index = toIndex(key);
276
+ if (index !== null) return getElement(arr, index);
277
+
278
+ return Reflect.get(arr, key);
279
+ },
280
+
281
+ // Get descriptors for elements and length.
282
+ getOwnPropertyDescriptor(arr, key) {
283
+ if (key === "length") {
284
+ // Cannot return `writable: false` unfortunately
285
+ return { value: getLength(arr), writable: true, enumerable: false, configurable: false };
286
+ }
287
+
288
+ const index = toIndex(key);
289
+ if (index !== null) {
290
+ const value = getElement(arr, index);
291
+ if (value === void 0) return void 0;
292
+ // Cannot return `configurable: false` unfortunately
293
+ return { value, writable: false, enumerable: true, configurable: true };
294
+ }
295
+
296
+ return Reflect.getOwnPropertyDescriptor(arr, key);
297
+ },
298
+
299
+ // Prevent setting `length` or entries.
300
+ // Catches:
301
+ // * `Object.defineProperty(arr, 0, {value: null})`.
302
+ // * `arr[1] = null`.
303
+ // * `arr.length = 0`.
304
+ // * `Object.defineProperty(arr, 'length', {value: 0})`.
305
+ // * Other operations which mutate entries e.g. `arr.push(123)`.
306
+ defineProperty(arr, key, descriptor) {
307
+ if (key === "length" || toIndex(key) !== null) return false;
308
+ return Reflect.defineProperty(arr, key, descriptor);
309
+ },
310
+
311
+ // Prevent deleting entries.
312
+ deleteProperty(arr, key) {
313
+ // Note: `Reflect.deleteProperty(arr, 'length')` already returns `false`
314
+ if (toIndex(key) !== null) return false;
315
+ return Reflect.deleteProperty(arr, key);
316
+ },
317
+
318
+ // Get keys, including element indexes.
319
+ ownKeys(arr) {
320
+ const keys = [],
321
+ length = getLength(arr);
322
+ for (let i = 0; i < length; i++) {
323
+ keys.push(i + "");
324
+ }
325
+ keys.push(...Reflect.ownKeys(arr));
326
+ return keys;
327
+ },
328
+ };
329
+
330
+ /**
331
+ * Convert key to array index, if it is a valid array index.
332
+ *
333
+ * Only strings comprising a plain integer are valid indexes.
334
+ * e.g. `"-1"`, `"01"`, `"0xFF"`, `"1e1"`, `"1 "` are not valid indexes.
335
+ * Integers >= 4294967295 are not valid indexes.
336
+ *
337
+ * @param {string|Symbol} - Key used for property lookup.
338
+ * @returns {number|null} - `key` converted to integer, if it's a valid array index, otherwise `null`.
339
+ */
340
+ function toIndex(key) {
341
+ if (typeof key === "string") {
342
+ if (key === "0") return 0;
343
+ if (INDEX_REGEX.test(key)) {
344
+ const index = +key;
345
+ if (index < 4294967295) return index;
346
+ }
347
+ }
348
+ return null;
349
+ }
350
+
351
+ const INDEX_REGEX = /^[1-9]\d*$/;
352
+
353
+ /**
354
+ * Convert value to integer.
355
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number#integer_conversion
356
+ *
357
+ * @param {*} value - Value to convert to integer.
358
+ * @returns {number} - Integer
359
+ */
360
+ function toInt(value) {
361
+ value = Math.trunc(+value);
362
+ // `value === 0` check is to convert -0 to 0
363
+ if (value === 0 || Number.isNaN(value)) return 0;
364
+ return value;
365
+ }
@@ -0,0 +1,52 @@
1
+ import { rawTransferSupported as rawTransferSupportedBinding } from "../bindings.js";
2
+
3
+ let rawTransferIsSupported = null;
4
+
5
+ /**
6
+ * Returns `true` if `experimentalRawTransfer` is option is supported.
7
+ *
8
+ * Raw transfer is only supported on 64-bit little-endian systems,
9
+ * and NodeJS >= v22.0.0 or Deno >= v2.0.0.
10
+ *
11
+ * Versions of NodeJS prior to v22.0.0 do not support creating an `ArrayBuffer` larger than 4 GiB.
12
+ * Bun (as at v1.2.4) also does not support creating an `ArrayBuffer` larger than 4 GiB.
13
+ * Support on Deno v1 is unknown and it's EOL, so treating Deno before v2.0.0 as unsupported.
14
+ *
15
+ * No easy way to determining pointer width (64 bit or 32 bit) in JS,
16
+ * so call a function on Rust side to find out.
17
+ *
18
+ * @returns {boolean} - `true` if raw transfer is supported on this platform
19
+ */
20
+ export function rawTransferSupported() {
21
+ if (rawTransferIsSupported === null) {
22
+ rawTransferIsSupported = rawTransferRuntimeSupported() && rawTransferSupportedBinding();
23
+ }
24
+ return rawTransferIsSupported;
25
+ }
26
+
27
+ // Checks copied from:
28
+ // https://github.com/unjs/std-env/blob/ab15595debec9e9115a9c1d31bc7597a8e71dbfd/src/runtimes.ts
29
+ // MIT license: https://github.com/unjs/std-env/blob/ab15595debec9e9115a9c1d31bc7597a8e71dbfd/LICENCE
30
+ function rawTransferRuntimeSupported() {
31
+ let global;
32
+ try {
33
+ global = globalThis;
34
+ } catch {
35
+ return false;
36
+ }
37
+
38
+ const isBun = !!global.Bun || !!global.process?.versions?.bun;
39
+ if (isBun) return false;
40
+
41
+ const isDeno = !!global.Deno;
42
+ if (isDeno) {
43
+ const match = Deno.version?.deno?.match(/^(\d+)\./);
44
+ return !!match && match[1] * 1 >= 2;
45
+ }
46
+
47
+ const isNode = global.process?.release?.name === "node";
48
+ if (!isNode) return false;
49
+
50
+ const match = process.version?.match(/^v(\d+)\./);
51
+ return !!match && match[1] * 1 >= 22;
52
+ }
@@ -0,0 +1,127 @@
1
+ import {
2
+ LEAF_NODE_TYPES_COUNT,
3
+ NODE_TYPE_IDS_MAP,
4
+ NODE_TYPES_COUNT,
5
+ } from "../generated/lazy/type_ids.js";
6
+
7
+ // Getter for private `#visitorsArr` property of `Visitor` class. Initialized in class body below.
8
+ let getVisitorsArrTemp;
9
+
10
+ /**
11
+ * Visitor class, used to visit an AST.
12
+ */
13
+ export class Visitor {
14
+ #visitorsArr;
15
+
16
+ /**
17
+ * Create `Visitor`.
18
+ *
19
+ * Provide an object where keys are names of AST nodes you want to visit,
20
+ * and values are visitor functions which receive AST node objects of that type.
21
+ *
22
+ * Keys can also be postfixed with `:exit` to visit when exiting the node, rather than entering.
23
+ *
24
+ * ```js
25
+ * const visitor = new Visitor({
26
+ * BinaryExpression(binExpr) {
27
+ * // Do stuff when entering a `BinaryExpression`
28
+ * },
29
+ * 'BinaryExpression:exit'(binExpr) {
30
+ * // Do stuff when exiting a `BinaryExpression`
31
+ * },
32
+ * });
33
+ * ```
34
+ *
35
+ * @class
36
+ * @param {Object} visitor - Object defining visit functions for AST nodes
37
+ * @returns {Visitor}
38
+ */
39
+ constructor(visitor) {
40
+ this.#visitorsArr = createVisitorsArr(visitor);
41
+ }
42
+
43
+ static {
44
+ getVisitorsArrTemp = (visitor) => visitor.#visitorsArr;
45
+ }
46
+ }
47
+
48
+ export const getVisitorsArr = getVisitorsArrTemp;
49
+
50
+ /**
51
+ * Create array of visitors, keyed by node type ID.
52
+ *
53
+ * Each element of array is one of:
54
+ *
55
+ * * No visitor for this type = `null`.
56
+ * * Visitor for leaf node = visit function.
57
+ * * Visitor for non-leaf node = object of form `{ enter, exit }`,
58
+ * where each property is either a visitor function or `null`.
59
+ *
60
+ * @param {Object} visitor - Visitors object from user
61
+ * @returns {Array<Object|Function|null>} - Array of visitors
62
+ */
63
+ function createVisitorsArr(visitor) {
64
+ if (visitor === null || typeof visitor !== "object") {
65
+ throw new Error("`visitor` must be an object");
66
+ }
67
+
68
+ // Create empty visitors array
69
+ const visitorsArr = [];
70
+ for (let i = NODE_TYPES_COUNT; i !== 0; i--) {
71
+ visitorsArr.push(null);
72
+ }
73
+
74
+ // Populate visitors array from provided object
75
+ for (let name of Object.keys(visitor)) {
76
+ const visitFn = visitor[name];
77
+ if (typeof visitFn !== "function") {
78
+ throw new Error(`'${name}' property of \`visitor\` object is not a function`);
79
+ }
80
+
81
+ const isExit = name.endsWith(":exit");
82
+ if (isExit) name = name.slice(0, -5);
83
+
84
+ const typeId = NODE_TYPE_IDS_MAP.get(name);
85
+ if (typeId === void 0) throw new Error(`Unknown node type '${name}' in \`visitor\` object`);
86
+
87
+ if (typeId < LEAF_NODE_TYPES_COUNT) {
88
+ // Leaf node. Store just 1 function.
89
+ const existingVisitFn = visitorsArr[typeId];
90
+ if (existingVisitFn === null) {
91
+ visitorsArr[typeId] = visitFn;
92
+ } else if (isExit) {
93
+ visitorsArr[typeId] = combineVisitFunctions(existingVisitFn, visitFn);
94
+ } else {
95
+ visitorsArr[typeId] = combineVisitFunctions(visitFn, existingVisitFn);
96
+ }
97
+ continue;
98
+ }
99
+
100
+ let enterExit = visitorsArr[typeId];
101
+ if (enterExit === null) {
102
+ enterExit = visitorsArr[typeId] = { enter: null, exit: null };
103
+ }
104
+
105
+ if (isExit) {
106
+ enterExit.exit = visitFn;
107
+ } else {
108
+ enterExit.enter = visitFn;
109
+ }
110
+ }
111
+
112
+ return visitorsArr;
113
+ }
114
+
115
+ /**
116
+ * Combine 2 visitor functions into 1.
117
+ *
118
+ * @param {function} visit1 - 1st visitor function
119
+ * @param {function} visit2 - 2nd visitor function
120
+ * @returns {function} - Combined visitor function
121
+ */
122
+ function combineVisitFunctions(visit1, visit2) {
123
+ return function (node) {
124
+ visit1(node);
125
+ visit2(node);
126
+ };
127
+ }
@@ -0,0 +1,41 @@
1
+ import { createRequire } from "node:module";
2
+
3
+ // Lazy-loaded when first construct a `Visitor`
4
+ let walkProgram = null,
5
+ addVisitorToCompiled,
6
+ createCompiledVisitor,
7
+ finalizeCompiledVisitor;
8
+
9
+ /**
10
+ * Visitor class for traversing AST.
11
+ */
12
+ export class Visitor {
13
+ #compiledVisitor = null;
14
+
15
+ constructor(visitor) {
16
+ if (walkProgram === null) {
17
+ const require = createRequire(import.meta.url);
18
+ ({ walkProgram } = require("../generated/visit/walk.js"));
19
+ ({
20
+ addVisitorToCompiled,
21
+ createCompiledVisitor,
22
+ finalizeCompiledVisitor,
23
+ } = require("./visitor.js"));
24
+ }
25
+
26
+ const compiledVisitor = createCompiledVisitor();
27
+ addVisitorToCompiled(visitor);
28
+ const needsVisit = finalizeCompiledVisitor();
29
+ if (needsVisit) this.#compiledVisitor = compiledVisitor;
30
+ }
31
+
32
+ /**
33
+ * Visit AST.
34
+ * @param program - The AST to visit.
35
+ * @returns {undefined}
36
+ */
37
+ visit(program) {
38
+ const compiledVisitor = this.#compiledVisitor;
39
+ if (compiledVisitor !== null) walkProgram(program, compiledVisitor);
40
+ }
41
+ }