@sdeverywhere/plugin-check 0.3.20 → 0.3.22

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 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(' sim-check baseline --save\t\tcopy the latest bundle to the `baselines` directory')
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] !== 'baseline') {
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 `baselines` directory under the current working directory, that the bundle filename
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 baselinesDir = joinPath(process.cwd(), 'baselines')
55
- if (!existsSync(baselinesDir)) {
56
- mkdirSync(baselinesDir, { recursive: true })
48
+ const bundlesDir = joinPath(process.cwd(), 'bundles')
49
+ if (!existsSync(bundlesDir)) {
50
+ mkdirSync(bundlesDir, { recursive: true })
57
51
  }
58
52
 
59
- const dstBundleFile = joinPath(baselinesDir, `${timestamp()}.js`)
53
+ const dstBundleFile = joinPath(bundlesDir, `${timestamp()}.js`)
60
54
  copyFileSync(srcBundleFile, dstBundleFile)
package/dist/index.cjs CHANGED
@@ -27,25 +27,66 @@ 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 src_exports = {};
31
- __export(src_exports, {
30
+ var index_exports = {};
31
+ __export(index_exports, {
32
32
  checkPlugin: () => checkPlugin
33
33
  });
34
- module.exports = __toCommonJS(src_exports);
34
+ module.exports = __toCommonJS(index_exports);
35
35
 
36
- // ../../node_modules/.pnpm/tsup@8.2.4_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.src || new URL("main.js", document.baseURI).href;
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 import_fs3 = require("fs");
42
- var import_promises = require("fs/promises");
43
- var import_path4 = require("path");
44
- var import_url4 = require("url");
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 import_chokidar = __toESM(require("chokidar"), 1);
47
46
  var import_check_core3 = require("@sdeverywhere/check-core");
48
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
+ }
89
+
49
90
  // src/run-suite.ts
50
91
  var import_perf_hooks = require("perf_hooks");
51
92
  var import_picocolors = __toESM(require("picocolors"), 1);
@@ -147,13 +188,13 @@ ${group.name}`);
147
188
  context.log("info", "");
148
189
  return allPassed;
149
190
  }
150
- function stat(label, n) {
191
+ function stat2(label, n) {
151
192
  return `${label}=${n.toFixed(1)}ms`;
152
193
  }
153
194
  function printPerfReportLine(context, perfReport) {
154
- const avg = stat("avg", perfReport.avgTime);
155
- const min = stat("min", perfReport.minTime);
156
- const max = stat("max", perfReport.maxTime);
195
+ const avg = stat2("avg", perfReport.avgTime);
196
+ const min = stat2("min", perfReport.minTime);
197
+ const max = stat2("max", perfReport.maxTime);
157
198
  context.log("info", ` ${avg} ${min} ${max}`);
158
199
  }
159
200
  function printPerfStats(context, comparisonConfig, report) {
@@ -303,7 +344,9 @@ async function createViteConfigForBundle(context, modelSpec) {
303
344
  replacement: "threads",
304
345
  customResolver: async function(source, importer, options) {
305
346
  const customResolver = (0, import_plugin_node_resolve.nodeResolve)({ browser: false });
306
- const resolved = await customResolver.resolveId.call(this, source, importer, options);
347
+ const resolveIdHook = customResolver.resolveId;
348
+ const resolveIdFn = typeof resolveIdHook === "function" ? resolveIdHook : resolveIdHook.handler;
349
+ const resolved = await resolveIdFn.call(this, source, importer, options);
307
350
  if (source === "threads/worker") {
308
351
  return resolved.id.replace("worker.mjs", "dist-esm/worker/index.js");
309
352
  } else {
@@ -367,29 +410,116 @@ let __non_webpack_require__ = () => {
367
410
  }
368
411
 
369
412
  // src/vite-config-for-report.ts
370
- var import_fs2 = require("fs");
371
- var import_path2 = require("path");
372
- var import_url2 = require("url");
413
+ var import_node_fs = require("fs");
414
+ var import_node_path3 = require("path");
415
+ var import_node_url2 = require("url");
373
416
  var import_plugin_replace = __toESM(require("@rollup/plugin-replace"), 1);
374
- var __filename3 = (0, import_url2.fileURLToPath)(importMetaUrl);
375
- var __dirname2 = (0, import_path2.dirname)(__filename3);
376
- function createViteConfigForReport(options, projDir, prepDir, currentBundleName, currentBundlePath, testConfigPath, suiteSummary) {
377
- const root = (0, import_path2.resolve)(__dirname2, "..", "template-report");
378
- const baselinesDir = (0, import_path2.resolve)(projDir, "baselines");
379
- if (!(0, import_fs2.existsSync)(baselinesDir)) {
380
- (0, import_fs2.mkdirSync)(baselinesDir, { recursive: true });
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
+ }
381
498
  }
382
- const templateSrcDir = (0, import_path2.resolve)(root, "src");
383
- const relProjDir = (0, import_path2.relative)(templateSrcDir, projDir);
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);
384
513
  const relProjDirPath = relProjDir.replaceAll("\\", "/");
385
- const baselinesPath = `${relProjDirPath}/baselines/*.js`;
514
+ const bundlesPath = `${relProjDirPath}/bundles/*.js`;
515
+ const currentBundleLastModified = (0, import_node_fs.statSync)(currentBundleSpec.path).mtime.toISOString();
386
516
  let reportPath;
387
517
  if (options?.reportPath) {
388
518
  reportPath = options.reportPath;
389
519
  } else {
390
- reportPath = (0, import_path2.join)(prepDir, "check-report");
520
+ reportPath = (0, import_node_path3.join)(prepDir, "check-report");
391
521
  }
392
- const outDir = (0, import_path2.relative)(root, reportPath);
522
+ const outDir = (0, import_node_path3.relative)(root, reportPath);
393
523
  const suiteSummaryJson = suiteSummary ? JSON.stringify(suiteSummary) : "";
394
524
  const alias = (find, replacement) => {
395
525
  return {
@@ -414,7 +544,7 @@ function createViteConfigForReport(options, projDir, prepDir, currentBundleName,
414
544
  // Use a custom cache directory under `prepDir`, as otherwise Vite will use
415
545
  // `packages/plugin-check/template-report/node_modules/.vite`, and we want to
416
546
  // avoid generating files in `template-report` (which should be read-only)
417
- cacheDir: (0, import_path2.join)(prepDir, ".vite-check-report"),
547
+ cacheDir: (0, import_node_path3.join)(prepDir, ".vite-check-report"),
418
548
  // Load static files from `static` (instead of the default `public`)
419
549
  // publicDir: 'static',
420
550
  // Don't clear the screen in dev mode so that we can see builder output
@@ -461,9 +591,9 @@ function createViteConfigForReport(options, projDir, prepDir, currentBundleName,
461
591
  alias: [
462
592
  // Use the configured "baseline" bundle if defined, otherwise use the "empty" bundle
463
593
  // (which will cause comparison tests to be skipped)
464
- alias("@_baseline_bundle_", options?.baseline ? options.baseline.path : "/src/empty-bundle.ts"),
594
+ alias("@_baseline_bundle_", baselineBundleSpec?.path || "/src/empty-bundle.ts"),
465
595
  // Use the configured "current" bundle
466
- alias("@_current_bundle_", currentBundlePath),
596
+ alias("@_current_bundle_", currentBundleSpec.path),
467
597
  // Use the configured test config file
468
598
  alias("@_test_config_", testConfigPath),
469
599
  // Make the overlay use the `messages.html` file that is written to the prep directory
@@ -483,10 +613,14 @@ function createViteConfigForReport(options, projDir, prepDir, currentBundleName,
483
613
  define: {
484
614
  // Inject the summary JSON into the build
485
615
  __SUITE_SUMMARY_JSON__: JSON.stringify(suiteSummaryJson),
486
- // Inject the baseline branch name
487
- __BASELINE_NAME__: JSON.stringify(options?.baseline?.name || ""),
488
- // Inject the current branch name
489
- __CURRENT_NAME__: JSON.stringify(currentBundleName)
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 || "")
490
624
  },
491
625
  plugins: [
492
626
  // Inject special values into the generated JS
@@ -498,15 +632,18 @@ function createViteConfigForReport(options, projDir, prepDir, currentBundleName,
498
632
  delimiters: ["", ""],
499
633
  values: {
500
634
  // Inject the path for baseline bundles
501
- // XXX: Note that we use './baselines/*.txt' instead of something special
635
+ // XXX: Note that we use './bundles/*.txt' instead of something special
502
636
  // like './__BASELINE_BUNDLES_PATH__' because sometimes Vite's dependency
503
637
  // scanner sees the latter (instead of the injected path) and reports
504
638
  // an error since the path does not exist. As a workaround, we use
505
- // './baselines/*.txt', which gets interpreted as the valid path
506
- // '.../template-report/src/baselines/*.txt' (see `baselines/unused.txt`).
507
- "./baselines/*.txt": baselinesPath
639
+ // './bundles/*.txt', which gets interpreted as the valid path
640
+ // '.../template-report/src/bundles/*.txt' (see `bundles/unused.txt`).
641
+ "./bundles/*.txt": bundlesPath
508
642
  }
509
- })
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)] : []
510
647
  ],
511
648
  build: {
512
649
  // Write output files to the configured directory (instead of the default `dist`);
@@ -545,19 +682,19 @@ function createViteConfigForReport(options, projDir, prepDir, currentBundleName,
545
682
  }
546
683
 
547
684
  // src/vite-config-for-tests.ts
548
- var import_path3 = require("path");
549
- var import_url3 = require("url");
685
+ var import_path2 = require("path");
686
+ var import_url2 = require("url");
550
687
  var import_plugin_replace2 = __toESM(require("@rollup/plugin-replace"), 1);
551
- var __filename4 = (0, import_url3.fileURLToPath)(importMetaUrl);
552
- var __dirname3 = (0, import_path3.dirname)(__filename4);
553
- function createViteConfigForTests(projDir, prepDir, mode) {
554
- const root = (0, import_path3.resolve)(__dirname3, "..", "template-tests");
555
- const templateSrcDir = (0, import_path3.resolve)(root, "src");
556
- const relProjDir = (0, import_path3.relative)(templateSrcDir, projDir);
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);
557
694
  const relProjDirPath = relProjDir.replaceAll("\\", "/");
558
695
  const yamlCheckGlobPatterns = `['${relProjDirPath}/**/checks/*.yaml', '${relProjDirPath}/**/*.check.yaml']`;
559
696
  const yamlComparisonGlobPatterns = `['${relProjDirPath}/**/comparisons/*.yaml']`;
560
- const outDir = (0, import_path3.relative)(root, prepDir);
697
+ const outDir = (0, import_path2.relative)(root, prepDir);
561
698
  return {
562
699
  // Don't use an external config file
563
700
  configFile: false,
@@ -621,26 +758,12 @@ var CheckPlugin = class {
621
758
  }
622
759
  async watch(config) {
623
760
  if (this.options?.testConfigPath === void 0) {
624
- await this.genTestConfig(config, "watch");
761
+ await this.genTestConfig("watch", config);
625
762
  }
626
- const testOptions = this.resolveTestOptions(config);
627
- 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);
628
765
  const server = await (0, import_vite.createServer)(viteConfig);
629
766
  await server.listen();
630
- const baselinesDir = "baselines";
631
- const watcher = import_chokidar.default.watch(baselinesDir, {
632
- // Watch paths are resolved relative to the project root directory
633
- cwd: config.rootDir,
634
- // Don't send initial "file added" events
635
- ignoreInitial: true,
636
- // XXX: Include a delay, otherwise on macOS we sometimes get multiple
637
- // change events when a file is saved just once
638
- awaitWriteFinish: {
639
- stabilityThreshold: 200
640
- }
641
- });
642
- watcher.on("add", () => server.restart());
643
- watcher.on("unlink", () => server.restart());
644
767
  }
645
768
  // TODO: Note that this plugin runs as a `postBuild` step because it currently
646
769
  // needs to run after other plugins, and those plugins need to run after the
@@ -650,7 +773,7 @@ var CheckPlugin = class {
650
773
  async postBuild(context, modelSpec) {
651
774
  const firstBuild = this.firstBuild;
652
775
  this.firstBuild = false;
653
- if (this.options?.current === void 0) {
776
+ if (this.options?.current?.path === void 0 && this.options?.current?.url === void 0) {
654
777
  if (context.config.mode === "development") {
655
778
  await this.copyPreviousBundle(context.config);
656
779
  }
@@ -660,50 +783,57 @@ var CheckPlugin = class {
660
783
  if (this.options?.testConfigPath === void 0) {
661
784
  if (context.config.mode === "production" || firstBuild) {
662
785
  context.log("info", "Generating model check test configuration...");
663
- await this.genTestConfig(context.config, "build");
786
+ await this.genTestConfig("bundle", context.config);
664
787
  }
665
788
  }
666
789
  if (context.config.mode === "production") {
667
- const testOptions = this.resolveTestOptions(context.config);
790
+ const testOptions = await this.resolveTestOptions("bundle", context.config);
668
791
  return this.runChecks(context, testOptions);
669
792
  } else {
670
793
  return true;
671
794
  }
672
795
  }
673
796
  async copyPreviousBundle(config) {
674
- const currentBundleFile = (0, import_path4.join)(config.prepDir, "check-bundle.js");
675
- if ((0, import_fs3.existsSync)(currentBundleFile)) {
676
- const baselinesDir = (0, import_path4.join)(config.rootDir, "baselines");
677
- if (!(0, import_fs3.existsSync)(baselinesDir)) {
678
- await (0, import_promises.mkdir)(baselinesDir, { recursive: true });
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 });
679
802
  }
680
- const previousBundleFile = (0, import_path4.join)(baselinesDir, "previous.js");
681
- await (0, import_promises.copyFile)(currentBundleFile, previousBundleFile);
803
+ const previousBundleFile = (0, import_node_path4.join)(bundlesDir, "previous.js");
804
+ await (0, import_promises3.copyFile)(currentBundleFile, previousBundleFile);
682
805
  }
683
806
  }
684
807
  async genCurrentBundle(context, modelSpec) {
685
808
  const viteConfig = await createViteConfigForBundle(context, modelSpec);
686
809
  await (0, import_vite.build)(viteConfig);
687
810
  }
688
- async genTestConfig(config, mode) {
811
+ async genTestConfig(mode, config) {
689
812
  const rootDir = config.rootDir;
690
813
  const prepDir = config.prepDir;
691
- const viteConfig = createViteConfigForTests(rootDir, prepDir, mode);
814
+ const viteConfig = createViteConfigForTests(mode, rootDir, prepDir);
692
815
  await (0, import_vite.build)(viteConfig);
693
816
  }
694
817
  async runChecks(context, testOptions) {
695
818
  context.log("info", "Running model checks...");
696
- const moduleR = await import(relativeToSourcePath(testOptions.currentBundlePath));
819
+ async function importBundleModule(bundleSpec) {
820
+ return import(relativeToSourcePath(bundleSpec.path));
821
+ }
822
+ const moduleR = await importBundleModule(testOptions.currentBundleSpec);
697
823
  const bundleR = moduleR.createBundle();
698
- const bundleNameR = testOptions.currentBundleName;
824
+ const bundleNameR = testOptions.currentBundleSpec.name;
699
825
  let bundleL;
700
826
  let bundleNameL;
701
- if (this.options?.baseline) {
702
- const moduleL = await import(relativeToSourcePath(this.options.baseline.path));
827
+ if (testOptions.baselineBundleSpec !== void 0) {
828
+ const moduleL = await importBundleModule(testOptions.baselineBundleSpec);
703
829
  const rawBundleL = moduleL.createBundle();
704
830
  if (rawBundleL.version === bundleR.version) {
705
831
  bundleL = rawBundleL;
706
- bundleNameL = this.options.baseline.name;
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
+ );
707
837
  }
708
838
  }
709
839
  const testConfigModule = await import(relativeToSourcePath(testOptions.testConfigPath));
@@ -720,47 +850,79 @@ var CheckPlugin = class {
720
850
  false
721
851
  );
722
852
  context.log("info", "Building model check report");
723
- const viteConfig = this.createViteConfigForReport(context.config, testOptions, result.suiteSummary);
853
+ const viteConfig = await this.createViteConfigForReport("bundle", context.config, testOptions, result.suiteSummary);
724
854
  await (0, import_vite.build)(viteConfig);
725
855
  return result.allChecksPassed;
726
856
  }
727
- resolveTestOptions(config) {
728
- let currentBundleName;
729
- let currentBundlePath;
730
- if (this.options?.current === void 0) {
731
- currentBundleName = "current";
732
- currentBundlePath = (0, import_path4.join)(config.prepDir, "check-bundle.js");
733
- } else {
734
- currentBundleName = this.options.current.name;
735
- currentBundlePath = this.options.current.path;
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(this.options?.current);
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
+ }
736
897
  }
737
898
  let testConfigPath;
738
899
  if (this.options?.testConfigPath === void 0) {
739
- testConfigPath = (0, import_path4.join)(config.prepDir, "check-tests.js");
900
+ testConfigPath = (0, import_node_path4.join)(config.prepDir, "check-tests.js");
740
901
  } else {
741
902
  testConfigPath = this.options.testConfigPath;
742
903
  }
743
904
  return {
744
- currentBundleName,
745
- currentBundlePath,
905
+ currentBundleSpec,
906
+ baselineBundleSpec,
746
907
  testConfigPath
747
908
  };
748
909
  }
749
- createViteConfigForReport(config, testOptions, suiteSummary) {
910
+ async createViteConfigForReport(mode, config, testOptions, suiteSummary) {
750
911
  return createViteConfigForReport(
912
+ mode,
751
913
  this.options,
752
914
  config.rootDir,
753
915
  config.prepDir,
754
- testOptions.currentBundleName,
755
- testOptions.currentBundlePath,
916
+ testOptions.currentBundleSpec,
917
+ testOptions.baselineBundleSpec,
756
918
  testOptions.testConfigPath,
757
919
  suiteSummary
758
920
  );
759
921
  }
760
922
  };
761
923
  function relativeToSourcePath(filePath) {
762
- const srcDir = (0, import_path4.dirname)((0, import_url4.fileURLToPath)(importMetaUrl));
763
- const relPath = (0, import_path4.relative)(srcDir, filePath);
924
+ const srcDir = (0, import_node_path4.dirname)((0, import_url3.fileURLToPath)(importMetaUrl));
925
+ const relPath = (0, import_node_path4.relative)(srcDir, filePath);
764
926
  return relPath.replaceAll("\\", "/");
765
927
  }
766
928
  // Annotate the CommonJS export names for ESM import in node: