@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/dist/index.js
CHANGED
|
@@ -1,12 +1,53 @@
|
|
|
1
1
|
// src/plugin.ts
|
|
2
2
|
import { existsSync as existsSync3 } from "fs";
|
|
3
|
-
import { copyFile, mkdir } from "fs/promises";
|
|
4
|
-
import { dirname as
|
|
3
|
+
import { copyFile, mkdir as mkdir2 } from "fs/promises";
|
|
4
|
+
import { dirname as dirname5, join as joinPath5, relative as relative5 } from "path";
|
|
5
5
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
6
6
|
import { build, createServer } from "vite";
|
|
7
|
-
import chokidar from "chokidar";
|
|
8
7
|
import { createConfig } from "@sdeverywhere/check-core";
|
|
9
8
|
|
|
9
|
+
// src/bundle-file-ops.ts
|
|
10
|
+
import { mkdir, readFile, stat, utimes, writeFile } from "fs/promises";
|
|
11
|
+
import { dirname, join as joinPath } from "path";
|
|
12
|
+
async function downloadBundle(url, name, lastModified, bundlesDir) {
|
|
13
|
+
const response = await fetch(url);
|
|
14
|
+
if (!response.ok) {
|
|
15
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
16
|
+
}
|
|
17
|
+
const bundleContent = await response.text();
|
|
18
|
+
const nameParts = name.split("/");
|
|
19
|
+
const filePath = joinPath(bundlesDir, ...nameParts) + ".js";
|
|
20
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
21
|
+
await writeFile(filePath, bundleContent, "utf8");
|
|
22
|
+
if (lastModified) {
|
|
23
|
+
const mtime = new Date(lastModified);
|
|
24
|
+
await utimes(filePath, mtime, mtime);
|
|
25
|
+
}
|
|
26
|
+
return filePath;
|
|
27
|
+
}
|
|
28
|
+
async function copyBundle(url, newName, bundlesDir) {
|
|
29
|
+
let srcPath;
|
|
30
|
+
if (url === "current") {
|
|
31
|
+
const sdePrepDir = joinPath(bundlesDir, "..", "sde-prep");
|
|
32
|
+
srcPath = joinPath(sdePrepDir, "check-bundle.js");
|
|
33
|
+
} else if (url.startsWith("file://")) {
|
|
34
|
+
srcPath = new URL(url).pathname;
|
|
35
|
+
} else {
|
|
36
|
+
throw new Error(`Cannot copy bundle with URL: ${url}`);
|
|
37
|
+
}
|
|
38
|
+
const bundleContent = await readFile(srcPath, "utf8");
|
|
39
|
+
const stats = await stat(srcPath);
|
|
40
|
+
const sourceLastModified = stats.mtime;
|
|
41
|
+
const nameParts = newName.split("/");
|
|
42
|
+
const filePath = joinPath(bundlesDir, ...nameParts) + ".js";
|
|
43
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
44
|
+
await writeFile(filePath, bundleContent, "utf8");
|
|
45
|
+
if (sourceLastModified) {
|
|
46
|
+
await utimes(filePath, sourceLastModified, sourceLastModified);
|
|
47
|
+
}
|
|
48
|
+
return filePath;
|
|
49
|
+
}
|
|
50
|
+
|
|
10
51
|
// src/run-suite.ts
|
|
11
52
|
import { performance } from "perf_hooks";
|
|
12
53
|
import pico from "picocolors";
|
|
@@ -33,14 +74,15 @@ async function runTestSuite(context, config, verbose) {
|
|
|
33
74
|
onComplete: (report) => {
|
|
34
75
|
try {
|
|
35
76
|
const t1 = performance.now();
|
|
36
|
-
const
|
|
77
|
+
const elapsedMillis = t1 - t0;
|
|
78
|
+
const elapsedSeconds = (elapsedMillis / 1e3).toFixed(1);
|
|
37
79
|
context.log("info", `
|
|
38
|
-
Test suite completed in ${
|
|
80
|
+
Test suite completed in ${elapsedSeconds}s`);
|
|
39
81
|
const allChecksPassed = printCheckSummary(context, report.checkReport, verbose);
|
|
40
82
|
if (report.comparisonReport) {
|
|
41
83
|
printPerfStats(context, config.comparison, report.comparisonReport);
|
|
42
84
|
}
|
|
43
|
-
const suiteSummary = suiteSummaryFromReport(report);
|
|
85
|
+
const suiteSummary = suiteSummaryFromReport(report, elapsedMillis);
|
|
44
86
|
resolve({
|
|
45
87
|
allChecksPassed,
|
|
46
88
|
suiteSummary
|
|
@@ -72,6 +114,9 @@ function printCheckSummary(context, checkReport, verbose) {
|
|
|
72
114
|
case "error":
|
|
73
115
|
statusChar = "\u203C";
|
|
74
116
|
break;
|
|
117
|
+
case "skipped":
|
|
118
|
+
statusChar = "\u2013";
|
|
119
|
+
break;
|
|
75
120
|
default:
|
|
76
121
|
statusChar = "";
|
|
77
122
|
break;
|
|
@@ -110,13 +155,13 @@ ${group.name}`);
|
|
|
110
155
|
context.log("info", "");
|
|
111
156
|
return allPassed;
|
|
112
157
|
}
|
|
113
|
-
function
|
|
158
|
+
function stat2(label, n) {
|
|
114
159
|
return `${label}=${n.toFixed(1)}ms`;
|
|
115
160
|
}
|
|
116
161
|
function printPerfReportLine(context, perfReport) {
|
|
117
|
-
const avg =
|
|
118
|
-
const min =
|
|
119
|
-
const max =
|
|
162
|
+
const avg = stat2("avg", perfReport.avgTime);
|
|
163
|
+
const min = stat2("min", perfReport.minTime);
|
|
164
|
+
const max = stat2("max", perfReport.maxTime);
|
|
120
165
|
context.log("info", ` ${avg} ${min} ${max}`);
|
|
121
166
|
}
|
|
122
167
|
function printPerfStats(context, comparisonConfig, report) {
|
|
@@ -129,13 +174,15 @@ function printPerfStats(context, comparisonConfig, report) {
|
|
|
129
174
|
}
|
|
130
175
|
|
|
131
176
|
// src/vite-config-for-bundle.ts
|
|
132
|
-
import { existsSync, statSync } from "fs";
|
|
133
|
-
import { basename, dirname, join as
|
|
177
|
+
import { existsSync, readFileSync, statSync } from "fs";
|
|
178
|
+
import { basename, dirname as dirname2, join as joinPath2, relative, resolve as resolvePath } from "path";
|
|
134
179
|
import { fileURLToPath } from "url";
|
|
135
180
|
import { nodeResolve } from "@rollup/plugin-node-resolve";
|
|
181
|
+
import { encodeImplVars } from "@sdeverywhere/check-core";
|
|
136
182
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
137
|
-
var __dirname2 =
|
|
183
|
+
var __dirname2 = dirname2(__filename2);
|
|
138
184
|
function injectModelSpec(context, modelSpec) {
|
|
185
|
+
const prepDir = context.config.prepDir;
|
|
139
186
|
const inputSpecs = [];
|
|
140
187
|
for (const modelInputSpec of modelSpec.inputs) {
|
|
141
188
|
if (modelInputSpec.defaultValue === void 0 || modelInputSpec.minValue === void 0 || modelInputSpec.maxValue === void 0) {
|
|
@@ -161,9 +208,20 @@ function injectModelSpec(context, modelSpec) {
|
|
|
161
208
|
...o
|
|
162
209
|
};
|
|
163
210
|
});
|
|
211
|
+
function readJsonListing() {
|
|
212
|
+
const path = joinPath2(prepDir, "build", "processed.json");
|
|
213
|
+
if (existsSync(path)) {
|
|
214
|
+
const json = readFileSync(path, "utf8");
|
|
215
|
+
return JSON.parse(json);
|
|
216
|
+
} else {
|
|
217
|
+
return {};
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
const listing = readJsonListing();
|
|
221
|
+
const varInstances = listing.varInstances || {};
|
|
222
|
+
const encodedImplVars = encodeImplVars(varInstances);
|
|
164
223
|
function stagedFileSize(filename) {
|
|
165
|
-
const
|
|
166
|
-
const path = joinPath(prepDir, "staged", "model", filename);
|
|
224
|
+
const path = joinPath2(prepDir, "staged", "model", filename);
|
|
167
225
|
if (existsSync(path)) {
|
|
168
226
|
return statSync(path).size;
|
|
169
227
|
} else {
|
|
@@ -175,6 +233,7 @@ function injectModelSpec(context, modelSpec) {
|
|
|
175
233
|
const moduleSrc = `
|
|
176
234
|
export const inputSpecs = ${JSON.stringify(inputSpecs)};
|
|
177
235
|
export const outputSpecs = ${JSON.stringify(outputSpecs)};
|
|
236
|
+
export const encodedImplVars = ${JSON.stringify(encodedImplVars)};
|
|
178
237
|
export const modelSizeInBytes = ${modelSizeInBytes};
|
|
179
238
|
export const dataSizeInBytes = ${dataSizeInBytes};
|
|
180
239
|
`;
|
|
@@ -217,7 +276,7 @@ async function createViteConfigForBundle(context, modelSpec) {
|
|
|
217
276
|
const root = resolvePath(__dirname2, "..", "template-bundle");
|
|
218
277
|
const prepDir = context.config.prepDir;
|
|
219
278
|
const outDir = relative(root, prepDir);
|
|
220
|
-
const modelWorkerPath =
|
|
279
|
+
const modelWorkerPath = joinPath2(prepDir, "staged", "model", "worker.js?raw");
|
|
221
280
|
return {
|
|
222
281
|
// Don't use an external config file
|
|
223
282
|
configFile: false,
|
|
@@ -252,7 +311,9 @@ async function createViteConfigForBundle(context, modelSpec) {
|
|
|
252
311
|
replacement: "threads",
|
|
253
312
|
customResolver: async function(source, importer, options) {
|
|
254
313
|
const customResolver = nodeResolve({ browser: false });
|
|
255
|
-
const
|
|
314
|
+
const resolveIdHook = customResolver.resolveId;
|
|
315
|
+
const resolveIdFn = typeof resolveIdHook === "function" ? resolveIdHook : resolveIdHook.handler;
|
|
316
|
+
const resolved = await resolveIdFn.call(this, source, importer, options);
|
|
256
317
|
if (source === "threads/worker") {
|
|
257
318
|
return resolved.id.replace("worker.mjs", "dist-esm/worker/index.js");
|
|
258
319
|
} else {
|
|
@@ -316,29 +377,116 @@ let __non_webpack_require__ = () => {
|
|
|
316
377
|
}
|
|
317
378
|
|
|
318
379
|
// src/vite-config-for-report.ts
|
|
319
|
-
import { existsSync as existsSync2, mkdirSync } from "fs";
|
|
320
|
-
import { dirname as
|
|
380
|
+
import { existsSync as existsSync2, mkdirSync, statSync as statSync2 } from "fs";
|
|
381
|
+
import { dirname as dirname3, relative as relative3, join as joinPath4, resolve as resolvePath2 } from "path";
|
|
321
382
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
322
383
|
import replace from "@rollup/plugin-replace";
|
|
384
|
+
|
|
385
|
+
// src/vite-local-bundles-plugin.ts
|
|
386
|
+
import { readdir, stat as stat3 } from "fs/promises";
|
|
387
|
+
import { join as joinPath3, relative as relative2, sep } from "path";
|
|
388
|
+
import { pathToFileURL } from "url";
|
|
389
|
+
import chokidar from "chokidar";
|
|
390
|
+
function localBundlesPlugin(bundlesDir) {
|
|
391
|
+
return {
|
|
392
|
+
name: "sde-local-bundles",
|
|
393
|
+
configureServer(server) {
|
|
394
|
+
const watcher = chokidar.watch(bundlesDir, {
|
|
395
|
+
// Don't send initial "file added" events
|
|
396
|
+
ignoreInitial: true,
|
|
397
|
+
// XXX: Include a delay, otherwise on macOS we sometimes get multiple
|
|
398
|
+
// change events when a file is saved just once
|
|
399
|
+
awaitWriteFinish: {
|
|
400
|
+
stabilityThreshold: 200
|
|
401
|
+
},
|
|
402
|
+
// Watch up to 10 levels deep
|
|
403
|
+
depth: 10
|
|
404
|
+
});
|
|
405
|
+
watcher.on("all", () => {
|
|
406
|
+
server.ws.send("bundles-changed", {});
|
|
407
|
+
});
|
|
408
|
+
server.httpServer?.on("close", () => {
|
|
409
|
+
watcher.close();
|
|
410
|
+
});
|
|
411
|
+
server.ws.on("list-bundles", async (_, client) => {
|
|
412
|
+
try {
|
|
413
|
+
const bundles = await scanBundlesRecursively(bundlesDir, bundlesDir);
|
|
414
|
+
client.send("list-bundles-success", { bundles });
|
|
415
|
+
} catch (error) {
|
|
416
|
+
console.error(`[sde-local-bundles] Failed to list bundles:`, error);
|
|
417
|
+
client.send("list-bundles-error", { error: error.message });
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
server.ws.on("download-bundle", async (data, client) => {
|
|
421
|
+
const { url, name, lastModified } = data;
|
|
422
|
+
try {
|
|
423
|
+
console.log(`[sde-local-bundles] Downloading bundle: name=${name} url=${url}`);
|
|
424
|
+
const filePath = await downloadBundle(url, name, lastModified, bundlesDir);
|
|
425
|
+
console.log(`[sde-local-bundles] Downloaded bundle to ${filePath}`);
|
|
426
|
+
client.send("download-bundle-success", { name, filePath: `${name}.js` });
|
|
427
|
+
} catch (error) {
|
|
428
|
+
console.error(`[sde-local-bundles] Failed to download bundle:`, error);
|
|
429
|
+
client.send("download-bundle-error", { name, error: error.message });
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
server.ws.on("copy-bundle", async (data, client) => {
|
|
433
|
+
const { url, name, newName } = data;
|
|
434
|
+
try {
|
|
435
|
+
console.log(`[sde-local-bundles] Copying bundle: src=${name} dst=${newName}`);
|
|
436
|
+
const filePath = await copyBundle(url, newName, bundlesDir);
|
|
437
|
+
console.log(`[sde-local-bundles] Copied bundle to ${filePath}`);
|
|
438
|
+
client.send("copy-bundle-success", { name: newName, filePath: `${newName}.js` });
|
|
439
|
+
} catch (error) {
|
|
440
|
+
console.error(`[sde-local-bundles] Failed to copy bundle:`, error);
|
|
441
|
+
client.send("copy-bundle-error", { name, error: error.message });
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
async function scanBundlesRecursively(dir, baseDir) {
|
|
448
|
+
const bundles = [];
|
|
449
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
450
|
+
for (const entry of entries) {
|
|
451
|
+
const fullPath = joinPath3(dir, entry.name);
|
|
452
|
+
if (entry.isDirectory()) {
|
|
453
|
+
const subBundles = await scanBundlesRecursively(fullPath, baseDir);
|
|
454
|
+
bundles.push(...subBundles);
|
|
455
|
+
} else if (entry.isFile() && entry.name.endsWith(".js")) {
|
|
456
|
+
const stats = await stat3(fullPath);
|
|
457
|
+
const relativePath = relative2(baseDir, fullPath);
|
|
458
|
+
const name = relativePath.replace(/\.js$/, "").split(sep).join("/");
|
|
459
|
+
bundles.push({
|
|
460
|
+
name,
|
|
461
|
+
url: pathToFileURL(fullPath).toString(),
|
|
462
|
+
lastModified: stats.mtime.toISOString()
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
return bundles;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// src/vite-config-for-report.ts
|
|
323
470
|
var __filename3 = fileURLToPath2(import.meta.url);
|
|
324
|
-
var __dirname3 =
|
|
325
|
-
function createViteConfigForReport(options, projDir, prepDir,
|
|
471
|
+
var __dirname3 = dirname3(__filename3);
|
|
472
|
+
function createViteConfigForReport(mode, options, projDir, prepDir, currentBundleSpec, baselineBundleSpec, testConfigPath, suiteSummary) {
|
|
326
473
|
const root = resolvePath2(__dirname3, "..", "template-report");
|
|
327
|
-
const
|
|
328
|
-
if (!existsSync2(
|
|
329
|
-
mkdirSync(
|
|
474
|
+
const bundlesDir = resolvePath2(projDir, "bundles");
|
|
475
|
+
if (!existsSync2(bundlesDir)) {
|
|
476
|
+
mkdirSync(bundlesDir, { recursive: true });
|
|
330
477
|
}
|
|
331
478
|
const templateSrcDir = resolvePath2(root, "src");
|
|
332
|
-
const relProjDir =
|
|
479
|
+
const relProjDir = relative3(templateSrcDir, projDir);
|
|
333
480
|
const relProjDirPath = relProjDir.replaceAll("\\", "/");
|
|
334
|
-
const
|
|
481
|
+
const bundlesPath = `${relProjDirPath}/bundles/*.js`;
|
|
482
|
+
const currentBundleLastModified = statSync2(currentBundleSpec.path).mtime.toISOString();
|
|
335
483
|
let reportPath;
|
|
336
484
|
if (options?.reportPath) {
|
|
337
485
|
reportPath = options.reportPath;
|
|
338
486
|
} else {
|
|
339
|
-
reportPath =
|
|
487
|
+
reportPath = joinPath4(prepDir, "check-report");
|
|
340
488
|
}
|
|
341
|
-
const outDir =
|
|
489
|
+
const outDir = relative3(root, reportPath);
|
|
342
490
|
const suiteSummaryJson = suiteSummary ? JSON.stringify(suiteSummary) : "";
|
|
343
491
|
const alias = (find, replacement) => {
|
|
344
492
|
return {
|
|
@@ -363,7 +511,7 @@ function createViteConfigForReport(options, projDir, prepDir, currentBundleName,
|
|
|
363
511
|
// Use a custom cache directory under `prepDir`, as otherwise Vite will use
|
|
364
512
|
// `packages/plugin-check/template-report/node_modules/.vite`, and we want to
|
|
365
513
|
// avoid generating files in `template-report` (which should be read-only)
|
|
366
|
-
cacheDir:
|
|
514
|
+
cacheDir: joinPath4(prepDir, ".vite-check-report"),
|
|
367
515
|
// Load static files from `static` (instead of the default `public`)
|
|
368
516
|
// publicDir: 'static',
|
|
369
517
|
// Don't clear the screen in dev mode so that we can see builder output
|
|
@@ -410,9 +558,9 @@ function createViteConfigForReport(options, projDir, prepDir, currentBundleName,
|
|
|
410
558
|
alias: [
|
|
411
559
|
// Use the configured "baseline" bundle if defined, otherwise use the "empty" bundle
|
|
412
560
|
// (which will cause comparison tests to be skipped)
|
|
413
|
-
alias("@_baseline_bundle_",
|
|
561
|
+
alias("@_baseline_bundle_", baselineBundleSpec?.path || "/src/empty-bundle.ts"),
|
|
414
562
|
// Use the configured "current" bundle
|
|
415
|
-
alias("@_current_bundle_",
|
|
563
|
+
alias("@_current_bundle_", currentBundleSpec.path),
|
|
416
564
|
// Use the configured test config file
|
|
417
565
|
alias("@_test_config_", testConfigPath),
|
|
418
566
|
// Make the overlay use the `messages.html` file that is written to the prep directory
|
|
@@ -432,10 +580,14 @@ function createViteConfigForReport(options, projDir, prepDir, currentBundleName,
|
|
|
432
580
|
define: {
|
|
433
581
|
// Inject the summary JSON into the build
|
|
434
582
|
__SUITE_SUMMARY_JSON__: JSON.stringify(suiteSummaryJson),
|
|
435
|
-
// Inject the baseline
|
|
436
|
-
__BASELINE_NAME__: JSON.stringify(
|
|
437
|
-
// Inject the current
|
|
438
|
-
__CURRENT_NAME__: JSON.stringify(
|
|
583
|
+
// Inject the baseline bundle name
|
|
584
|
+
__BASELINE_NAME__: JSON.stringify(baselineBundleSpec?.name || ""),
|
|
585
|
+
// Inject the current bundle name
|
|
586
|
+
__CURRENT_NAME__: JSON.stringify(currentBundleSpec.name),
|
|
587
|
+
// Inject the last modified time of the current bundle
|
|
588
|
+
__CURRENT_BUNDLE_LAST_MODIFIED__: JSON.stringify(currentBundleLastModified),
|
|
589
|
+
// Inject the remote bundles URL
|
|
590
|
+
__REMOTE_BUNDLES_URL__: JSON.stringify(options?.remoteBundlesUrl || "")
|
|
439
591
|
},
|
|
440
592
|
plugins: [
|
|
441
593
|
// Inject special values into the generated JS
|
|
@@ -447,15 +599,18 @@ function createViteConfigForReport(options, projDir, prepDir, currentBundleName,
|
|
|
447
599
|
delimiters: ["", ""],
|
|
448
600
|
values: {
|
|
449
601
|
// Inject the path for baseline bundles
|
|
450
|
-
// XXX: Note that we use './
|
|
602
|
+
// XXX: Note that we use './bundles/*.txt' instead of something special
|
|
451
603
|
// like './__BASELINE_BUNDLES_PATH__' because sometimes Vite's dependency
|
|
452
604
|
// scanner sees the latter (instead of the injected path) and reports
|
|
453
605
|
// an error since the path does not exist. As a workaround, we use
|
|
454
|
-
// './
|
|
455
|
-
// '.../template-report/src/
|
|
456
|
-
"./
|
|
606
|
+
// './bundles/*.txt', which gets interpreted as the valid path
|
|
607
|
+
// '.../template-report/src/bundles/*.txt' (see `bundles/unused.txt`).
|
|
608
|
+
"./bundles/*.txt": bundlesPath
|
|
457
609
|
}
|
|
458
|
-
})
|
|
610
|
+
}),
|
|
611
|
+
// When local development mode is active, enable the local bundles plugin that
|
|
612
|
+
// allows the report app to access the local bundles directory
|
|
613
|
+
...mode === "watch" ? [localBundlesPlugin(bundlesDir)] : []
|
|
459
614
|
],
|
|
460
615
|
build: {
|
|
461
616
|
// Write output files to the configured directory (instead of the default `dist`);
|
|
@@ -494,19 +649,19 @@ function createViteConfigForReport(options, projDir, prepDir, currentBundleName,
|
|
|
494
649
|
}
|
|
495
650
|
|
|
496
651
|
// src/vite-config-for-tests.ts
|
|
497
|
-
import { dirname as
|
|
652
|
+
import { dirname as dirname4, relative as relative4, resolve as resolvePath3 } from "path";
|
|
498
653
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
499
654
|
import replace2 from "@rollup/plugin-replace";
|
|
500
655
|
var __filename4 = fileURLToPath3(import.meta.url);
|
|
501
|
-
var __dirname4 =
|
|
502
|
-
function createViteConfigForTests(projDir, prepDir
|
|
656
|
+
var __dirname4 = dirname4(__filename4);
|
|
657
|
+
function createViteConfigForTests(mode, projDir, prepDir) {
|
|
503
658
|
const root = resolvePath3(__dirname4, "..", "template-tests");
|
|
504
659
|
const templateSrcDir = resolvePath3(root, "src");
|
|
505
|
-
const relProjDir =
|
|
660
|
+
const relProjDir = relative4(templateSrcDir, projDir);
|
|
506
661
|
const relProjDirPath = relProjDir.replaceAll("\\", "/");
|
|
507
662
|
const yamlCheckGlobPatterns = `['${relProjDirPath}/**/checks/*.yaml', '${relProjDirPath}/**/*.check.yaml']`;
|
|
508
663
|
const yamlComparisonGlobPatterns = `['${relProjDirPath}/**/comparisons/*.yaml']`;
|
|
509
|
-
const outDir =
|
|
664
|
+
const outDir = relative4(root, prepDir);
|
|
510
665
|
return {
|
|
511
666
|
// Don't use an external config file
|
|
512
667
|
configFile: false,
|
|
@@ -570,26 +725,12 @@ var CheckPlugin = class {
|
|
|
570
725
|
}
|
|
571
726
|
async watch(config) {
|
|
572
727
|
if (this.options?.testConfigPath === void 0) {
|
|
573
|
-
await this.genTestConfig(
|
|
728
|
+
await this.genTestConfig("watch", config);
|
|
574
729
|
}
|
|
575
|
-
const testOptions = this.resolveTestOptions(config);
|
|
576
|
-
const viteConfig = this.createViteConfigForReport(config, testOptions, void 0);
|
|
730
|
+
const testOptions = await this.resolveTestOptions("watch", config);
|
|
731
|
+
const viteConfig = await this.createViteConfigForReport("watch", config, testOptions, void 0);
|
|
577
732
|
const server = await createServer(viteConfig);
|
|
578
733
|
await server.listen();
|
|
579
|
-
const baselinesDir = "baselines";
|
|
580
|
-
const watcher = chokidar.watch(baselinesDir, {
|
|
581
|
-
// Watch paths are resolved relative to the project root directory
|
|
582
|
-
cwd: config.rootDir,
|
|
583
|
-
// Don't send initial "file added" events
|
|
584
|
-
ignoreInitial: true,
|
|
585
|
-
// XXX: Include a delay, otherwise on macOS we sometimes get multiple
|
|
586
|
-
// change events when a file is saved just once
|
|
587
|
-
awaitWriteFinish: {
|
|
588
|
-
stabilityThreshold: 200
|
|
589
|
-
}
|
|
590
|
-
});
|
|
591
|
-
watcher.on("add", () => server.restart());
|
|
592
|
-
watcher.on("unlink", () => server.restart());
|
|
593
734
|
}
|
|
594
735
|
// TODO: Note that this plugin runs as a `postBuild` step because it currently
|
|
595
736
|
// needs to run after other plugins, and those plugins need to run after the
|
|
@@ -599,7 +740,7 @@ var CheckPlugin = class {
|
|
|
599
740
|
async postBuild(context, modelSpec) {
|
|
600
741
|
const firstBuild = this.firstBuild;
|
|
601
742
|
this.firstBuild = false;
|
|
602
|
-
if (this.options?.current === void 0) {
|
|
743
|
+
if (this.options?.current?.path === void 0 && this.options?.current?.url === void 0) {
|
|
603
744
|
if (context.config.mode === "development") {
|
|
604
745
|
await this.copyPreviousBundle(context.config);
|
|
605
746
|
}
|
|
@@ -609,24 +750,24 @@ var CheckPlugin = class {
|
|
|
609
750
|
if (this.options?.testConfigPath === void 0) {
|
|
610
751
|
if (context.config.mode === "production" || firstBuild) {
|
|
611
752
|
context.log("info", "Generating model check test configuration...");
|
|
612
|
-
await this.genTestConfig(context.config
|
|
753
|
+
await this.genTestConfig("bundle", context.config);
|
|
613
754
|
}
|
|
614
755
|
}
|
|
615
756
|
if (context.config.mode === "production") {
|
|
616
|
-
const testOptions = this.resolveTestOptions(context.config);
|
|
757
|
+
const testOptions = await this.resolveTestOptions("bundle", context.config);
|
|
617
758
|
return this.runChecks(context, testOptions);
|
|
618
759
|
} else {
|
|
619
760
|
return true;
|
|
620
761
|
}
|
|
621
762
|
}
|
|
622
763
|
async copyPreviousBundle(config) {
|
|
623
|
-
const currentBundleFile =
|
|
764
|
+
const currentBundleFile = joinPath5(config.prepDir, "check-bundle.js");
|
|
624
765
|
if (existsSync3(currentBundleFile)) {
|
|
625
|
-
const
|
|
626
|
-
if (!existsSync3(
|
|
627
|
-
await
|
|
766
|
+
const bundlesDir = joinPath5(config.rootDir, "bundles");
|
|
767
|
+
if (!existsSync3(bundlesDir)) {
|
|
768
|
+
await mkdir2(bundlesDir, { recursive: true });
|
|
628
769
|
}
|
|
629
|
-
const previousBundleFile =
|
|
770
|
+
const previousBundleFile = joinPath5(bundlesDir, "previous.js");
|
|
630
771
|
await copyFile(currentBundleFile, previousBundleFile);
|
|
631
772
|
}
|
|
632
773
|
}
|
|
@@ -634,25 +775,32 @@ var CheckPlugin = class {
|
|
|
634
775
|
const viteConfig = await createViteConfigForBundle(context, modelSpec);
|
|
635
776
|
await build(viteConfig);
|
|
636
777
|
}
|
|
637
|
-
async genTestConfig(
|
|
778
|
+
async genTestConfig(mode, config) {
|
|
638
779
|
const rootDir = config.rootDir;
|
|
639
780
|
const prepDir = config.prepDir;
|
|
640
|
-
const viteConfig = createViteConfigForTests(rootDir, prepDir
|
|
781
|
+
const viteConfig = createViteConfigForTests(mode, rootDir, prepDir);
|
|
641
782
|
await build(viteConfig);
|
|
642
783
|
}
|
|
643
784
|
async runChecks(context, testOptions) {
|
|
644
785
|
context.log("info", "Running model checks...");
|
|
645
|
-
|
|
786
|
+
async function importBundleModule(bundleSpec) {
|
|
787
|
+
return import(relativeToSourcePath(bundleSpec.path));
|
|
788
|
+
}
|
|
789
|
+
const moduleR = await importBundleModule(testOptions.currentBundleSpec);
|
|
646
790
|
const bundleR = moduleR.createBundle();
|
|
647
|
-
const bundleNameR = testOptions.
|
|
791
|
+
const bundleNameR = testOptions.currentBundleSpec.name;
|
|
648
792
|
let bundleL;
|
|
649
793
|
let bundleNameL;
|
|
650
|
-
if (
|
|
651
|
-
const moduleL = await
|
|
794
|
+
if (testOptions.baselineBundleSpec !== void 0) {
|
|
795
|
+
const moduleL = await importBundleModule(testOptions.baselineBundleSpec);
|
|
652
796
|
const rawBundleL = moduleL.createBundle();
|
|
653
797
|
if (rawBundleL.version === bundleR.version) {
|
|
654
798
|
bundleL = rawBundleL;
|
|
655
|
-
bundleNameL =
|
|
799
|
+
bundleNameL = testOptions.baselineBundleSpec.name || "base";
|
|
800
|
+
} else {
|
|
801
|
+
console.warn(
|
|
802
|
+
`WARNING: Bundle version mismatch (baseline=${rawBundleL.version} current=${bundleR.version}); check tests will be run but comparisons will be skipped`
|
|
803
|
+
);
|
|
656
804
|
}
|
|
657
805
|
}
|
|
658
806
|
const testConfigModule = await import(relativeToSourcePath(testOptions.testConfigPath));
|
|
@@ -669,47 +817,79 @@ var CheckPlugin = class {
|
|
|
669
817
|
false
|
|
670
818
|
);
|
|
671
819
|
context.log("info", "Building model check report");
|
|
672
|
-
const viteConfig = this.createViteConfigForReport(context.config, testOptions, result.suiteSummary);
|
|
820
|
+
const viteConfig = await this.createViteConfigForReport("bundle", context.config, testOptions, result.suiteSummary);
|
|
673
821
|
await build(viteConfig);
|
|
674
822
|
return result.allChecksPassed;
|
|
675
823
|
}
|
|
676
|
-
resolveTestOptions(config) {
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
824
|
+
async resolveTestOptions(mode, config) {
|
|
825
|
+
async function resolveBundle(bundle) {
|
|
826
|
+
if (bundle?.url !== void 0) {
|
|
827
|
+
const localBundlePath = await downloadBundle(
|
|
828
|
+
bundle.url,
|
|
829
|
+
bundle.name,
|
|
830
|
+
// TODO: We don't know the last modified time of the remote bundle here, so we use
|
|
831
|
+
// undefined (which means the local file will be created with the current timestamp)
|
|
832
|
+
void 0,
|
|
833
|
+
joinPath5(config.rootDir, "bundles")
|
|
834
|
+
);
|
|
835
|
+
return {
|
|
836
|
+
name: bundle.name,
|
|
837
|
+
path: localBundlePath
|
|
838
|
+
};
|
|
839
|
+
} else if (bundle?.path !== void 0) {
|
|
840
|
+
return {
|
|
841
|
+
name: bundle.name,
|
|
842
|
+
path: bundle.path
|
|
843
|
+
};
|
|
844
|
+
} else {
|
|
845
|
+
return {
|
|
846
|
+
name: bundle?.name || "current",
|
|
847
|
+
path: joinPath5(config.prepDir, "check-bundle.js")
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
const currentBundleSpec = await resolveBundle(mode === "bundle" ? this.options?.current : void 0);
|
|
852
|
+
let baselineBundleSpec;
|
|
853
|
+
if (mode === "bundle" && this.options?.baseline) {
|
|
854
|
+
try {
|
|
855
|
+
baselineBundleSpec = await resolveBundle(this.options.baseline);
|
|
856
|
+
} catch (e) {
|
|
857
|
+
const name = this.options.baseline.name;
|
|
858
|
+
const loc = this.options.baseline.url || this.options.baseline.path;
|
|
859
|
+
console.warn(
|
|
860
|
+
`WARNING: Failed to load '${name}' bundle from '${loc}'; check tests will be run but comparisons will be skipped. Cause:`,
|
|
861
|
+
e
|
|
862
|
+
);
|
|
863
|
+
}
|
|
685
864
|
}
|
|
686
865
|
let testConfigPath;
|
|
687
866
|
if (this.options?.testConfigPath === void 0) {
|
|
688
|
-
testConfigPath =
|
|
867
|
+
testConfigPath = joinPath5(config.prepDir, "check-tests.js");
|
|
689
868
|
} else {
|
|
690
869
|
testConfigPath = this.options.testConfigPath;
|
|
691
870
|
}
|
|
692
871
|
return {
|
|
693
|
-
|
|
694
|
-
|
|
872
|
+
currentBundleSpec,
|
|
873
|
+
baselineBundleSpec,
|
|
695
874
|
testConfigPath
|
|
696
875
|
};
|
|
697
876
|
}
|
|
698
|
-
createViteConfigForReport(config, testOptions, suiteSummary) {
|
|
877
|
+
async createViteConfigForReport(mode, config, testOptions, suiteSummary) {
|
|
699
878
|
return createViteConfigForReport(
|
|
879
|
+
mode,
|
|
700
880
|
this.options,
|
|
701
881
|
config.rootDir,
|
|
702
882
|
config.prepDir,
|
|
703
|
-
testOptions.
|
|
704
|
-
testOptions.
|
|
883
|
+
testOptions.currentBundleSpec,
|
|
884
|
+
testOptions.baselineBundleSpec,
|
|
705
885
|
testOptions.testConfigPath,
|
|
706
886
|
suiteSummary
|
|
707
887
|
);
|
|
708
888
|
}
|
|
709
889
|
};
|
|
710
890
|
function relativeToSourcePath(filePath) {
|
|
711
|
-
const srcDir =
|
|
712
|
-
const relPath =
|
|
891
|
+
const srcDir = dirname5(fileURLToPath4(import.meta.url));
|
|
892
|
+
const relPath = relative5(srcDir, filePath);
|
|
713
893
|
return relPath.replaceAll("\\", "/");
|
|
714
894
|
}
|
|
715
895
|
export {
|