@sdeverywhere/plugin-check 0.3.20 → 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/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 dirname4, join as joinPath3, relative as relative4 } from "path";
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";
@@ -114,13 +155,13 @@ ${group.name}`);
114
155
  context.log("info", "");
115
156
  return allPassed;
116
157
  }
117
- function stat(label, n) {
158
+ function stat2(label, n) {
118
159
  return `${label}=${n.toFixed(1)}ms`;
119
160
  }
120
161
  function printPerfReportLine(context, perfReport) {
121
- const avg = stat("avg", perfReport.avgTime);
122
- const min = stat("min", perfReport.minTime);
123
- const max = stat("max", perfReport.maxTime);
162
+ const avg = stat2("avg", perfReport.avgTime);
163
+ const min = stat2("min", perfReport.minTime);
164
+ const max = stat2("max", perfReport.maxTime);
124
165
  context.log("info", ` ${avg} ${min} ${max}`);
125
166
  }
126
167
  function printPerfStats(context, comparisonConfig, report) {
@@ -134,12 +175,12 @@ function printPerfStats(context, comparisonConfig, report) {
134
175
 
135
176
  // src/vite-config-for-bundle.ts
136
177
  import { existsSync, readFileSync, statSync } from "fs";
137
- import { basename, dirname, join as joinPath, relative, resolve as resolvePath } from "path";
178
+ import { basename, dirname as dirname2, join as joinPath2, relative, resolve as resolvePath } from "path";
138
179
  import { fileURLToPath } from "url";
139
180
  import { nodeResolve } from "@rollup/plugin-node-resolve";
140
181
  import { encodeImplVars } from "@sdeverywhere/check-core";
141
182
  var __filename2 = fileURLToPath(import.meta.url);
142
- var __dirname2 = dirname(__filename2);
183
+ var __dirname2 = dirname2(__filename2);
143
184
  function injectModelSpec(context, modelSpec) {
144
185
  const prepDir = context.config.prepDir;
145
186
  const inputSpecs = [];
@@ -168,7 +209,7 @@ function injectModelSpec(context, modelSpec) {
168
209
  };
169
210
  });
170
211
  function readJsonListing() {
171
- const path = joinPath(prepDir, "build", "processed.json");
212
+ const path = joinPath2(prepDir, "build", "processed.json");
172
213
  if (existsSync(path)) {
173
214
  const json = readFileSync(path, "utf8");
174
215
  return JSON.parse(json);
@@ -180,7 +221,7 @@ function injectModelSpec(context, modelSpec) {
180
221
  const varInstances = listing.varInstances || {};
181
222
  const encodedImplVars = encodeImplVars(varInstances);
182
223
  function stagedFileSize(filename) {
183
- const path = joinPath(prepDir, "staged", "model", filename);
224
+ const path = joinPath2(prepDir, "staged", "model", filename);
184
225
  if (existsSync(path)) {
185
226
  return statSync(path).size;
186
227
  } else {
@@ -235,7 +276,7 @@ async function createViteConfigForBundle(context, modelSpec) {
235
276
  const root = resolvePath(__dirname2, "..", "template-bundle");
236
277
  const prepDir = context.config.prepDir;
237
278
  const outDir = relative(root, prepDir);
238
- const modelWorkerPath = joinPath(prepDir, "staged", "model", "worker.js?raw");
279
+ const modelWorkerPath = joinPath2(prepDir, "staged", "model", "worker.js?raw");
239
280
  return {
240
281
  // Don't use an external config file
241
282
  configFile: false,
@@ -270,7 +311,9 @@ async function createViteConfigForBundle(context, modelSpec) {
270
311
  replacement: "threads",
271
312
  customResolver: async function(source, importer, options) {
272
313
  const customResolver = nodeResolve({ browser: false });
273
- const resolved = await customResolver.resolveId.call(this, source, importer, options);
314
+ const resolveIdHook = customResolver.resolveId;
315
+ const resolveIdFn = typeof resolveIdHook === "function" ? resolveIdHook : resolveIdHook.handler;
316
+ const resolved = await resolveIdFn.call(this, source, importer, options);
274
317
  if (source === "threads/worker") {
275
318
  return resolved.id.replace("worker.mjs", "dist-esm/worker/index.js");
276
319
  } else {
@@ -334,29 +377,116 @@ let __non_webpack_require__ = () => {
334
377
  }
335
378
 
336
379
  // src/vite-config-for-report.ts
337
- import { existsSync as existsSync2, mkdirSync } from "fs";
338
- import { dirname as dirname2, relative as relative2, join as joinPath2, resolve as resolvePath2 } from "path";
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";
339
382
  import { fileURLToPath as fileURLToPath2 } from "url";
340
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
341
470
  var __filename3 = fileURLToPath2(import.meta.url);
342
- var __dirname3 = dirname2(__filename3);
343
- function createViteConfigForReport(options, projDir, prepDir, currentBundleName, currentBundlePath, testConfigPath, suiteSummary) {
471
+ var __dirname3 = dirname3(__filename3);
472
+ function createViteConfigForReport(mode, options, projDir, prepDir, currentBundleSpec, baselineBundleSpec, testConfigPath, suiteSummary) {
344
473
  const root = resolvePath2(__dirname3, "..", "template-report");
345
- const baselinesDir = resolvePath2(projDir, "baselines");
346
- if (!existsSync2(baselinesDir)) {
347
- mkdirSync(baselinesDir, { recursive: true });
474
+ const bundlesDir = resolvePath2(projDir, "bundles");
475
+ if (!existsSync2(bundlesDir)) {
476
+ mkdirSync(bundlesDir, { recursive: true });
348
477
  }
349
478
  const templateSrcDir = resolvePath2(root, "src");
350
- const relProjDir = relative2(templateSrcDir, projDir);
479
+ const relProjDir = relative3(templateSrcDir, projDir);
351
480
  const relProjDirPath = relProjDir.replaceAll("\\", "/");
352
- const baselinesPath = `${relProjDirPath}/baselines/*.js`;
481
+ const bundlesPath = `${relProjDirPath}/bundles/*.js`;
482
+ const currentBundleLastModified = statSync2(currentBundleSpec.path).mtime.toISOString();
353
483
  let reportPath;
354
484
  if (options?.reportPath) {
355
485
  reportPath = options.reportPath;
356
486
  } else {
357
- reportPath = joinPath2(prepDir, "check-report");
487
+ reportPath = joinPath4(prepDir, "check-report");
358
488
  }
359
- const outDir = relative2(root, reportPath);
489
+ const outDir = relative3(root, reportPath);
360
490
  const suiteSummaryJson = suiteSummary ? JSON.stringify(suiteSummary) : "";
361
491
  const alias = (find, replacement) => {
362
492
  return {
@@ -381,7 +511,7 @@ function createViteConfigForReport(options, projDir, prepDir, currentBundleName,
381
511
  // Use a custom cache directory under `prepDir`, as otherwise Vite will use
382
512
  // `packages/plugin-check/template-report/node_modules/.vite`, and we want to
383
513
  // avoid generating files in `template-report` (which should be read-only)
384
- cacheDir: joinPath2(prepDir, ".vite-check-report"),
514
+ cacheDir: joinPath4(prepDir, ".vite-check-report"),
385
515
  // Load static files from `static` (instead of the default `public`)
386
516
  // publicDir: 'static',
387
517
  // Don't clear the screen in dev mode so that we can see builder output
@@ -428,9 +558,9 @@ function createViteConfigForReport(options, projDir, prepDir, currentBundleName,
428
558
  alias: [
429
559
  // Use the configured "baseline" bundle if defined, otherwise use the "empty" bundle
430
560
  // (which will cause comparison tests to be skipped)
431
- alias("@_baseline_bundle_", options?.baseline ? options.baseline.path : "/src/empty-bundle.ts"),
561
+ alias("@_baseline_bundle_", baselineBundleSpec?.path || "/src/empty-bundle.ts"),
432
562
  // Use the configured "current" bundle
433
- alias("@_current_bundle_", currentBundlePath),
563
+ alias("@_current_bundle_", currentBundleSpec.path),
434
564
  // Use the configured test config file
435
565
  alias("@_test_config_", testConfigPath),
436
566
  // Make the overlay use the `messages.html` file that is written to the prep directory
@@ -450,10 +580,14 @@ function createViteConfigForReport(options, projDir, prepDir, currentBundleName,
450
580
  define: {
451
581
  // Inject the summary JSON into the build
452
582
  __SUITE_SUMMARY_JSON__: JSON.stringify(suiteSummaryJson),
453
- // Inject the baseline branch name
454
- __BASELINE_NAME__: JSON.stringify(options?.baseline?.name || ""),
455
- // Inject the current branch name
456
- __CURRENT_NAME__: JSON.stringify(currentBundleName)
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 || "")
457
591
  },
458
592
  plugins: [
459
593
  // Inject special values into the generated JS
@@ -465,15 +599,18 @@ function createViteConfigForReport(options, projDir, prepDir, currentBundleName,
465
599
  delimiters: ["", ""],
466
600
  values: {
467
601
  // Inject the path for baseline bundles
468
- // XXX: Note that we use './baselines/*.txt' instead of something special
602
+ // XXX: Note that we use './bundles/*.txt' instead of something special
469
603
  // like './__BASELINE_BUNDLES_PATH__' because sometimes Vite's dependency
470
604
  // scanner sees the latter (instead of the injected path) and reports
471
605
  // an error since the path does not exist. As a workaround, we use
472
- // './baselines/*.txt', which gets interpreted as the valid path
473
- // '.../template-report/src/baselines/*.txt' (see `baselines/unused.txt`).
474
- "./baselines/*.txt": baselinesPath
606
+ // './bundles/*.txt', which gets interpreted as the valid path
607
+ // '.../template-report/src/bundles/*.txt' (see `bundles/unused.txt`).
608
+ "./bundles/*.txt": bundlesPath
475
609
  }
476
- })
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)] : []
477
614
  ],
478
615
  build: {
479
616
  // Write output files to the configured directory (instead of the default `dist`);
@@ -512,19 +649,19 @@ function createViteConfigForReport(options, projDir, prepDir, currentBundleName,
512
649
  }
513
650
 
514
651
  // src/vite-config-for-tests.ts
515
- import { dirname as dirname3, relative as relative3, resolve as resolvePath3 } from "path";
652
+ import { dirname as dirname4, relative as relative4, resolve as resolvePath3 } from "path";
516
653
  import { fileURLToPath as fileURLToPath3 } from "url";
517
654
  import replace2 from "@rollup/plugin-replace";
518
655
  var __filename4 = fileURLToPath3(import.meta.url);
519
- var __dirname4 = dirname3(__filename4);
520
- function createViteConfigForTests(projDir, prepDir, mode) {
656
+ var __dirname4 = dirname4(__filename4);
657
+ function createViteConfigForTests(mode, projDir, prepDir) {
521
658
  const root = resolvePath3(__dirname4, "..", "template-tests");
522
659
  const templateSrcDir = resolvePath3(root, "src");
523
- const relProjDir = relative3(templateSrcDir, projDir);
660
+ const relProjDir = relative4(templateSrcDir, projDir);
524
661
  const relProjDirPath = relProjDir.replaceAll("\\", "/");
525
662
  const yamlCheckGlobPatterns = `['${relProjDirPath}/**/checks/*.yaml', '${relProjDirPath}/**/*.check.yaml']`;
526
663
  const yamlComparisonGlobPatterns = `['${relProjDirPath}/**/comparisons/*.yaml']`;
527
- const outDir = relative3(root, prepDir);
664
+ const outDir = relative4(root, prepDir);
528
665
  return {
529
666
  // Don't use an external config file
530
667
  configFile: false,
@@ -588,26 +725,12 @@ var CheckPlugin = class {
588
725
  }
589
726
  async watch(config) {
590
727
  if (this.options?.testConfigPath === void 0) {
591
- await this.genTestConfig(config, "watch");
728
+ await this.genTestConfig("watch", config);
592
729
  }
593
- const testOptions = this.resolveTestOptions(config);
594
- 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);
595
732
  const server = await createServer(viteConfig);
596
733
  await server.listen();
597
- const baselinesDir = "baselines";
598
- const watcher = chokidar.watch(baselinesDir, {
599
- // Watch paths are resolved relative to the project root directory
600
- cwd: config.rootDir,
601
- // Don't send initial "file added" events
602
- ignoreInitial: true,
603
- // XXX: Include a delay, otherwise on macOS we sometimes get multiple
604
- // change events when a file is saved just once
605
- awaitWriteFinish: {
606
- stabilityThreshold: 200
607
- }
608
- });
609
- watcher.on("add", () => server.restart());
610
- watcher.on("unlink", () => server.restart());
611
734
  }
612
735
  // TODO: Note that this plugin runs as a `postBuild` step because it currently
613
736
  // needs to run after other plugins, and those plugins need to run after the
@@ -617,7 +740,7 @@ var CheckPlugin = class {
617
740
  async postBuild(context, modelSpec) {
618
741
  const firstBuild = this.firstBuild;
619
742
  this.firstBuild = false;
620
- if (this.options?.current === void 0) {
743
+ if (this.options?.current?.path === void 0 && this.options?.current?.url === void 0) {
621
744
  if (context.config.mode === "development") {
622
745
  await this.copyPreviousBundle(context.config);
623
746
  }
@@ -627,24 +750,24 @@ var CheckPlugin = class {
627
750
  if (this.options?.testConfigPath === void 0) {
628
751
  if (context.config.mode === "production" || firstBuild) {
629
752
  context.log("info", "Generating model check test configuration...");
630
- await this.genTestConfig(context.config, "build");
753
+ await this.genTestConfig("bundle", context.config);
631
754
  }
632
755
  }
633
756
  if (context.config.mode === "production") {
634
- const testOptions = this.resolveTestOptions(context.config);
757
+ const testOptions = await this.resolveTestOptions("bundle", context.config);
635
758
  return this.runChecks(context, testOptions);
636
759
  } else {
637
760
  return true;
638
761
  }
639
762
  }
640
763
  async copyPreviousBundle(config) {
641
- const currentBundleFile = joinPath3(config.prepDir, "check-bundle.js");
764
+ const currentBundleFile = joinPath5(config.prepDir, "check-bundle.js");
642
765
  if (existsSync3(currentBundleFile)) {
643
- const baselinesDir = joinPath3(config.rootDir, "baselines");
644
- if (!existsSync3(baselinesDir)) {
645
- await mkdir(baselinesDir, { recursive: true });
766
+ const bundlesDir = joinPath5(config.rootDir, "bundles");
767
+ if (!existsSync3(bundlesDir)) {
768
+ await mkdir2(bundlesDir, { recursive: true });
646
769
  }
647
- const previousBundleFile = joinPath3(baselinesDir, "previous.js");
770
+ const previousBundleFile = joinPath5(bundlesDir, "previous.js");
648
771
  await copyFile(currentBundleFile, previousBundleFile);
649
772
  }
650
773
  }
@@ -652,25 +775,32 @@ var CheckPlugin = class {
652
775
  const viteConfig = await createViteConfigForBundle(context, modelSpec);
653
776
  await build(viteConfig);
654
777
  }
655
- async genTestConfig(config, mode) {
778
+ async genTestConfig(mode, config) {
656
779
  const rootDir = config.rootDir;
657
780
  const prepDir = config.prepDir;
658
- const viteConfig = createViteConfigForTests(rootDir, prepDir, mode);
781
+ const viteConfig = createViteConfigForTests(mode, rootDir, prepDir);
659
782
  await build(viteConfig);
660
783
  }
661
784
  async runChecks(context, testOptions) {
662
785
  context.log("info", "Running model checks...");
663
- const moduleR = await import(relativeToSourcePath(testOptions.currentBundlePath));
786
+ async function importBundleModule(bundleSpec) {
787
+ return import(relativeToSourcePath(bundleSpec.path));
788
+ }
789
+ const moduleR = await importBundleModule(testOptions.currentBundleSpec);
664
790
  const bundleR = moduleR.createBundle();
665
- const bundleNameR = testOptions.currentBundleName;
791
+ const bundleNameR = testOptions.currentBundleSpec.name;
666
792
  let bundleL;
667
793
  let bundleNameL;
668
- if (this.options?.baseline) {
669
- const moduleL = await import(relativeToSourcePath(this.options.baseline.path));
794
+ if (testOptions.baselineBundleSpec !== void 0) {
795
+ const moduleL = await importBundleModule(testOptions.baselineBundleSpec);
670
796
  const rawBundleL = moduleL.createBundle();
671
797
  if (rawBundleL.version === bundleR.version) {
672
798
  bundleL = rawBundleL;
673
- bundleNameL = this.options.baseline.name;
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
+ );
674
804
  }
675
805
  }
676
806
  const testConfigModule = await import(relativeToSourcePath(testOptions.testConfigPath));
@@ -687,47 +817,79 @@ var CheckPlugin = class {
687
817
  false
688
818
  );
689
819
  context.log("info", "Building model check report");
690
- const viteConfig = this.createViteConfigForReport(context.config, testOptions, result.suiteSummary);
820
+ const viteConfig = await this.createViteConfigForReport("bundle", context.config, testOptions, result.suiteSummary);
691
821
  await build(viteConfig);
692
822
  return result.allChecksPassed;
693
823
  }
694
- resolveTestOptions(config) {
695
- let currentBundleName;
696
- let currentBundlePath;
697
- if (this.options?.current === void 0) {
698
- currentBundleName = "current";
699
- currentBundlePath = joinPath3(config.prepDir, "check-bundle.js");
700
- } else {
701
- currentBundleName = this.options.current.name;
702
- currentBundlePath = this.options.current.path;
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
+ }
703
864
  }
704
865
  let testConfigPath;
705
866
  if (this.options?.testConfigPath === void 0) {
706
- testConfigPath = joinPath3(config.prepDir, "check-tests.js");
867
+ testConfigPath = joinPath5(config.prepDir, "check-tests.js");
707
868
  } else {
708
869
  testConfigPath = this.options.testConfigPath;
709
870
  }
710
871
  return {
711
- currentBundleName,
712
- currentBundlePath,
872
+ currentBundleSpec,
873
+ baselineBundleSpec,
713
874
  testConfigPath
714
875
  };
715
876
  }
716
- createViteConfigForReport(config, testOptions, suiteSummary) {
877
+ async createViteConfigForReport(mode, config, testOptions, suiteSummary) {
717
878
  return createViteConfigForReport(
879
+ mode,
718
880
  this.options,
719
881
  config.rootDir,
720
882
  config.prepDir,
721
- testOptions.currentBundleName,
722
- testOptions.currentBundlePath,
883
+ testOptions.currentBundleSpec,
884
+ testOptions.baselineBundleSpec,
723
885
  testOptions.testConfigPath,
724
886
  suiteSummary
725
887
  );
726
888
  }
727
889
  };
728
890
  function relativeToSourcePath(filePath) {
729
- const srcDir = dirname4(fileURLToPath4(import.meta.url));
730
- const relPath = relative4(srcDir, filePath);
891
+ const srcDir = dirname5(fileURLToPath4(import.meta.url));
892
+ const relPath = relative5(srcDir, filePath);
731
893
  return relPath.replaceAll("\\", "/");
732
894
  }
733
895
  export {