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 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` is classified at parse time so consumers can route comments without rescanning the value:
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
- const NULL = 0xFFFFFFFF;
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 Uint32Array(buffer, 0, aLen >> 2);
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 + 2];
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.10",
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.10",
21
- "@yuku-parser/binding-linux-arm64-gnu": "0.5.10",
22
- "@yuku-parser/binding-linux-arm-gnu": "0.5.10",
23
- "@yuku-parser/binding-linux-x64-musl": "0.5.10",
24
- "@yuku-parser/binding-linux-arm64-musl": "0.5.10",
25
- "@yuku-parser/binding-linux-arm-musl": "0.5.10",
26
- "@yuku-parser/binding-darwin-x64": "0.5.10",
27
- "@yuku-parser/binding-darwin-arm64": "0.5.10",
28
- "@yuku-parser/binding-win32-x64": "0.5.10",
29
- "@yuku-parser/binding-win32-arm64": "0.5.10",
30
- "@yuku-parser/binding-freebsd-x64": "0.5.10"
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",