@recursica/adapter-tester 5.3.0 → 5.4.0
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 +7 -1
- package/dist/cli.cjs +12 -11
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +354 -212
- package/dist/cli.js.map +1 -1
- package/dist/devServer.d.ts +5 -2
- package/dist/devServer.d.ts.map +1 -1
- package/dist/fileConfig.d.ts +1 -1
- package/dist/harness/mantineSourceOfTruth.d.ts +1 -1
- package/dist/harness/mantineSourceOfTruth.d.ts.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.html +226 -3
- package/dist/index.js +1 -1
- package/dist/{mantineSourceOfTruth-6KZlsQ67.cjs → mantineSourceOfTruth-DiQas6OS.cjs} +29 -16
- package/dist/mantineSourceOfTruth-DiQas6OS.cjs.map +1 -0
- package/dist/{mantineSourceOfTruth-CAKhcVCT.js → mantineSourceOfTruth-xUEi1k8U.js} +42 -29
- package/dist/mantineSourceOfTruth-xUEi1k8U.js.map +1 -0
- package/dist/runVisualRegression-CQk97Qkv.js +5051 -0
- package/dist/runVisualRegression-CQk97Qkv.js.map +1 -0
- package/dist/runVisualRegression-DNafR8Lb.cjs +13 -0
- package/dist/runVisualRegression-DNafR8Lb.cjs.map +1 -0
- package/dist/testing/runVisualRegression.d.ts +16 -2
- package/dist/testing/runVisualRegression.d.ts.map +1 -1
- package/dist/testing.cjs +1 -5
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.js +2 -380
- package/dist/testing.js.map +1 -1
- package/package.json +3 -2
- package/public/index.html +226 -3
- package/dist/index-C6uYPRmx.cjs +0 -9
- package/dist/index-C6uYPRmx.cjs.map +0 -1
- package/dist/index-DqQzFSAH.js +0 -4667
- package/dist/index-DqQzFSAH.js.map +0 -1
- package/dist/mantineSourceOfTruth-6KZlsQ67.cjs.map +0 -1
- package/dist/mantineSourceOfTruth-CAKhcVCT.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { spawn as
|
|
3
|
-
import { readFileSync as
|
|
4
|
-
import { dirname as
|
|
5
|
-
import { fileURLToPath as
|
|
2
|
+
import { spawn as W, spawnSync as re } from "node:child_process";
|
|
3
|
+
import { readFileSync as O, mkdirSync as B, writeFileSync as E, existsSync as A } from "node:fs";
|
|
4
|
+
import { dirname as M, join as l, resolve as C } from "node:path";
|
|
5
|
+
import { fileURLToPath as z, pathToFileURL as oe } from "node:url";
|
|
6
|
+
import { chromium as se } from "@playwright/test";
|
|
6
7
|
import K from "express";
|
|
7
|
-
import { createProxyMiddleware as
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
const
|
|
12
|
-
function
|
|
8
|
+
import { createProxyMiddleware as ne } from "http-proxy-middleware";
|
|
9
|
+
import { a as ie, b as ae, c as ce, d as de, e as ue, f as le, i as pe } from "./runVisualRegression-CQk97Qkv.js";
|
|
10
|
+
import he from "node:net";
|
|
11
|
+
import { m as fe } from "./mantineSourceOfTruth-xUEi1k8U.js";
|
|
12
|
+
const me = /\x1b\[[0-9;]*m/g, ge = /Local:\s*(https?:\/\/localhost:\d+)/;
|
|
13
|
+
function ye(e) {
|
|
13
14
|
return new Promise((t) => {
|
|
14
|
-
const r = new
|
|
15
|
+
const r = new he.Socket();
|
|
15
16
|
r.setTimeout(200), r.once("connect", () => {
|
|
16
17
|
r.destroy(), t(!0);
|
|
17
18
|
}), r.once("timeout", () => {
|
|
@@ -21,60 +22,60 @@ function re(e) {
|
|
|
21
22
|
}), r.connect(e, "127.0.0.1");
|
|
22
23
|
});
|
|
23
24
|
}
|
|
24
|
-
function
|
|
25
|
+
function we(e) {
|
|
25
26
|
try {
|
|
26
|
-
const t = JSON.parse(
|
|
27
|
+
const t = JSON.parse(O(e, "utf8")).port;
|
|
27
28
|
return typeof t == "number" ? t : void 0;
|
|
28
29
|
} catch {
|
|
29
30
|
return;
|
|
30
31
|
}
|
|
31
32
|
}
|
|
32
|
-
function
|
|
33
|
-
M(
|
|
33
|
+
function ve(e, t) {
|
|
34
|
+
B(M(e), { recursive: !0 }), E(e, JSON.stringify({ port: t }));
|
|
34
35
|
}
|
|
35
|
-
function
|
|
36
|
-
return new Promise((r,
|
|
37
|
-
var
|
|
38
|
-
const
|
|
36
|
+
function be(e, t) {
|
|
37
|
+
return new Promise((r, s) => {
|
|
38
|
+
var w, u;
|
|
39
|
+
const a = W(e.command, {
|
|
39
40
|
cwd: e.cwd,
|
|
40
41
|
stdio: "pipe",
|
|
41
42
|
shell: !0
|
|
42
43
|
});
|
|
43
|
-
let
|
|
44
|
-
const
|
|
45
|
-
|
|
44
|
+
let i = !1;
|
|
45
|
+
const g = setTimeout(() => {
|
|
46
|
+
i || (i = !0, a.kill(), s(
|
|
46
47
|
new Error(
|
|
47
48
|
`Timed out after ${t}ms waiting for ${e.name}'s Storybook to report its URL (no "Local: http://localhost:<port>" line seen in its output).`
|
|
48
49
|
)
|
|
49
50
|
));
|
|
50
|
-
}, t),
|
|
51
|
-
const
|
|
52
|
-
for (const
|
|
51
|
+
}, t), y = (o, n) => {
|
|
52
|
+
const c = o.toString();
|
|
53
|
+
for (const p of c.split(`
|
|
53
54
|
`))
|
|
54
|
-
if (
|
|
55
|
-
const
|
|
56
|
-
(
|
|
55
|
+
if (p.trim()) {
|
|
56
|
+
const f = n ? `${e.name} SB ERROR` : `${e.name} SB`;
|
|
57
|
+
(n ? console.error : console.log)(`[${f}] ${p.trim()}`);
|
|
57
58
|
}
|
|
58
|
-
if (
|
|
59
|
-
const
|
|
60
|
-
if (
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
|
|
59
|
+
if (i) return;
|
|
60
|
+
const d = c.replace(me, "").match(ge);
|
|
61
|
+
if (d) {
|
|
62
|
+
i = !0, clearTimeout(g);
|
|
63
|
+
const p = d[1], f = Number(new URL(p).port);
|
|
64
|
+
ve(e.cacheFile, f), r({ url: p, port: f, process: a });
|
|
64
65
|
}
|
|
65
66
|
};
|
|
66
|
-
(
|
|
67
|
-
|
|
68
|
-
}),
|
|
69
|
-
|
|
67
|
+
(w = a.stdout) == null || w.on("data", (o) => y(o, !1)), (u = a.stderr) == null || u.on("data", (o) => y(o, !0)), a.on("error", (o) => {
|
|
68
|
+
i || (i = !0, clearTimeout(g), s(o));
|
|
69
|
+
}), a.on("exit", (o) => {
|
|
70
|
+
i || (i = !0, clearTimeout(g), s(
|
|
70
71
|
new Error(
|
|
71
|
-
`${e.name}'s Storybook exited (code ${
|
|
72
|
+
`${e.name}'s Storybook exited (code ${o}) before reporting its URL.`
|
|
72
73
|
)
|
|
73
74
|
));
|
|
74
75
|
});
|
|
75
76
|
});
|
|
76
77
|
}
|
|
77
|
-
function
|
|
78
|
+
function L(e, t) {
|
|
78
79
|
return {
|
|
79
80
|
name: e,
|
|
80
81
|
command: t.command,
|
|
@@ -84,10 +85,10 @@ function A(e, t) {
|
|
|
84
85
|
timeoutMs: t.timeout
|
|
85
86
|
};
|
|
86
87
|
}
|
|
87
|
-
async function
|
|
88
|
+
async function R(e) {
|
|
88
89
|
if (e.reuseExistingServer) {
|
|
89
|
-
const t =
|
|
90
|
-
if (t !== void 0 && await
|
|
90
|
+
const t = we(e.cacheFile);
|
|
91
|
+
if (t !== void 0 && await ye(t))
|
|
91
92
|
return console.log(
|
|
92
93
|
`[Dev Launcher] ${e.name} is already running on port ${t} (reused).`
|
|
93
94
|
), {
|
|
@@ -96,155 +97,285 @@ async function D(e) {
|
|
|
96
97
|
process: null
|
|
97
98
|
};
|
|
98
99
|
}
|
|
99
|
-
return console.log(`[Dev Launcher] Launching Storybook for ${e.name}...`),
|
|
100
|
+
return console.log(`[Dev Launcher] Launching Storybook for ${e.name}...`), be(e, e.timeoutMs ?? 120 * 1e3);
|
|
100
101
|
}
|
|
101
|
-
const
|
|
102
|
-
function
|
|
102
|
+
const G = M(z(import.meta.url));
|
|
103
|
+
function Te(e) {
|
|
103
104
|
const t = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
104
|
-
console.log(`[Dev Launcher] Auto-launching browser: ${e}`),
|
|
105
|
+
console.log(`[Dev Launcher] Auto-launching browser: ${e}`), W(t, [e], { shell: process.platform === "win32" }).on(
|
|
105
106
|
"error",
|
|
106
107
|
(r) => {
|
|
107
108
|
console.error("[Dev Launcher] Failed to auto-launch browser:", r);
|
|
108
109
|
}
|
|
109
110
|
);
|
|
110
111
|
}
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
|
|
112
|
+
function ke(e) {
|
|
113
|
+
const t = l(
|
|
114
|
+
e,
|
|
115
|
+
"node_modules/@recursica/adapter-mantine-v8/package.json"
|
|
116
|
+
), r = A(t) ? t : l(e, "package.json");
|
|
117
|
+
return JSON.parse(O(r, "utf8")).version;
|
|
118
|
+
}
|
|
119
|
+
const Se = 6010, U = (e) => `data:image/png;base64,${e.toString("base64")}`;
|
|
120
|
+
function Oe(e, t, r, s, a) {
|
|
121
|
+
let i, g = Promise.resolve();
|
|
122
|
+
const y = (u) => ue(
|
|
123
|
+
u,
|
|
124
|
+
Object.fromEntries(
|
|
125
|
+
Object.entries(t.stories ?? {}).filter(([, o]) => o.sourceOfTruthThreshold !== void 0).map(([o, n]) => [o, n.sourceOfTruthThreshold])
|
|
126
|
+
),
|
|
127
|
+
t.sourceOfTruthThresholdPixels
|
|
128
|
+
), w = async (u) => {
|
|
129
|
+
i ?? (i = se.launch());
|
|
130
|
+
const o = await (await i).newPage(), n = ae(u, t.stories ?? {});
|
|
131
|
+
try {
|
|
132
|
+
await o.setViewportSize(n), await o.goto(`${r}/iframe.html?id=${u}&viewMode=story`, {
|
|
133
|
+
waitUntil: "networkidle"
|
|
134
|
+
});
|
|
135
|
+
const c = await ce(o), d = await s.readImage(u), p = y(u);
|
|
136
|
+
if (!d)
|
|
137
|
+
return {
|
|
138
|
+
status: "no-golden",
|
|
139
|
+
diffPixels: null,
|
|
140
|
+
threshold: p,
|
|
141
|
+
viewport: n,
|
|
142
|
+
golden: null,
|
|
143
|
+
diff: null
|
|
144
|
+
};
|
|
145
|
+
const { diffPixels: f, diffImage: v } = de(c, d);
|
|
146
|
+
return {
|
|
147
|
+
status: v ? "ok" : "size-mismatch",
|
|
148
|
+
diffPixels: v ? f : null,
|
|
149
|
+
threshold: p,
|
|
150
|
+
viewport: n,
|
|
151
|
+
golden: U(d),
|
|
152
|
+
diff: v ? U(v) : null
|
|
153
|
+
};
|
|
154
|
+
} finally {
|
|
155
|
+
await o.close();
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
return e.get("/__golden/diff", (u, o) => {
|
|
159
|
+
if (s instanceof Error) {
|
|
160
|
+
o.status(503).json({ error: s.message });
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const n = String(u.query.id ?? "");
|
|
164
|
+
if (!n) {
|
|
165
|
+
o.status(400).json({ error: "id is required" });
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const c = g.then(() => w(n));
|
|
169
|
+
g = c.catch(() => {
|
|
170
|
+
}), c.then(
|
|
171
|
+
(d) => o.json(d),
|
|
172
|
+
(d) => o.status(500).json({ error: d.message })
|
|
173
|
+
);
|
|
174
|
+
}), e.get("/__golden/image", async (u, o) => {
|
|
175
|
+
const n = s instanceof Error ? null : await s.readImage(String(u.query.id ?? ""));
|
|
176
|
+
if (!n) {
|
|
177
|
+
o.status(404).end();
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
o.type("image/png").send(n);
|
|
181
|
+
}), e.post("/__golden/threshold", K.json(), (u, o) => {
|
|
182
|
+
var p, f;
|
|
183
|
+
const { id: n, value: c } = u.body ?? {};
|
|
184
|
+
if (typeof n != "string" || !n || !Number.isInteger(c) || c < 0) {
|
|
185
|
+
o.status(400).json({ error: "id (string) and value (integer >= 0) are required" });
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const d = A(a) ? JSON.parse(O(a, "utf8")) : {};
|
|
189
|
+
d.stories = {
|
|
190
|
+
...d.stories,
|
|
191
|
+
[n]: { ...(p = d.stories) == null ? void 0 : p[n], sourceOfTruthThreshold: c }
|
|
192
|
+
}, E(a, JSON.stringify(d, null, 2) + `
|
|
193
|
+
`), t.stories = {
|
|
194
|
+
...t.stories,
|
|
195
|
+
[n]: { ...(f = t.stories) == null ? void 0 : f[n], sourceOfTruthThreshold: c }
|
|
196
|
+
}, o.json({ id: n, value: c });
|
|
197
|
+
}), {
|
|
198
|
+
close: async () => {
|
|
199
|
+
i && await (await i).close();
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
async function xe(e, t, r) {
|
|
204
|
+
const [s, a] = e.targets, [i, g] = t;
|
|
205
|
+
if (!(s != null && s.sourceOfTruth) || !a || a.sourceOfTruth || !g || !i)
|
|
114
206
|
throw new Error(
|
|
115
207
|
"adapter-tester dev mode requires exactly two targets: [sourceOfTruth, target] — check adapter-tester.config.json."
|
|
116
208
|
);
|
|
117
|
-
|
|
209
|
+
if (!e.sourceOfTruthGolden)
|
|
210
|
+
throw new Error(
|
|
211
|
+
"adapter-tester dev mode needs a sourceOfTruthGolden location to diff against — check adapter-tester.config.json."
|
|
212
|
+
);
|
|
118
213
|
console.log(
|
|
119
|
-
`[Dev Launcher] Resolving ${
|
|
214
|
+
`[Dev Launcher] Resolving ${s.name} and ${a.name} Storybooks...`
|
|
120
215
|
);
|
|
121
|
-
const [y, w] = await Promise.all([
|
|
122
|
-
|
|
123
|
-
|
|
216
|
+
const [y, w, u] = await Promise.all([
|
|
217
|
+
R(
|
|
218
|
+
L(s.name, i)
|
|
124
219
|
),
|
|
125
|
-
|
|
220
|
+
R(L(a.name, g)),
|
|
221
|
+
// The live Storybooks still work without the golden (e.g. offline), so
|
|
222
|
+
// a failure here only disables Golden/Compute Diff, loudly.
|
|
223
|
+
ie(e.sourceOfTruthGolden).catch(
|
|
224
|
+
(m) => (console.warn(
|
|
225
|
+
`[Dev Launcher] WARNING: source-of-truth golden unavailable — Golden and Compute Diff are disabled: ${m.message}`
|
|
226
|
+
), m)
|
|
227
|
+
)
|
|
126
228
|
]);
|
|
127
|
-
console.log("[Dev Launcher]
|
|
128
|
-
const
|
|
129
|
-
(
|
|
130
|
-
),
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
229
|
+
console.log("[Dev Launcher] Dev Mode is ready!");
|
|
230
|
+
const o = [y.process, w.process].filter(
|
|
231
|
+
(m) => !!m
|
|
232
|
+
), n = K(), c = l(G, "../public"), d = l(G, "../report-header.txt"), p = ke(
|
|
233
|
+
i.cwd
|
|
234
|
+
), f = Oe(
|
|
235
|
+
n,
|
|
236
|
+
e,
|
|
237
|
+
w.url,
|
|
238
|
+
u,
|
|
239
|
+
r.configPath
|
|
240
|
+
);
|
|
241
|
+
n.get("/", (m, S, x) => {
|
|
242
|
+
if (m.query.path) {
|
|
243
|
+
x();
|
|
134
244
|
return;
|
|
135
245
|
}
|
|
136
|
-
const
|
|
246
|
+
const te = O(l(c, "index.html"), "utf8").replace(
|
|
137
247
|
"<head>",
|
|
138
248
|
`<head>
|
|
139
249
|
<script>window.__ADAPTER_TESTER__ = ${JSON.stringify({
|
|
140
|
-
ownName:
|
|
141
|
-
sourceOfTruthName:
|
|
250
|
+
ownName: a.name,
|
|
251
|
+
sourceOfTruthName: s.name,
|
|
252
|
+
sourceOfTruthVersion: p,
|
|
142
253
|
sourceOfTruthPort: y.port
|
|
143
254
|
})};<\/script>`
|
|
144
255
|
);
|
|
145
|
-
|
|
146
|
-
}),
|
|
256
|
+
S.send(te);
|
|
257
|
+
}), n.get("/report-header.txt", (m, S) => {
|
|
147
258
|
if (e.reportHeader !== void 0) {
|
|
148
|
-
|
|
259
|
+
S.type("text/plain").send(e.reportHeader);
|
|
149
260
|
return;
|
|
150
261
|
}
|
|
151
262
|
try {
|
|
152
|
-
|
|
263
|
+
S.type("text/plain").send(O(d, "utf8"));
|
|
153
264
|
} catch {
|
|
154
|
-
|
|
265
|
+
S.status(500).send("Error loading report-header.txt");
|
|
155
266
|
}
|
|
156
|
-
}),
|
|
267
|
+
}), n.use(
|
|
157
268
|
"/",
|
|
158
|
-
|
|
269
|
+
ne({
|
|
159
270
|
target: w.url,
|
|
160
271
|
changeOrigin: !0,
|
|
161
272
|
ws: !0
|
|
162
273
|
})
|
|
163
274
|
);
|
|
164
|
-
let
|
|
165
|
-
const
|
|
166
|
-
if (!
|
|
167
|
-
|
|
275
|
+
let v = !1;
|
|
276
|
+
const P = () => {
|
|
277
|
+
if (!v) {
|
|
278
|
+
v = !0, console.log(
|
|
168
279
|
`
|
|
169
280
|
[Dev Launcher] Shutting down Dev Mode server and spawned Storybooks...`
|
|
170
281
|
);
|
|
171
|
-
for (const
|
|
172
|
-
process.exit(0);
|
|
282
|
+
for (const m of o) m.kill("SIGINT");
|
|
283
|
+
f.close().finally(() => process.exit(0));
|
|
173
284
|
}
|
|
174
285
|
};
|
|
175
|
-
process.on("SIGINT",
|
|
176
|
-
|
|
286
|
+
process.on("SIGINT", P), process.on("SIGTERM", P);
|
|
287
|
+
const b = r.port ?? Se, N = r.port ? b : b + 19;
|
|
288
|
+
let k = b;
|
|
289
|
+
for (; ; )
|
|
290
|
+
try {
|
|
291
|
+
await new Promise((m, S) => {
|
|
292
|
+
const x = n.listen(k);
|
|
293
|
+
x.once("listening", () => m()), x.once("error", S);
|
|
294
|
+
});
|
|
295
|
+
break;
|
|
296
|
+
} catch (m) {
|
|
297
|
+
if (m.code !== "EADDRINUSE" || k >= N) {
|
|
298
|
+
console.error(
|
|
299
|
+
`[Dev Launcher] Dev Mode server failed to listen on port ${k}: ${m.message}. Pass --port <n> to use another port.`
|
|
300
|
+
);
|
|
301
|
+
for (const x of o) x.kill("SIGINT");
|
|
302
|
+
process.exit(1);
|
|
303
|
+
}
|
|
304
|
+
console.log(
|
|
305
|
+
`[Dev Launcher] Port ${k} is in use, trying ${k + 1}...`
|
|
306
|
+
), k++;
|
|
307
|
+
}
|
|
308
|
+
console.log(`
|
|
177
309
|
====================================================
|
|
178
310
|
🚀 Adapter Dev Mode proxy running at:
|
|
179
|
-
http://localhost:${
|
|
311
|
+
http://localhost:${k}
|
|
180
312
|
====================================================
|
|
181
|
-
`),
|
|
182
|
-
});
|
|
313
|
+
`), Te(`http://localhost:${k}`);
|
|
183
314
|
}
|
|
184
|
-
const
|
|
185
|
-
$schema:
|
|
186
|
-
$id:
|
|
187
|
-
title:
|
|
188
|
-
description:
|
|
189
|
-
type:
|
|
190
|
-
additionalProperties:
|
|
191
|
-
properties:
|
|
192
|
-
},
|
|
193
|
-
|
|
194
|
-
const
|
|
195
|
-
function
|
|
196
|
-
if (
|
|
197
|
-
const r = (
|
|
198
|
-
var
|
|
199
|
-
const
|
|
200
|
-
return ` - ${
|
|
315
|
+
const Pe = "http://json-schema.org/draft-07/schema#", De = "https://github.com/borderux/recursica/tree/main/packages/adapter-tester/src/adapter-tester.schema.json", Ee = "adapter-tester.config.json", je = "Configuration for @recursica/adapter-tester. All fields are optional — an adapter repo with a normal `storybook` npm script needs no config file at all.", Ae = "object", Ne = !1, _e = { $schema: { type: "string", description: 'Optional pointer to this schema for editor autocomplete, e.g. "https://raw.githubusercontent.com/borderux/recursica/main/packages/adapter-tester/src/adapter-tester.schema.json".' }, name: { type: "string", description: `Label for this project's own target. Defaults to the unscoped name in this project's package.json (e.g. "@recursica/some-adapter" -> "some-adapter").` }, storybook: { type: "object", additionalProperties: !1, description: "This project's own Storybook target.", properties: { port: { type: "integer", minimum: 1, maximum: 65535, description: "First-guess port only, not authoritative — the real port is auto-detected from this Storybook's own startup output, since Storybook silently falls back to an OS-assigned port whenever this one is taken. Auto-detected from this project's own `scripts.storybook` (a `-p <port>`/`--port <port>` flag) when omitted, falling back to 6006." }, command: { type: "string", description: "Command that boots the target's Storybook. Defaults to `npm run storybook`." }, cwd: { type: "string", description: 'Directory the command runs in, relative to the config file. Defaults to ".".' } } }, sourceOfTruth: { description: 'The comparison target. Defaults to `{ "type": "mantine-harness" }`.', oneOf: [{ type: "object", additionalProperties: !1, description: "Default mode: boots a throwaway harness that installs the published @recursica/adapter-mantine-v8 from npm — no monorepo checkout required.", properties: { type: { const: "mantine-harness" }, port: { type: "integer", minimum: 1, maximum: 65535, description: "First-guess port only, not authoritative — the real port is auto-detected from the throwaway harness's Storybook's own startup output." }, mantineAdapterVersion: { type: "string", description: "npm version/tag of @recursica/adapter-mantine-v8 to install in the harness. Defaults to `latest`." }, storybookTemplateVersion: { type: "string", description: "npm version/tag of @recursica/storybook-template to install in the harness. Defaults to `latest`." }, tokensSource: { type: "string", enum: ["target", "mantine-package"], description: `Which Recursica token snapshot the harness's Mantine components render with. "target" (default): this project's own committed recursica_variables_scoped.css/recursica_tokens.json/recursica_brand.json/recursica_ui-kit.json — makes the divergence check mean exactly "given the same tokens this project is using, does it render the same as Mantine's own components," isolating adapter-mapping correctness from any token-snapshot drift between repos. "mantine-package": @recursica/adapter-mantine-v8's own bundled token files instead (whatever it was last published with) — also flags, as a side effect, a project whose own committed tokens have gone stale relative to Mantine's. Never the deprecated @recursica/official-release package.` } } }, { type: "object", additionalProperties: !1, description: "Non-standard mode: points at an already-addressable Storybook — e.g. a sibling workspace package's own Storybook inside this monorepo.", required: ["type"], properties: { type: { const: "url" }, name: { type: "string", description: 'Label for the source-of-truth target. Defaults to "Mantine".' }, port: { type: "integer", minimum: 1, maximum: 65535, description: "First-guess port only, not authoritative — the real port is auto-detected from the source-of-truth Storybook's own startup output. Defaults to 6011." }, command: { type: "string", description: "Command that boots the source-of-truth Storybook. Defaults to `npm run storybook`." }, cwd: { type: "string", description: 'Directory the command runs in, relative to the config file. Defaults to ".".' } } }] }, isSourceOfTruthAdapter: { type: "boolean", description: "True only for the source-of-truth adapter's own config (@recursica/adapter-mantine-v8). Skips `sourceOfTruth` entirely — there's nothing above it to diverge from — and runs the own-drift golden check standalone, against just this project's own Storybook. Defaults to false." }, goldenThresholdPixels: { type: "number", minimum: 0, description: "Global visual diff threshold for the own-drift check (this project's live render vs. its own committed `test/golden/` image), in mismatched pixels, before a story is considered a failure. Same library/component on both sides, so this should stay tight. Defaults to 10. AI agents must not modify this field — see AGENT.md.", default: 10 }, sourceOfTruthThresholdPixels: { type: "number", minimum: 0, description: "Global visual diff threshold for the source-of-truth divergence check (this project's golden vs. the source-of-truth adapter's golden), in mismatched pixels, before a difference is flagged. Comparing across two different component libraries has legitimate structural variation (native control widgets, font rendering, etc.), so this is expected to be set much higher than `goldenThresholdPixels`. Ignored on the source-of-truth adapter's own config (`isSourceOfTruthAdapter: true`), which has no divergence check. Defaults to 3500. AI agents must not modify this field — see AGENT.md.", default: 3500 }, stories: { type: "object", additionalProperties: { type: "object", additionalProperties: !1, properties: { goldenThreshold: { type: "number", minimum: 0, description: "Diff threshold override for matching stories' own-drift check, in mismatched pixels. Overrides `goldenThresholdPixels`." }, sourceOfTruthThreshold: { type: "number", minimum: 0, description: "Diff threshold override for matching stories' source-of-truth divergence check, in mismatched pixels. Overrides `sourceOfTruthThresholdPixels`." }, viewport: { type: "object", additionalProperties: !1, required: ["width", "height"], description: "Browser viewport (CSS pixels) matching stories are captured at, instead of the default 800x600. For stories whose rendering depends on the page width itself, such as `@media` breakpoints, which a story can't change from inside.", properties: { width: { type: "integer", minimum: 1 }, height: { type: "integer", minimum: 1 } } }, exclude: { type: "boolean", description: "Skip matching stories entirely — no own-drift check, no divergence check, no golden captured. For stories that don't have a cross-adapter counterpart; use `excludeTitlePrefixes` to drop a whole title instead." } } }, description: "Per-story overrides, keyed by story id prefix (a story matches if its id equals the key or starts with it). When more than one key matches a story, the longest (most specific) key wins." }, excludeTitlePrefixes: { type: "array", items: { type: "string" }, description: "Storybook title prefixes to exclude from comparison, in addition to the built-in denylist (`Theme/*`, `Tokens/*`, `Introduction`)." }, reportHeader: { type: "string", description: 'Overrides the AI report header text shown in Dev Mode\'s "Full Report" export. Defaults to the contents of `report-header.txt`.' } }, $e = {
|
|
316
|
+
$schema: Pe,
|
|
317
|
+
$id: De,
|
|
318
|
+
title: Ee,
|
|
319
|
+
description: je,
|
|
320
|
+
type: Ae,
|
|
321
|
+
additionalProperties: Ne,
|
|
322
|
+
properties: _e
|
|
323
|
+
}, Ie = pe, X = new le.Ajv({ allErrors: !0, strict: !0 });
|
|
324
|
+
Ie(X);
|
|
325
|
+
const V = X.compile($e);
|
|
326
|
+
function Le(e, t) {
|
|
327
|
+
if (V(e)) return;
|
|
328
|
+
const r = (V.errors ?? []).map((s) => {
|
|
329
|
+
var i;
|
|
330
|
+
const a = (i = s.params) != null && i.additionalProperty ? ` '${s.params.additionalProperty}'` : "";
|
|
331
|
+
return ` - ${s.instancePath || "root"} ${s.message}${a}`;
|
|
201
332
|
}).join(`
|
|
202
333
|
`);
|
|
203
334
|
throw new Error(`Invalid ${t}:
|
|
204
335
|
${r}`);
|
|
205
336
|
}
|
|
206
|
-
const
|
|
207
|
-
function
|
|
208
|
-
const t =
|
|
209
|
-
if (!
|
|
337
|
+
const Re = "@recursica/adapter-mantine-v8", Y = "adapter-tester.config.json", q = "npm run storybook", Fe = 6006, J = 6011, H = "Mantine", Me = 10, Ce = 3500;
|
|
338
|
+
function Q(e) {
|
|
339
|
+
const t = l(e, "package.json");
|
|
340
|
+
if (!A(t))
|
|
210
341
|
throw new Error(
|
|
211
342
|
`No package.json found in ${e} — run adapter-tester from the root of the project being tested.`
|
|
212
343
|
);
|
|
213
|
-
return JSON.parse(
|
|
344
|
+
return JSON.parse(O(t, "utf8"));
|
|
214
345
|
}
|
|
215
|
-
function
|
|
216
|
-
var
|
|
217
|
-
const r = (
|
|
218
|
-
return
|
|
346
|
+
function Ge(e) {
|
|
347
|
+
var a;
|
|
348
|
+
const r = (a = Q(e).scripts) == null ? void 0 : a.storybook, s = r == null ? void 0 : r.match(/(?:-p|--port)[ =](\d+)/);
|
|
349
|
+
return s ? Number(s[1]) : Fe;
|
|
219
350
|
}
|
|
220
|
-
function
|
|
221
|
-
const r =
|
|
351
|
+
function Ue(e) {
|
|
352
|
+
const r = Q(e).name;
|
|
222
353
|
return r ? r.split("/").pop() : "Adapter";
|
|
223
354
|
}
|
|
224
|
-
function
|
|
225
|
-
const t =
|
|
226
|
-
if (!
|
|
355
|
+
function Ve(e) {
|
|
356
|
+
const t = l(e, Y);
|
|
357
|
+
if (!A(t))
|
|
227
358
|
return {};
|
|
228
|
-
const r = JSON.parse(
|
|
229
|
-
return
|
|
359
|
+
const r = JSON.parse(O(t, "utf8"));
|
|
360
|
+
return Le(r, t), r;
|
|
230
361
|
}
|
|
231
|
-
function
|
|
232
|
-
var
|
|
233
|
-
const r =
|
|
234
|
-
if (t.mantineAdapterVersion &&
|
|
362
|
+
function qe(e, t = {}) {
|
|
363
|
+
var f, v, P;
|
|
364
|
+
const r = Ve(e), s = r.isSourceOfTruthAdapter ?? !1;
|
|
365
|
+
if (t.mantineAdapterVersion && s)
|
|
235
366
|
throw new Error(
|
|
236
367
|
"--source-of-truth-version has no effect on the source-of-truth adapter's own config (isSourceOfTruthAdapter: true) — there's nothing to pin a version for."
|
|
237
368
|
);
|
|
238
|
-
const
|
|
239
|
-
command:
|
|
240
|
-
port:
|
|
241
|
-
cwd:
|
|
369
|
+
const a = r.name ?? Ue(e), i = ((f = r.storybook) == null ? void 0 : f.port) ?? Ge(e), g = ((v = r.storybook) == null ? void 0 : v.command) ?? q, y = C(e, ((P = r.storybook) == null ? void 0 : P.cwd) ?? "."), w = {
|
|
370
|
+
command: g,
|
|
371
|
+
port: i,
|
|
372
|
+
cwd: y,
|
|
242
373
|
reuseExistingServer: !process.env.CI,
|
|
243
|
-
cacheFile:
|
|
374
|
+
cacheFile: l(e, ".adapter-tester", "last-port-own.json"),
|
|
244
375
|
timeout: 120 * 1e3
|
|
245
|
-
},
|
|
246
|
-
goldenThresholdPixels: r.goldenThresholdPixels ??
|
|
247
|
-
sourceOfTruthThresholdPixels: r.sourceOfTruthThresholdPixels ??
|
|
376
|
+
}, u = {
|
|
377
|
+
goldenThresholdPixels: r.goldenThresholdPixels ?? Me,
|
|
378
|
+
sourceOfTruthThresholdPixels: r.sourceOfTruthThresholdPixels ?? Ce,
|
|
248
379
|
stories: r.stories,
|
|
249
380
|
excludeTitlePrefixes: r.excludeTitlePrefixes,
|
|
250
381
|
reportHeader: r.reportHeader,
|
|
@@ -254,153 +385,164 @@ function Ee(e, t = {}) {
|
|
|
254
385
|
// to that package's own `test/golden/` (the same directory used when
|
|
255
386
|
// that package runs adapter-tester directly), not a directory under
|
|
256
387
|
// wherever this config file lives.
|
|
257
|
-
goldenDir:
|
|
258
|
-
isSourceOfTruthAdapter:
|
|
388
|
+
goldenDir: l(y, "test", "golden"),
|
|
389
|
+
isSourceOfTruthAdapter: s,
|
|
259
390
|
// Overwritten by the CLI from --update-golden/--approve-divergence.
|
|
260
391
|
goldenMode: "check",
|
|
261
392
|
// Overwritten by the CLI from --divergence-only.
|
|
262
393
|
checkMode: "own"
|
|
263
394
|
};
|
|
264
|
-
if (
|
|
395
|
+
if (s)
|
|
265
396
|
return {
|
|
266
397
|
engineConfig: {
|
|
267
|
-
...
|
|
268
|
-
targets: [{ name:
|
|
398
|
+
...u,
|
|
399
|
+
targets: [{ name: a, url: `http://localhost:${i}` }]
|
|
269
400
|
},
|
|
270
|
-
webServers: [
|
|
401
|
+
webServers: [w]
|
|
271
402
|
};
|
|
272
|
-
const
|
|
403
|
+
const o = r.sourceOfTruth ?? {
|
|
273
404
|
type: "mantine-harness"
|
|
274
405
|
};
|
|
275
|
-
let
|
|
276
|
-
if (
|
|
406
|
+
let n, c, d, p;
|
|
407
|
+
if (o.type === "url") {
|
|
277
408
|
if (t.mantineAdapterVersion)
|
|
278
409
|
throw new Error(
|
|
279
410
|
`--source-of-truth-version has no effect with sourceOfTruth.type: "url" — that mode reads a sibling package's local checkout directly, not a published version.`
|
|
280
411
|
);
|
|
281
|
-
|
|
282
|
-
const
|
|
283
|
-
|
|
284
|
-
command:
|
|
285
|
-
port:
|
|
286
|
-
cwd:
|
|
412
|
+
n = o.name ?? H, c = o.port ?? J;
|
|
413
|
+
const b = C(e, o.cwd ?? ".");
|
|
414
|
+
d = {
|
|
415
|
+
command: o.command ?? q,
|
|
416
|
+
port: c,
|
|
417
|
+
cwd: b,
|
|
287
418
|
reuseExistingServer: !process.env.CI,
|
|
288
|
-
cacheFile:
|
|
419
|
+
cacheFile: l(e, ".adapter-tester", "last-port-source-of-truth.json"),
|
|
289
420
|
timeout: 120 * 1e3
|
|
290
|
-
},
|
|
421
|
+
}, p = {
|
|
291
422
|
type: "local",
|
|
292
|
-
dir:
|
|
423
|
+
dir: l(b, "test", "golden")
|
|
293
424
|
};
|
|
294
425
|
} else {
|
|
295
|
-
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
dir:
|
|
299
|
-
port:
|
|
300
|
-
mantineAdapterVersion:
|
|
301
|
-
storybookTemplateVersion:
|
|
302
|
-
tokensSource:
|
|
426
|
+
n = H, c = o.port ?? J;
|
|
427
|
+
const b = t.mantineAdapterVersion ?? o.mantineAdapterVersion, N = o.tokensSource ?? "target";
|
|
428
|
+
d = fe({
|
|
429
|
+
dir: l(e, ".adapter-tester/mantine-harness"),
|
|
430
|
+
port: c,
|
|
431
|
+
mantineAdapterVersion: b,
|
|
432
|
+
storybookTemplateVersion: o.storybookTemplateVersion,
|
|
433
|
+
tokensSource: N,
|
|
303
434
|
// `ownCwd`, not `cwd` — the project actually being tested (same
|
|
304
435
|
// reasoning `goldenDir` above uses), source of the 4 token files
|
|
305
436
|
// copied into the harness when `tokensSource` is "target".
|
|
306
|
-
targetDir:
|
|
307
|
-
}),
|
|
437
|
+
targetDir: y
|
|
438
|
+
}), p = {
|
|
308
439
|
type: "npm",
|
|
309
|
-
packageName:
|
|
310
|
-
versionSpec:
|
|
311
|
-
cacheDir:
|
|
440
|
+
packageName: Re,
|
|
441
|
+
versionSpec: b ?? "latest",
|
|
442
|
+
cacheDir: l(e, ".adapter-tester/mantine-golden-cache")
|
|
312
443
|
};
|
|
313
444
|
}
|
|
314
445
|
return {
|
|
315
446
|
engineConfig: {
|
|
316
|
-
...
|
|
447
|
+
...u,
|
|
317
448
|
targets: [
|
|
318
449
|
{
|
|
319
|
-
name:
|
|
320
|
-
url: `http://localhost:${
|
|
450
|
+
name: n,
|
|
451
|
+
url: `http://localhost:${c}`,
|
|
321
452
|
sourceOfTruth: !0
|
|
322
453
|
},
|
|
323
|
-
{ name:
|
|
454
|
+
{ name: a, url: `http://localhost:${i}` }
|
|
324
455
|
],
|
|
325
|
-
sourceOfTruthGolden:
|
|
456
|
+
sourceOfTruthGolden: p
|
|
326
457
|
},
|
|
327
458
|
// [sourceOfTruth, own] — Dev Mode needs both; the automated golden run
|
|
328
459
|
// (cli.ts) only ever boots the last entry, since its divergence check
|
|
329
460
|
// reads stored golden files, not a live source-of-truth page.
|
|
330
|
-
webServers: [
|
|
461
|
+
webServers: [d, w]
|
|
331
462
|
};
|
|
332
463
|
}
|
|
333
|
-
const
|
|
334
|
-
let
|
|
335
|
-
const
|
|
336
|
-
if (
|
|
337
|
-
const e =
|
|
464
|
+
const Je = M(z(import.meta.url)), He = oe(l(Je, "testing.js")).href, h = process.argv.slice(2);
|
|
465
|
+
let Z;
|
|
466
|
+
const _ = h.indexOf("--source-of-truth-version");
|
|
467
|
+
if (_ !== -1) {
|
|
468
|
+
const e = h[_ + 1];
|
|
338
469
|
if (e === void 0 || e.startsWith("--"))
|
|
339
470
|
throw new Error(
|
|
340
471
|
"--source-of-truth-version requires a value, e.g. --source-of-truth-version 0.53.0"
|
|
341
472
|
);
|
|
342
|
-
|
|
473
|
+
Z = e, h.splice(_, 2);
|
|
474
|
+
}
|
|
475
|
+
let D;
|
|
476
|
+
const $ = h.indexOf("--port");
|
|
477
|
+
if ($ !== -1) {
|
|
478
|
+
const e = h[$ + 1];
|
|
479
|
+
if (D = Number(e), e === void 0 || !Number.isInteger(D) || D <= 0)
|
|
480
|
+
throw new Error("--port requires a port number, e.g. --port 6020");
|
|
481
|
+
h.splice($, 2);
|
|
343
482
|
}
|
|
344
|
-
let
|
|
345
|
-
const
|
|
346
|
-
if (
|
|
347
|
-
const e =
|
|
483
|
+
let ee;
|
|
484
|
+
const I = h.indexOf("--story");
|
|
485
|
+
if (I !== -1) {
|
|
486
|
+
const e = h[I + 1];
|
|
348
487
|
if (e === void 0 || e.startsWith("--"))
|
|
349
488
|
throw new Error(
|
|
350
489
|
"--story requires a story id, e.g. --story ui-kit-button--loading"
|
|
351
490
|
);
|
|
352
|
-
|
|
491
|
+
ee = e, h.splice(I, 2);
|
|
353
492
|
}
|
|
354
|
-
const
|
|
355
|
-
mantineAdapterVersion:
|
|
493
|
+
const j = process.cwd(), { engineConfig: T, webServers: F } = qe(j, {
|
|
494
|
+
mantineAdapterVersion: Z
|
|
356
495
|
});
|
|
357
|
-
|
|
358
|
-
if (
|
|
359
|
-
if (
|
|
496
|
+
h.includes("--update-golden") ? T.goldenMode = "update-golden" : h.includes("--approve-divergence") && (T.goldenMode = "approve-divergence");
|
|
497
|
+
if (h.includes("--divergence-only")) {
|
|
498
|
+
if (T.isSourceOfTruthAdapter)
|
|
360
499
|
throw new Error(
|
|
361
500
|
"--divergence-only has nothing to diverge from — isSourceOfTruthAdapter is true in this project's adapter-tester.config.json."
|
|
362
501
|
);
|
|
363
|
-
|
|
364
|
-
} else if (
|
|
502
|
+
T.checkMode = "divergence";
|
|
503
|
+
} else if (T.goldenMode === "approve-divergence")
|
|
365
504
|
throw new Error(
|
|
366
505
|
"--approve-divergence only makes sense with --divergence-only — there's nothing to approve without the divergence check running."
|
|
367
506
|
);
|
|
368
|
-
|
|
369
|
-
const
|
|
507
|
+
h.includes("--dry-run") && (console.log(JSON.stringify({ engineConfig: T, webServers: F }, null, 2)), process.exit(0));
|
|
508
|
+
const We = /* @__PURE__ */ new Set([
|
|
370
509
|
"--dry-run",
|
|
371
510
|
"--serve",
|
|
372
511
|
"--update-golden",
|
|
373
512
|
"--approve-divergence",
|
|
374
513
|
"--divergence-only"
|
|
375
|
-
]),
|
|
376
|
-
|
|
514
|
+
]), Be = h.filter((e) => !We.has(e));
|
|
515
|
+
ze().catch((e) => {
|
|
377
516
|
console.error(e), process.exitCode = 1;
|
|
378
517
|
});
|
|
379
|
-
async function
|
|
380
|
-
if (
|
|
381
|
-
if (
|
|
518
|
+
async function ze() {
|
|
519
|
+
if (h.includes("--serve")) {
|
|
520
|
+
if (T.isSourceOfTruthAdapter)
|
|
382
521
|
throw new Error(
|
|
383
522
|
"Dev Mode (--serve) has nothing to sync this project's Storybook against — isSourceOfTruthAdapter is true in this project's adapter-tester.config.json."
|
|
384
523
|
);
|
|
385
|
-
await
|
|
524
|
+
await xe(T, F, {
|
|
525
|
+
port: D,
|
|
526
|
+
configPath: l(j, Y)
|
|
527
|
+
});
|
|
386
528
|
} else {
|
|
387
|
-
const e =
|
|
529
|
+
const e = F.at(-1);
|
|
388
530
|
if (!e)
|
|
389
531
|
throw new Error("resolveConfig() returned no webServers to boot.");
|
|
390
|
-
await
|
|
532
|
+
await Ke(e, T);
|
|
391
533
|
}
|
|
392
534
|
}
|
|
393
|
-
async function
|
|
394
|
-
const r =
|
|
395
|
-
|
|
396
|
-
const
|
|
397
|
-
if (!
|
|
535
|
+
async function Ke(e, t) {
|
|
536
|
+
const r = l(j, ".adapter-tester/run");
|
|
537
|
+
B(r, { recursive: !0 });
|
|
538
|
+
const s = t.targets.at(-1);
|
|
539
|
+
if (!s)
|
|
398
540
|
throw new Error("resolveConfig() returned no targets to check.");
|
|
399
|
-
const { url:
|
|
400
|
-
|
|
541
|
+
const { url: a, process: i } = await R(
|
|
542
|
+
L(s.name, e)
|
|
401
543
|
);
|
|
402
|
-
|
|
403
|
-
|
|
544
|
+
s.url = a, E(
|
|
545
|
+
l(r, "playwright.config.js"),
|
|
404
546
|
`// Generated by \`adapter-tester\` — do not edit, regenerated on every run.
|
|
405
547
|
import { defineConfig, devices } from "@playwright/test";
|
|
406
548
|
|
|
@@ -423,10 +565,10 @@ export default defineConfig({
|
|
|
423
565
|
});
|
|
424
566
|
`
|
|
425
567
|
), E(
|
|
426
|
-
|
|
568
|
+
l(r, "visual-regression.spec.js"),
|
|
427
569
|
`// Generated by \`adapter-tester\` — do not edit, regenerated on every run.
|
|
428
570
|
import { test, expect } from "@playwright/test";
|
|
429
|
-
import { resolveVisualRegressionPlan } from ${JSON.stringify(
|
|
571
|
+
import { resolveVisualRegressionPlan } from ${JSON.stringify(He)};
|
|
430
572
|
|
|
431
573
|
// \`test.describe\`/\`test\` are called here, not inside resolveVisualRegressionPlan,
|
|
432
574
|
// so Playwright's HTML report groups the suite under this file instead of a
|
|
@@ -436,7 +578,7 @@ const { ownTargetName, suiteLabel, stories, missingFromSourceOfTruth, checkStory
|
|
|
436
578
|
|
|
437
579
|
// Set from \`--story <story-id>\` — scopes this run to exactly one story and
|
|
438
580
|
// skips the (suite-wide) parity check, which has nothing to do with it.
|
|
439
|
-
const storyId = ${JSON.stringify(
|
|
581
|
+
const storyId = ${JSON.stringify(ee ?? null)};
|
|
440
582
|
const scopedStories = storyId
|
|
441
583
|
? stories.filter((story) => story.id === storyId)
|
|
442
584
|
: stories;
|
|
@@ -462,20 +604,20 @@ test.describe(\`\${ownTargetName} — \${suiteLabel}\`, () => {
|
|
|
462
604
|
`
|
|
463
605
|
);
|
|
464
606
|
try {
|
|
465
|
-
const
|
|
607
|
+
const g = re(
|
|
466
608
|
"npx",
|
|
467
609
|
[
|
|
468
610
|
"playwright",
|
|
469
611
|
"test",
|
|
470
612
|
"--config",
|
|
471
|
-
|
|
472
|
-
...
|
|
613
|
+
l(r, "playwright.config.js"),
|
|
614
|
+
...Be
|
|
473
615
|
],
|
|
474
|
-
{ stdio: "inherit", cwd:
|
|
616
|
+
{ stdio: "inherit", cwd: j, shell: process.platform === "win32" }
|
|
475
617
|
);
|
|
476
|
-
process.exitCode =
|
|
618
|
+
process.exitCode = g.status ?? 1;
|
|
477
619
|
} finally {
|
|
478
|
-
|
|
620
|
+
i == null || i.kill("SIGTERM");
|
|
479
621
|
}
|
|
480
622
|
}
|
|
481
623
|
//# sourceMappingURL=cli.js.map
|