as-test 1.1.5 → 1.1.7

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 (43) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/README.md +4 -9
  3. package/assembly/index.ts +10 -15
  4. package/assembly/src/expectation.ts +11 -11
  5. package/assembly/src/fuzz.ts +11 -7
  6. package/assembly/src/log.ts +2 -2
  7. package/assembly/src/suite.ts +5 -5
  8. package/assembly/src/tests.ts +8 -8
  9. package/assembly/util/wipc.ts +5 -1
  10. package/bin/build-worker-pool.js +146 -142
  11. package/bin/build-worker.js +37 -34
  12. package/bin/commands/build-core.js +577 -465
  13. package/bin/commands/build.js +49 -29
  14. package/bin/commands/clean-core.js +120 -113
  15. package/bin/commands/clean.js +14 -8
  16. package/bin/commands/doctor-core.js +288 -289
  17. package/bin/commands/doctor.js +1 -1
  18. package/bin/commands/fuzz-core.js +467 -414
  19. package/bin/commands/fuzz.js +27 -10
  20. package/bin/commands/init-core.js +905 -794
  21. package/bin/commands/init.js +2 -2
  22. package/bin/commands/run-core.js +2675 -2344
  23. package/bin/commands/run.js +43 -25
  24. package/bin/commands/test.js +56 -32
  25. package/bin/commands/web-runner-source.js +1 -1
  26. package/bin/commands/web-session.js +516 -525
  27. package/bin/coverage-points.js +363 -341
  28. package/bin/crash-store.js +56 -66
  29. package/bin/index.js +4092 -3150
  30. package/bin/reporters/default.js +1090 -890
  31. package/bin/reporters/tap.js +319 -325
  32. package/bin/types.js +67 -67
  33. package/bin/util.js +1290 -1239
  34. package/bin/wipc.js +70 -73
  35. package/lib/build/index.d.ts +3 -1
  36. package/lib/build/index.js +1039 -1034
  37. package/lib/build/web-runner/client.js +1 -1
  38. package/lib/build/web-runner/html.js +1 -1
  39. package/lib/build/web-runner/worker.js +1 -1
  40. package/package.json +6 -3
  41. package/transform/lib/coverage.js +4 -4
  42. package/transform/lib/log.js +9 -5
  43. package/assembly/util/json.ts +0 -112
package/CHANGELOG.md CHANGED
@@ -1,11 +1,26 @@
1
1
  # Change Log
2
2
 
3
- ## 2026-05-15 - v1.1.5
3
+ ## 2026-05-18 - v1.1.7
4
+
5
+ ### Modes & CLI
6
+
7
+ - fix: when a config declares named modes, the implicit base config no longer runs alongside them; only modes with `default !== false` are selected. Configs without any declared modes still fall back to the base mode.
8
+ - feat: add `--watch` to `ast test` to re-run on source or spec changes (150ms debounce, clear-screen, `Ctrl+C` to stop). Watches all base directories derived from `input` patterns plus `assembly/` and the resolved config file; ignores `node_modules`, `.git`, and the configured `outDir`.
9
+
10
+ ### Runtime
11
+
12
+ - chore: replace the homegrown JSON serialization helpers (`quote`, `rawOrNull`, `stringifyValue`, `escape`, `unicodeEscape`, `stringifyArray`) with `json-as` across the AssemblyScript runtime and transform, and delete `assembly/util/json.ts`. The `LogTransform` now injects `import { JSON } from "json-as/assembly"` per instrumented source and inlines `JSON.stringify<T>()` into the log helper.
13
+ - chore: promote `json-as` to a required peer dependency (`>=1.0.0`). npm 7+ installs it automatically; pnpm and yarn users must add `json-as` to their own `devDependencies` alongside `as-test`.
14
+ - chore: remove the unused `__as_test_log_default` and `__as_test_json_value` exports.
15
+
16
+ ## 2026-05-14 - v1.1.6
4
17
 
5
18
  - fix: coverage `mode` and `dependencies` filtering now correctly handles AssemblyScript-normalized `~lib/<pkg>/...` paths, which are the actual runtime paths emitted for `node_modules` imports.
6
19
  - fix: `ENTRY_FILE` injected by the transform now uses the full relative path instead of the basename, preventing snapshot key collisions between specs with the same filename in different directories; snapshot lookup normalizes the file prefix to maintain backward compatibility with existing `.snap` files.
7
20
  - fix: transform visitor and coverage instrumentation now resolve `NodeKind` values at runtime instead of relying on compile-time const enum inlining, so they remain correct across AssemblyScript versions.
8
21
  - fix: add a no-op `TupleType` case to the transform visitor so files using tuple types no longer throw during instrumentation.
22
+ - fix: coverage transform no longer wraps `return this` in constructors, preventing AS231 ("A class with a constructor explicitly returning something else than 'this' must be '@final'").
23
+ - fix: coverage transform preserves expression-body arrows instead of converting them to block bodies, preventing TS1140 ("Type argument expected") on typed arrow parameters such as `[1,2,3].map((x: i32) => x + 1)`.
9
24
 
10
25
  ## 2026-05-14 - v1.1.4
11
26
 
package/README.md CHANGED
@@ -2,14 +2,6 @@
2
2
  ╠═╣ ╚═╗ ══ ║ ╠═ ╚═╗ ║
3
3
  ╩ ╩ ╚═╝ ╩ ╚═╝ ╚═╝ ╩ </pre></h1>
4
4
 
5
- > **Upgrading to 1.1.0**
6
- >
7
- > See [CHANGELOG.md](./CHANGELOG.md) for upgrade notes. In most projects, refreshing generated runners is enough:
8
- >
9
- > ```bash
10
- > rm -rf .as-test/runners && npx as-test init
11
- > ```
12
-
13
5
  <details>
14
6
  <summary>Table of Contents</summary>
15
7
 
@@ -47,9 +39,12 @@ That gives you a basic config file, a sample test, and optionally a sample fuzze
47
39
  If you already have a project and just want the package:
48
40
 
49
41
  ```bash
50
- npm install --save-dev as-test
42
+ npm install --save-dev as-test json-as
51
43
  ```
52
44
 
45
+ `json-as` is a required peer dependency (used for value serialization in
46
+ assertions, snapshots, and `log()`)
47
+
53
48
  ## Docs
54
49
 
55
50
  Full documentation lives at:
package/assembly/index.ts CHANGED
@@ -14,8 +14,8 @@ import {
14
14
  sendFileStart,
15
15
  sendReport,
16
16
  } from "./util/wipc";
17
- import { quote } from "./util/json";
18
- import { bold, formatValue, green, red } from "./util/format";
17
+ import { JSON } from "json-as/assembly";
18
+ import { bold, green, red } from "./util/format";
19
19
  import {
20
20
  createFuzzer,
21
21
  FuzzerBase,
@@ -37,7 +37,6 @@ export {
37
37
  StringOptions,
38
38
  } from "./src/fuzz";
39
39
  export { __as_test_deep_equal } from "./src/expectation";
40
- export { __as_test_json_value } from "./util/json";
41
40
 
42
41
  let entrySuites: Suite[] = [];
43
42
  let entryFuzzers: FuzzerBase[] = [];
@@ -241,11 +240,7 @@ export function xexpect<T>(
241
240
  */
242
241
  export function log<T>(data: T): void {
243
242
  if (!__as_test_log_is_enabled()) return;
244
- __as_test_log_serialized(__as_test_log_default<T>(data));
245
- }
246
-
247
- export function __as_test_log_default<T>(data: T): string {
248
- return formatValue(data);
243
+ __as_test_log_serialized(JSON.stringify<T>(data));
249
244
  }
250
245
 
251
246
  export function __as_test_log_is_enabled(): bool {
@@ -556,23 +551,23 @@ class CoveragePointReport {
556
551
  serialize(): string {
557
552
  return (
558
553
  '{"hash":' +
559
- quote(this.hash) +
554
+ JSON.stringify<string>(this.hash) +
560
555
  ',"file":' +
561
- quote(this.file) +
556
+ JSON.stringify<string>(this.file) +
562
557
  ',"line":' +
563
558
  this.line.toString() +
564
559
  ',"column":' +
565
560
  this.column.toString() +
566
561
  ',"type":' +
567
- quote(this.type) +
562
+ JSON.stringify<string>(this.type) +
568
563
  ',"executed":' +
569
564
  (this.executed ? "true" : "false") +
570
565
  ',"parentHash":' +
571
- quote(this.parentHash) +
566
+ JSON.stringify<string>(this.parentHash) +
572
567
  ',"scopeKind":' +
573
- quote(this.scopeKind) +
568
+ JSON.stringify<string>(this.scopeKind) +
574
569
  ',"scopeName":' +
575
- quote(this.scopeName) +
570
+ JSON.stringify<string>(this.scopeName) +
576
571
  ',"depth":' +
577
572
  this.depth.toString() +
578
573
  "}"
@@ -698,7 +693,7 @@ export class Result {
698
693
  serialize(): string {
699
694
  return (
700
695
  '{"name":' +
701
- quote(this.name) +
696
+ JSON.stringify<string>(this.name) +
702
697
  ',"arg1":' +
703
698
  this.arg1.toString() +
704
699
  ',"arg2":' +
@@ -1,6 +1,6 @@
1
1
  import { visualize } from "../util/helpers";
2
2
  import { Tests } from "./tests";
3
- import { quote, stringifyValue } from "../util/json";
3
+ import { JSON } from "json-as/assembly";
4
4
  import { namedSnapshotKey, nextUnnamedSnapshotKey } from "..";
5
5
  import {
6
6
  sendAssertionFailure,
@@ -425,8 +425,8 @@ export class Expectation<T> extends Tests {
425
425
  this._resolve(
426
426
  passed,
427
427
  "toContain",
428
- stringifyValue<T>(this._left),
429
- stringifyValue<valueof<T>>(value),
428
+ JSON.stringify<T>(this._left),
429
+ JSON.stringify<valueof<T>>(value),
430
430
  message,
431
431
  );
432
432
  return;
@@ -451,7 +451,7 @@ export class Expectation<T> extends Tests {
451
451
  ? namedSnapshotKey(this._snapshotKey, name)
452
452
  : nextUnnamedSnapshotKey(this._snapshotKey);
453
453
 
454
- const actual = stringifyValue<T>(this._left);
454
+ const actual = JSON.stringify<T>(this._left);
455
455
  const res = snapshotAssert(key, actual);
456
456
  this._resolve(res.ok, "toMatchSnapshot", actual, res.expected, message);
457
457
  }
@@ -491,8 +491,8 @@ export class Expectation<T> extends Tests {
491
491
  this._resolve(
492
492
  passed,
493
493
  "toBe",
494
- stringifyValue<T>(this._left),
495
- stringifyValue<T>(equals),
494
+ JSON.stringify<T>(this._left),
495
+ JSON.stringify<T>(equals),
496
496
  message,
497
497
  );
498
498
  }
@@ -505,8 +505,8 @@ export class Expectation<T> extends Tests {
505
505
  this._resolve(
506
506
  passed,
507
507
  "toEqual",
508
- stringifyValue<T>(this._left),
509
- stringifyValue<T>(equals),
508
+ JSON.stringify<T>(this._left),
509
+ JSON.stringify<T>(equals),
510
510
  message,
511
511
  );
512
512
  }
@@ -519,8 +519,8 @@ export class Expectation<T> extends Tests {
519
519
  this._resolve(
520
520
  passed,
521
521
  "toStrictEqual",
522
- stringifyValue<T>(this._left),
523
- stringifyValue<T>(equals),
522
+ JSON.stringify<T>(this._left),
523
+ JSON.stringify<T>(equals),
524
524
  message,
525
525
  );
526
526
  }
@@ -606,5 +606,5 @@ function isTruthy<T>(value: T): bool {
606
606
  }
607
607
 
608
608
  function q(value: string): string {
609
- return quote(value);
609
+ return JSON.stringify<string>(value);
610
610
  }
@@ -1,4 +1,8 @@
1
- import { quote, rawOrNull, stringifyValue } from "../util/json";
1
+ import { JSON } from "json-as/assembly";
2
+
3
+ function quote(s: string): string {
4
+ return JSON.stringify<string>(s);
5
+ }
2
6
 
3
7
  export class StringOptions {
4
8
  charset: string = "ascii";
@@ -656,7 +660,7 @@ export class FuzzFailure {
656
660
  ',"seed":' +
657
661
  this.seed.toString() +
658
662
  ',"input":' +
659
- rawOrNull(this.input) +
663
+ (this.input.length ? this.input : "null") +
660
664
  "}"
661
665
  );
662
666
  }
@@ -877,7 +881,7 @@ export class Fuzzer1<A, R> extends FuzzerBase {
877
881
  );
878
882
  } else {
879
883
  this.generator(seed, (a: A): R => {
880
- __as_test_fuzz_input = "[" + stringifyValue<A>(a) + "]";
884
+ __as_test_fuzz_input = "[" + JSON.stringify<A>(a) + "]";
881
885
  return changetype<(a: A) => R>(__fuzz_run1)(a);
882
886
  });
883
887
  }
@@ -948,7 +952,7 @@ export class Fuzzer2<A, B, R> extends FuzzerBase {
948
952
  } else {
949
953
  this.generator(seed, (a: A, b: B): R => {
950
954
  __as_test_fuzz_input =
951
- "[" + stringifyValue<A>(a) + "," + stringifyValue<B>(b) + "]";
955
+ "[" + JSON.stringify<A>(a) + "," + JSON.stringify<B>(b) + "]";
952
956
  return changetype<(a: A, b: B) => R>(__fuzz_run2)(a, b);
953
957
  });
954
958
  }
@@ -1022,11 +1026,11 @@ export class Fuzzer3<A, B, C, R> extends FuzzerBase {
1022
1026
  )(seed, (a: A, b: B, c: C): R => {
1023
1027
  __as_test_fuzz_input =
1024
1028
  "[" +
1025
- stringifyValue<A>(a) +
1029
+ JSON.stringify<A>(a) +
1026
1030
  "," +
1027
- stringifyValue<B>(b) +
1031
+ JSON.stringify<B>(b) +
1028
1032
  "," +
1029
- stringifyValue<C>(c) +
1033
+ JSON.stringify<C>(c) +
1030
1034
  "]";
1031
1035
  return changetype<(a: A, b: B, c: C) => R>(__fuzz_run3)(a, b, c);
1032
1036
  });
@@ -1,4 +1,4 @@
1
- import { quote } from "../util/json";
1
+ import { JSON } from "json-as/assembly";
2
2
 
3
3
  import { sendLog } from "../util/wipc";
4
4
 
@@ -21,7 +21,7 @@ export class Log {
21
21
  ',"depth":' +
22
22
  this.depth.toString() +
23
23
  ',"text":' +
24
- quote(this.text) +
24
+ JSON.stringify<string>(this.text) +
25
25
  "}"
26
26
  );
27
27
  }
@@ -4,7 +4,7 @@ import { Tests } from "./tests";
4
4
  import { Log } from "./log";
5
5
  import { after_each_callback, before_each_callback } from "..";
6
6
  import { sendSuiteEnd, sendSuiteStart } from "../util/wipc";
7
- import { quote } from "../util/json";
7
+ import { JSON } from "json-as/assembly";
8
8
 
9
9
  export class Suite {
10
10
  public file: string = "unknown";
@@ -175,17 +175,17 @@ export class Suite {
175
175
  serialize(): string {
176
176
  let out = "{";
177
177
  if (this.depth <= 0) {
178
- out += '"file":' + quote(this.file) + ",";
178
+ out += '"file":' + JSON.stringify<string>(this.file) + ",";
179
179
  }
180
180
  out += '"order":' + this.order.toString();
181
181
  out += ',"time":' + this.time.serialize();
182
- out += ',"description":' + quote(this.description);
182
+ out += ',"description":' + JSON.stringify<string>(this.description);
183
183
  out += ',"depth":' + this.depth.toString();
184
184
  out += ',"suites":' + serializeSuites(this.suites);
185
185
  out += ',"tests":' + serializeTests(this.tests);
186
186
  out += ',"logs":' + serializeLogs(this.logs);
187
- out += ',"kind":' + quote(this.kind);
188
- out += ',"verdict":' + quote(this.verdict);
187
+ out += ',"kind":' + JSON.stringify<string>(this.kind);
188
+ out += ',"verdict":' + JSON.stringify<string>(this.verdict);
189
189
  out += "}";
190
190
  return out;
191
191
  }
@@ -1,4 +1,4 @@
1
- import { quote, rawOrNull } from "../util/json";
1
+ import { JSON } from "json-as/assembly";
2
2
 
3
3
  export class Tests {
4
4
  public order: i32 = 0;
@@ -15,19 +15,19 @@ export class Tests {
15
15
  '{"order":' +
16
16
  this.order.toString() +
17
17
  ',"type":' +
18
- quote(this.type) +
18
+ JSON.stringify<string>(this.type) +
19
19
  ',"verdict":' +
20
- quote(this.verdict) +
20
+ JSON.stringify<string>(this.verdict) +
21
21
  ',"left":' +
22
- rawOrNull(this.left) +
22
+ (this.left.length ? this.left : "null") +
23
23
  ',"right":' +
24
- rawOrNull(this.right) +
24
+ (this.right.length ? this.right : "null") +
25
25
  ',"instr":' +
26
- quote(this.instr) +
26
+ JSON.stringify<string>(this.instr) +
27
27
  ',"message":' +
28
- quote(this.message) +
28
+ JSON.stringify<string>(this.message) +
29
29
  ',"location":' +
30
- quote(this.location) +
30
+ JSON.stringify<string>(this.location) +
31
31
  "}"
32
32
  );
33
33
  }
@@ -1,4 +1,8 @@
1
- import { quote as q } from "./json";
1
+ import { JSON } from "json-as/assembly";
2
+
3
+ function q(s: string): string {
4
+ return JSON.stringify<string>(s);
5
+ }
2
6
 
3
7
  // @ts-ignore
4
8
  @external("env", "process.stdout.write")
@@ -1,155 +1,159 @@
1
1
  import { spawn } from "child_process";
2
2
  import { fileURLToPath } from "url";
3
3
  export class BuildWorkerPool {
4
- constructor(size) {
5
- this.pools = new Map();
6
- this.nextId = 1;
7
- this.closed = false;
8
- this.size = Math.max(1, size);
4
+ constructor(size) {
5
+ this.pools = new Map();
6
+ this.nextId = 1;
7
+ this.closed = false;
8
+ this.size = Math.max(1, size);
9
+ }
10
+ buildFileMode(args) {
11
+ if (this.closed) {
12
+ return Promise.reject(new Error("build worker pool is closed"));
9
13
  }
10
- buildFileMode(args) {
11
- if (this.closed) {
12
- return Promise.reject(new Error("build worker pool is closed"));
13
- }
14
- const featureToggles = args.featureToggles ?? {};
15
- const overrides = args.overrides ?? {};
16
- const signature = buildSignature(args.modeName, featureToggles, overrides);
17
- const pool = this.getPool(signature);
18
- return new Promise((resolve, reject) => {
19
- pool.queue.push({
20
- id: this.nextId++,
21
- configPath: args.configPath,
22
- file: args.file,
23
- modeName: args.modeName,
24
- buildCommand: args.buildCommand,
25
- featureToggles,
26
- overrides,
27
- resolve,
28
- reject,
29
- });
30
- this.pump(pool);
31
- });
32
- }
33
- async close() {
34
- this.closed = true;
35
- const waits = [];
36
- for (const pool of this.pools.values()) {
37
- while (pool.queue.length) {
38
- const task = pool.queue.shift();
39
- task.reject(new Error("build worker pool closed"));
40
- }
41
- for (const worker of pool.workers) {
42
- waits.push(new Promise((resolve) => {
43
- if (worker.child.exitCode != null || worker.child.killed) {
44
- resolve();
45
- return;
46
- }
47
- worker.child.once("exit", () => resolve());
48
- worker.child.kill();
49
- }));
50
- }
51
- }
52
- await Promise.all(waits);
53
- }
54
- getPool(signature) {
55
- let pool = this.pools.get(signature);
56
- if (pool)
57
- return pool;
58
- pool = {
59
- workers: Array.from({ length: this.size }, () => this.spawnWorker(signature)),
60
- queue: [],
61
- };
62
- this.pools.set(signature, pool);
63
- return pool;
64
- }
65
- spawnWorker(signature) {
66
- const workerPath = fileURLToPath(new URL("./build-worker.js", import.meta.url));
67
- const child = spawn(process.execPath, [workerPath], {
68
- stdio: ["ignore", "ignore", "ignore", "ipc"],
69
- });
70
- const worker = {
71
- child,
72
- busy: false,
73
- task: null,
74
- };
75
- child.on("message", (message) => {
76
- this.onMessage(signature, worker, message);
77
- });
78
- child.on("exit", () => {
79
- const pool = this.pools.get(signature);
80
- const failedTask = worker.task;
81
- worker.busy = false;
82
- worker.task = null;
83
- if (failedTask) {
84
- const modeLabel = failedTask.modeName ?? "default";
85
- const fileLabel = failedTask.file;
86
- const commandText = failedTask.buildCommand?.trim().length
87
- ? `\nBuild command: ${failedTask.buildCommand}`
88
- : "";
89
- failedTask.reject(new Error(`build worker exited unexpectedly while building ${fileLabel} in mode ${modeLabel}${commandText}`));
90
- }
91
- if (!pool || this.closed)
92
- return;
93
- const index = pool.workers.indexOf(worker);
94
- if (index >= 0) {
95
- pool.workers[index] = this.spawnWorker(signature);
14
+ const featureToggles = args.featureToggles ?? {};
15
+ const overrides = args.overrides ?? {};
16
+ const signature = buildSignature(args.modeName, featureToggles, overrides);
17
+ const pool = this.getPool(signature);
18
+ return new Promise((resolve, reject) => {
19
+ pool.queue.push({
20
+ id: this.nextId++,
21
+ configPath: args.configPath,
22
+ file: args.file,
23
+ modeName: args.modeName,
24
+ buildCommand: args.buildCommand,
25
+ featureToggles,
26
+ overrides,
27
+ resolve,
28
+ reject,
29
+ });
30
+ this.pump(pool);
31
+ });
32
+ }
33
+ async close() {
34
+ this.closed = true;
35
+ const waits = [];
36
+ for (const pool of this.pools.values()) {
37
+ while (pool.queue.length) {
38
+ const task = pool.queue.shift();
39
+ task.reject(new Error("build worker pool closed"));
40
+ }
41
+ for (const worker of pool.workers) {
42
+ waits.push(
43
+ new Promise((resolve) => {
44
+ if (worker.child.exitCode != null || worker.child.killed) {
45
+ resolve();
46
+ return;
96
47
  }
97
- this.pump(pool);
98
- });
99
- return worker;
48
+ worker.child.once("exit", () => resolve());
49
+ worker.child.kill();
50
+ }),
51
+ );
52
+ }
100
53
  }
101
- onMessage(signature, worker, message) {
102
- const pool = this.pools.get(signature);
103
- const task = worker.task;
104
- if (!pool || !task || task.id !== message.id)
105
- return;
106
- worker.busy = false;
107
- worker.task = null;
108
- if (message.type == "done") {
109
- task.resolve();
110
- }
111
- else {
112
- task.reject(deserializeError(message.error));
113
- }
114
- this.pump(pool);
54
+ await Promise.all(waits);
55
+ }
56
+ getPool(signature) {
57
+ let pool = this.pools.get(signature);
58
+ if (pool) return pool;
59
+ pool = {
60
+ workers: Array.from({ length: this.size }, () =>
61
+ this.spawnWorker(signature),
62
+ ),
63
+ queue: [],
64
+ };
65
+ this.pools.set(signature, pool);
66
+ return pool;
67
+ }
68
+ spawnWorker(signature) {
69
+ const workerPath = fileURLToPath(
70
+ new URL("./build-worker.js", import.meta.url),
71
+ );
72
+ const child = spawn(process.execPath, [workerPath], {
73
+ stdio: ["ignore", "ignore", "ignore", "ipc"],
74
+ });
75
+ const worker = {
76
+ child,
77
+ busy: false,
78
+ task: null,
79
+ };
80
+ child.on("message", (message) => {
81
+ this.onMessage(signature, worker, message);
82
+ });
83
+ child.on("exit", () => {
84
+ const pool = this.pools.get(signature);
85
+ const failedTask = worker.task;
86
+ worker.busy = false;
87
+ worker.task = null;
88
+ if (failedTask) {
89
+ const modeLabel = failedTask.modeName ?? "default";
90
+ const fileLabel = failedTask.file;
91
+ const commandText = failedTask.buildCommand?.trim().length
92
+ ? `\nBuild command: ${failedTask.buildCommand}`
93
+ : "";
94
+ failedTask.reject(
95
+ new Error(
96
+ `build worker exited unexpectedly while building ${fileLabel} in mode ${modeLabel}${commandText}`,
97
+ ),
98
+ );
99
+ }
100
+ if (!pool || this.closed) return;
101
+ const index = pool.workers.indexOf(worker);
102
+ if (index >= 0) {
103
+ pool.workers[index] = this.spawnWorker(signature);
104
+ }
105
+ this.pump(pool);
106
+ });
107
+ return worker;
108
+ }
109
+ onMessage(signature, worker, message) {
110
+ const pool = this.pools.get(signature);
111
+ const task = worker.task;
112
+ if (!pool || !task || task.id !== message.id) return;
113
+ worker.busy = false;
114
+ worker.task = null;
115
+ if (message.type == "done") {
116
+ task.resolve();
117
+ } else {
118
+ task.reject(deserializeError(message.error));
115
119
  }
116
- pump(pool) {
117
- for (const worker of pool.workers) {
118
- if (worker.busy)
119
- continue;
120
- const task = pool.queue.shift();
121
- if (!task)
122
- return;
123
- worker.busy = true;
124
- worker.task = task;
125
- worker.child.send({
126
- type: "build-file",
127
- id: task.id,
128
- configPath: task.configPath,
129
- file: task.file,
130
- modeName: task.modeName,
131
- featureToggles: task.featureToggles,
132
- overrides: task.overrides,
133
- });
134
- }
120
+ this.pump(pool);
121
+ }
122
+ pump(pool) {
123
+ for (const worker of pool.workers) {
124
+ if (worker.busy) continue;
125
+ const task = pool.queue.shift();
126
+ if (!task) return;
127
+ worker.busy = true;
128
+ worker.task = task;
129
+ worker.child.send({
130
+ type: "build-file",
131
+ id: task.id,
132
+ configPath: task.configPath,
133
+ file: task.file,
134
+ modeName: task.modeName,
135
+ featureToggles: task.featureToggles,
136
+ overrides: task.overrides,
137
+ });
135
138
  }
139
+ }
136
140
  }
137
141
  function buildSignature(modeName, featureToggles, overrides) {
138
- return JSON.stringify({
139
- modeName: modeName ?? "default",
140
- featureToggles,
141
- overrides,
142
- });
142
+ return JSON.stringify({
143
+ modeName: modeName ?? "default",
144
+ featureToggles,
145
+ overrides,
146
+ });
143
147
  }
144
148
  function deserializeError(payload) {
145
- const error = new Error(typeof payload.message == "string" ? payload.message : "unknown error");
146
- error.name = typeof payload.name == "string" ? payload.name : "Error";
147
- if (typeof payload.stack == "string")
148
- error.stack = payload.stack;
149
- for (const [key, value] of Object.entries(payload)) {
150
- if (key == "name" || key == "message" || key == "stack")
151
- continue;
152
- error[key] = value;
153
- }
154
- return error;
149
+ const error = new Error(
150
+ typeof payload.message == "string" ? payload.message : "unknown error",
151
+ );
152
+ error.name = typeof payload.name == "string" ? payload.name : "Error";
153
+ if (typeof payload.stack == "string") error.stack = payload.stack;
154
+ for (const [key, value] of Object.entries(payload)) {
155
+ if (key == "name" || key == "message" || key == "stack") continue;
156
+ error[key] = value;
157
+ }
158
+ return error;
155
159
  }