yuku-parser 0.5.10 → 0.5.14
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/README.md +14 -20
- package/decode.js +5 -5
- package/index.d.ts +26 -15
- package/index.js +10 -0
- package/package.json +12 -12
package/README.md
CHANGED
|
@@ -57,6 +57,19 @@ sourceTypeFromPath("foo.cjs"); // "script"
|
|
|
57
57
|
sourceTypeFromPath("foo.mjs"); // "module"
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
+
## Resolving offsets to `(line, column)`
|
|
61
|
+
|
|
62
|
+
Nodes, comments, and diagnostics carry `start`/`end` offsets. To turn an offset into a `{ line, column }` pair, use `locOf` with `lineStarts` from the parse result:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
import { parse, locOf } from "yuku-parser";
|
|
66
|
+
|
|
67
|
+
const { program, lineStarts } = parse(source);
|
|
68
|
+
const { line, column } = locOf(lineStarts, program.body[0].start);
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
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, well over 10M ops/s even on million-line files, so it's safe to call per node during a walk.
|
|
72
|
+
|
|
60
73
|
## Walking the AST
|
|
61
74
|
|
|
62
75
|
The AST is standard ESTree, so any ESTree-compatible walker works. For example, with [zimmerframe](https://github.com/sveltejs/zimmerframe):
|
|
@@ -142,36 +155,17 @@ This incurs a very small performance overhead. If your build pipeline already ha
|
|
|
142
155
|
|
|
143
156
|
### Comments
|
|
144
157
|
|
|
145
|
-
Every entry in `result.comments`
|
|
158
|
+
Every entry in `result.comments` describes one source comment:
|
|
146
159
|
|
|
147
160
|
```ts
|
|
148
161
|
interface Comment {
|
|
149
162
|
type: "Line" | "Block";
|
|
150
|
-
kind: CommentKind;
|
|
151
163
|
precededByNewline: boolean;
|
|
152
164
|
followedByNewline: boolean;
|
|
153
165
|
value: string; // text without the surrounding delimiters
|
|
154
166
|
start: number; // byte offset
|
|
155
167
|
end: number; // byte offset
|
|
156
168
|
}
|
|
157
|
-
|
|
158
|
-
type CommentKind =
|
|
159
|
-
| "normal" // plain comment, no special meaning
|
|
160
|
-
| "legal" // /*! ... */ or contains @license / @preserve / @cc_on
|
|
161
|
-
| "jsdoc" // /** ... */ block
|
|
162
|
-
| "annotation" // /*# ... */ or /*@ ... */ other than tree-shaking
|
|
163
|
-
| "pure" // /*#__PURE__*/ or /*@__PURE__*/
|
|
164
|
-
| "no_side_effects"; // /*#__NO_SIDE_EFFECTS__*/ or /*@__NO_SIDE_EFFECTS__*/
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
Common patterns:
|
|
168
|
-
|
|
169
|
-
```js
|
|
170
|
-
// Extract legal banners for a sidecar file.
|
|
171
|
-
const banners = result.comments.filter((c) => c.kind === "legal");
|
|
172
|
-
|
|
173
|
-
// Find tree-shaking annotations.
|
|
174
|
-
const pureMarkers = result.comments.filter((c) => c.kind === "pure");
|
|
175
169
|
```
|
|
176
170
|
|
|
177
171
|
`precededByNewline` and `followedByNewline` capture line layout, useful for tools that need to reconstruct source positioning.
|
package/decode.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// generated by tools/gen_estree_decoder.zig, do not edit
|
|
2
|
-
|
|
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
|
+
const NULL = -1;
|
|
3
5
|
const _td = new TextDecoder("utf-8", { ignoreBOM: true });
|
|
4
6
|
const BINARY_OPS = ["==", "!=", "===", "!==", "<", "<=", ">", ">=", "+", "-", "*", "/", "%", "**", "|", "^", "&", "<<", ">>", ">>>", "in", "instanceof"];
|
|
5
7
|
const LOGICAL_OPS = ["&&", "||", "??"];
|
|
@@ -12,7 +14,6 @@ const METHOD_KINDS = ["constructor", "method", "get", "set"];
|
|
|
12
14
|
const FUNCTION_TYPES = ["FunctionDeclaration", "FunctionExpression", "TSDeclareFunction", "TSEmptyBodyFunctionExpression"];
|
|
13
15
|
const CLASS_TYPES = ["ClassDeclaration", "ClassExpression"];
|
|
14
16
|
const COMMENT_TYPES = ["Line", "Block"];
|
|
15
|
-
const COMMENT_KINDS = ["normal", "legal", "jsdoc", "annotation", "pure", "no_side_effects"];
|
|
16
17
|
const SEVERITY = ["error", "warning", "hint", "info"];
|
|
17
18
|
const IMPORT_EXPORT_KINDS = ["value", "type"];
|
|
18
19
|
const ACCESSIBILITY = [null, "public", "private", "protected"];
|
|
@@ -53,7 +54,7 @@ function buildPosMap(src, byteLen, startByte) {
|
|
|
53
54
|
function decode(buffer, source) {
|
|
54
55
|
const _u8 = new Uint8Array(buffer);
|
|
55
56
|
const aLen = (buffer.byteLength >> 2) << 2;
|
|
56
|
-
const _u32 = new
|
|
57
|
+
const _u32 = new Int32Array(buffer, 0, aLen >> 2);
|
|
57
58
|
const _src = source;
|
|
58
59
|
const _srcLen = _u32[3];
|
|
59
60
|
const nodeCount = _u32[0], extraCount = _u32[1], spLen = _u32[2];
|
|
@@ -308,10 +309,9 @@ function decode(buffer, source) {
|
|
|
308
309
|
const out = new Array(commentCount);
|
|
309
310
|
for (let j = 0; j < commentCount; j++) {
|
|
310
311
|
const o = cOff + j * 20;
|
|
311
|
-
const flags = _u8[o +
|
|
312
|
+
const flags = _u8[o + 1];
|
|
312
313
|
out[j] = {
|
|
313
314
|
type: COMMENT_TYPES[_u8[o + 0]],
|
|
314
|
-
kind: COMMENT_KINDS[_u8[o + 1]],
|
|
315
315
|
precededByNewline: (flags & 1) !== 0,
|
|
316
316
|
followedByNewline: (flags & 2) !== 0,
|
|
317
317
|
value: str(dv.getUint32(o + 12, true), dv.getUint32(o + 16, true)),
|
package/index.d.ts
CHANGED
|
@@ -45,23 +45,9 @@ interface ParseOptions {
|
|
|
45
45
|
/** Whether a {@link Comment} came from a line or block source comment. */
|
|
46
46
|
type CommentType = "Line" | "Block";
|
|
47
47
|
|
|
48
|
-
/**
|
|
49
|
-
* Semantic classification of a {@link Comment}, computed at parse time so
|
|
50
|
-
* consumers can route comments without rescanning their value.
|
|
51
|
-
*
|
|
52
|
-
* - `"normal"`: plain comment with no special meaning.
|
|
53
|
-
* - `"legal"`: `/*! ... *\/` or contains `@license`, `@preserve`, or `@cc_on`.
|
|
54
|
-
* - `"jsdoc"`: `/** ... *\/` block.
|
|
55
|
-
* - `"annotation"`: `/*# ... *\/` or `/*@ ... *\/` other than tree-shaking.
|
|
56
|
-
* - `"pure"`: `/*#__PURE__*\/` or `/*@__PURE__*\/`.
|
|
57
|
-
* - `"no_side_effects"`: `/*#__NO_SIDE_EFFECTS__*\/` or `/*@__NO_SIDE_EFFECTS__*\/`.
|
|
58
|
-
*/
|
|
59
|
-
type CommentKind = "normal" | "legal" | "jsdoc" | "annotation" | "pure" | "no_side_effects";
|
|
60
|
-
|
|
61
48
|
/** A source code comment. */
|
|
62
49
|
interface Comment {
|
|
63
50
|
type: CommentType;
|
|
64
|
-
kind: CommentKind;
|
|
65
51
|
/** True when a line terminator immediately precedes this comment. */
|
|
66
52
|
precededByNewline: boolean;
|
|
67
53
|
/** True when a line terminator immediately follows this comment. */
|
|
@@ -103,6 +89,17 @@ interface Diagnostic {
|
|
|
103
89
|
labels: DiagnosticLabel[];
|
|
104
90
|
}
|
|
105
91
|
|
|
92
|
+
/**
|
|
93
|
+
* A `(line, column)` pair into the source, matching ESTree's `loc` convention.
|
|
94
|
+
* Lines are 1-based; columns are 0-based.
|
|
95
|
+
*/
|
|
96
|
+
interface SourceLocation {
|
|
97
|
+
/** 1-based line number. */
|
|
98
|
+
line: number;
|
|
99
|
+
/** 0-based column number within the line. */
|
|
100
|
+
column: number;
|
|
101
|
+
}
|
|
102
|
+
|
|
106
103
|
/** The result returned by the parser. */
|
|
107
104
|
interface ParseResult {
|
|
108
105
|
/** Root ESTree/TypeScript-ESTree AST node. */
|
|
@@ -139,6 +136,20 @@ export function langFromPath(path: string): SourceLang;
|
|
|
139
136
|
*/
|
|
140
137
|
export function sourceTypeFromPath(path: string): SourceType;
|
|
141
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Resolves a `{ line, column }` {@link SourceLocation} for an offset, using
|
|
141
|
+
* {@link ParseResult.lineStarts}. Runs in O(log n).
|
|
142
|
+
*
|
|
143
|
+
* Lines are 1-based; columns are 0-based. The unit of `offset` (UTF-16 code
|
|
144
|
+
* units in JS) must match the unit of `lineStarts`. Both come from the same
|
|
145
|
+
* {@link ParseResult}, so this is automatic.
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* const { program, lineStarts } = parse(source);
|
|
149
|
+
* locOf(lineStarts, program.body[0].start); // => { line, column }
|
|
150
|
+
*/
|
|
151
|
+
export function locOf(lineStarts: number[], offset: number): SourceLocation;
|
|
152
|
+
|
|
142
153
|
// AST node types
|
|
143
154
|
|
|
144
155
|
interface BaseNode {
|
|
@@ -1738,13 +1749,13 @@ export type {
|
|
|
1738
1749
|
ParseResult,
|
|
1739
1750
|
Comment,
|
|
1740
1751
|
CommentType,
|
|
1741
|
-
CommentKind,
|
|
1742
1752
|
Diagnostic,
|
|
1743
1753
|
DiagnosticLabel,
|
|
1744
1754
|
DiagnosticSeverity,
|
|
1745
1755
|
SourceType,
|
|
1746
1756
|
ModuleKind,
|
|
1747
1757
|
SourceLang,
|
|
1758
|
+
SourceLocation,
|
|
1748
1759
|
BaseNode,
|
|
1749
1760
|
Span,
|
|
1750
1761
|
Program,
|
package/index.js
CHANGED
|
@@ -16,3 +16,13 @@ export function langFromPath(path) {
|
|
|
16
16
|
export function sourceTypeFromPath(path) {
|
|
17
17
|
return path.endsWith(".cjs") || path.endsWith(".cts") ? "script" : "module";
|
|
18
18
|
}
|
|
19
|
+
|
|
20
|
+
export function locOf(lineStarts, offset) {
|
|
21
|
+
let lo = 0, hi = lineStarts.length;
|
|
22
|
+
while (lo < hi) {
|
|
23
|
+
const mid = (lo + hi) >>> 1;
|
|
24
|
+
if (lineStarts[mid] <= offset) lo = mid + 1;
|
|
25
|
+
else hi = mid;
|
|
26
|
+
}
|
|
27
|
+
return { line: lo, column: offset - lineStarts[lo - 1] };
|
|
28
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yuku-parser",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.14",
|
|
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.
|
|
21
|
-
"@yuku-parser/binding-linux-arm64-gnu": "0.5.
|
|
22
|
-
"@yuku-parser/binding-linux-arm-gnu": "0.5.
|
|
23
|
-
"@yuku-parser/binding-linux-x64-musl": "0.5.
|
|
24
|
-
"@yuku-parser/binding-linux-arm64-musl": "0.5.
|
|
25
|
-
"@yuku-parser/binding-linux-arm-musl": "0.5.
|
|
26
|
-
"@yuku-parser/binding-darwin-x64": "0.5.
|
|
27
|
-
"@yuku-parser/binding-darwin-arm64": "0.5.
|
|
28
|
-
"@yuku-parser/binding-win32-x64": "0.5.
|
|
29
|
-
"@yuku-parser/binding-win32-arm64": "0.5.
|
|
30
|
-
"@yuku-parser/binding-freebsd-x64": "0.5.
|
|
20
|
+
"@yuku-parser/binding-linux-x64-gnu": "0.5.14",
|
|
21
|
+
"@yuku-parser/binding-linux-arm64-gnu": "0.5.14",
|
|
22
|
+
"@yuku-parser/binding-linux-arm-gnu": "0.5.14",
|
|
23
|
+
"@yuku-parser/binding-linux-x64-musl": "0.5.14",
|
|
24
|
+
"@yuku-parser/binding-linux-arm64-musl": "0.5.14",
|
|
25
|
+
"@yuku-parser/binding-linux-arm-musl": "0.5.14",
|
|
26
|
+
"@yuku-parser/binding-darwin-x64": "0.5.14",
|
|
27
|
+
"@yuku-parser/binding-darwin-arm64": "0.5.14",
|
|
28
|
+
"@yuku-parser/binding-win32-x64": "0.5.14",
|
|
29
|
+
"@yuku-parser/binding-win32-arm64": "0.5.14",
|
|
30
|
+
"@yuku-parser/binding-freebsd-x64": "0.5.14"
|
|
31
31
|
},
|
|
32
32
|
"keywords": [
|
|
33
33
|
"acorn",
|