@sdeverywhere/plugin-check 0.3.19 → 0.3.21
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/bin/sde-check.js +8 -14
- package/dist/index.cjs +289 -109
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -5
- package/dist/index.d.ts +48 -5
- package/dist/index.js +275 -95
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
- package/template-bundle/src/bundle-model-runner.ts +159 -0
- package/template-bundle/src/bundle.ts +20 -73
- package/template-bundle/src/empty-model-spec.ts +4 -0
- package/template-bundle/src/impl-vars.ts +59 -0
- package/template-report/index.html +22 -0
- package/template-report/src/{baselines → bundles}/unused.txt +1 -1
- package/template-report/src/env.d.ts +3 -1
- package/template-report/src/index.ts +289 -84
package/bin/sde-check.js
CHANGED
|
@@ -11,8 +11,7 @@ import { join as joinPath } from 'path'
|
|
|
11
11
|
function printUsage() {
|
|
12
12
|
console.log()
|
|
13
13
|
console.log('Usage:')
|
|
14
|
-
console.log('
|
|
15
|
-
// console.log(' sim-check baseline --clear-all\tremove all bundles from the `baselines` directory')
|
|
14
|
+
console.log(' sde-check save-current-bundle\t\tcopy the latest bundle to the `bundles` directory')
|
|
16
15
|
console.log()
|
|
17
16
|
}
|
|
18
17
|
|
|
@@ -31,19 +30,14 @@ function timestamp() {
|
|
|
31
30
|
}
|
|
32
31
|
|
|
33
32
|
const args = process.argv.slice(2)
|
|
34
|
-
if (args.length !== 2 || args[0] !== '
|
|
35
|
-
printUsage()
|
|
36
|
-
process.exit(1)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (args[1] !== '--save') {
|
|
33
|
+
if (args.length !== 2 || args[0] !== 'save-current-bundle') {
|
|
40
34
|
printUsage()
|
|
41
35
|
process.exit(1)
|
|
42
36
|
}
|
|
43
37
|
|
|
44
38
|
// TODO: For now we make a number of assumptions (e.g., that the bundle will be copied to
|
|
45
|
-
// the `
|
|
46
|
-
// will contain the current timestamp); should make these configurable
|
|
39
|
+
// the `bundles` directory under the current working directory, that the bundle filename
|
|
40
|
+
// will contain the current timestamp); we should make these configurable
|
|
47
41
|
const prepDir = joinPath(process.cwd(), 'sde-prep')
|
|
48
42
|
const srcBundleFile = joinPath(prepDir, 'check-bundle.js')
|
|
49
43
|
if (!existsSync(srcBundleFile)) {
|
|
@@ -51,10 +45,10 @@ if (!existsSync(srcBundleFile)) {
|
|
|
51
45
|
process.exit(1)
|
|
52
46
|
}
|
|
53
47
|
|
|
54
|
-
const
|
|
55
|
-
if (!existsSync(
|
|
56
|
-
mkdirSync(
|
|
48
|
+
const bundlesDir = joinPath(process.cwd(), 'bundles')
|
|
49
|
+
if (!existsSync(bundlesDir)) {
|
|
50
|
+
mkdirSync(bundlesDir, { recursive: true })
|
|
57
51
|
}
|
|
58
52
|
|
|
59
|
-
const dstBundleFile = joinPath(
|
|
53
|
+
const dstBundleFile = joinPath(bundlesDir, `${timestamp()}.js`)
|
|
60
54
|
copyFileSync(srcBundleFile, dstBundleFile)
|
package/dist/index.cjs
CHANGED
|
@@ -27,24 +27,65 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
27
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
28
|
|
|
29
29
|
// src/index.ts
|
|
30
|
-
var
|
|
31
|
-
__export(
|
|
30
|
+
var index_exports = {};
|
|
31
|
+
__export(index_exports, {
|
|
32
32
|
checkPlugin: () => checkPlugin
|
|
33
33
|
});
|
|
34
|
-
module.exports = __toCommonJS(
|
|
34
|
+
module.exports = __toCommonJS(index_exports);
|
|
35
35
|
|
|
36
|
-
// ../../node_modules/.pnpm/tsup@8.
|
|
37
|
-
var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.src
|
|
36
|
+
// ../../node_modules/.pnpm/tsup@8.5.1_postcss@8.5.6_typescript@5.2.2/node_modules/tsup/assets/cjs_shims.js
|
|
37
|
+
var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
|
|
38
38
|
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
39
39
|
|
|
40
40
|
// src/plugin.ts
|
|
41
|
-
var
|
|
42
|
-
var
|
|
43
|
-
var
|
|
44
|
-
var
|
|
41
|
+
var import_node_fs2 = require("fs");
|
|
42
|
+
var import_promises3 = require("fs/promises");
|
|
43
|
+
var import_node_path4 = require("path");
|
|
44
|
+
var import_url3 = require("url");
|
|
45
45
|
var import_vite = require("vite");
|
|
46
|
-
var
|
|
47
|
-
|
|
46
|
+
var import_check_core3 = require("@sdeverywhere/check-core");
|
|
47
|
+
|
|
48
|
+
// src/bundle-file-ops.ts
|
|
49
|
+
var import_promises = require("fs/promises");
|
|
50
|
+
var import_node_path = require("path");
|
|
51
|
+
async function downloadBundle(url, name, lastModified, bundlesDir) {
|
|
52
|
+
const response = await fetch(url);
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
55
|
+
}
|
|
56
|
+
const bundleContent = await response.text();
|
|
57
|
+
const nameParts = name.split("/");
|
|
58
|
+
const filePath = (0, import_node_path.join)(bundlesDir, ...nameParts) + ".js";
|
|
59
|
+
await (0, import_promises.mkdir)((0, import_node_path.dirname)(filePath), { recursive: true });
|
|
60
|
+
await (0, import_promises.writeFile)(filePath, bundleContent, "utf8");
|
|
61
|
+
if (lastModified) {
|
|
62
|
+
const mtime = new Date(lastModified);
|
|
63
|
+
await (0, import_promises.utimes)(filePath, mtime, mtime);
|
|
64
|
+
}
|
|
65
|
+
return filePath;
|
|
66
|
+
}
|
|
67
|
+
async function copyBundle(url, newName, bundlesDir) {
|
|
68
|
+
let srcPath;
|
|
69
|
+
if (url === "current") {
|
|
70
|
+
const sdePrepDir = (0, import_node_path.join)(bundlesDir, "..", "sde-prep");
|
|
71
|
+
srcPath = (0, import_node_path.join)(sdePrepDir, "check-bundle.js");
|
|
72
|
+
} else if (url.startsWith("file://")) {
|
|
73
|
+
srcPath = new URL(url).pathname;
|
|
74
|
+
} else {
|
|
75
|
+
throw new Error(`Cannot copy bundle with URL: ${url}`);
|
|
76
|
+
}
|
|
77
|
+
const bundleContent = await (0, import_promises.readFile)(srcPath, "utf8");
|
|
78
|
+
const stats = await (0, import_promises.stat)(srcPath);
|
|
79
|
+
const sourceLastModified = stats.mtime;
|
|
80
|
+
const nameParts = newName.split("/");
|
|
81
|
+
const filePath = (0, import_node_path.join)(bundlesDir, ...nameParts) + ".js";
|
|
82
|
+
await (0, import_promises.mkdir)((0, import_node_path.dirname)(filePath), { recursive: true });
|
|
83
|
+
await (0, import_promises.writeFile)(filePath, bundleContent, "utf8");
|
|
84
|
+
if (sourceLastModified) {
|
|
85
|
+
await (0, import_promises.utimes)(filePath, sourceLastModified, sourceLastModified);
|
|
86
|
+
}
|
|
87
|
+
return filePath;
|
|
88
|
+
}
|
|
48
89
|
|
|
49
90
|
// src/run-suite.ts
|
|
50
91
|
var import_perf_hooks = require("perf_hooks");
|
|
@@ -66,14 +107,15 @@ async function runTestSuite(context, config, verbose) {
|
|
|
66
107
|
onComplete: (report) => {
|
|
67
108
|
try {
|
|
68
109
|
const t1 = import_perf_hooks.performance.now();
|
|
69
|
-
const
|
|
110
|
+
const elapsedMillis = t1 - t0;
|
|
111
|
+
const elapsedSeconds = (elapsedMillis / 1e3).toFixed(1);
|
|
70
112
|
context.log("info", `
|
|
71
|
-
Test suite completed in ${
|
|
113
|
+
Test suite completed in ${elapsedSeconds}s`);
|
|
72
114
|
const allChecksPassed = printCheckSummary(context, report.checkReport, verbose);
|
|
73
115
|
if (report.comparisonReport) {
|
|
74
116
|
printPerfStats(context, config.comparison, report.comparisonReport);
|
|
75
117
|
}
|
|
76
|
-
const suiteSummary = (0, import_check_core.suiteSummaryFromReport)(report);
|
|
118
|
+
const suiteSummary = (0, import_check_core.suiteSummaryFromReport)(report, elapsedMillis);
|
|
77
119
|
resolve({
|
|
78
120
|
allChecksPassed,
|
|
79
121
|
suiteSummary
|
|
@@ -105,6 +147,9 @@ function printCheckSummary(context, checkReport, verbose) {
|
|
|
105
147
|
case "error":
|
|
106
148
|
statusChar = "\u203C";
|
|
107
149
|
break;
|
|
150
|
+
case "skipped":
|
|
151
|
+
statusChar = "\u2013";
|
|
152
|
+
break;
|
|
108
153
|
default:
|
|
109
154
|
statusChar = "";
|
|
110
155
|
break;
|
|
@@ -143,13 +188,13 @@ ${group.name}`);
|
|
|
143
188
|
context.log("info", "");
|
|
144
189
|
return allPassed;
|
|
145
190
|
}
|
|
146
|
-
function
|
|
191
|
+
function stat2(label, n) {
|
|
147
192
|
return `${label}=${n.toFixed(1)}ms`;
|
|
148
193
|
}
|
|
149
194
|
function printPerfReportLine(context, perfReport) {
|
|
150
|
-
const avg =
|
|
151
|
-
const min =
|
|
152
|
-
const max =
|
|
195
|
+
const avg = stat2("avg", perfReport.avgTime);
|
|
196
|
+
const min = stat2("min", perfReport.minTime);
|
|
197
|
+
const max = stat2("max", perfReport.maxTime);
|
|
153
198
|
context.log("info", ` ${avg} ${min} ${max}`);
|
|
154
199
|
}
|
|
155
200
|
function printPerfStats(context, comparisonConfig, report) {
|
|
@@ -166,9 +211,11 @@ var import_fs = require("fs");
|
|
|
166
211
|
var import_path = require("path");
|
|
167
212
|
var import_url = require("url");
|
|
168
213
|
var import_plugin_node_resolve = require("@rollup/plugin-node-resolve");
|
|
214
|
+
var import_check_core2 = require("@sdeverywhere/check-core");
|
|
169
215
|
var __filename2 = (0, import_url.fileURLToPath)(importMetaUrl);
|
|
170
216
|
var __dirname = (0, import_path.dirname)(__filename2);
|
|
171
217
|
function injectModelSpec(context, modelSpec) {
|
|
218
|
+
const prepDir = context.config.prepDir;
|
|
172
219
|
const inputSpecs = [];
|
|
173
220
|
for (const modelInputSpec of modelSpec.inputs) {
|
|
174
221
|
if (modelInputSpec.defaultValue === void 0 || modelInputSpec.minValue === void 0 || modelInputSpec.maxValue === void 0) {
|
|
@@ -194,8 +241,19 @@ function injectModelSpec(context, modelSpec) {
|
|
|
194
241
|
...o
|
|
195
242
|
};
|
|
196
243
|
});
|
|
244
|
+
function readJsonListing() {
|
|
245
|
+
const path = (0, import_path.join)(prepDir, "build", "processed.json");
|
|
246
|
+
if ((0, import_fs.existsSync)(path)) {
|
|
247
|
+
const json = (0, import_fs.readFileSync)(path, "utf8");
|
|
248
|
+
return JSON.parse(json);
|
|
249
|
+
} else {
|
|
250
|
+
return {};
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
const listing = readJsonListing();
|
|
254
|
+
const varInstances = listing.varInstances || {};
|
|
255
|
+
const encodedImplVars = (0, import_check_core2.encodeImplVars)(varInstances);
|
|
197
256
|
function stagedFileSize(filename) {
|
|
198
|
-
const prepDir = context.config.prepDir;
|
|
199
257
|
const path = (0, import_path.join)(prepDir, "staged", "model", filename);
|
|
200
258
|
if ((0, import_fs.existsSync)(path)) {
|
|
201
259
|
return (0, import_fs.statSync)(path).size;
|
|
@@ -208,6 +266,7 @@ function injectModelSpec(context, modelSpec) {
|
|
|
208
266
|
const moduleSrc = `
|
|
209
267
|
export const inputSpecs = ${JSON.stringify(inputSpecs)};
|
|
210
268
|
export const outputSpecs = ${JSON.stringify(outputSpecs)};
|
|
269
|
+
export const encodedImplVars = ${JSON.stringify(encodedImplVars)};
|
|
211
270
|
export const modelSizeInBytes = ${modelSizeInBytes};
|
|
212
271
|
export const dataSizeInBytes = ${dataSizeInBytes};
|
|
213
272
|
`;
|
|
@@ -285,7 +344,9 @@ async function createViteConfigForBundle(context, modelSpec) {
|
|
|
285
344
|
replacement: "threads",
|
|
286
345
|
customResolver: async function(source, importer, options) {
|
|
287
346
|
const customResolver = (0, import_plugin_node_resolve.nodeResolve)({ browser: false });
|
|
288
|
-
const
|
|
347
|
+
const resolveIdHook = customResolver.resolveId;
|
|
348
|
+
const resolveIdFn = typeof resolveIdHook === "function" ? resolveIdHook : resolveIdHook.handler;
|
|
349
|
+
const resolved = await resolveIdFn.call(this, source, importer, options);
|
|
289
350
|
if (source === "threads/worker") {
|
|
290
351
|
return resolved.id.replace("worker.mjs", "dist-esm/worker/index.js");
|
|
291
352
|
} else {
|
|
@@ -349,29 +410,116 @@ let __non_webpack_require__ = () => {
|
|
|
349
410
|
}
|
|
350
411
|
|
|
351
412
|
// src/vite-config-for-report.ts
|
|
352
|
-
var
|
|
353
|
-
var
|
|
354
|
-
var
|
|
413
|
+
var import_node_fs = require("fs");
|
|
414
|
+
var import_node_path3 = require("path");
|
|
415
|
+
var import_node_url2 = require("url");
|
|
355
416
|
var import_plugin_replace = __toESM(require("@rollup/plugin-replace"), 1);
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
417
|
+
|
|
418
|
+
// src/vite-local-bundles-plugin.ts
|
|
419
|
+
var import_promises2 = require("fs/promises");
|
|
420
|
+
var import_node_path2 = require("path");
|
|
421
|
+
var import_node_url = require("url");
|
|
422
|
+
var import_chokidar = __toESM(require("chokidar"), 1);
|
|
423
|
+
function localBundlesPlugin(bundlesDir) {
|
|
424
|
+
return {
|
|
425
|
+
name: "sde-local-bundles",
|
|
426
|
+
configureServer(server) {
|
|
427
|
+
const watcher = import_chokidar.default.watch(bundlesDir, {
|
|
428
|
+
// Don't send initial "file added" events
|
|
429
|
+
ignoreInitial: true,
|
|
430
|
+
// XXX: Include a delay, otherwise on macOS we sometimes get multiple
|
|
431
|
+
// change events when a file is saved just once
|
|
432
|
+
awaitWriteFinish: {
|
|
433
|
+
stabilityThreshold: 200
|
|
434
|
+
},
|
|
435
|
+
// Watch up to 10 levels deep
|
|
436
|
+
depth: 10
|
|
437
|
+
});
|
|
438
|
+
watcher.on("all", () => {
|
|
439
|
+
server.ws.send("bundles-changed", {});
|
|
440
|
+
});
|
|
441
|
+
server.httpServer?.on("close", () => {
|
|
442
|
+
watcher.close();
|
|
443
|
+
});
|
|
444
|
+
server.ws.on("list-bundles", async (_, client) => {
|
|
445
|
+
try {
|
|
446
|
+
const bundles = await scanBundlesRecursively(bundlesDir, bundlesDir);
|
|
447
|
+
client.send("list-bundles-success", { bundles });
|
|
448
|
+
} catch (error) {
|
|
449
|
+
console.error(`[sde-local-bundles] Failed to list bundles:`, error);
|
|
450
|
+
client.send("list-bundles-error", { error: error.message });
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
server.ws.on("download-bundle", async (data, client) => {
|
|
454
|
+
const { url, name, lastModified } = data;
|
|
455
|
+
try {
|
|
456
|
+
console.log(`[sde-local-bundles] Downloading bundle: name=${name} url=${url}`);
|
|
457
|
+
const filePath = await downloadBundle(url, name, lastModified, bundlesDir);
|
|
458
|
+
console.log(`[sde-local-bundles] Downloaded bundle to ${filePath}`);
|
|
459
|
+
client.send("download-bundle-success", { name, filePath: `${name}.js` });
|
|
460
|
+
} catch (error) {
|
|
461
|
+
console.error(`[sde-local-bundles] Failed to download bundle:`, error);
|
|
462
|
+
client.send("download-bundle-error", { name, error: error.message });
|
|
463
|
+
}
|
|
464
|
+
});
|
|
465
|
+
server.ws.on("copy-bundle", async (data, client) => {
|
|
466
|
+
const { url, name, newName } = data;
|
|
467
|
+
try {
|
|
468
|
+
console.log(`[sde-local-bundles] Copying bundle: src=${name} dst=${newName}`);
|
|
469
|
+
const filePath = await copyBundle(url, newName, bundlesDir);
|
|
470
|
+
console.log(`[sde-local-bundles] Copied bundle to ${filePath}`);
|
|
471
|
+
client.send("copy-bundle-success", { name: newName, filePath: `${newName}.js` });
|
|
472
|
+
} catch (error) {
|
|
473
|
+
console.error(`[sde-local-bundles] Failed to copy bundle:`, error);
|
|
474
|
+
client.send("copy-bundle-error", { name, error: error.message });
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
async function scanBundlesRecursively(dir, baseDir) {
|
|
481
|
+
const bundles = [];
|
|
482
|
+
const entries = await (0, import_promises2.readdir)(dir, { withFileTypes: true });
|
|
483
|
+
for (const entry of entries) {
|
|
484
|
+
const fullPath = (0, import_node_path2.join)(dir, entry.name);
|
|
485
|
+
if (entry.isDirectory()) {
|
|
486
|
+
const subBundles = await scanBundlesRecursively(fullPath, baseDir);
|
|
487
|
+
bundles.push(...subBundles);
|
|
488
|
+
} else if (entry.isFile() && entry.name.endsWith(".js")) {
|
|
489
|
+
const stats = await (0, import_promises2.stat)(fullPath);
|
|
490
|
+
const relativePath = (0, import_node_path2.relative)(baseDir, fullPath);
|
|
491
|
+
const name = relativePath.replace(/\.js$/, "").split(import_node_path2.sep).join("/");
|
|
492
|
+
bundles.push({
|
|
493
|
+
name,
|
|
494
|
+
url: (0, import_node_url.pathToFileURL)(fullPath).toString(),
|
|
495
|
+
lastModified: stats.mtime.toISOString()
|
|
496
|
+
});
|
|
497
|
+
}
|
|
363
498
|
}
|
|
364
|
-
|
|
365
|
-
|
|
499
|
+
return bundles;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// src/vite-config-for-report.ts
|
|
503
|
+
var __filename3 = (0, import_node_url2.fileURLToPath)(importMetaUrl);
|
|
504
|
+
var __dirname2 = (0, import_node_path3.dirname)(__filename3);
|
|
505
|
+
function createViteConfigForReport(mode, options, projDir, prepDir, currentBundleSpec, baselineBundleSpec, testConfigPath, suiteSummary) {
|
|
506
|
+
const root = (0, import_node_path3.resolve)(__dirname2, "..", "template-report");
|
|
507
|
+
const bundlesDir = (0, import_node_path3.resolve)(projDir, "bundles");
|
|
508
|
+
if (!(0, import_node_fs.existsSync)(bundlesDir)) {
|
|
509
|
+
(0, import_node_fs.mkdirSync)(bundlesDir, { recursive: true });
|
|
510
|
+
}
|
|
511
|
+
const templateSrcDir = (0, import_node_path3.resolve)(root, "src");
|
|
512
|
+
const relProjDir = (0, import_node_path3.relative)(templateSrcDir, projDir);
|
|
366
513
|
const relProjDirPath = relProjDir.replaceAll("\\", "/");
|
|
367
|
-
const
|
|
514
|
+
const bundlesPath = `${relProjDirPath}/bundles/*.js`;
|
|
515
|
+
const currentBundleLastModified = (0, import_node_fs.statSync)(currentBundleSpec.path).mtime.toISOString();
|
|
368
516
|
let reportPath;
|
|
369
517
|
if (options?.reportPath) {
|
|
370
518
|
reportPath = options.reportPath;
|
|
371
519
|
} else {
|
|
372
|
-
reportPath = (0,
|
|
520
|
+
reportPath = (0, import_node_path3.join)(prepDir, "check-report");
|
|
373
521
|
}
|
|
374
|
-
const outDir = (0,
|
|
522
|
+
const outDir = (0, import_node_path3.relative)(root, reportPath);
|
|
375
523
|
const suiteSummaryJson = suiteSummary ? JSON.stringify(suiteSummary) : "";
|
|
376
524
|
const alias = (find, replacement) => {
|
|
377
525
|
return {
|
|
@@ -396,7 +544,7 @@ function createViteConfigForReport(options, projDir, prepDir, currentBundleName,
|
|
|
396
544
|
// Use a custom cache directory under `prepDir`, as otherwise Vite will use
|
|
397
545
|
// `packages/plugin-check/template-report/node_modules/.vite`, and we want to
|
|
398
546
|
// avoid generating files in `template-report` (which should be read-only)
|
|
399
|
-
cacheDir: (0,
|
|
547
|
+
cacheDir: (0, import_node_path3.join)(prepDir, ".vite-check-report"),
|
|
400
548
|
// Load static files from `static` (instead of the default `public`)
|
|
401
549
|
// publicDir: 'static',
|
|
402
550
|
// Don't clear the screen in dev mode so that we can see builder output
|
|
@@ -443,9 +591,9 @@ function createViteConfigForReport(options, projDir, prepDir, currentBundleName,
|
|
|
443
591
|
alias: [
|
|
444
592
|
// Use the configured "baseline" bundle if defined, otherwise use the "empty" bundle
|
|
445
593
|
// (which will cause comparison tests to be skipped)
|
|
446
|
-
alias("@_baseline_bundle_",
|
|
594
|
+
alias("@_baseline_bundle_", baselineBundleSpec?.path || "/src/empty-bundle.ts"),
|
|
447
595
|
// Use the configured "current" bundle
|
|
448
|
-
alias("@_current_bundle_",
|
|
596
|
+
alias("@_current_bundle_", currentBundleSpec.path),
|
|
449
597
|
// Use the configured test config file
|
|
450
598
|
alias("@_test_config_", testConfigPath),
|
|
451
599
|
// Make the overlay use the `messages.html` file that is written to the prep directory
|
|
@@ -465,10 +613,14 @@ function createViteConfigForReport(options, projDir, prepDir, currentBundleName,
|
|
|
465
613
|
define: {
|
|
466
614
|
// Inject the summary JSON into the build
|
|
467
615
|
__SUITE_SUMMARY_JSON__: JSON.stringify(suiteSummaryJson),
|
|
468
|
-
// Inject the baseline
|
|
469
|
-
__BASELINE_NAME__: JSON.stringify(
|
|
470
|
-
// Inject the current
|
|
471
|
-
__CURRENT_NAME__: JSON.stringify(
|
|
616
|
+
// Inject the baseline bundle name
|
|
617
|
+
__BASELINE_NAME__: JSON.stringify(baselineBundleSpec?.name || ""),
|
|
618
|
+
// Inject the current bundle name
|
|
619
|
+
__CURRENT_NAME__: JSON.stringify(currentBundleSpec.name),
|
|
620
|
+
// Inject the last modified time of the current bundle
|
|
621
|
+
__CURRENT_BUNDLE_LAST_MODIFIED__: JSON.stringify(currentBundleLastModified),
|
|
622
|
+
// Inject the remote bundles URL
|
|
623
|
+
__REMOTE_BUNDLES_URL__: JSON.stringify(options?.remoteBundlesUrl || "")
|
|
472
624
|
},
|
|
473
625
|
plugins: [
|
|
474
626
|
// Inject special values into the generated JS
|
|
@@ -480,15 +632,18 @@ function createViteConfigForReport(options, projDir, prepDir, currentBundleName,
|
|
|
480
632
|
delimiters: ["", ""],
|
|
481
633
|
values: {
|
|
482
634
|
// Inject the path for baseline bundles
|
|
483
|
-
// XXX: Note that we use './
|
|
635
|
+
// XXX: Note that we use './bundles/*.txt' instead of something special
|
|
484
636
|
// like './__BASELINE_BUNDLES_PATH__' because sometimes Vite's dependency
|
|
485
637
|
// scanner sees the latter (instead of the injected path) and reports
|
|
486
638
|
// an error since the path does not exist. As a workaround, we use
|
|
487
|
-
// './
|
|
488
|
-
// '.../template-report/src/
|
|
489
|
-
"./
|
|
639
|
+
// './bundles/*.txt', which gets interpreted as the valid path
|
|
640
|
+
// '.../template-report/src/bundles/*.txt' (see `bundles/unused.txt`).
|
|
641
|
+
"./bundles/*.txt": bundlesPath
|
|
490
642
|
}
|
|
491
|
-
})
|
|
643
|
+
}),
|
|
644
|
+
// When local development mode is active, enable the local bundles plugin that
|
|
645
|
+
// allows the report app to access the local bundles directory
|
|
646
|
+
...mode === "watch" ? [localBundlesPlugin(bundlesDir)] : []
|
|
492
647
|
],
|
|
493
648
|
build: {
|
|
494
649
|
// Write output files to the configured directory (instead of the default `dist`);
|
|
@@ -527,19 +682,19 @@ function createViteConfigForReport(options, projDir, prepDir, currentBundleName,
|
|
|
527
682
|
}
|
|
528
683
|
|
|
529
684
|
// src/vite-config-for-tests.ts
|
|
530
|
-
var
|
|
531
|
-
var
|
|
685
|
+
var import_path2 = require("path");
|
|
686
|
+
var import_url2 = require("url");
|
|
532
687
|
var import_plugin_replace2 = __toESM(require("@rollup/plugin-replace"), 1);
|
|
533
|
-
var __filename4 = (0,
|
|
534
|
-
var __dirname3 = (0,
|
|
535
|
-
function createViteConfigForTests(projDir, prepDir
|
|
536
|
-
const root = (0,
|
|
537
|
-
const templateSrcDir = (0,
|
|
538
|
-
const relProjDir = (0,
|
|
688
|
+
var __filename4 = (0, import_url2.fileURLToPath)(importMetaUrl);
|
|
689
|
+
var __dirname3 = (0, import_path2.dirname)(__filename4);
|
|
690
|
+
function createViteConfigForTests(mode, projDir, prepDir) {
|
|
691
|
+
const root = (0, import_path2.resolve)(__dirname3, "..", "template-tests");
|
|
692
|
+
const templateSrcDir = (0, import_path2.resolve)(root, "src");
|
|
693
|
+
const relProjDir = (0, import_path2.relative)(templateSrcDir, projDir);
|
|
539
694
|
const relProjDirPath = relProjDir.replaceAll("\\", "/");
|
|
540
695
|
const yamlCheckGlobPatterns = `['${relProjDirPath}/**/checks/*.yaml', '${relProjDirPath}/**/*.check.yaml']`;
|
|
541
696
|
const yamlComparisonGlobPatterns = `['${relProjDirPath}/**/comparisons/*.yaml']`;
|
|
542
|
-
const outDir = (0,
|
|
697
|
+
const outDir = (0, import_path2.relative)(root, prepDir);
|
|
543
698
|
return {
|
|
544
699
|
// Don't use an external config file
|
|
545
700
|
configFile: false,
|
|
@@ -603,26 +758,12 @@ var CheckPlugin = class {
|
|
|
603
758
|
}
|
|
604
759
|
async watch(config) {
|
|
605
760
|
if (this.options?.testConfigPath === void 0) {
|
|
606
|
-
await this.genTestConfig(
|
|
761
|
+
await this.genTestConfig("watch", config);
|
|
607
762
|
}
|
|
608
|
-
const testOptions = this.resolveTestOptions(config);
|
|
609
|
-
const viteConfig = this.createViteConfigForReport(config, testOptions, void 0);
|
|
763
|
+
const testOptions = await this.resolveTestOptions("watch", config);
|
|
764
|
+
const viteConfig = await this.createViteConfigForReport("watch", config, testOptions, void 0);
|
|
610
765
|
const server = await (0, import_vite.createServer)(viteConfig);
|
|
611
766
|
await server.listen();
|
|
612
|
-
const baselinesDir = "baselines";
|
|
613
|
-
const watcher = import_chokidar.default.watch(baselinesDir, {
|
|
614
|
-
// Watch paths are resolved relative to the project root directory
|
|
615
|
-
cwd: config.rootDir,
|
|
616
|
-
// Don't send initial "file added" events
|
|
617
|
-
ignoreInitial: true,
|
|
618
|
-
// XXX: Include a delay, otherwise on macOS we sometimes get multiple
|
|
619
|
-
// change events when a file is saved just once
|
|
620
|
-
awaitWriteFinish: {
|
|
621
|
-
stabilityThreshold: 200
|
|
622
|
-
}
|
|
623
|
-
});
|
|
624
|
-
watcher.on("add", () => server.restart());
|
|
625
|
-
watcher.on("unlink", () => server.restart());
|
|
626
767
|
}
|
|
627
768
|
// TODO: Note that this plugin runs as a `postBuild` step because it currently
|
|
628
769
|
// needs to run after other plugins, and those plugins need to run after the
|
|
@@ -632,7 +773,7 @@ var CheckPlugin = class {
|
|
|
632
773
|
async postBuild(context, modelSpec) {
|
|
633
774
|
const firstBuild = this.firstBuild;
|
|
634
775
|
this.firstBuild = false;
|
|
635
|
-
if (this.options?.current === void 0) {
|
|
776
|
+
if (this.options?.current?.path === void 0 && this.options?.current?.url === void 0) {
|
|
636
777
|
if (context.config.mode === "development") {
|
|
637
778
|
await this.copyPreviousBundle(context.config);
|
|
638
779
|
}
|
|
@@ -642,50 +783,57 @@ var CheckPlugin = class {
|
|
|
642
783
|
if (this.options?.testConfigPath === void 0) {
|
|
643
784
|
if (context.config.mode === "production" || firstBuild) {
|
|
644
785
|
context.log("info", "Generating model check test configuration...");
|
|
645
|
-
await this.genTestConfig(context.config
|
|
786
|
+
await this.genTestConfig("bundle", context.config);
|
|
646
787
|
}
|
|
647
788
|
}
|
|
648
789
|
if (context.config.mode === "production") {
|
|
649
|
-
const testOptions = this.resolveTestOptions(context.config);
|
|
790
|
+
const testOptions = await this.resolveTestOptions("bundle", context.config);
|
|
650
791
|
return this.runChecks(context, testOptions);
|
|
651
792
|
} else {
|
|
652
793
|
return true;
|
|
653
794
|
}
|
|
654
795
|
}
|
|
655
796
|
async copyPreviousBundle(config) {
|
|
656
|
-
const currentBundleFile = (0,
|
|
657
|
-
if ((0,
|
|
658
|
-
const
|
|
659
|
-
if (!(0,
|
|
660
|
-
await (0,
|
|
797
|
+
const currentBundleFile = (0, import_node_path4.join)(config.prepDir, "check-bundle.js");
|
|
798
|
+
if ((0, import_node_fs2.existsSync)(currentBundleFile)) {
|
|
799
|
+
const bundlesDir = (0, import_node_path4.join)(config.rootDir, "bundles");
|
|
800
|
+
if (!(0, import_node_fs2.existsSync)(bundlesDir)) {
|
|
801
|
+
await (0, import_promises3.mkdir)(bundlesDir, { recursive: true });
|
|
661
802
|
}
|
|
662
|
-
const previousBundleFile = (0,
|
|
663
|
-
await (0,
|
|
803
|
+
const previousBundleFile = (0, import_node_path4.join)(bundlesDir, "previous.js");
|
|
804
|
+
await (0, import_promises3.copyFile)(currentBundleFile, previousBundleFile);
|
|
664
805
|
}
|
|
665
806
|
}
|
|
666
807
|
async genCurrentBundle(context, modelSpec) {
|
|
667
808
|
const viteConfig = await createViteConfigForBundle(context, modelSpec);
|
|
668
809
|
await (0, import_vite.build)(viteConfig);
|
|
669
810
|
}
|
|
670
|
-
async genTestConfig(
|
|
811
|
+
async genTestConfig(mode, config) {
|
|
671
812
|
const rootDir = config.rootDir;
|
|
672
813
|
const prepDir = config.prepDir;
|
|
673
|
-
const viteConfig = createViteConfigForTests(rootDir, prepDir
|
|
814
|
+
const viteConfig = createViteConfigForTests(mode, rootDir, prepDir);
|
|
674
815
|
await (0, import_vite.build)(viteConfig);
|
|
675
816
|
}
|
|
676
817
|
async runChecks(context, testOptions) {
|
|
677
818
|
context.log("info", "Running model checks...");
|
|
678
|
-
|
|
819
|
+
async function importBundleModule(bundleSpec) {
|
|
820
|
+
return import(relativeToSourcePath(bundleSpec.path));
|
|
821
|
+
}
|
|
822
|
+
const moduleR = await importBundleModule(testOptions.currentBundleSpec);
|
|
679
823
|
const bundleR = moduleR.createBundle();
|
|
680
|
-
const bundleNameR = testOptions.
|
|
824
|
+
const bundleNameR = testOptions.currentBundleSpec.name;
|
|
681
825
|
let bundleL;
|
|
682
826
|
let bundleNameL;
|
|
683
|
-
if (
|
|
684
|
-
const moduleL = await
|
|
827
|
+
if (testOptions.baselineBundleSpec !== void 0) {
|
|
828
|
+
const moduleL = await importBundleModule(testOptions.baselineBundleSpec);
|
|
685
829
|
const rawBundleL = moduleL.createBundle();
|
|
686
830
|
if (rawBundleL.version === bundleR.version) {
|
|
687
831
|
bundleL = rawBundleL;
|
|
688
|
-
bundleNameL =
|
|
832
|
+
bundleNameL = testOptions.baselineBundleSpec.name || "base";
|
|
833
|
+
} else {
|
|
834
|
+
console.warn(
|
|
835
|
+
`WARNING: Bundle version mismatch (baseline=${rawBundleL.version} current=${bundleR.version}); check tests will be run but comparisons will be skipped`
|
|
836
|
+
);
|
|
689
837
|
}
|
|
690
838
|
}
|
|
691
839
|
const testConfigModule = await import(relativeToSourcePath(testOptions.testConfigPath));
|
|
@@ -694,7 +842,7 @@ var CheckPlugin = class {
|
|
|
694
842
|
bundleNameR
|
|
695
843
|
};
|
|
696
844
|
const configOptions = await testConfigModule.getConfigOptions(bundleL, bundleR, configInitOptions);
|
|
697
|
-
const checkConfig = await (0,
|
|
845
|
+
const checkConfig = await (0, import_check_core3.createConfig)(configOptions);
|
|
698
846
|
const result = await runTestSuite(
|
|
699
847
|
context,
|
|
700
848
|
checkConfig,
|
|
@@ -702,47 +850,79 @@ var CheckPlugin = class {
|
|
|
702
850
|
false
|
|
703
851
|
);
|
|
704
852
|
context.log("info", "Building model check report");
|
|
705
|
-
const viteConfig = this.createViteConfigForReport(context.config, testOptions, result.suiteSummary);
|
|
853
|
+
const viteConfig = await this.createViteConfigForReport("bundle", context.config, testOptions, result.suiteSummary);
|
|
706
854
|
await (0, import_vite.build)(viteConfig);
|
|
707
855
|
return result.allChecksPassed;
|
|
708
856
|
}
|
|
709
|
-
resolveTestOptions(config) {
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
857
|
+
async resolveTestOptions(mode, config) {
|
|
858
|
+
async function resolveBundle(bundle) {
|
|
859
|
+
if (bundle?.url !== void 0) {
|
|
860
|
+
const localBundlePath = await downloadBundle(
|
|
861
|
+
bundle.url,
|
|
862
|
+
bundle.name,
|
|
863
|
+
// TODO: We don't know the last modified time of the remote bundle here, so we use
|
|
864
|
+
// undefined (which means the local file will be created with the current timestamp)
|
|
865
|
+
void 0,
|
|
866
|
+
(0, import_node_path4.join)(config.rootDir, "bundles")
|
|
867
|
+
);
|
|
868
|
+
return {
|
|
869
|
+
name: bundle.name,
|
|
870
|
+
path: localBundlePath
|
|
871
|
+
};
|
|
872
|
+
} else if (bundle?.path !== void 0) {
|
|
873
|
+
return {
|
|
874
|
+
name: bundle.name,
|
|
875
|
+
path: bundle.path
|
|
876
|
+
};
|
|
877
|
+
} else {
|
|
878
|
+
return {
|
|
879
|
+
name: bundle?.name || "current",
|
|
880
|
+
path: (0, import_node_path4.join)(config.prepDir, "check-bundle.js")
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
const currentBundleSpec = await resolveBundle(mode === "bundle" ? this.options?.current : void 0);
|
|
885
|
+
let baselineBundleSpec;
|
|
886
|
+
if (mode === "bundle" && this.options?.baseline) {
|
|
887
|
+
try {
|
|
888
|
+
baselineBundleSpec = await resolveBundle(this.options.baseline);
|
|
889
|
+
} catch (e) {
|
|
890
|
+
const name = this.options.baseline.name;
|
|
891
|
+
const loc = this.options.baseline.url || this.options.baseline.path;
|
|
892
|
+
console.warn(
|
|
893
|
+
`WARNING: Failed to load '${name}' bundle from '${loc}'; check tests will be run but comparisons will be skipped. Cause:`,
|
|
894
|
+
e
|
|
895
|
+
);
|
|
896
|
+
}
|
|
718
897
|
}
|
|
719
898
|
let testConfigPath;
|
|
720
899
|
if (this.options?.testConfigPath === void 0) {
|
|
721
|
-
testConfigPath = (0,
|
|
900
|
+
testConfigPath = (0, import_node_path4.join)(config.prepDir, "check-tests.js");
|
|
722
901
|
} else {
|
|
723
902
|
testConfigPath = this.options.testConfigPath;
|
|
724
903
|
}
|
|
725
904
|
return {
|
|
726
|
-
|
|
727
|
-
|
|
905
|
+
currentBundleSpec,
|
|
906
|
+
baselineBundleSpec,
|
|
728
907
|
testConfigPath
|
|
729
908
|
};
|
|
730
909
|
}
|
|
731
|
-
createViteConfigForReport(config, testOptions, suiteSummary) {
|
|
910
|
+
async createViteConfigForReport(mode, config, testOptions, suiteSummary) {
|
|
732
911
|
return createViteConfigForReport(
|
|
912
|
+
mode,
|
|
733
913
|
this.options,
|
|
734
914
|
config.rootDir,
|
|
735
915
|
config.prepDir,
|
|
736
|
-
testOptions.
|
|
737
|
-
testOptions.
|
|
916
|
+
testOptions.currentBundleSpec,
|
|
917
|
+
testOptions.baselineBundleSpec,
|
|
738
918
|
testOptions.testConfigPath,
|
|
739
919
|
suiteSummary
|
|
740
920
|
);
|
|
741
921
|
}
|
|
742
922
|
};
|
|
743
923
|
function relativeToSourcePath(filePath) {
|
|
744
|
-
const srcDir = (0,
|
|
745
|
-
const relPath = (0,
|
|
924
|
+
const srcDir = (0, import_node_path4.dirname)((0, import_url3.fileURLToPath)(importMetaUrl));
|
|
925
|
+
const relPath = (0, import_node_path4.relative)(srcDir, filePath);
|
|
746
926
|
return relPath.replaceAll("\\", "/");
|
|
747
927
|
}
|
|
748
928
|
// Annotate the CommonJS export names for ESM import in node:
|