reg-cli 0.18.16 → 0.19.0-rc0

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 (46) hide show
  1. package/README.md +155 -46
  2. package/dist/cli.cjs +221 -0
  3. package/dist/cli.d.cts +1 -0
  4. package/dist/cli.d.mts +1 -0
  5. package/dist/cli.mjs +221 -0
  6. package/dist/entry.cjs +119 -0
  7. package/dist/entry.d.cts +15 -0
  8. package/dist/entry.d.mts +15 -0
  9. package/dist/entry.mjs +118 -0
  10. package/dist/index.cjs +212 -0
  11. package/dist/index.d.cts +52 -0
  12. package/dist/index.d.mts +52 -0
  13. package/dist/index.mjs +206 -0
  14. package/dist/progress-BB7D-xke.mjs +28 -0
  15. package/dist/progress-UOiKHaTY.cjs +33 -0
  16. package/dist/reg.wasm +0 -0
  17. package/dist/rolldown-runtime-D_mwlA32.cjs +43 -0
  18. package/{report/ui/dist/worker.js → dist/shared/report-worker.js} +859 -247
  19. package/dist/tracing-7HO8hac9.mjs +378 -0
  20. package/dist/tracing-DlLL541f.cjs +445 -0
  21. package/dist/worker.cjs +143 -0
  22. package/dist/worker.d.cts +1 -0
  23. package/dist/worker.d.mts +1 -0
  24. package/dist/worker.mjs +142 -0
  25. package/dist/ximgdiff-DzkRRnkW.mjs +41 -0
  26. package/dist/ximgdiff-wmW1BH1H.cjs +41 -0
  27. package/package.json +61 -112
  28. package/dist/.keep +0 -0
  29. package/dist/cli.js +0 -214
  30. package/dist/diff.js +0 -80
  31. package/dist/icon.js +0 -12
  32. package/dist/image-finder.js +0 -25
  33. package/dist/index.js +0 -200
  34. package/dist/log.js +0 -21
  35. package/dist/process-adaptor.js +0 -40
  36. package/dist/report.js +0 -170
  37. package/dist/tracing.js +0 -169
  38. package/report/assets/favicon_failure.png +0 -0
  39. package/report/assets/favicon_success.png +0 -0
  40. package/report/sample/actual/sample.png +0 -0
  41. package/report/sample/diff/sample.png +0 -0
  42. package/report/sample/expected/sample.png +0 -0
  43. package/report/ui/dist/report.js +0 -123
  44. package/report/ui/dist/style.css +0 -1
  45. package/template/template.html +0 -20
  46. /package/{template → dist/shared}/worker_pre.js +0 -0
@@ -0,0 +1,52 @@
1
+ import EventEmitter from "node:events";
2
+
3
+ //#region src/index.d.ts
4
+ declare const dir: () => string;
5
+ declare const run: (argv: string[]) => EventEmitter;
6
+ type CompareInput = {
7
+ actualDir: string;
8
+ expectedDir: string;
9
+ diffDir: string;
10
+ report?: string;
11
+ junitReport?: string;
12
+ json?: string; /** Re-render HTML from an existing reg.json (classic `-F/--from`). */
13
+ from?: string;
14
+ /** "none" (default) | "client" — enable the report's browser-side
15
+ * second-pass detector (classic `-X/--additionalDetection`). */
16
+ additionalDetection?: 'none' | 'client';
17
+ update?: boolean;
18
+ extendedErrors?: boolean;
19
+ /** Classic reg-cli's `-I/--ignoreChange` — governs the CLI's exit code
20
+ * only. Accepted and silently dropped by `compare()` for drop-in
21
+ * compat with reg-suit's `processor.ts:107`. */
22
+ ignoreChange?: boolean;
23
+ urlPrefix?: string;
24
+ matchingThreshold?: number;
25
+ threshold?: number;
26
+ thresholdRate?: number;
27
+ thresholdPixel?: number;
28
+ concurrency?: number;
29
+ enableAntialias?: boolean;
30
+ enableClientAdditionalDetection?: boolean;
31
+ /** Classic reg-cli's CLI-side x-img-diff extra detection pass. The
32
+ * Wasm pipeline's diff already includes the equivalent classification,
33
+ * so this is a no-op but accepted for drop-in compat with reg-suit's
34
+ * `processor.ts:114`. */
35
+ enableCliAdditionalDetection?: boolean;
36
+ };
37
+ type CompareOutput = {
38
+ failedItems: string[];
39
+ newItems: string[];
40
+ deletedItems: string[];
41
+ passedItems: string[];
42
+ expectedItems: string[];
43
+ actualItems: string[];
44
+ diffItems: string[];
45
+ actualDir: string;
46
+ expectedDir: string;
47
+ diffDir: string;
48
+ };
49
+ declare const compare: (input: CompareInput) => EventEmitter;
50
+ declare function writeRegJson(path: string, data: CompareOutput): Promise<void>;
51
+ //#endregion
52
+ export { CompareInput, CompareOutput, compare, dir, run, writeRegJson };
@@ -0,0 +1,52 @@
1
+ import EventEmitter from "node:events";
2
+
3
+ //#region src/index.d.ts
4
+ declare const dir: () => string;
5
+ declare const run: (argv: string[]) => EventEmitter;
6
+ type CompareInput = {
7
+ actualDir: string;
8
+ expectedDir: string;
9
+ diffDir: string;
10
+ report?: string;
11
+ junitReport?: string;
12
+ json?: string; /** Re-render HTML from an existing reg.json (classic `-F/--from`). */
13
+ from?: string;
14
+ /** "none" (default) | "client" — enable the report's browser-side
15
+ * second-pass detector (classic `-X/--additionalDetection`). */
16
+ additionalDetection?: 'none' | 'client';
17
+ update?: boolean;
18
+ extendedErrors?: boolean;
19
+ /** Classic reg-cli's `-I/--ignoreChange` — governs the CLI's exit code
20
+ * only. Accepted and silently dropped by `compare()` for drop-in
21
+ * compat with reg-suit's `processor.ts:107`. */
22
+ ignoreChange?: boolean;
23
+ urlPrefix?: string;
24
+ matchingThreshold?: number;
25
+ threshold?: number;
26
+ thresholdRate?: number;
27
+ thresholdPixel?: number;
28
+ concurrency?: number;
29
+ enableAntialias?: boolean;
30
+ enableClientAdditionalDetection?: boolean;
31
+ /** Classic reg-cli's CLI-side x-img-diff extra detection pass. The
32
+ * Wasm pipeline's diff already includes the equivalent classification,
33
+ * so this is a no-op but accepted for drop-in compat with reg-suit's
34
+ * `processor.ts:114`. */
35
+ enableCliAdditionalDetection?: boolean;
36
+ };
37
+ type CompareOutput = {
38
+ failedItems: string[];
39
+ newItems: string[];
40
+ deletedItems: string[];
41
+ passedItems: string[];
42
+ expectedItems: string[];
43
+ actualItems: string[];
44
+ diffItems: string[];
45
+ actualDir: string;
46
+ expectedDir: string;
47
+ diffDir: string;
48
+ };
49
+ declare const compare: (input: CompareInput) => EventEmitter;
50
+ declare function writeRegJson(path: string, data: CompareOutput): Promise<void>;
51
+ //#endregion
52
+ export { CompareInput, CompareOutput, compare, dir, run, writeRegJson };
package/dist/index.mjs ADDED
@@ -0,0 +1,206 @@
1
+ import { a as processWorkerSpans, d as resolveExtention, i as processRustTraceData, l as isCJS, n as initTracing, o as shutdownTracing, r as isTracingEnabled, s as startRootSpan, t as endRootSpan } from "./tracing-7HO8hac9.mjs";
2
+ import EventEmitter from "node:events";
3
+ import { Worker } from "node:worker_threads";
4
+ import { fileURLToPath } from "url";
5
+ import { dirname, join } from "node:path";
6
+ //#region src/index.ts
7
+ const dir = () => {
8
+ return isCJS ? __dirname : dirname(fileURLToPath(import.meta.url));
9
+ };
10
+ const mainSpans = [];
11
+ const recordMainSpan = (name, start_ms, attributes) => {
12
+ if (!isTracingEnabled()) return;
13
+ mainSpans.push({
14
+ name,
15
+ start_ms,
16
+ end_ms: Date.now(),
17
+ attributes,
18
+ worker_label: "main"
19
+ });
20
+ };
21
+ const run = (argv) => {
22
+ const emitter = new EventEmitter();
23
+ setImmediate(() => emitter.emit("start"));
24
+ runInternal(argv, emitter);
25
+ return emitter;
26
+ };
27
+ const runInternal = async (argv, emitter) => {
28
+ const run_start_ms = Date.now();
29
+ const t_init = Date.now();
30
+ if (isTracingEnabled()) {
31
+ await initTracing();
32
+ recordMainSpan("main.init_tracing", t_init);
33
+ }
34
+ const traceContext = isTracingEnabled() ? startRootSpan("reg-cli") : null;
35
+ const t_new_entry = Date.now();
36
+ const worker = new Worker(join(dir(), `./entry.${resolveExtention()}`), { workerData: { argv } });
37
+ recordMainSpan("main.new_entry_worker", t_new_entry);
38
+ let nextTid = 1;
39
+ const workers = [worker];
40
+ const threadWorkerSpans = [];
41
+ const spawn = (startArg, threadId, memory) => {
42
+ const t_new_worker = Date.now();
43
+ const worker = new Worker(join(dir(), `./worker.${resolveExtention()}`), { workerData: { argv } });
44
+ const tid = nextTid++;
45
+ recordMainSpan("main.new_thread_worker", t_new_worker, { tid });
46
+ workers.push(worker);
47
+ worker.on("message", (msg) => {
48
+ const { cmd } = msg;
49
+ if (cmd === "loaded") {
50
+ if (typeof worker.unref === "function") worker.unref();
51
+ } else if (cmd === "thread-spawn") spawn(msg.startArg, msg.threadId, msg.memory);
52
+ else if (cmd === "worker-spans") {
53
+ if (Array.isArray(msg.workerSpans)) threadWorkerSpans.push(...msg.workerSpans);
54
+ } else if (cmd === "compare-event") {
55
+ if (msg.event) emitter.emit("compare", msg.event);
56
+ }
57
+ });
58
+ worker.on("error", (e) => {
59
+ workers.forEach((w) => w.terminate());
60
+ emitter.emit("error", e);
61
+ });
62
+ if (threadId) {
63
+ Atomics.store(threadId, 0, tid);
64
+ Atomics.notify(threadId, 0);
65
+ }
66
+ worker.postMessage({
67
+ startArg,
68
+ tid,
69
+ memory
70
+ });
71
+ return tid;
72
+ };
73
+ worker.on("message", async ({ cmd, startArg, threadId, memory, data, traceData, workerSpans, event }) => {
74
+ if (cmd === "compare-event") {
75
+ if (event) emitter.emit("compare", event);
76
+ return;
77
+ }
78
+ if (cmd === "complete") {
79
+ const t_post_complete = Date.now();
80
+ if (isTracingEnabled()) {
81
+ if (Array.isArray(workerSpans)) processWorkerSpans(workerSpans, "entry");
82
+ if (threadWorkerSpans.length) processWorkerSpans(threadWorkerSpans, "thread");
83
+ if (traceData) processRustTraceData(traceData);
84
+ recordMainSpan("main.process_trace_and_spans", t_post_complete);
85
+ mainSpans.push({
86
+ name: "main.run_total",
87
+ start_ms: run_start_ms,
88
+ end_ms: Date.now(),
89
+ worker_label: "main"
90
+ });
91
+ processWorkerSpans(mainSpans, "main");
92
+ endRootSpan(true);
93
+ await shutdownTracing();
94
+ }
95
+ workers.forEach((w) => w.terminate());
96
+ emitter.emit("complete", data);
97
+ return;
98
+ }
99
+ if (cmd === "loaded") {
100
+ if (typeof worker.unref === "function") worker.unref();
101
+ return;
102
+ }
103
+ if (cmd === "thread-spawn") spawn(startArg, threadId, memory);
104
+ });
105
+ worker.on("error", (err) => {
106
+ if (traceContext) endRootSpan(false);
107
+ workers.forEach((w) => w.terminate());
108
+ emitter.emit("error", err);
109
+ });
110
+ };
111
+ /** Flags that `compare()` handles itself in JS; they are NOT forwarded to
112
+ * the Wasm binary. `extendedErrors` WAS here but now also feeds the
113
+ * Rust-side junit generator, so it's forwarded below via KEY_REMAP.
114
+ *
115
+ * `ignoreChange` and `enableCliAdditionalDetection` are stripped because
116
+ * reg-suit unconditionally passes them (see
117
+ * reg-viz/reg-suit `packages/reg-suit-core/src/processor.ts` `compare({…})`)
118
+ * and the Rust clap layer doesn't recognise them — forwarding would abort
119
+ * the binary with "unexpected argument". Semantically both are no-ops at
120
+ * the library-event layer:
121
+ * - `ignoreChange` only governs classic reg-cli's process exit code;
122
+ * the EventEmitter surface never needed it.
123
+ * - `enableCliAdditionalDetection` was classic's flag for running an
124
+ * extra CLI-side x-img-diff pass; the Wasm port's diff pipeline
125
+ * already produces the final pass/fail classification, so toggling
126
+ * it changes nothing. (The *client*-side variant is handled via
127
+ * `additionalDetection: 'client'` / the legacy
128
+ * `enableClientAdditionalDetection: true` alias further down.) */
129
+ const CLI_ONLY_KEYS = new Set([
130
+ "update",
131
+ "ignoreChange",
132
+ "enableCliAdditionalDetection"
133
+ ]);
134
+ /** Library option names that must be forwarded to the Wasm binary under a
135
+ * different flag name (Rust uses `--junit`, callers pass `junitReport`). */
136
+ const KEY_REMAP = { junitReport: "junit" };
137
+ const compare = (input) => {
138
+ const { actualDir, expectedDir, diffDir, threshold, update, ...rest } = input;
139
+ if (rest.diffFormat == null) rest.diffFormat = "png";
140
+ if (rest.json == null) rest.json = "./reg.json";
141
+ if (threshold != null && rest.thresholdRate == null) rest.thresholdRate = threshold;
142
+ const restAny = rest;
143
+ if (restAny.enableClientAdditionalDetection && restAny.additionalDetection == null) restAny.additionalDetection = "client";
144
+ delete restAny.enableClientAdditionalDetection;
145
+ for (const k of CLI_ONLY_KEYS) delete rest[k];
146
+ const inner = run([
147
+ "--",
148
+ actualDir,
149
+ expectedDir,
150
+ diffDir,
151
+ ...Object.entries(rest).flatMap(([k, v]) => {
152
+ if (v == null || v === "") return [];
153
+ return [`--${KEY_REMAP[k] ?? k}`, String(v)];
154
+ })
155
+ ]);
156
+ const outer = new EventEmitter();
157
+ inner.on("start", () => outer.emit("start"));
158
+ inner.on("compare", (p) => outer.emit("compare", p));
159
+ inner.on("error", (e) => outer.emit("error", e));
160
+ inner.on("complete", async (data) => {
161
+ try {
162
+ if (restAny.additionalDetection === "client" && typeof input.report === "string") {
163
+ const { writeXimgdiffAssets } = await import("./ximgdiff-DzkRRnkW.mjs").then((n) => n.n);
164
+ await writeXimgdiffAssets({
165
+ reportPath: input.report,
166
+ urlPrefix: typeof input.urlPrefix === "string" ? input.urlPrefix : "",
167
+ distDir: dir()
168
+ });
169
+ }
170
+ if (update) {
171
+ await updateExpected(actualDir, expectedDir, {
172
+ newItems: data.newItems ?? [],
173
+ failedItems: data.failedItems ?? [],
174
+ deletedItems: data.deletedItems ?? []
175
+ });
176
+ outer.emit("update");
177
+ }
178
+ } catch (e) {
179
+ outer.emit("error", e);
180
+ return;
181
+ }
182
+ outer.emit("complete", data);
183
+ });
184
+ return outer;
185
+ };
186
+ async function writeRegJson(path, data) {
187
+ const { writeFile, mkdir } = await import("node:fs/promises");
188
+ const { dirname } = await import("node:path");
189
+ await mkdir(dirname(path), { recursive: true });
190
+ await writeFile(path, JSON.stringify(data, null, 2) + "\n", "utf8");
191
+ }
192
+ async function updateExpected(actualDir, expectedDir, items) {
193
+ const { mkdir, copyFile, rm } = await import("node:fs/promises");
194
+ const { dirname, join } = await import("node:path");
195
+ const toRemove = [...items.deletedItems, ...items.failedItems];
196
+ for (const img of toRemove) await rm(join(expectedDir, img), { force: true });
197
+ const toCopy = [...items.newItems, ...items.failedItems];
198
+ for (const img of toCopy) {
199
+ const src = join(actualDir, img);
200
+ const dst = join(expectedDir, img);
201
+ await mkdir(dirname(dst), { recursive: true });
202
+ await copyFile(src, dst);
203
+ }
204
+ }
205
+ //#endregion
206
+ export { compare, dir, run, writeRegJson };
@@ -0,0 +1,28 @@
1
+ //#region src/progress.ts
2
+ const MARKER = "__REG_CLI_EVT__ ";
3
+ /**
4
+ * Build a WASI `printErr` hook that forwards progress events to `onEvent`
5
+ * and everything else to `fallback` (defaults to `console.error`).
6
+ *
7
+ * `@tybys/wasm-util`'s `StandardOutput.write` already buffers until a
8
+ * newline and invokes the callback with one stripped line per call (see
9
+ * `node_modules/@tybys/wasm-util/dist/wasm-util.esm-bundler.js` line
10
+ * ~749-779). So we can treat each invocation as a single complete line.
11
+ */
12
+ const createPrintErrHook = (onEvent, fallback = (s) => console.error(s)) => {
13
+ return (line) => {
14
+ if (line.startsWith(MARKER)) {
15
+ const json = line.slice(16);
16
+ try {
17
+ const ev = JSON.parse(json);
18
+ if (ev && typeof ev.type === "string" && typeof ev.path === "string") {
19
+ onEvent(ev);
20
+ return;
21
+ }
22
+ } catch {}
23
+ }
24
+ fallback(line);
25
+ };
26
+ };
27
+ //#endregion
28
+ export { createPrintErrHook as t };
@@ -0,0 +1,33 @@
1
+ //#region src/progress.ts
2
+ const MARKER = "__REG_CLI_EVT__ ";
3
+ /**
4
+ * Build a WASI `printErr` hook that forwards progress events to `onEvent`
5
+ * and everything else to `fallback` (defaults to `console.error`).
6
+ *
7
+ * `@tybys/wasm-util`'s `StandardOutput.write` already buffers until a
8
+ * newline and invokes the callback with one stripped line per call (see
9
+ * `node_modules/@tybys/wasm-util/dist/wasm-util.esm-bundler.js` line
10
+ * ~749-779). So we can treat each invocation as a single complete line.
11
+ */
12
+ const createPrintErrHook = (onEvent, fallback = (s) => console.error(s)) => {
13
+ return (line) => {
14
+ if (line.startsWith(MARKER)) {
15
+ const json = line.slice(16);
16
+ try {
17
+ const ev = JSON.parse(json);
18
+ if (ev && typeof ev.type === "string" && typeof ev.path === "string") {
19
+ onEvent(ev);
20
+ return;
21
+ }
22
+ } catch {}
23
+ }
24
+ fallback(line);
25
+ };
26
+ };
27
+ //#endregion
28
+ Object.defineProperty(exports, "createPrintErrHook", {
29
+ enumerable: true,
30
+ get: function() {
31
+ return createPrintErrHook;
32
+ }
33
+ });
package/dist/reg.wasm ADDED
Binary file
@@ -0,0 +1,43 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __exportAll = (all, no_symbols) => {
9
+ let target = {};
10
+ for (var name in all) __defProp(target, name, {
11
+ get: all[name],
12
+ enumerable: true
13
+ });
14
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
15
+ return target;
16
+ };
17
+ var __copyProps = (to, from, except, desc) => {
18
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
19
+ key = keys[i];
20
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
21
+ get: ((k) => from[k]).bind(null, key),
22
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
23
+ });
24
+ }
25
+ return to;
26
+ };
27
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
28
+ value: mod,
29
+ enumerable: true
30
+ }) : target, mod));
31
+ //#endregion
32
+ Object.defineProperty(exports, "__exportAll", {
33
+ enumerable: true,
34
+ get: function() {
35
+ return __exportAll;
36
+ }
37
+ });
38
+ Object.defineProperty(exports, "__toESM", {
39
+ enumerable: true,
40
+ get: function() {
41
+ return __toESM;
42
+ }
43
+ });