yuku-codegen 0.5.16 → 0.5.17

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.
Files changed (5) hide show
  1. package/README.md +21 -19
  2. package/encode.js +26 -12
  3. package/index.d.ts +16 -9
  4. package/index.js +24 -17
  5. package/package.json +13 -13
package/README.md CHANGED
@@ -16,15 +16,15 @@ npm install yuku-codegen
16
16
  import { parse } from "yuku-parser";
17
17
  import { print } from "yuku-codegen";
18
18
 
19
- const result = parse("const x = 1 + 2;");
20
- const { code } = print(result);
19
+ const { program } = parse("const x = 1 + 2;");
20
+ const { code } = print(program);
21
21
 
22
22
  console.log(code); // "const x = 1 + 2;"
23
23
  ```
24
24
 
25
25
  ## API
26
26
 
27
- All three entry points take the `ParseResult` returned by [`yuku-parser`](https://www.npmjs.com/package/yuku-parser) and share the same options and result shape.
27
+ All three entry points take the `Program` node off the `ParseResult` returned by [`yuku-parser`](https://www.npmjs.com/package/yuku-parser) and share the same options and result shape.
28
28
 
29
29
  | Function | Behavior |
30
30
  | -------- | ----------------------------------------------------------------------------------------------------- |
@@ -47,12 +47,12 @@ interface CodegenResult {
47
47
  ## Options
48
48
 
49
49
  ```js
50
- const out = print(result, {
50
+ const out = print(program, {
51
51
  format: "pretty",
52
52
  indent: 2,
53
53
  quotes: "double",
54
54
  comments: "some",
55
- sourceMaps: true,
55
+ sourceMaps: { lineStarts },
56
56
  });
57
57
  ```
58
58
 
@@ -62,21 +62,22 @@ const out = print(result, {
62
62
  | `indent` | `number` | `2` | Spaces per indentation level. Applies in pretty mode only. |
63
63
  | `quotes` | `"double" \| "single"` | `"double"` | Quote style for emitted string literals. |
64
64
  | `comments` | `boolean \| "some" \| "line" \| "block"` | `"some"` | Comment passthrough filter. See [Comments](#comments). |
65
- | `sourceMaps` | `boolean \| SourceMapOptions` | `false` | `true` emits a Source Map V3 with default metadata; pass an object for fine-grained control. See [Source maps](#source-maps). |
65
+ | `sourceMaps` | `SourceMapOptions` | `undefined` | Pass an object to emit a Source Map V3. Its `lineStarts` is required, the rest of the metadata is optional. See [Source maps](#source-maps). |
66
66
 
67
67
  ## Source maps
68
68
 
69
- Set `sourceMaps: true` for a Source Map V3 with default metadata, or pass a `SourceMapOptions` object to fill in `file`, `sources`, `sourcesContent`, and `sourceRoot`. Source maps are read off `result.lineStarts`, so the `ParseResult` must come from `yuku-parser`.
69
+ Pass a `SourceMapOptions` object to emit a Source Map V3. Its `lineStarts` field is required: feed it the `lineStarts` array from the parser's `ParseResult` (this is what maps generated positions back to the source). The remaining fields (`file`, `sources`, `sourcesContent`, `sourceRoot`) are optional metadata.
70
70
 
71
71
  ```js
72
72
  import { parse } from "yuku-parser";
73
73
  import { print } from "yuku-codegen";
74
74
 
75
75
  const source = `const greet = (name) => "Hello, " + name;`;
76
- const result = parse(source);
76
+ const { program, lineStarts } = parse(source);
77
77
 
78
- const { code, map } = print(result, {
78
+ const { code, map } = print(program, {
79
79
  sourceMaps: {
80
+ lineStarts,
80
81
  file: "out.js",
81
82
  sourceFileName: "in.js",
82
83
  sourcesContent: source,
@@ -89,9 +90,10 @@ await Bun.write("out.js.map", JSON.stringify(map));
89
90
 
90
91
  ### `SourceMapOptions`
91
92
 
92
- | Field | Type | Description |
93
- | ---------------- | -------- | ----------------------------------------------------------------------------------- |
94
- | `file` | `string` | Output filename, embedded as the map's `file`. |
93
+ | Field | Type | Description |
94
+ | ---------------- | ---------- | --------------------------------------------------------------------------------- |
95
+ | `lineStarts` | `number[]` | **Required.** The parser's `ParseResult.lineStarts`, used to map positions to the source. |
96
+ | `file` | `string` | Output filename, embedded as the map's `file`. |
95
97
  | `sourceFileName` | `string` | Source filename, embedded as the single entry of `sources`. |
96
98
  | `sourceRoot` | `string` | Prefix embedded as `sourceRoot`. |
97
99
  | `sourcesContent` | `string` | When set, embedded as the single entry of the map's `sourcesContent`. Omit to skip. |
@@ -122,8 +124,8 @@ Columns are 0-indexed UTF-16 code units, matching Chrome DevTools and consumer-s
122
124
  import { parse } from "yuku-parser";
123
125
  import { strip } from "yuku-codegen";
124
126
 
125
- const result = parse(`const x: number = 1;`, { lang: "ts" });
126
- console.log(strip(result).code); // "const x = 1;"
127
+ const { program } = parse(`const x: number = 1;`, { lang: "ts" });
128
+ console.log(strip(program).code); // "const x = 1;"
127
129
  ```
128
130
 
129
131
  Type annotations, type aliases, interfaces, and other type-only constructs are dropped. Constructs that have no clean JavaScript equivalent (`enum`, `namespace`, `import = require()`, `export =`) are reported in `errors` and elided. The output is always syntactically valid JavaScript.
@@ -133,8 +135,8 @@ Type annotations, type aliases, interfaces, and other type-only constructs are d
133
135
  Comments live on the AST nodes they were attached to during parsing. To preserve them, parse with `attachComments: true`:
134
136
 
135
137
  ```js
136
- const result = parse(source, { attachComments: true });
137
- print(result).code;
138
+ const { program } = parse(source, { attachComments: true });
139
+ print(program).code;
138
140
  ```
139
141
 
140
142
  The `comments` option then selects which attached comments are emitted. The default is `"some"`, which matches the bundler convention of keeping legal banners, JSDoc, and tree-shaking annotations while dropping plain noise.
@@ -148,8 +150,8 @@ The `comments` option then selects which attached comments are emitted. The defa
148
150
  | `"block"` | Emit `/* ... */` only. |
149
151
 
150
152
  ```js
151
- const result = parse(`// hello\nconst x = 1;`, { attachComments: true });
152
- print(result, { comments: true }).code;
153
+ const { program } = parse(`// hello\nconst x = 1;`, { attachComments: true });
154
+ print(program, { comments: true }).code;
153
155
  // "// hello\nconst x = 1;"
154
156
  ```
155
157
 
@@ -171,7 +173,7 @@ Combine with `format: "compact"` for full minification:
171
173
  ```js
172
174
  import { minify } from "yuku-codegen";
173
175
 
174
- const { code } = minify(result, { format: "compact" });
176
+ const { code } = minify(program, { format: "compact" });
175
177
  ```
176
178
 
177
179
  ## License
package/encode.js CHANGED
@@ -35,18 +35,18 @@ const ACCESSIBILITY_INV = (v) =>
35
35
  : 3;
36
36
  const TS_MAPPED_OPTIONAL_INV = (v) => v === true ? 1 : v === "+" ? 2 : v === "-" ? 3 : 0;
37
37
  const TS_MAPPED_READONLY_INV = (v) => v === true ? 1 : v === "+" ? 2 : v === "-" ? 3 : 0;
38
- function encode(result) {
39
- const root = result.program;
40
- const lineStarts = result.lineStarts;
41
- let nodeCap = 512;
38
+ function encode(program, lineStarts) {
39
+ const root = program;
40
+ const sizeHint = root.end || 0;
41
+ let nodeCap = Math.max(512, (sizeHint / 7) | 0);
42
42
  let nodeAB = new ArrayBuffer(nodeCap * NODE_SIZE);
43
43
  let nU8 = new Uint8Array(nodeAB);
44
44
  let nU32 = new Uint32Array(nodeAB);
45
45
  let nodeCount = 0;
46
- let extraCap = 512;
46
+ let extraCap = Math.max(512, (sizeHint / 20) | 0);
47
47
  let extras = new Uint32Array(extraCap);
48
48
  let extraCount = 0;
49
- let poolCap = 4096;
49
+ let poolCap = Math.max(4096, (sizeHint / 8) | 0);
50
50
  let pool = new Uint8Array(poolCap);
51
51
  let poolLen = 0;
52
52
  const strMap = new Map();
@@ -119,15 +119,29 @@ function encode(result) {
119
119
  if (s == null || s === "") return { start: 0, end: 0 };
120
120
  const hit = strMap.get(s);
121
121
  if (hit !== undefined) return hit;
122
- const bytes = _enc.encode(s);
123
- while (poolLen + bytes.length > poolCap) {
124
- poolCap *= 2;
122
+ const n = s.length;
123
+ if (poolLen + n * 3 > poolCap) {
124
+ do { poolCap *= 2; } while (poolLen + n * 3 > poolCap);
125
125
  const np = new Uint8Array(poolCap);
126
126
  np.set(pool); pool = np;
127
127
  }
128
- pool.set(bytes, poolLen);
129
- const r = { start: poolLen, end: poolLen + bytes.length };
130
- poolLen += bytes.length;
128
+ const start = poolLen;
129
+ let p = start;
130
+ let ascii = true;
131
+ for (let i = 0; i < n; i++) {
132
+ const c = s.charCodeAt(i);
133
+ if (c < 0x80) { pool[p++] = c; } else { ascii = false; break; }
134
+ }
135
+ let r;
136
+ if (ascii) {
137
+ r = { start, end: p };
138
+ poolLen = p;
139
+ } else {
140
+ const bytes = _enc.encode(s);
141
+ pool.set(bytes, start);
142
+ r = { start, end: start + bytes.length };
143
+ poolLen = start + bytes.length;
144
+ }
131
145
  strMap.set(s, r);
132
146
  return r;
133
147
  }
package/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Comment, ParseResult } from "yuku-parser";
1
+ import type { Comment, Program } from "yuku-parser";
2
2
 
3
3
  /** Whitespace mode for the generated output. */
4
4
  export type Format = "pretty" | "compact";
@@ -21,6 +21,12 @@ export type { Comment };
21
21
 
22
22
  /** Source-map configuration. Pass to `CodegenOptions.sourceMaps` to enable. */
23
23
  export interface SourceMapOptions {
24
+ /**
25
+ * UTF-16 line-start offsets, taken straight from the parser's
26
+ * `ParseResult.lineStarts`. Required: this is what maps generated positions
27
+ * back to the original source.
28
+ */
29
+ lineStarts: number[];
24
30
  /** Output filename, embedded as the map's `file`. */
25
31
  file?: string;
26
32
  /** Source filename, embedded as the single entry of `sources`. */
@@ -43,12 +49,13 @@ export interface CodegenOptions {
43
49
  /** @default "double" */
44
50
  quotes?: Quotes;
45
51
  /**
46
- * Enable Source Map V3 output. `true` uses default metadata; pass a
47
- * {@link SourceMapOptions} object to fill in `file`, `sources`,
48
- * `sourcesContent`, and `sourceRoot`.
49
- * @default false
52
+ * Enable Source Map V3 output. Pass a {@link SourceMapOptions} object. Its
53
+ * `lineStarts` (from the parser) is required. The rest of the metadata
54
+ * (`file`, `sources`, `sourcesContent`, `sourceRoot`) is optional. Omit to
55
+ * disable.
56
+ * @default undefined
50
57
  */
51
- sourceMaps?: boolean | SourceMapOptions;
58
+ sourceMaps?: SourceMapOptions;
52
59
  /**
53
60
  * Comment passthrough filter. Defaults to `"some"`, which preserves
54
61
  * legal headers, JSDoc, and tree-shaking annotations.
@@ -88,13 +95,13 @@ export interface CodegenResult {
88
95
  }
89
96
 
90
97
  /** Renders the AST verbatim, preserving TypeScript syntax. */
91
- export function print(result: ParseResult, options?: CodegenOptions): CodegenResult;
98
+ export function print(program: Program, options?: CodegenOptions): CodegenResult;
92
99
 
93
100
  /** Renders the AST as JavaScript, dropping TypeScript-specific syntax. */
94
- export function strip(result: ParseResult, options?: CodegenOptions): CodegenResult;
101
+ export function strip(program: Program, options?: CodegenOptions): CodegenResult;
95
102
 
96
103
  /**
97
104
  * Renders the AST with size-reducing rewrites. Combine with
98
105
  * `format: "compact"` for full minification.
99
106
  */
100
- export function minify(result: ParseResult, options?: CodegenOptions): CodegenResult;
107
+ export function minify(program: Program, options?: CodegenOptions): CodegenResult;
package/index.js CHANGED
@@ -7,37 +7,44 @@ function normalizeOptions(options) {
7
7
  const c = next.comments;
8
8
  if (c === true) next.comments = "all";
9
9
  else if (c === false) next.comments = "none";
10
+ // `lineStarts` is consumed by the encoder, not the native binding, so strip
11
+ // it from the source-map options. Only V3 metadata reaches the binding.
10
12
  const s = next.sourceMaps;
11
- if (s === true) next.sourceMaps = {};
12
- else if (s === false) next.sourceMaps = undefined;
13
+ if (s) {
14
+ const { lineStarts, ...meta } = s;
15
+ next.sourceMaps = meta;
16
+ } else {
17
+ next.sourceMaps = undefined;
18
+ }
13
19
  return next;
14
20
  }
15
21
 
16
- function prepare(result, options) {
17
- if (!result || !result.program || !result.program.type) {
22
+ function run(method, program, options) {
23
+ if (!program || !program.type) {
18
24
  throw new TypeError(
19
- "Expected a `ParseResult` from yuku-parser, got " +
20
- (result === null ? "null" : typeof result),
25
+ "Expected a `Program` node from yuku-parser, got " +
26
+ (program === null ? "null" : typeof program),
21
27
  );
22
28
  }
23
- if (options?.sourceMaps && !result.lineStarts) {
29
+ const sourceMaps = options?.sourceMaps;
30
+ const lineStarts = sourceMaps ? sourceMaps.lineStarts : null;
31
+ if (sourceMaps && !Array.isArray(lineStarts)) {
24
32
  throw new Error(
25
- "Source maps require a `ParseResult` with `lineStarts`. " +
26
- "If you built or transformed this result outside of yuku-parser, " +
27
- "either drop `sourceMaps` or attach a `lineStarts` array.",
33
+ "`sourceMaps` requires a `lineStarts` array. Pass the parser's " +
34
+ "`ParseResult.lineStarts`, e.g. `{ sourceMaps: { lineStarts } }`.",
28
35
  );
29
36
  }
30
- return encode(result);
37
+ return binding[method](encode(program, lineStarts), normalizeOptions(options));
31
38
  }
32
39
 
33
- export function print(result, options) {
34
- return binding.print(prepare(result, options), normalizeOptions(options));
40
+ export function print(program, options) {
41
+ return run("print", program, options);
35
42
  }
36
43
 
37
- export function strip(result, options) {
38
- return binding.strip(prepare(result, options), normalizeOptions(options));
44
+ export function strip(program, options) {
45
+ return run("strip", program, options);
39
46
  }
40
47
 
41
- export function minify(result, options) {
42
- return binding.minify(prepare(result, options), normalizeOptions(options));
48
+ export function minify(program, options) {
49
+ return run("minify", program, options);
43
50
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yuku-codegen",
3
- "version": "0.5.16",
3
+ "version": "0.5.17",
4
4
  "description": "High-performance JavaScript/TypeScript code generator written in Zig",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -17,17 +17,17 @@
17
17
  "encode.js"
18
18
  ],
19
19
  "optionalDependencies": {
20
- "@yuku-codegen/binding-linux-x64-gnu": "0.5.16",
21
- "@yuku-codegen/binding-linux-arm64-gnu": "0.5.16",
22
- "@yuku-codegen/binding-linux-arm-gnu": "0.5.16",
23
- "@yuku-codegen/binding-linux-x64-musl": "0.5.16",
24
- "@yuku-codegen/binding-linux-arm64-musl": "0.5.16",
25
- "@yuku-codegen/binding-linux-arm-musl": "0.5.16",
26
- "@yuku-codegen/binding-darwin-x64": "0.5.16",
27
- "@yuku-codegen/binding-darwin-arm64": "0.5.16",
28
- "@yuku-codegen/binding-win32-x64": "0.5.16",
29
- "@yuku-codegen/binding-win32-arm64": "0.5.16",
30
- "@yuku-codegen/binding-freebsd-x64": "0.5.16"
20
+ "@yuku-codegen/binding-linux-x64-gnu": "0.5.17",
21
+ "@yuku-codegen/binding-linux-arm64-gnu": "0.5.17",
22
+ "@yuku-codegen/binding-linux-arm-gnu": "0.5.17",
23
+ "@yuku-codegen/binding-linux-x64-musl": "0.5.17",
24
+ "@yuku-codegen/binding-linux-arm64-musl": "0.5.17",
25
+ "@yuku-codegen/binding-linux-arm-musl": "0.5.17",
26
+ "@yuku-codegen/binding-darwin-x64": "0.5.17",
27
+ "@yuku-codegen/binding-darwin-arm64": "0.5.17",
28
+ "@yuku-codegen/binding-win32-x64": "0.5.17",
29
+ "@yuku-codegen/binding-win32-arm64": "0.5.17",
30
+ "@yuku-codegen/binding-freebsd-x64": "0.5.17"
31
31
  },
32
32
  "keywords": [
33
33
  "ast",
@@ -39,7 +39,7 @@
39
39
  "printer",
40
40
  "typescript"
41
41
  ],
42
- "dependencies": {
42
+ "peerDependencies": {
43
43
  "yuku-parser": "0.5.14"
44
44
  }
45
45
  }