@sdeverywhere/plugin-check 0.3.11 → 0.3.13

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.cjs CHANGED
@@ -166,28 +166,9 @@ var import_fs = require("fs");
166
166
  var import_path = require("path");
167
167
  var import_url = require("url");
168
168
  var import_plugin_node_resolve = require("@rollup/plugin-node-resolve");
169
-
170
- // src/var-names.ts
171
- function sdeNameForVensimName(name) {
172
- return "_" + name.trim().replace(/"/g, "_").replace(/\s+!$/g, "!").replace(/\s/g, "_").replace(/,/g, "_").replace(/-/g, "_").replace(/\./g, "_").replace(/\$/g, "_").replace(/'/g, "_").replace(/&/g, "_").replace(/%/g, "_").replace(/\//g, "_").replace(/\|/g, "_").toLowerCase();
173
- }
174
- function sdeNameForVensimVarName(varName) {
175
- const m = varName.match(/([^[]+)(?:\[([^\]]+)\])?/);
176
- if (!m) {
177
- throw new Error(`Invalid Vensim name: ${varName}`);
178
- }
179
- let id = sdeNameForVensimName(m[1]);
180
- if (m[2]) {
181
- const subscripts = m[2].split(",").map((x) => sdeNameForVensimName(x));
182
- id += `[${subscripts.join(",")}]`;
183
- }
184
- return id;
185
- }
186
-
187
- // src/vite-config-for-bundle.ts
188
169
  var __filename2 = (0, import_url.fileURLToPath)(importMetaUrl);
189
170
  var __dirname = (0, import_path.dirname)(__filename2);
190
- function injectModelSpec(prepDir, modelSpec) {
171
+ function injectModelSpec(context, modelSpec) {
191
172
  const inputSpecs = [];
192
173
  for (const modelInputSpec of modelSpec.inputs) {
193
174
  if (modelInputSpec.defaultValue === void 0 || modelInputSpec.minValue === void 0 || modelInputSpec.maxValue === void 0) {
@@ -199,7 +180,7 @@ function injectModelSpec(prepDir, modelSpec) {
199
180
  console.warn(msg);
200
181
  continue;
201
182
  }
202
- const varId = sdeNameForVensimVarName(modelInputSpec.varName);
183
+ const varId = context.canonicalVarId(modelInputSpec.varName);
203
184
  const inputId = modelInputSpec.inputId || varId;
204
185
  inputSpecs.push({
205
186
  inputId,
@@ -209,11 +190,12 @@ function injectModelSpec(prepDir, modelSpec) {
209
190
  }
210
191
  const outputSpecs = modelSpec.outputs.map((o) => {
211
192
  return {
212
- varId: sdeNameForVensimVarName(o.varName),
193
+ varId: context.canonicalVarId(o.varName),
213
194
  ...o
214
195
  };
215
196
  });
216
197
  function stagedFileSize(filename) {
198
+ const prepDir = context.config.prepDir;
217
199
  const path = (0, import_path.join)(prepDir, "staged", "model", filename);
218
200
  if ((0, import_fs.existsSync)(path)) {
219
201
  return (0, import_fs.statSync)(path).size;
@@ -264,8 +246,9 @@ function overrideViteResolvePlugin(viteConfig) {
264
246
  return originalResolveId.call(this, id, importer, options);
265
247
  };
266
248
  }
267
- async function createViteConfigForBundle(prepDir, modelSpec) {
249
+ async function createViteConfigForBundle(context, modelSpec) {
268
250
  const root = (0, import_path.resolve)(__dirname, "..", "template-bundle");
251
+ const prepDir = context.config.prepDir;
269
252
  const outDir = (0, import_path.relative)(root, prepDir);
270
253
  const modelWorkerPath = (0, import_path.join)(prepDir, "staged", "model", "worker.js?raw");
271
254
  return {
@@ -314,7 +297,7 @@ async function createViteConfigForBundle(prepDir, modelSpec) {
314
297
  },
315
298
  plugins: [
316
299
  // Use a virtual module plugin to inject the model spec values
317
- injectModelSpec(prepDir, modelSpec),
300
+ injectModelSpec(context, modelSpec),
318
301
  // XXX: Install a wrapper around the built-in `vite:resolve` plugin so that we can
319
302
  // override the default resolver behavior that tries to resolve the `browser` section
320
303
  // of the `package.json` for the threads package.
@@ -543,7 +526,8 @@ function createViteConfigForTests(projDir, prepDir, mode) {
543
526
  const templateSrcDir = (0, import_path3.resolve)(root, "src");
544
527
  const relProjDir = (0, import_path3.relative)(templateSrcDir, projDir);
545
528
  const relProjDirPath = relProjDir.replaceAll("\\", "/");
546
- const yamlPath = `${relProjDirPath}/**/*.check.yaml`;
529
+ const yamlCheckGlobPatterns = `['${relProjDirPath}/**/checks/*.yaml', '${relProjDirPath}/**/*.check.yaml']`;
530
+ const yamlComparisonGlobPatterns = `['${relProjDirPath}/**/comparisons/*.yaml']`;
547
531
  const outDir = (0, import_path3.relative)(root, prepDir);
548
532
  return {
549
533
  // Don't use an external config file
@@ -563,8 +547,10 @@ function createViteConfigForTests(projDir, prepDir, mode) {
563
547
  preventAssignment: true,
564
548
  delimiters: ["", ""],
565
549
  values: {
566
- // Inject the glob pattern for matching check yaml files
567
- "./__YAML_PATH__": yamlPath
550
+ // Inject the glob patterns for matching model check yaml files
551
+ '"./__YAML_CHECK_GLOB_PATTERNS__"': yamlCheckGlobPatterns,
552
+ // Inject the glob patterns for matching model comparison yaml files
553
+ '"./__YAML_COMPARISON_GLOB_PATTERNS__"': yamlComparisonGlobPatterns
568
554
  }
569
555
  })
570
556
  ],
@@ -640,7 +626,7 @@ var CheckPlugin = class {
640
626
  await this.copyPreviousBundle(context.config);
641
627
  }
642
628
  context.log("info", "Generating model check bundle...");
643
- await this.genCurrentBundle(context.config, modelSpec);
629
+ await this.genCurrentBundle(context, modelSpec);
644
630
  }
645
631
  if (this.options?.testConfigPath === void 0) {
646
632
  if (context.config.mode === "production" || firstBuild) {
@@ -666,9 +652,8 @@ var CheckPlugin = class {
666
652
  await (0, import_promises.copyFile)(currentBundleFile, previousBundleFile);
667
653
  }
668
654
  }
669
- async genCurrentBundle(config, modelSpec) {
670
- const prepDir = config.prepDir;
671
- const viteConfig = await createViteConfigForBundle(prepDir, modelSpec);
655
+ async genCurrentBundle(context, modelSpec) {
656
+ const viteConfig = await createViteConfigForBundle(context, modelSpec);
672
657
  await (0, import_vite.build)(viteConfig);
673
658
  }
674
659
  async genTestConfig(config, mode) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../../../node_modules/.pnpm/tsup@8.2.4_typescript@5.2.2/node_modules/tsup/assets/cjs_shims.js","../src/plugin.ts","../src/run-suite.ts","../src/vite-config-for-bundle.ts","../src/var-names.ts","../src/vite-config-for-report.ts","../src/vite-config-for-tests.ts"],"sourcesContent":["// Copyright (c) 2022 Climate Interactive / New Venture Fund\n\nexport type { CheckBundle, CheckPluginOptions } from './options'\nexport { checkPlugin } from './plugin'\n","// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () =>\n typeof document === 'undefined'\n ? new URL(`file:${__filename}`).href\n : (document.currentScript && document.currentScript.src) ||\n new URL('main.js', document.baseURI).href\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","// Copyright (c) 2022 Climate Interactive / New Venture Fund\n\nimport { existsSync } from 'fs'\nimport { copyFile, mkdir } from 'fs/promises'\nimport { dirname, join as joinPath, relative } from 'path'\nimport { fileURLToPath } from 'url'\n\nimport type { InlineConfig, ViteDevServer } from 'vite'\nimport { build, createServer } from 'vite'\n\nimport chokidar from 'chokidar'\n\nimport type { BuildContext, Plugin, ResolvedConfig, ResolvedModelSpec } from '@sdeverywhere/build'\n\nimport type { Bundle, ConfigInitOptions, SuiteSummary } from '@sdeverywhere/check-core'\nimport { createConfig } from '@sdeverywhere/check-core'\n\nimport type { CheckPluginOptions } from './options'\nimport { runTestSuite } from './run-suite'\nimport { createViteConfigForBundle } from './vite-config-for-bundle'\nimport { createViteConfigForReport } from './vite-config-for-report'\nimport { createViteConfigForTests } from './vite-config-for-tests'\n\nexport function checkPlugin(options?: CheckPluginOptions): Plugin {\n return new CheckPlugin(options)\n}\n\ninterface TestOptions {\n currentBundleName: string\n currentBundlePath: string\n testConfigPath: string\n}\n\nclass CheckPlugin implements Plugin {\n private firstBuild = true\n\n constructor(private readonly options?: CheckPluginOptions) {}\n\n async watch(config: ResolvedConfig): Promise<void> {\n if (this.options?.testConfigPath === undefined) {\n // Test config was not provided, so generate a default config in watch mode.\n // The test template uses import.meta.glob so that checks are re-run\n // automatically when the *.check.yaml files are changed.\n await this.genTestConfig(config, 'watch')\n }\n\n // For development mode, run Vite in dev mode so that it serves the\n // model-check report locally (with live reload enabled). When a model\n // test file is changed, the tests will be re-run in the browser.\n const testOptions = this.resolveTestOptions(config)\n const viteConfig = this.createViteConfigForReport(config, testOptions, undefined)\n const server: ViteDevServer = await createServer(viteConfig)\n await server.listen()\n\n // XXX: Currently Vite doesn't reload the page if a file is added/removed\n // in the baselines directory (Vite's import.meta.glob handling doesn't\n // seem to do this automatically), so as a workaround, watch the baselines\n // directory and restart the server if files are added/removed\n // TODO: The same problem also applies to the glob for `.check.yaml` files\n // in the test config, so we should also reload if files match/unmatch\n // the `.check.yaml` glob\n // TODO: Use the baselines directory from the config\n const baselinesDir = 'baselines'\n const watcher = chokidar.watch(baselinesDir, {\n // Watch paths are resolved relative to the project root directory\n cwd: config.rootDir,\n // Don't send initial \"file added\" events\n ignoreInitial: true,\n // XXX: Include a delay, otherwise on macOS we sometimes get multiple\n // change events when a file is saved just once\n awaitWriteFinish: {\n stabilityThreshold: 200\n }\n })\n watcher.on('add', () => server.restart())\n watcher.on('unlink', () => server.restart())\n }\n\n // TODO: Note that this plugin runs as a `postBuild` step because it currently\n // needs to run after other plugins, and those plugins need to run after the\n // staged files are copied to their final destination(s). We should probably\n // make it configurable so that it can either be run as a `postGenerate` or a\n // `postBuild` step.\n async postBuild(context: BuildContext, modelSpec: ResolvedModelSpec): Promise<boolean> {\n const firstBuild = this.firstBuild\n this.firstBuild = false\n\n // For both production builds and local development, generate default bundle\n // in this post-build step each time a source file is changed\n // TODO: We could potentially use watch mode for the bundle similar to\n // what we do for the test config, but the bundle depends on the ModelSpec,\n // which currently isn't made available to the `watch` function\n if (this.options?.current === undefined) {\n // Path to current bundle was not provided, so generate a default bundle\n if (context.config.mode === 'development') {\n // Copy the previous bundle to the `baselines` directory so that\n // we automatically have it available as a baseline for comparison\n await this.copyPreviousBundle(context.config)\n }\n context.log('info', 'Generating model check bundle...')\n await this.genCurrentBundle(context.config, modelSpec)\n }\n\n // For production builds (and for the initial build in local development mode),\n // generate default test config in this post-build step\n if (this.options?.testConfigPath === undefined) {\n if (context.config.mode === 'production' || firstBuild) {\n // Test config was not provided, so generate a default config\n context.log('info', 'Generating model check test configuration...')\n await this.genTestConfig(context.config, 'build')\n }\n }\n\n if (context.config.mode === 'production') {\n // For production builds, run the model checks/comparisons, and then\n // inject the results into the generated report\n const testOptions = this.resolveTestOptions(context.config)\n return this.runChecks(context, testOptions)\n } else {\n // Nothing to do here in dev mode; the dev server will refresh and\n // re-run the tests in the browser when changes are detected\n return true\n }\n }\n\n private async copyPreviousBundle(config: ResolvedConfig): Promise<void> {\n // Only copy if the current bundle exists\n const currentBundleFile = joinPath(config.prepDir, 'check-bundle.js')\n if (existsSync(currentBundleFile)) {\n // TODO: Use the baselines directory from the config (not yet available)\n const baselinesDir = joinPath(config.rootDir, 'baselines')\n if (!existsSync(baselinesDir)) {\n await mkdir(baselinesDir, { recursive: true })\n }\n const previousBundleFile = joinPath(baselinesDir, 'previous.js')\n await copyFile(currentBundleFile, previousBundleFile)\n }\n }\n\n private async genCurrentBundle(config: ResolvedConfig, modelSpec: ResolvedModelSpec): Promise<void> {\n const prepDir = config.prepDir\n const viteConfig = await createViteConfigForBundle(prepDir, modelSpec)\n await build(viteConfig)\n }\n\n private async genTestConfig(config: ResolvedConfig, mode: 'build' | 'watch'): Promise<void> {\n const rootDir = config.rootDir\n const prepDir = config.prepDir\n const viteConfig = createViteConfigForTests(rootDir, prepDir, mode)\n await build(viteConfig)\n }\n\n private async runChecks(context: BuildContext, testOptions: TestOptions): Promise<boolean> {\n context.log('info', 'Running model checks...')\n\n // Load the bundles used by the model check/compare configuration. We\n // always initialize the \"current\" bundle.\n const moduleR = await import(relativeToSourcePath(testOptions.currentBundlePath))\n const bundleR = moduleR.createBundle() as Bundle\n const bundleNameR = testOptions.currentBundleName\n\n // Only initialize the \"baseline\" bundle if it is defined and the version\n // is the same as the \"current\" one. If the baseline bundle has a different\n // version, we will skip the comparison tests and only run the checks on the\n // current bundle.\n let bundleL: Bundle\n let bundleNameL: string\n if (this.options?.baseline) {\n const moduleL = await import(relativeToSourcePath(this.options.baseline.path))\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const rawBundleL: any = moduleL.createBundle() as any\n if (rawBundleL.version === bundleR.version) {\n bundleL = rawBundleL as Bundle\n bundleNameL = this.options.baseline.name\n }\n }\n\n // Get the model check/comparison configuration\n const testConfigModule = await import(relativeToSourcePath(testOptions.testConfigPath))\n const configInitOptions: ConfigInitOptions = {\n bundleNameL,\n bundleNameR\n }\n const configOptions = await testConfigModule.getConfigOptions(bundleL, bundleR, configInitOptions)\n\n // Run the suite of checks and comparisons\n const checkConfig = await createConfig(configOptions)\n const result = await runTestSuite(context, checkConfig, /*verbose=*/ false)\n\n // Build the report (using Vite)\n context.log('info', 'Building model check report')\n const viteConfig = this.createViteConfigForReport(context.config, testOptions, result.suiteSummary)\n await build(viteConfig)\n\n // context.log('info', 'Done!')\n\n return result.allChecksPassed\n }\n\n private resolveTestOptions(config: ResolvedConfig): TestOptions {\n let currentBundleName: string\n let currentBundlePath: string\n if (this.options?.current === undefined) {\n // Path to current bundle was not provided, so use a generated bundle\n currentBundleName = 'current'\n currentBundlePath = joinPath(config.prepDir, 'check-bundle.js')\n } else {\n // Use the provided bundle\n currentBundleName = this.options.current.name\n currentBundlePath = this.options.current.path\n }\n\n let testConfigPath: string\n if (this.options?.testConfigPath === undefined) {\n // Test config was not provided, so use a generated config\n testConfigPath = joinPath(config.prepDir, 'check-tests.js')\n } else {\n // Use the provided test config\n testConfigPath = this.options.testConfigPath\n }\n\n return {\n currentBundleName,\n currentBundlePath,\n testConfigPath\n }\n }\n\n private createViteConfigForReport(\n config: ResolvedConfig,\n testOptions: TestOptions,\n suiteSummary: SuiteSummary | undefined\n ): InlineConfig {\n return createViteConfigForReport(\n this.options,\n config.rootDir,\n config.prepDir,\n testOptions.currentBundleName,\n testOptions.currentBundlePath,\n testOptions.testConfigPath,\n suiteSummary\n )\n }\n}\n\n/**\n * Return a Unix-style path (e.g. '../../foo.js') that is relative to the directory of\n * the current source file. This can be used to construct a path that is safe for\n * dynamic import on either Unix or Windows.\n *\n * @param filePath The path to make relative.\n */\nfunction relativeToSourcePath(filePath: string): string {\n const srcDir = dirname(fileURLToPath(import.meta.url))\n const relPath = relative(srcDir, filePath)\n return relPath.replaceAll('\\\\', '/')\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport { performance } from 'perf_hooks'\n\nimport pico from 'picocolors'\n\nimport type { BuildContext } from '@sdeverywhere/build'\n\nimport type {\n Config,\n ComparisonReport,\n PerfReport,\n RunSuiteCallbacks,\n CheckReport,\n CheckStatus,\n ComparisonConfig,\n CheckTestReport,\n SuiteSummary\n} from '@sdeverywhere/check-core'\nimport {\n datasetMessage,\n predicateMessage,\n runSuite,\n scenarioMessage,\n suiteSummaryFromReport\n} from '@sdeverywhere/check-core'\n\nexport interface RunTestSuiteResult {\n allChecksPassed: boolean\n suiteSummary: SuiteSummary\n}\n\n/**\n * Runs the test suite.\n */\nexport async function runTestSuite(\n context: BuildContext,\n config: Config,\n verbose: boolean\n): Promise<RunTestSuiteResult> {\n return new Promise((resolve, reject) => {\n const t0 = performance.now()\n let lastPctByInc: number\n const callbacks: RunSuiteCallbacks = {\n onProgress: progress => {\n const pct = Math.round(progress * 100)\n const pctByInc = Math.floor(pct / 5) * 5\n if (lastPctByInc === undefined || pctByInc > lastPctByInc) {\n lastPctByInc = pctByInc\n context.log('info', `${pctByInc}%`)\n }\n },\n onComplete: report => {\n try {\n const t1 = performance.now()\n const elapsed = ((t1 - t0) / 1000).toFixed(1)\n context.log('info', `\\nTest suite completed in ${elapsed}s`)\n\n // Print check summary to the console\n const allChecksPassed = printCheckSummary(context, report.checkReport, verbose)\n\n if (report.comparisonReport) {\n // Print the perf stats to the console\n printPerfStats(context, config.comparison, report.comparisonReport)\n }\n\n // Convert check and compare reports to terse form that only includes\n // failed/errored checks or comparisons with differences\n // TODO: The terse form was originally used when we had to write the\n // results to a JSON file and then read them back in when building\n // the report, but we no longer use that intermediate file, so there's\n // less reason to use the terse form (since it requires the web app\n // code to reconstruct the results). But for now, we will continue\n // to use the terse form, and later we can update the app code.\n const suiteSummary = suiteSummaryFromReport(report)\n\n resolve({\n allChecksPassed,\n suiteSummary\n })\n } catch (e) {\n reject(e)\n }\n },\n onError: error => {\n reject(error)\n }\n }\n runSuite(config, callbacks)\n })\n}\n\nfunction printCheckSummary(context: BuildContext, checkReport: CheckReport, verbose: boolean): boolean {\n function printResult(indent: number, status: CheckStatus, text: string): void {\n if (!verbose && status === 'passed' && indent > 1) {\n return\n }\n let statusChar: string\n switch (status) {\n case 'passed':\n statusChar = '✓'\n break\n case 'failed':\n statusChar = '✗'\n break\n case 'error':\n statusChar = '‼'\n break\n default:\n statusChar = ''\n break\n }\n const msg = `${' '.repeat(indent)}${statusChar} ${text}`\n context.log('info', status === 'passed' ? pico.green(msg) : pico.red(msg))\n }\n\n function bold(s: string): string {\n return pico.bold(s)\n }\n\n function printTest(test: CheckTestReport): void {\n const msg = `${test.name}${verbose || test.status !== 'passed' ? ':' : ''}`\n printResult(1, test.status, msg)\n }\n\n let allPassed = true\n context.log('info', '\\nCheck results:')\n for (const group of checkReport.groups) {\n context.log('info', `\\n${group.name}`)\n\n for (const test of group.tests) {\n if (test.status !== 'passed') {\n allPassed = false\n }\n printTest(test)\n\n for (const scenario of test.scenarios) {\n printResult(3, scenario.status, scenarioMessage(scenario, bold))\n\n for (const dataset of scenario.datasets) {\n printResult(5, dataset.status, datasetMessage(dataset, bold))\n\n for (const predicate of dataset.predicates) {\n printResult(7, predicate.result.status, predicateMessage(predicate, bold))\n }\n }\n }\n }\n }\n context.log('info', '')\n\n return allPassed\n}\n\nfunction stat(label: string, n: number): string {\n return `${label}=${n.toFixed(1)}ms`\n}\n\nfunction printPerfReportLine(context: BuildContext, perfReport: PerfReport): void {\n const avg = stat('avg', perfReport.avgTime)\n const min = stat('min', perfReport.minTime)\n const max = stat('max', perfReport.maxTime)\n context.log('info', ` ${avg} ${min} ${max}`)\n}\n\nfunction printPerfStats(context: BuildContext, comparisonConfig: ComparisonConfig, report: ComparisonReport): void {\n context.log('info', '\\nPerformance stats:')\n context.log('info', ` ${comparisonConfig.bundleL.name}:`)\n printPerfReportLine(context, report.perfReportL)\n context.log('info', ` ${comparisonConfig.bundleR.name}:`)\n printPerfReportLine(context, report.perfReportR)\n context.log('info', '')\n}\n","// Copyright (c) 2022 Climate Interactive / New Venture Fund\n\nimport { existsSync, statSync } from 'fs'\nimport { basename, dirname, join as joinPath, relative, resolve as resolvePath } from 'path'\nimport { fileURLToPath } from 'url'\n\nimport type { InlineConfig, ResolvedConfig, Plugin as VitePlugin } from 'vite'\nimport { nodeResolve } from '@rollup/plugin-node-resolve'\n\nimport type { ResolvedModelSpec } from '@sdeverywhere/build'\n\nimport { sdeNameForVensimVarName } from './var-names'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\n/**\n * This is a virtual module plugin used to inject model-specific configuration\n * values into the generated worker bundle.\n *\n * This follows the \"Virtual Modules Convention\" described here:\n * https://vitejs.dev/guide/api-plugin.html#virtual-modules-convention\n *\n * TODO: This could be simplified by using `vite-plugin-virtual` but that\n * doesn't seem to be working correctly in an ESM setting\n */\nfunction injectModelSpec(prepDir: string, modelSpec: ResolvedModelSpec): VitePlugin {\n // Include the SDE variable ID with each spec\n const inputSpecs = []\n for (const modelInputSpec of modelSpec.inputs) {\n // Note that the `InputSpec` interface in the `@sdeverywhere/build` package\n // allows the default/min/max values to be undefined, which can be the case\n // if the user doesn't return full `InputSpec` instances in the `ModelSpec`.\n // We will log a warning and skip the input if these values are not defined.\n if (\n modelInputSpec.defaultValue === undefined ||\n modelInputSpec.minValue === undefined ||\n modelInputSpec.maxValue === undefined\n ) {\n let msg = ''\n msg += `WARNING: The {defaultValue,minValue,maxValue} properties are required by plugin-check, `\n msg += `but are undefined in the InputSpec for '${modelInputSpec.varName}'. `\n msg += `This input variable will be excluded from the model-check bundle until those properties `\n msg += `are defined.`\n console.warn(msg)\n continue\n }\n\n // Use the `inputId` if defined for the `InputSpec`, otherwise use `varId`. The\n // latter is less resilient if the variable is renamed between two versions of\n // the model, but will be sufficient for now. Note that `plugin-config` defines\n // a stable `inputId` for each row in the `inputs.csv`, and that is the most\n // common way to configure a `ModelSpec`, so it will be uncommon for `inputId`\n // to be undefined here.\n const varId = sdeNameForVensimVarName(modelInputSpec.varName)\n const inputId = modelInputSpec.inputId || varId\n inputSpecs.push({\n inputId,\n varId,\n ...modelInputSpec\n })\n }\n const outputSpecs = modelSpec.outputs.map(o => {\n return {\n varId: sdeNameForVensimVarName(o.varName),\n ...o\n }\n })\n\n function stagedFileSize(filename: string): number {\n const path = joinPath(prepDir, 'staged', 'model', filename)\n if (existsSync(path)) {\n return statSync(path).size\n } else {\n return 0\n }\n }\n\n // The size (in bytes) of the `generated-model.js` file\n // TODO: Ideally we would measure the size of the raw Wasm binary, but currently\n // we inline it as a base64 blob inside the JS file, so we take the size of the\n // whole JS file as the second best option\n const modelSizeInBytes = stagedFileSize('generated-model.js')\n\n // The size (in bytes) of the `static-data.ts` file\n // TODO: Ideally we would measure the size of the minified JS file here, or\n // at least ignore things like whitespace\n const dataSizeInBytes = stagedFileSize('static-data.ts')\n\n const moduleSrc = `\nexport const inputSpecs = ${JSON.stringify(inputSpecs)};\nexport const outputSpecs = ${JSON.stringify(outputSpecs)};\nexport const modelSizeInBytes = ${modelSizeInBytes};\nexport const dataSizeInBytes = ${dataSizeInBytes};\n`\n\n const virtualModuleId = 'virtual:model-spec'\n const resolvedVirtualModuleId = '\\0' + virtualModuleId\n\n return {\n name: 'vite-plugin-virtual-custom',\n resolveId(id: string) {\n if (id === virtualModuleId) {\n return resolvedVirtualModuleId\n }\n },\n load(id: string) {\n if (id === resolvedVirtualModuleId) {\n return moduleSrc\n }\n }\n }\n}\n\n/**\n * XXX: This overrides the built-in `vite:resolve` plugin so that we can intercept `resolveId`\n * calls for the threads package.\n */\nfunction overrideViteResolvePlugin(viteConfig: ResolvedConfig) {\n const resolvePlugin = viteConfig.plugins.find(p => p.name === 'vite:resolve')\n if (resolvePlugin === undefined) {\n throw new Error('Failed to locate the built-in vite:resolve plugin')\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const originalResolveId = resolvePlugin.resolveId as any\n resolvePlugin.resolveId = async function resolveId(id, importer, options) {\n if (id.startsWith('./implementation') && importer.includes('threads/dist-esm')) {\n // XXX: The default resolver behavior will look at the `browser` mappings in\n // `threads/package.json` and try to resolve `implementation.js` to\n // `implementation.browser.js` because it thinks we're in a browser-only context.\n // We don't want that. Instead we want to keep the generic implementation from\n // threads that chooses between the Node and browser implementations at runtime.\n //\n // If we get here, importer will be something like:\n // /.../node_modules/.pnpm/threads@1.7.0/node_modules/threads/dist-esm/{worker,master}/index.js\n // And id will be:\n // ./implementation\n // So resolve the ID to:\n // /.../node_modules/.pnpm/threads@1.7.0/node_modules/threads/dist-esm/{worker,master}/implementation.js\n //\n // Or, importer will be:\n // /.../node_modules/.pnpm/threads@1.7.0/node_modules/threads/dist-esm/{worker,master}/implementation.js\n // And id will be one of:\n // ./implementation.browser\n // ./implementation.node\n // ./implementation.worker_threads\n // So resolve the ID to:\n // /.../node_modules/.pnpm/threads@1.7.0/node_modules/threads/dist-esm/{worker,master}/implementation.{...}.js\n const idFileName = id.replace('./', '')\n const importerFileName = basename(importer)\n const resolvedId = importer.replace(importerFileName, `${idFileName}.js`)\n return {\n id: resolvedId,\n moduleSideEffects: false\n }\n }\n\n // For all other cases, fall back on the default resolver\n return originalResolveId.call(this, id, importer, options)\n }\n}\n\nexport async function createViteConfigForBundle(prepDir: string, modelSpec: ResolvedModelSpec): Promise<InlineConfig> {\n // Use `template-bundle` as the root directory for the bundle project\n const root = resolvePath(__dirname, '..', 'template-bundle')\n\n // Calculate output directory relative to the template root\n // TODO: For now we write it to `prepDir`; make this configurable?\n const outDir = relative(root, prepDir)\n\n // Use the model worker from the staged directory\n // TODO: Make this configurable?\n const modelWorkerPath = joinPath(prepDir, 'staged', 'model', 'worker.js?raw')\n\n return {\n // Don't use an external config file\n configFile: false,\n\n // Use the root directory configured above\n root,\n\n // Don't clear the screen in dev mode so that we can see builder output\n clearScreen: false,\n\n // TODO: Disable vite output by default?\n // logLevel: 'silent',\n\n // Configure path aliases\n resolve: {\n alias: [\n // Inject the configured model worker\n {\n find: '@_model_worker_',\n replacement: modelWorkerPath\n },\n\n // XXX: Prevent Vite from using the `browser` section of `threads/package.json`\n // since we want to force the use of the general module (under dist-esm) that chooses\n // the correct implementation (Web Worker vs worker_threads) at runtime. Currently\n // Vite's library mode is browser focused and generally chooses the right imports,\n // except in the case of the threads package where we want to use the generic\n // `implementation.js` that chooses between Web Worker and worker_threads at runtime.\n // Note that we could in theory set `resolve.browserField` to false, but that would\n // make Vite not use the browser field for all other packages, and there is not\n // currently a way to tell Vite to use the browser field on a case-by-case basis.\n // So for now we need this workaround here to make it resolve to `dist-esm`, and then\n // a second workaround in `overrideViteResolvePlugin` to prevent the resolver from\n // using the browser field when resolving the threads package.\n {\n find: 'threads',\n replacement: 'threads',\n customResolver: async function (source, importer, options) {\n // Note that we need to use `resolveId.call` here in order to provide the\n // right `this` context, which provides Rollup plugin functionality\n const customResolver = nodeResolve({ browser: false })\n const resolved = await customResolver.resolveId.call(this, source, importer, options)\n // Force the use of the `dist-esm` variant of the threads.js package\n if (source === 'threads/worker') {\n return resolved.id.replace('worker.mjs', 'dist-esm/worker/index.js')\n } else {\n return resolved.id.replace('index.mjs', 'dist-esm/index.js')\n }\n }\n }\n ]\n },\n\n plugins: [\n // Use a virtual module plugin to inject the model spec values\n injectModelSpec(prepDir, modelSpec),\n\n // XXX: Install a wrapper around the built-in `vite:resolve` plugin so that we can\n // override the default resolver behavior that tries to resolve the `browser` section\n // of the `package.json` for the threads package.\n {\n name: 'vite-plugin-override-resolve',\n configResolved(viteConfig) {\n overrideViteResolvePlugin(viteConfig)\n }\n }\n ],\n\n build: {\n // Write output files to the configured directory (instead of the default `dist`);\n // note that this must be relative to the project `root`\n outDir,\n emptyOutDir: false,\n\n // Uncomment for debugging purposes\n // minify: false,\n\n lib: {\n entry: './src/index.ts',\n formats: ['es'],\n fileName: () => 'check-bundle.js'\n },\n\n rollupOptions: {\n // Don't transform Node imports used by threads.js\n external: ['events', 'os', 'path', 'url'],\n\n // XXX: Insert custom code at the top of the generated bundle that defines\n // the special `__non_webpack_require__` function that is used by threads.js\n // in its Node implementation. This import ensures that threads.js uses\n // the native `worker_threads` implementation when using the bundle in a\n // Node environment. When importing the bundle for use in the browser,\n // Vite will transform this import into an empty module due to the empty\n // polyfill that is configured in `vite-config-for-report.ts`.\n output: {\n banner: `\nimport * as worker_threads from 'worker_threads'\nlet __non_webpack_require__ = () => {\n return worker_threads;\n};\n`\n },\n\n onwarn: (warning, warn) => {\n // XXX: Suppress \"Use of eval is strongly discouraged\" warnings that are\n // triggered by use of the following pattern in threads.js:\n // eval(\"require\")(\"worker_threads\")\n // It would be nice to avoid use of `eval` there, but it's not critical for\n // our use case so we will suppress the warnings for now\n if (warning.code !== 'EVAL') {\n warn(warning)\n }\n }\n }\n }\n }\n}\n","// Copyright (c) 2022 Climate Interactive / New Venture Fund\n\n/**\n * Helper function that converts a Vensim variable or subscript name\n * into a valid C identifier as used by SDE.\n * TODO: Import helper function from `compile` package instead\n */\nfunction sdeNameForVensimName(name: string): string {\n return (\n '_' +\n name\n .trim()\n .replace(/\"/g, '_')\n .replace(/\\s+!$/g, '!')\n .replace(/\\s/g, '_')\n .replace(/,/g, '_')\n .replace(/-/g, '_')\n .replace(/\\./g, '_')\n .replace(/\\$/g, '_')\n .replace(/'/g, '_')\n .replace(/&/g, '_')\n .replace(/%/g, '_')\n .replace(/\\//g, '_')\n .replace(/\\|/g, '_')\n .toLowerCase()\n )\n}\n\n/**\n * Helper function that converts a Vensim variable name (possibly containing\n * subscripts) into a valid C identifier as used by SDE.\n * TODO: Import helper function from `compile` package instead\n */\nexport function sdeNameForVensimVarName(varName: string): string {\n const m = varName.match(/([^[]+)(?:\\[([^\\]]+)\\])?/)\n if (!m) {\n throw new Error(`Invalid Vensim name: ${varName}`)\n }\n let id = sdeNameForVensimName(m[1])\n if (m[2]) {\n const subscripts = m[2].split(',').map(x => sdeNameForVensimName(x))\n id += `[${subscripts.join(',')}]`\n }\n\n return id\n}\n","// Copyright (c) 2022 Climate Interactive / New Venture Fund\n\nimport { dirname, relative, join as joinPath, resolve as resolvePath } from 'path'\nimport { fileURLToPath } from 'url'\n\nimport type { Alias, InlineConfig, PluginOption } from 'vite'\nimport replace from '@rollup/plugin-replace'\n\nimport type { SuiteSummary } from '@sdeverywhere/check-core'\n\nimport type { CheckPluginOptions } from './options'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\nexport function createViteConfigForReport(\n options: CheckPluginOptions | undefined,\n projDir: string,\n prepDir: string,\n currentBundleName: string,\n currentBundlePath: string,\n testConfigPath: string,\n suiteSummary: SuiteSummary | undefined\n): InlineConfig {\n // Use `template-report` as the root directory for the report project\n const root = resolvePath(__dirname, '..', 'template-report')\n\n // Include `baselines/*.js` files under the configured project root directory. This\n // glob path apparently must be a relative path (relative to the `template-report/src`\n // directory where the glob is used).\n const templateSrcDir = resolvePath(root, 'src')\n const relProjDir = relative(templateSrcDir, projDir)\n // XXX: The glob pattern must use forward slashes only, so on Windows we need to\n // convert backslashes to slashes\n const relProjDirPath = relProjDir.replaceAll('\\\\', '/')\n // TODO: Use baselinesDir from options\n const baselinesPath = `${relProjDirPath}/baselines/*.js`\n\n // Calculate output directory relative to the template root\n let reportPath: string\n if (options?.reportPath) {\n reportPath = options.reportPath\n } else {\n reportPath = joinPath(prepDir, 'check-report')\n }\n const outDir = relative(root, reportPath)\n\n // Convert the suite summary to JSON, which is what the app currently expects\n const suiteSummaryJson = suiteSummary ? JSON.stringify(suiteSummary) : ''\n\n const alias = (find: string, replacement: string) => {\n return {\n find,\n replacement\n } as Alias\n }\n\n // XXX: This provides custom handling for Node built-ins such as 'events' that are\n // referenced by the check bundle (specifically in the Node implementation of\n // threads.js). These are not actually used in the browser, so we just need\n // to provide no-op polyfills for these.\n const noopPolyfillAlias = (find: string) => {\n return {\n find,\n replacement: '/polyfills/noop-polyfills.ts'\n } as Alias\n }\n\n return {\n // Don't use an external config file\n configFile: false,\n\n // Use the root directory configured above\n root,\n\n // Use `.` as the base directory (instead of the default `/`); this controls\n // how the path to the js/css files are generated in `index.html`\n base: '',\n\n // Use a custom cache directory under `prepDir`, as otherwise Vite will use\n // `packages/plugin-check/template-report/node_modules/.vite`, and we want to\n // avoid generating files in `template-report` (which should be read-only)\n cacheDir: joinPath(prepDir, '.vite-check-report'),\n\n // Load static files from `static` (instead of the default `public`)\n // publicDir: 'static',\n\n // Don't clear the screen in dev mode so that we can see builder output\n clearScreen: false,\n\n // TODO\n // logLevel: 'silent',\n\n optimizeDeps: {\n // Prevent Vite from examining other html files when scanning entrypoints\n // for dependency optimization\n entries: ['index.html'],\n\n // XXX: When plugin-check is installed via pnpm, the Vite dev server seems\n // to have no trouble resolving other dependencies using the optimizeDeps\n // mechanism. However, this fails when the package is installed via yarn\n // or npm (probably due to the fact that the `template-report` directory\n // is located under the top-level `node_modules` directory); in the browser,\n // there will be \"import not found\" errors for the packages referenced below.\n // As a terrible workaround, explicitly include the direct dependencies so\n // that Vite optimizes them; this works for pnpm, yarn, and npm. We should\n // find a less fragile solution.\n include: [\n // from check-core\n '@sdeverywhere/check-core > assert-never',\n '@sdeverywhere/check-core > ajv',\n '@sdeverywhere/check-core > neverthrow',\n '@sdeverywhere/check-core > yaml',\n // from check-ui-shell\n '@sdeverywhere/check-ui-shell > fontfaceobserver',\n '@sdeverywhere/check-ui-shell > copy-text-to-clipboard',\n '@sdeverywhere/check-ui-shell > chart.js'\n ],\n\n exclude: [\n // XXX: The threads.js implementation references `tiny-worker` as an optional\n // dependency, but it doesn't get used at runtime, so we can just exclude it\n // so that Vite doesn't complain in dev mode\n 'tiny-worker'\n\n // XXX: Similarly, chart.js treats `moment` as an optional dependency, but we\n // don't use it at runtime; we need to exclude it here, otherwise Vite will\n // complain about missing dependencies in dev mode\n // 'moment'\n ]\n },\n\n // Configure path aliases\n resolve: {\n alias: [\n // Use the configured \"baseline\" bundle if defined, otherwise use the \"empty\" bundle\n // (which will cause comparison tests to be skipped)\n alias('@_baseline_bundle_', options?.baseline ? options.baseline.path : '/src/empty-bundle.ts'),\n\n // Use the configured \"current\" bundle\n alias('@_current_bundle_', currentBundlePath),\n\n // Use the configured test config file\n alias('@_test_config_', testConfigPath),\n\n // Make the overlay use the `messages.html` file that is written to the prep directory\n alias('@_prep_', prepDir),\n\n // XXX: Include no-op polyfills for these modules that are used in the Node-specific\n // implementation of threads.js; this allows us to use one bundle that works in both\n // Node and browser environments\n noopPolyfillAlias('events'),\n noopPolyfillAlias('fs'),\n noopPolyfillAlias('os'),\n noopPolyfillAlias('path'),\n noopPolyfillAlias('url'),\n noopPolyfillAlias('worker_threads')\n ]\n },\n\n // Inject special values into the generated JS\n define: {\n // Inject the summary JSON into the build\n __SUITE_SUMMARY_JSON__: JSON.stringify(suiteSummaryJson),\n\n // Inject the baseline branch name\n __BASELINE_NAME__: JSON.stringify(options?.baseline?.name || ''),\n\n // Inject the current branch name\n __CURRENT_NAME__: JSON.stringify(currentBundleName)\n },\n\n plugins: [\n // Inject special values into the generated JS\n // TODO: We currently have to use `@rollup/plugin-replace` instead of Vite's\n // built-in `define` feature because the latter does not seem to run before\n // the glob handler (which requires the glob to be injected as a literal)\n replace({\n preventAssignment: true,\n delimiters: ['', ''],\n values: {\n // Inject the path for baseline bundles\n './__BASELINE_BUNDLES_PATH__': baselinesPath\n }\n }) as unknown as PluginOption\n ],\n\n build: {\n // Write output files to the configured directory (instead of the default `dist`);\n // note that this must be relative to the project `root`\n outDir,\n\n // Write js/css files to `public` (instead of the default `<outDir>/assets`)\n assetsDir: '',\n\n rollupOptions: {\n output: {\n // XXX: Prevent vite from creating a separate `vendor.js` file\n manualChunks: undefined\n },\n\n onwarn: (warning, warn) => {\n // XXX: Suppress \"Use of eval is strongly discouraged\" warnings that are\n // triggered by use of the following pattern in threads.js:\n // eval(\"require\")(\"worker_threads\")\n // It would be nice to avoid use of `eval` there, but it's not critical for\n // our use case so we will suppress the warnings for now\n if (warning.code !== 'EVAL') {\n warn(warning)\n }\n }\n }\n },\n\n server: {\n // Run the dev server at `localhost:8081` by default\n port: options?.serverPort || 8081,\n\n // Open the app in the browser by default\n open: '/index.html',\n\n // XXX: Add a small delay, otherwise on macOS we sometimes get multiple\n // change events when a file is saved just once. That is a relatively\n // harmless issue except that it causes redundant messages in the console\n // and can cause extra churn when refreshing the app.\n watch: {\n awaitWriteFinish: {\n stabilityThreshold: 100\n }\n }\n }\n }\n}\n","// Copyright (c) 2022 Climate Interactive / New Venture Fund\n\nimport { dirname, relative, resolve as resolvePath } from 'path'\nimport { fileURLToPath } from 'url'\n\nimport type { InlineConfig, PluginOption } from 'vite'\nimport replace from '@rollup/plugin-replace'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\nexport function createViteConfigForTests(projDir: string, prepDir: string, mode: 'build' | 'watch'): InlineConfig {\n // Use `template-tests` as the root directory for the tests project\n const root = resolvePath(__dirname, '..', 'template-tests')\n\n // Include `*.check.yaml` files under the configured project root directory. This\n // glob path apparently must be a relative path (relative to the `template-tests/src`\n // directory where the glob is used).\n const templateSrcDir = resolvePath(root, 'src')\n const relProjDir = relative(templateSrcDir, projDir)\n // XXX: The glob pattern must use forward slashes only, so on Windows we need to\n // convert backslashes to slashes\n const relProjDirPath = relProjDir.replaceAll('\\\\', '/')\n // TODO: Use yamlPath from options\n const yamlPath = `${relProjDirPath}/**/*.check.yaml`\n\n // // Read the `package.json` for the template project\n // const pkgPath = resolvePath(root, 'package.json')\n // const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))\n\n // Calculate output directory relative to the template root\n // TODO: For now we write it to `prepDir`; make this configurable?\n const outDir = relative(root, prepDir)\n\n return {\n // Don't use an external config file\n configFile: false,\n\n // Use the root directory configured above\n root,\n\n // Don't clear the screen in dev mode so that we can see builder output\n clearScreen: false,\n\n // TODO: Disable vite output by default?\n // logLevel: 'silent',\n\n plugins: [\n // Inject special values into the generated JS\n // TODO: We currently have to use `@rollup/plugin-replace` instead of Vite's\n // built-in `define` feature because the latter does not seem to run before\n // the glob handler (which requires the glob to be injected as a literal)\n replace({\n preventAssignment: true,\n delimiters: ['', ''],\n values: {\n // Inject the glob pattern for matching check yaml files\n './__YAML_PATH__': yamlPath\n }\n }) as unknown as PluginOption\n ],\n\n build: {\n // Write output files to the configured directory (instead of the default `dist`);\n // note that this must be relative to the project `root`\n outDir,\n emptyOutDir: false,\n\n lib: {\n entry: './src/index.ts',\n formats: ['es'],\n fileName: () => 'check-tests.js'\n },\n\n // Enable watch mode if requested\n watch: mode === 'watch' && {},\n\n rollupOptions: {\n // Prevent dependencies from being included in packaged library\n // TODO: For now we include check-core in the packaged library so that its\n // dependencies are correctly resolved at runtime. Ideally this would only\n // include a couple functions that are used for defining tests, but Vite 2.x\n // does not implement tree shaking for ES libraries, which means the generated\n // library is much larger than it needs to be. Once we upgrade to Vite 3.x,\n // the generated library should be smaller; see related fix:\n // https://github.com/vitejs/vite/pull/8737\n // external: Object.keys(pkg.dependencies)\n }\n }\n }\n}\n"],"mappings":"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","names":["import_fs","import_path","import_url","import_check_core","pico","__filename","joinPath","resolvePath","import_path","import_url","__filename","__dirname","resolvePath","joinPath","replace","import_path","import_url","import_plugin_replace","__filename","__dirname","resolvePath","replace","chokidar","joinPath"]}
1
+ {"version":3,"sources":["../src/index.ts","../../../node_modules/.pnpm/tsup@8.2.4_typescript@5.2.2/node_modules/tsup/assets/cjs_shims.js","../src/plugin.ts","../src/run-suite.ts","../src/vite-config-for-bundle.ts","../src/vite-config-for-report.ts","../src/vite-config-for-tests.ts"],"sourcesContent":["// Copyright (c) 2022 Climate Interactive / New Venture Fund\n\nexport type { CheckBundle, CheckPluginOptions } from './options'\nexport { checkPlugin } from './plugin'\n","// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () =>\n typeof document === 'undefined'\n ? new URL(`file:${__filename}`).href\n : (document.currentScript && document.currentScript.src) ||\n new URL('main.js', document.baseURI).href\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","// Copyright (c) 2022 Climate Interactive / New Venture Fund\n\nimport { existsSync } from 'fs'\nimport { copyFile, mkdir } from 'fs/promises'\nimport { dirname, join as joinPath, relative } from 'path'\nimport { fileURLToPath } from 'url'\n\nimport type { InlineConfig, ViteDevServer } from 'vite'\nimport { build, createServer } from 'vite'\n\nimport chokidar from 'chokidar'\n\nimport type { BuildContext, Plugin, ResolvedConfig, ResolvedModelSpec } from '@sdeverywhere/build'\n\nimport type { Bundle, ConfigInitOptions, SuiteSummary } from '@sdeverywhere/check-core'\nimport { createConfig } from '@sdeverywhere/check-core'\n\nimport type { CheckPluginOptions } from './options'\nimport { runTestSuite } from './run-suite'\nimport { createViteConfigForBundle } from './vite-config-for-bundle'\nimport { createViteConfigForReport } from './vite-config-for-report'\nimport { createViteConfigForTests } from './vite-config-for-tests'\n\nexport function checkPlugin(options?: CheckPluginOptions): Plugin {\n return new CheckPlugin(options)\n}\n\ninterface TestOptions {\n currentBundleName: string\n currentBundlePath: string\n testConfigPath: string\n}\n\nclass CheckPlugin implements Plugin {\n private firstBuild = true\n\n constructor(private readonly options?: CheckPluginOptions) {}\n\n async watch(config: ResolvedConfig): Promise<void> {\n if (this.options?.testConfigPath === undefined) {\n // Test config was not provided, so generate a default config in watch mode.\n // The test template uses import.meta.glob so that checks are re-run\n // automatically when the `{checks/comparisons}/*.yaml` files are changed.\n await this.genTestConfig(config, 'watch')\n }\n\n // For development mode, run Vite in dev mode so that it serves the\n // model-check report locally (with live reload enabled). When a model\n // test file is changed, the tests will be re-run in the browser.\n const testOptions = this.resolveTestOptions(config)\n const viteConfig = this.createViteConfigForReport(config, testOptions, undefined)\n const server: ViteDevServer = await createServer(viteConfig)\n await server.listen()\n\n // XXX: Currently Vite doesn't reload the page if a file is added/removed\n // in the baselines directory (Vite's import.meta.glob handling doesn't\n // seem to do this automatically), so as a workaround, watch the baselines\n // directory and restart the server if files are added/removed\n // TODO: The same problem also applies to the glob for `checks/*.yaml` and\n // `comparisons/*.yaml` files in the test config, so we should also reload\n // if files match/unmatch those glob patterns\n // TODO: Use the baselines directory from the config\n const baselinesDir = 'baselines'\n const watcher = chokidar.watch(baselinesDir, {\n // Watch paths are resolved relative to the project root directory\n cwd: config.rootDir,\n // Don't send initial \"file added\" events\n ignoreInitial: true,\n // XXX: Include a delay, otherwise on macOS we sometimes get multiple\n // change events when a file is saved just once\n awaitWriteFinish: {\n stabilityThreshold: 200\n }\n })\n watcher.on('add', () => server.restart())\n watcher.on('unlink', () => server.restart())\n }\n\n // TODO: Note that this plugin runs as a `postBuild` step because it currently\n // needs to run after other plugins, and those plugins need to run after the\n // staged files are copied to their final destination(s). We should probably\n // make it configurable so that it can either be run as a `postGenerate` or a\n // `postBuild` step.\n async postBuild(context: BuildContext, modelSpec: ResolvedModelSpec): Promise<boolean> {\n const firstBuild = this.firstBuild\n this.firstBuild = false\n\n // For both production builds and local development, generate default bundle\n // in this post-build step each time a source file is changed\n // TODO: We could potentially use watch mode for the bundle similar to\n // what we do for the test config, but the bundle depends on the ModelSpec,\n // which currently isn't made available to the `watch` function\n if (this.options?.current === undefined) {\n // Path to current bundle was not provided, so generate a default bundle\n if (context.config.mode === 'development') {\n // Copy the previous bundle to the `baselines` directory so that\n // we automatically have it available as a baseline for comparison\n await this.copyPreviousBundle(context.config)\n }\n context.log('info', 'Generating model check bundle...')\n await this.genCurrentBundle(context, modelSpec)\n }\n\n // For production builds (and for the initial build in local development mode),\n // generate default test config in this post-build step\n if (this.options?.testConfigPath === undefined) {\n if (context.config.mode === 'production' || firstBuild) {\n // Test config was not provided, so generate a default config\n context.log('info', 'Generating model check test configuration...')\n await this.genTestConfig(context.config, 'build')\n }\n }\n\n if (context.config.mode === 'production') {\n // For production builds, run the model checks/comparisons, and then\n // inject the results into the generated report\n const testOptions = this.resolveTestOptions(context.config)\n return this.runChecks(context, testOptions)\n } else {\n // Nothing to do here in dev mode; the dev server will refresh and\n // re-run the tests in the browser when changes are detected\n return true\n }\n }\n\n private async copyPreviousBundle(config: ResolvedConfig): Promise<void> {\n // Only copy if the current bundle exists\n const currentBundleFile = joinPath(config.prepDir, 'check-bundle.js')\n if (existsSync(currentBundleFile)) {\n // TODO: Use the baselines directory from the config (not yet available)\n const baselinesDir = joinPath(config.rootDir, 'baselines')\n if (!existsSync(baselinesDir)) {\n await mkdir(baselinesDir, { recursive: true })\n }\n const previousBundleFile = joinPath(baselinesDir, 'previous.js')\n await copyFile(currentBundleFile, previousBundleFile)\n }\n }\n\n private async genCurrentBundle(context: BuildContext, modelSpec: ResolvedModelSpec): Promise<void> {\n const viteConfig = await createViteConfigForBundle(context, modelSpec)\n await build(viteConfig)\n }\n\n private async genTestConfig(config: ResolvedConfig, mode: 'build' | 'watch'): Promise<void> {\n const rootDir = config.rootDir\n const prepDir = config.prepDir\n const viteConfig = createViteConfigForTests(rootDir, prepDir, mode)\n await build(viteConfig)\n }\n\n private async runChecks(context: BuildContext, testOptions: TestOptions): Promise<boolean> {\n context.log('info', 'Running model checks...')\n\n // Load the bundles used by the model check/compare configuration. We\n // always initialize the \"current\" bundle.\n const moduleR = await import(relativeToSourcePath(testOptions.currentBundlePath))\n const bundleR = moduleR.createBundle() as Bundle\n const bundleNameR = testOptions.currentBundleName\n\n // Only initialize the \"baseline\" bundle if it is defined and the version\n // is the same as the \"current\" one. If the baseline bundle has a different\n // version, we will skip the comparison tests and only run the checks on the\n // current bundle.\n let bundleL: Bundle\n let bundleNameL: string\n if (this.options?.baseline) {\n const moduleL = await import(relativeToSourcePath(this.options.baseline.path))\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const rawBundleL: any = moduleL.createBundle() as any\n if (rawBundleL.version === bundleR.version) {\n bundleL = rawBundleL as Bundle\n bundleNameL = this.options.baseline.name\n }\n }\n\n // Get the model check/comparison configuration\n const testConfigModule = await import(relativeToSourcePath(testOptions.testConfigPath))\n const configInitOptions: ConfigInitOptions = {\n bundleNameL,\n bundleNameR\n }\n const configOptions = await testConfigModule.getConfigOptions(bundleL, bundleR, configInitOptions)\n\n // Run the suite of checks and comparisons\n const checkConfig = await createConfig(configOptions)\n const result = await runTestSuite(context, checkConfig, /*verbose=*/ false)\n\n // Build the report (using Vite)\n context.log('info', 'Building model check report')\n const viteConfig = this.createViteConfigForReport(context.config, testOptions, result.suiteSummary)\n await build(viteConfig)\n\n // context.log('info', 'Done!')\n\n return result.allChecksPassed\n }\n\n private resolveTestOptions(config: ResolvedConfig): TestOptions {\n let currentBundleName: string\n let currentBundlePath: string\n if (this.options?.current === undefined) {\n // Path to current bundle was not provided, so use a generated bundle\n currentBundleName = 'current'\n currentBundlePath = joinPath(config.prepDir, 'check-bundle.js')\n } else {\n // Use the provided bundle\n currentBundleName = this.options.current.name\n currentBundlePath = this.options.current.path\n }\n\n let testConfigPath: string\n if (this.options?.testConfigPath === undefined) {\n // Test config was not provided, so use a generated config\n testConfigPath = joinPath(config.prepDir, 'check-tests.js')\n } else {\n // Use the provided test config\n testConfigPath = this.options.testConfigPath\n }\n\n return {\n currentBundleName,\n currentBundlePath,\n testConfigPath\n }\n }\n\n private createViteConfigForReport(\n config: ResolvedConfig,\n testOptions: TestOptions,\n suiteSummary: SuiteSummary | undefined\n ): InlineConfig {\n return createViteConfigForReport(\n this.options,\n config.rootDir,\n config.prepDir,\n testOptions.currentBundleName,\n testOptions.currentBundlePath,\n testOptions.testConfigPath,\n suiteSummary\n )\n }\n}\n\n/**\n * Return a Unix-style path (e.g. '../../foo.js') that is relative to the directory of\n * the current source file. This can be used to construct a path that is safe for\n * dynamic import on either Unix or Windows.\n *\n * @param filePath The path to make relative.\n */\nfunction relativeToSourcePath(filePath: string): string {\n const srcDir = dirname(fileURLToPath(import.meta.url))\n const relPath = relative(srcDir, filePath)\n return relPath.replaceAll('\\\\', '/')\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport { performance } from 'perf_hooks'\n\nimport pico from 'picocolors'\n\nimport type { BuildContext } from '@sdeverywhere/build'\n\nimport type {\n Config,\n ComparisonReport,\n PerfReport,\n RunSuiteCallbacks,\n CheckReport,\n CheckStatus,\n ComparisonConfig,\n CheckTestReport,\n SuiteSummary\n} from '@sdeverywhere/check-core'\nimport {\n datasetMessage,\n predicateMessage,\n runSuite,\n scenarioMessage,\n suiteSummaryFromReport\n} from '@sdeverywhere/check-core'\n\nexport interface RunTestSuiteResult {\n allChecksPassed: boolean\n suiteSummary: SuiteSummary\n}\n\n/**\n * Runs the test suite.\n */\nexport async function runTestSuite(\n context: BuildContext,\n config: Config,\n verbose: boolean\n): Promise<RunTestSuiteResult> {\n return new Promise((resolve, reject) => {\n const t0 = performance.now()\n let lastPctByInc: number\n const callbacks: RunSuiteCallbacks = {\n onProgress: progress => {\n const pct = Math.round(progress * 100)\n const pctByInc = Math.floor(pct / 5) * 5\n if (lastPctByInc === undefined || pctByInc > lastPctByInc) {\n lastPctByInc = pctByInc\n context.log('info', `${pctByInc}%`)\n }\n },\n onComplete: report => {\n try {\n const t1 = performance.now()\n const elapsed = ((t1 - t0) / 1000).toFixed(1)\n context.log('info', `\\nTest suite completed in ${elapsed}s`)\n\n // Print check summary to the console\n const allChecksPassed = printCheckSummary(context, report.checkReport, verbose)\n\n if (report.comparisonReport) {\n // Print the perf stats to the console\n printPerfStats(context, config.comparison, report.comparisonReport)\n }\n\n // Convert check and compare reports to terse form that only includes\n // failed/errored checks or comparisons with differences\n // TODO: The terse form was originally used when we had to write the\n // results to a JSON file and then read them back in when building\n // the report, but we no longer use that intermediate file, so there's\n // less reason to use the terse form (since it requires the web app\n // code to reconstruct the results). But for now, we will continue\n // to use the terse form, and later we can update the app code.\n const suiteSummary = suiteSummaryFromReport(report)\n\n resolve({\n allChecksPassed,\n suiteSummary\n })\n } catch (e) {\n reject(e)\n }\n },\n onError: error => {\n reject(error)\n }\n }\n runSuite(config, callbacks)\n })\n}\n\nfunction printCheckSummary(context: BuildContext, checkReport: CheckReport, verbose: boolean): boolean {\n function printResult(indent: number, status: CheckStatus, text: string): void {\n if (!verbose && status === 'passed' && indent > 1) {\n return\n }\n let statusChar: string\n switch (status) {\n case 'passed':\n statusChar = '✓'\n break\n case 'failed':\n statusChar = '✗'\n break\n case 'error':\n statusChar = '‼'\n break\n default:\n statusChar = ''\n break\n }\n const msg = `${' '.repeat(indent)}${statusChar} ${text}`\n context.log('info', status === 'passed' ? pico.green(msg) : pico.red(msg))\n }\n\n function bold(s: string): string {\n return pico.bold(s)\n }\n\n function printTest(test: CheckTestReport): void {\n const msg = `${test.name}${verbose || test.status !== 'passed' ? ':' : ''}`\n printResult(1, test.status, msg)\n }\n\n let allPassed = true\n context.log('info', '\\nCheck results:')\n for (const group of checkReport.groups) {\n context.log('info', `\\n${group.name}`)\n\n for (const test of group.tests) {\n if (test.status !== 'passed') {\n allPassed = false\n }\n printTest(test)\n\n for (const scenario of test.scenarios) {\n printResult(3, scenario.status, scenarioMessage(scenario, bold))\n\n for (const dataset of scenario.datasets) {\n printResult(5, dataset.status, datasetMessage(dataset, bold))\n\n for (const predicate of dataset.predicates) {\n printResult(7, predicate.result.status, predicateMessage(predicate, bold))\n }\n }\n }\n }\n }\n context.log('info', '')\n\n return allPassed\n}\n\nfunction stat(label: string, n: number): string {\n return `${label}=${n.toFixed(1)}ms`\n}\n\nfunction printPerfReportLine(context: BuildContext, perfReport: PerfReport): void {\n const avg = stat('avg', perfReport.avgTime)\n const min = stat('min', perfReport.minTime)\n const max = stat('max', perfReport.maxTime)\n context.log('info', ` ${avg} ${min} ${max}`)\n}\n\nfunction printPerfStats(context: BuildContext, comparisonConfig: ComparisonConfig, report: ComparisonReport): void {\n context.log('info', '\\nPerformance stats:')\n context.log('info', ` ${comparisonConfig.bundleL.name}:`)\n printPerfReportLine(context, report.perfReportL)\n context.log('info', ` ${comparisonConfig.bundleR.name}:`)\n printPerfReportLine(context, report.perfReportR)\n context.log('info', '')\n}\n","// Copyright (c) 2022 Climate Interactive / New Venture Fund\n\nimport { existsSync, statSync } from 'fs'\nimport { basename, dirname, join as joinPath, relative, resolve as resolvePath } from 'path'\nimport { fileURLToPath } from 'url'\n\nimport type { InlineConfig, ResolvedConfig, Plugin as VitePlugin } from 'vite'\nimport { nodeResolve } from '@rollup/plugin-node-resolve'\n\nimport type { BuildContext, ResolvedModelSpec } from '@sdeverywhere/build'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\n/**\n * This is a virtual module plugin used to inject model-specific configuration\n * values into the generated worker bundle.\n *\n * This follows the \"Virtual Modules Convention\" described here:\n * https://vitejs.dev/guide/api-plugin.html#virtual-modules-convention\n *\n * TODO: This could be simplified by using `vite-plugin-virtual` but that\n * doesn't seem to be working correctly in an ESM setting\n */\nfunction injectModelSpec(context: BuildContext, modelSpec: ResolvedModelSpec): VitePlugin {\n // Include the SDE variable ID with each spec\n const inputSpecs = []\n for (const modelInputSpec of modelSpec.inputs) {\n // Note that the `InputSpec` interface in the `@sdeverywhere/build` package\n // allows the default/min/max values to be undefined, which can be the case\n // if the user doesn't return full `InputSpec` instances in the `ModelSpec`.\n // We will log a warning and skip the input if these values are not defined.\n if (\n modelInputSpec.defaultValue === undefined ||\n modelInputSpec.minValue === undefined ||\n modelInputSpec.maxValue === undefined\n ) {\n let msg = ''\n msg += `WARNING: The {defaultValue,minValue,maxValue} properties are required by plugin-check, `\n msg += `but are undefined in the InputSpec for '${modelInputSpec.varName}'. `\n msg += `This input variable will be excluded from the model-check bundle until those properties `\n msg += `are defined.`\n console.warn(msg)\n continue\n }\n\n // Use the `inputId` if defined for the `InputSpec`, otherwise use `varId`. The\n // latter is less resilient if the variable is renamed between two versions of\n // the model, but will be sufficient for now. Note that `plugin-config` defines\n // a stable `inputId` for each row in the `inputs.csv`, and that is the most\n // common way to configure a `ModelSpec`, so it will be uncommon for `inputId`\n // to be undefined here.\n const varId = context.canonicalVarId(modelInputSpec.varName)\n const inputId = modelInputSpec.inputId || varId\n inputSpecs.push({\n inputId,\n varId,\n ...modelInputSpec\n })\n }\n const outputSpecs = modelSpec.outputs.map(o => {\n return {\n varId: context.canonicalVarId(o.varName),\n ...o\n }\n })\n\n function stagedFileSize(filename: string): number {\n const prepDir = context.config.prepDir\n const path = joinPath(prepDir, 'staged', 'model', filename)\n if (existsSync(path)) {\n return statSync(path).size\n } else {\n return 0\n }\n }\n\n // The size (in bytes) of the `generated-model.js` file\n // TODO: Ideally we would measure the size of the raw Wasm binary, but currently\n // we inline it as a base64 blob inside the JS file, so we take the size of the\n // whole JS file as the second best option\n const modelSizeInBytes = stagedFileSize('generated-model.js')\n\n // The size (in bytes) of the `static-data.ts` file\n // TODO: Ideally we would measure the size of the minified JS file here, or\n // at least ignore things like whitespace\n const dataSizeInBytes = stagedFileSize('static-data.ts')\n\n const moduleSrc = `\nexport const inputSpecs = ${JSON.stringify(inputSpecs)};\nexport const outputSpecs = ${JSON.stringify(outputSpecs)};\nexport const modelSizeInBytes = ${modelSizeInBytes};\nexport const dataSizeInBytes = ${dataSizeInBytes};\n`\n\n const virtualModuleId = 'virtual:model-spec'\n const resolvedVirtualModuleId = '\\0' + virtualModuleId\n\n return {\n name: 'vite-plugin-virtual-custom',\n resolveId(id: string) {\n if (id === virtualModuleId) {\n return resolvedVirtualModuleId\n }\n },\n load(id: string) {\n if (id === resolvedVirtualModuleId) {\n return moduleSrc\n }\n }\n }\n}\n\n/**\n * XXX: This overrides the built-in `vite:resolve` plugin so that we can intercept `resolveId`\n * calls for the threads package.\n */\nfunction overrideViteResolvePlugin(viteConfig: ResolvedConfig) {\n const resolvePlugin = viteConfig.plugins.find(p => p.name === 'vite:resolve')\n if (resolvePlugin === undefined) {\n throw new Error('Failed to locate the built-in vite:resolve plugin')\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const originalResolveId = resolvePlugin.resolveId as any\n resolvePlugin.resolveId = async function resolveId(id, importer, options) {\n if (id.startsWith('./implementation') && importer.includes('threads/dist-esm')) {\n // XXX: The default resolver behavior will look at the `browser` mappings in\n // `threads/package.json` and try to resolve `implementation.js` to\n // `implementation.browser.js` because it thinks we're in a browser-only context.\n // We don't want that. Instead we want to keep the generic implementation from\n // threads that chooses between the Node and browser implementations at runtime.\n //\n // If we get here, importer will be something like:\n // /.../node_modules/.pnpm/threads@1.7.0/node_modules/threads/dist-esm/{worker,master}/index.js\n // And id will be:\n // ./implementation\n // So resolve the ID to:\n // /.../node_modules/.pnpm/threads@1.7.0/node_modules/threads/dist-esm/{worker,master}/implementation.js\n //\n // Or, importer will be:\n // /.../node_modules/.pnpm/threads@1.7.0/node_modules/threads/dist-esm/{worker,master}/implementation.js\n // And id will be one of:\n // ./implementation.browser\n // ./implementation.node\n // ./implementation.worker_threads\n // So resolve the ID to:\n // /.../node_modules/.pnpm/threads@1.7.0/node_modules/threads/dist-esm/{worker,master}/implementation.{...}.js\n const idFileName = id.replace('./', '')\n const importerFileName = basename(importer)\n const resolvedId = importer.replace(importerFileName, `${idFileName}.js`)\n return {\n id: resolvedId,\n moduleSideEffects: false\n }\n }\n\n // For all other cases, fall back on the default resolver\n return originalResolveId.call(this, id, importer, options)\n }\n}\n\nexport async function createViteConfigForBundle(\n context: BuildContext,\n modelSpec: ResolvedModelSpec\n): Promise<InlineConfig> {\n // Use `template-bundle` as the root directory for the bundle project\n const root = resolvePath(__dirname, '..', 'template-bundle')\n\n // Calculate output directory relative to the template root\n // TODO: For now we write it to `prepDir`; make this configurable?\n const prepDir = context.config.prepDir\n const outDir = relative(root, prepDir)\n\n // Use the model worker from the staged directory\n // TODO: Make this configurable?\n const modelWorkerPath = joinPath(prepDir, 'staged', 'model', 'worker.js?raw')\n\n return {\n // Don't use an external config file\n configFile: false,\n\n // Use the root directory configured above\n root,\n\n // Don't clear the screen in dev mode so that we can see builder output\n clearScreen: false,\n\n // TODO: Disable vite output by default?\n // logLevel: 'silent',\n\n // Configure path aliases\n resolve: {\n alias: [\n // Inject the configured model worker\n {\n find: '@_model_worker_',\n replacement: modelWorkerPath\n },\n\n // XXX: Prevent Vite from using the `browser` section of `threads/package.json`\n // since we want to force the use of the general module (under dist-esm) that chooses\n // the correct implementation (Web Worker vs worker_threads) at runtime. Currently\n // Vite's library mode is browser focused and generally chooses the right imports,\n // except in the case of the threads package where we want to use the generic\n // `implementation.js` that chooses between Web Worker and worker_threads at runtime.\n // Note that we could in theory set `resolve.browserField` to false, but that would\n // make Vite not use the browser field for all other packages, and there is not\n // currently a way to tell Vite to use the browser field on a case-by-case basis.\n // So for now we need this workaround here to make it resolve to `dist-esm`, and then\n // a second workaround in `overrideViteResolvePlugin` to prevent the resolver from\n // using the browser field when resolving the threads package.\n {\n find: 'threads',\n replacement: 'threads',\n customResolver: async function (source, importer, options) {\n // Note that we need to use `resolveId.call` here in order to provide the\n // right `this` context, which provides Rollup plugin functionality\n const customResolver = nodeResolve({ browser: false })\n const resolved = await customResolver.resolveId.call(this, source, importer, options)\n // Force the use of the `dist-esm` variant of the threads.js package\n if (source === 'threads/worker') {\n return resolved.id.replace('worker.mjs', 'dist-esm/worker/index.js')\n } else {\n return resolved.id.replace('index.mjs', 'dist-esm/index.js')\n }\n }\n }\n ]\n },\n\n plugins: [\n // Use a virtual module plugin to inject the model spec values\n injectModelSpec(context, modelSpec),\n\n // XXX: Install a wrapper around the built-in `vite:resolve` plugin so that we can\n // override the default resolver behavior that tries to resolve the `browser` section\n // of the `package.json` for the threads package.\n {\n name: 'vite-plugin-override-resolve',\n configResolved(viteConfig) {\n overrideViteResolvePlugin(viteConfig)\n }\n }\n ],\n\n build: {\n // Write output files to the configured directory (instead of the default `dist`);\n // note that this must be relative to the project `root`\n outDir,\n emptyOutDir: false,\n\n // Uncomment for debugging purposes\n // minify: false,\n\n lib: {\n entry: './src/index.ts',\n formats: ['es'],\n fileName: () => 'check-bundle.js'\n },\n\n rollupOptions: {\n // Don't transform Node imports used by threads.js\n external: ['events', 'os', 'path', 'url'],\n\n // XXX: Insert custom code at the top of the generated bundle that defines\n // the special `__non_webpack_require__` function that is used by threads.js\n // in its Node implementation. This import ensures that threads.js uses\n // the native `worker_threads` implementation when using the bundle in a\n // Node environment. When importing the bundle for use in the browser,\n // Vite will transform this import into an empty module due to the empty\n // polyfill that is configured in `vite-config-for-report.ts`.\n output: {\n banner: `\nimport * as worker_threads from 'worker_threads'\nlet __non_webpack_require__ = () => {\n return worker_threads;\n};\n`\n },\n\n onwarn: (warning, warn) => {\n // XXX: Suppress \"Use of eval is strongly discouraged\" warnings that are\n // triggered by use of the following pattern in threads.js:\n // eval(\"require\")(\"worker_threads\")\n // It would be nice to avoid use of `eval` there, but it's not critical for\n // our use case so we will suppress the warnings for now\n if (warning.code !== 'EVAL') {\n warn(warning)\n }\n }\n }\n }\n }\n}\n","// Copyright (c) 2022 Climate Interactive / New Venture Fund\n\nimport { dirname, relative, join as joinPath, resolve as resolvePath } from 'path'\nimport { fileURLToPath } from 'url'\n\nimport type { Alias, InlineConfig, PluginOption } from 'vite'\nimport replace from '@rollup/plugin-replace'\n\nimport type { SuiteSummary } from '@sdeverywhere/check-core'\n\nimport type { CheckPluginOptions } from './options'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\nexport function createViteConfigForReport(\n options: CheckPluginOptions | undefined,\n projDir: string,\n prepDir: string,\n currentBundleName: string,\n currentBundlePath: string,\n testConfigPath: string,\n suiteSummary: SuiteSummary | undefined\n): InlineConfig {\n // Use `template-report` as the root directory for the report project\n const root = resolvePath(__dirname, '..', 'template-report')\n\n // Include `baselines/*.js` files under the configured project root directory. This\n // glob path apparently must be a relative path (relative to the `template-report/src`\n // directory where the glob is used).\n const templateSrcDir = resolvePath(root, 'src')\n const relProjDir = relative(templateSrcDir, projDir)\n // XXX: The glob pattern must use forward slashes only, so on Windows we need to\n // convert backslashes to slashes\n const relProjDirPath = relProjDir.replaceAll('\\\\', '/')\n // TODO: Use baselinesDir from options\n const baselinesPath = `${relProjDirPath}/baselines/*.js`\n\n // Calculate output directory relative to the template root\n let reportPath: string\n if (options?.reportPath) {\n reportPath = options.reportPath\n } else {\n reportPath = joinPath(prepDir, 'check-report')\n }\n const outDir = relative(root, reportPath)\n\n // Convert the suite summary to JSON, which is what the app currently expects\n const suiteSummaryJson = suiteSummary ? JSON.stringify(suiteSummary) : ''\n\n const alias = (find: string, replacement: string) => {\n return {\n find,\n replacement\n } as Alias\n }\n\n // XXX: This provides custom handling for Node built-ins such as 'events' that are\n // referenced by the check bundle (specifically in the Node implementation of\n // threads.js). These are not actually used in the browser, so we just need\n // to provide no-op polyfills for these.\n const noopPolyfillAlias = (find: string) => {\n return {\n find,\n replacement: '/polyfills/noop-polyfills.ts'\n } as Alias\n }\n\n return {\n // Don't use an external config file\n configFile: false,\n\n // Use the root directory configured above\n root,\n\n // Use `.` as the base directory (instead of the default `/`); this controls\n // how the path to the js/css files are generated in `index.html`\n base: '',\n\n // Use a custom cache directory under `prepDir`, as otherwise Vite will use\n // `packages/plugin-check/template-report/node_modules/.vite`, and we want to\n // avoid generating files in `template-report` (which should be read-only)\n cacheDir: joinPath(prepDir, '.vite-check-report'),\n\n // Load static files from `static` (instead of the default `public`)\n // publicDir: 'static',\n\n // Don't clear the screen in dev mode so that we can see builder output\n clearScreen: false,\n\n // TODO\n // logLevel: 'silent',\n\n optimizeDeps: {\n // Prevent Vite from examining other html files when scanning entrypoints\n // for dependency optimization\n entries: ['index.html'],\n\n // XXX: When plugin-check is installed via pnpm, the Vite dev server seems\n // to have no trouble resolving other dependencies using the optimizeDeps\n // mechanism. However, this fails when the package is installed via yarn\n // or npm (probably due to the fact that the `template-report` directory\n // is located under the top-level `node_modules` directory); in the browser,\n // there will be \"import not found\" errors for the packages referenced below.\n // As a terrible workaround, explicitly include the direct dependencies so\n // that Vite optimizes them; this works for pnpm, yarn, and npm. We should\n // find a less fragile solution.\n include: [\n // from check-core\n '@sdeverywhere/check-core > assert-never',\n '@sdeverywhere/check-core > ajv',\n '@sdeverywhere/check-core > neverthrow',\n '@sdeverywhere/check-core > yaml',\n // from check-ui-shell\n '@sdeverywhere/check-ui-shell > fontfaceobserver',\n '@sdeverywhere/check-ui-shell > copy-text-to-clipboard',\n '@sdeverywhere/check-ui-shell > chart.js'\n ],\n\n exclude: [\n // XXX: The threads.js implementation references `tiny-worker` as an optional\n // dependency, but it doesn't get used at runtime, so we can just exclude it\n // so that Vite doesn't complain in dev mode\n 'tiny-worker'\n\n // XXX: Similarly, chart.js treats `moment` as an optional dependency, but we\n // don't use it at runtime; we need to exclude it here, otherwise Vite will\n // complain about missing dependencies in dev mode\n // 'moment'\n ]\n },\n\n // Configure path aliases\n resolve: {\n alias: [\n // Use the configured \"baseline\" bundle if defined, otherwise use the \"empty\" bundle\n // (which will cause comparison tests to be skipped)\n alias('@_baseline_bundle_', options?.baseline ? options.baseline.path : '/src/empty-bundle.ts'),\n\n // Use the configured \"current\" bundle\n alias('@_current_bundle_', currentBundlePath),\n\n // Use the configured test config file\n alias('@_test_config_', testConfigPath),\n\n // Make the overlay use the `messages.html` file that is written to the prep directory\n alias('@_prep_', prepDir),\n\n // XXX: Include no-op polyfills for these modules that are used in the Node-specific\n // implementation of threads.js; this allows us to use one bundle that works in both\n // Node and browser environments\n noopPolyfillAlias('events'),\n noopPolyfillAlias('fs'),\n noopPolyfillAlias('os'),\n noopPolyfillAlias('path'),\n noopPolyfillAlias('url'),\n noopPolyfillAlias('worker_threads')\n ]\n },\n\n // Inject special values into the generated JS\n define: {\n // Inject the summary JSON into the build\n __SUITE_SUMMARY_JSON__: JSON.stringify(suiteSummaryJson),\n\n // Inject the baseline branch name\n __BASELINE_NAME__: JSON.stringify(options?.baseline?.name || ''),\n\n // Inject the current branch name\n __CURRENT_NAME__: JSON.stringify(currentBundleName)\n },\n\n plugins: [\n // Inject special values into the generated JS\n // TODO: We currently have to use `@rollup/plugin-replace` instead of Vite's\n // built-in `define` feature because the latter does not seem to run before\n // the glob handler (which requires the glob to be injected as a literal)\n replace({\n preventAssignment: true,\n delimiters: ['', ''],\n values: {\n // Inject the path for baseline bundles\n './__BASELINE_BUNDLES_PATH__': baselinesPath\n }\n }) as unknown as PluginOption\n ],\n\n build: {\n // Write output files to the configured directory (instead of the default `dist`);\n // note that this must be relative to the project `root`\n outDir,\n\n // Write js/css files to `public` (instead of the default `<outDir>/assets`)\n assetsDir: '',\n\n rollupOptions: {\n output: {\n // XXX: Prevent vite from creating a separate `vendor.js` file\n manualChunks: undefined\n },\n\n onwarn: (warning, warn) => {\n // XXX: Suppress \"Use of eval is strongly discouraged\" warnings that are\n // triggered by use of the following pattern in threads.js:\n // eval(\"require\")(\"worker_threads\")\n // It would be nice to avoid use of `eval` there, but it's not critical for\n // our use case so we will suppress the warnings for now\n if (warning.code !== 'EVAL') {\n warn(warning)\n }\n }\n }\n },\n\n server: {\n // Run the dev server at `localhost:8081` by default\n port: options?.serverPort || 8081,\n\n // Open the app in the browser by default\n open: '/index.html',\n\n // XXX: Add a small delay, otherwise on macOS we sometimes get multiple\n // change events when a file is saved just once. That is a relatively\n // harmless issue except that it causes redundant messages in the console\n // and can cause extra churn when refreshing the app.\n watch: {\n awaitWriteFinish: {\n stabilityThreshold: 100\n }\n }\n }\n }\n}\n","// Copyright (c) 2022 Climate Interactive / New Venture Fund\n\nimport { dirname, relative, resolve as resolvePath } from 'path'\nimport { fileURLToPath } from 'url'\n\nimport type { InlineConfig, PluginOption } from 'vite'\nimport replace from '@rollup/plugin-replace'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\nexport function createViteConfigForTests(projDir: string, prepDir: string, mode: 'build' | 'watch'): InlineConfig {\n // Use `template-tests` as the root directory for the tests project\n const root = resolvePath(__dirname, '..', 'template-tests')\n\n // Get the base glob path; apparently this must be a relative path (relative to\n // the `template-tests/src` directory where the glob is used)\n const templateSrcDir = resolvePath(root, 'src')\n const relProjDir = relative(templateSrcDir, projDir)\n // XXX: The glob pattern must use forward slashes only, so on Windows we need to\n // convert backslashes to slashes\n const relProjDirPath = relProjDir.replaceAll('\\\\', '/')\n\n // Include check test definitions in files matching `checks/*.yaml` under\n // the configured project root directory. We also include `*.check.yaml`,\n // which was the naming used in earlier versions of the create package and\n // related examples.\n // TODO: Use yaml path/pattern from options\n const yamlCheckGlobPatterns = `['${relProjDirPath}/**/checks/*.yaml', '${relProjDirPath}/**/*.check.yaml']`\n\n // Include comparison test definitions in files matching `comparisons/*.yaml`\n // under the configured project root directory\n // TODO: Use yaml path/pattern from options\n const yamlComparisonGlobPatterns = `['${relProjDirPath}/**/comparisons/*.yaml']`\n\n // Calculate output directory relative to the template root\n // TODO: For now we write it to `prepDir`; make this configurable?\n const outDir = relative(root, prepDir)\n\n return {\n // Don't use an external config file\n configFile: false,\n\n // Use the root directory configured above\n root,\n\n // Don't clear the screen in dev mode so that we can see builder output\n clearScreen: false,\n\n // TODO: Disable vite output by default?\n // logLevel: 'silent',\n\n plugins: [\n // Inject special values into the generated JS\n // TODO: We currently have to use `@rollup/plugin-replace` instead of Vite's\n // built-in `define` feature because the latter does not seem to run before\n // the glob handler (which requires the glob to be injected as a literal)\n replace({\n preventAssignment: true,\n delimiters: ['', ''],\n values: {\n // Inject the glob patterns for matching model check yaml files\n '\"./__YAML_CHECK_GLOB_PATTERNS__\"': yamlCheckGlobPatterns,\n // Inject the glob patterns for matching model comparison yaml files\n '\"./__YAML_COMPARISON_GLOB_PATTERNS__\"': yamlComparisonGlobPatterns\n }\n }) as unknown as PluginOption\n ],\n\n build: {\n // Write output files to the configured directory (instead of the default `dist`);\n // note that this must be relative to the project `root`\n outDir,\n emptyOutDir: false,\n\n lib: {\n entry: './src/index.ts',\n formats: ['es'],\n fileName: () => 'check-tests.js'\n },\n\n // Enable watch mode if requested\n watch: mode === 'watch' && {},\n\n rollupOptions: {\n // Prevent dependencies from being included in packaged library\n // TODO: For now we include check-core in the packaged library so that its\n // dependencies are correctly resolved at runtime. Ideally this would only\n // include a couple functions that are used for defining tests, but Vite 2.x\n // does not implement tree shaking for ES libraries, which means the generated\n // library is much larger than it needs to be. Once we upgrade to Vite 3.x,\n // the generated library should be smaller; see related fix:\n // https://github.com/vitejs/vite/pull/8737\n // external: Object.keys(pkg.dependencies)\n }\n }\n }\n}\n"],"mappings":"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","names":["import_fs","import_path","import_url","import_check_core","pico","__filename","joinPath","resolvePath","import_path","import_url","__filename","__dirname","resolvePath","joinPath","replace","import_path","import_url","import_plugin_replace","__filename","__dirname","resolvePath","replace","chokidar","joinPath"]}
package/dist/index.js CHANGED
@@ -133,28 +133,9 @@ import { existsSync, statSync } from "fs";
133
133
  import { basename, dirname, join as joinPath, relative, resolve as resolvePath } from "path";
134
134
  import { fileURLToPath } from "url";
135
135
  import { nodeResolve } from "@rollup/plugin-node-resolve";
136
-
137
- // src/var-names.ts
138
- function sdeNameForVensimName(name) {
139
- return "_" + name.trim().replace(/"/g, "_").replace(/\s+!$/g, "!").replace(/\s/g, "_").replace(/,/g, "_").replace(/-/g, "_").replace(/\./g, "_").replace(/\$/g, "_").replace(/'/g, "_").replace(/&/g, "_").replace(/%/g, "_").replace(/\//g, "_").replace(/\|/g, "_").toLowerCase();
140
- }
141
- function sdeNameForVensimVarName(varName) {
142
- const m = varName.match(/([^[]+)(?:\[([^\]]+)\])?/);
143
- if (!m) {
144
- throw new Error(`Invalid Vensim name: ${varName}`);
145
- }
146
- let id = sdeNameForVensimName(m[1]);
147
- if (m[2]) {
148
- const subscripts = m[2].split(",").map((x) => sdeNameForVensimName(x));
149
- id += `[${subscripts.join(",")}]`;
150
- }
151
- return id;
152
- }
153
-
154
- // src/vite-config-for-bundle.ts
155
136
  var __filename2 = fileURLToPath(import.meta.url);
156
137
  var __dirname2 = dirname(__filename2);
157
- function injectModelSpec(prepDir, modelSpec) {
138
+ function injectModelSpec(context, modelSpec) {
158
139
  const inputSpecs = [];
159
140
  for (const modelInputSpec of modelSpec.inputs) {
160
141
  if (modelInputSpec.defaultValue === void 0 || modelInputSpec.minValue === void 0 || modelInputSpec.maxValue === void 0) {
@@ -166,7 +147,7 @@ function injectModelSpec(prepDir, modelSpec) {
166
147
  console.warn(msg);
167
148
  continue;
168
149
  }
169
- const varId = sdeNameForVensimVarName(modelInputSpec.varName);
150
+ const varId = context.canonicalVarId(modelInputSpec.varName);
170
151
  const inputId = modelInputSpec.inputId || varId;
171
152
  inputSpecs.push({
172
153
  inputId,
@@ -176,11 +157,12 @@ function injectModelSpec(prepDir, modelSpec) {
176
157
  }
177
158
  const outputSpecs = modelSpec.outputs.map((o) => {
178
159
  return {
179
- varId: sdeNameForVensimVarName(o.varName),
160
+ varId: context.canonicalVarId(o.varName),
180
161
  ...o
181
162
  };
182
163
  });
183
164
  function stagedFileSize(filename) {
165
+ const prepDir = context.config.prepDir;
184
166
  const path = joinPath(prepDir, "staged", "model", filename);
185
167
  if (existsSync(path)) {
186
168
  return statSync(path).size;
@@ -231,8 +213,9 @@ function overrideViteResolvePlugin(viteConfig) {
231
213
  return originalResolveId.call(this, id, importer, options);
232
214
  };
233
215
  }
234
- async function createViteConfigForBundle(prepDir, modelSpec) {
216
+ async function createViteConfigForBundle(context, modelSpec) {
235
217
  const root = resolvePath(__dirname2, "..", "template-bundle");
218
+ const prepDir = context.config.prepDir;
236
219
  const outDir = relative(root, prepDir);
237
220
  const modelWorkerPath = joinPath(prepDir, "staged", "model", "worker.js?raw");
238
221
  return {
@@ -281,7 +264,7 @@ async function createViteConfigForBundle(prepDir, modelSpec) {
281
264
  },
282
265
  plugins: [
283
266
  // Use a virtual module plugin to inject the model spec values
284
- injectModelSpec(prepDir, modelSpec),
267
+ injectModelSpec(context, modelSpec),
285
268
  // XXX: Install a wrapper around the built-in `vite:resolve` plugin so that we can
286
269
  // override the default resolver behavior that tries to resolve the `browser` section
287
270
  // of the `package.json` for the threads package.
@@ -510,7 +493,8 @@ function createViteConfigForTests(projDir, prepDir, mode) {
510
493
  const templateSrcDir = resolvePath3(root, "src");
511
494
  const relProjDir = relative3(templateSrcDir, projDir);
512
495
  const relProjDirPath = relProjDir.replaceAll("\\", "/");
513
- const yamlPath = `${relProjDirPath}/**/*.check.yaml`;
496
+ const yamlCheckGlobPatterns = `['${relProjDirPath}/**/checks/*.yaml', '${relProjDirPath}/**/*.check.yaml']`;
497
+ const yamlComparisonGlobPatterns = `['${relProjDirPath}/**/comparisons/*.yaml']`;
514
498
  const outDir = relative3(root, prepDir);
515
499
  return {
516
500
  // Don't use an external config file
@@ -530,8 +514,10 @@ function createViteConfigForTests(projDir, prepDir, mode) {
530
514
  preventAssignment: true,
531
515
  delimiters: ["", ""],
532
516
  values: {
533
- // Inject the glob pattern for matching check yaml files
534
- "./__YAML_PATH__": yamlPath
517
+ // Inject the glob patterns for matching model check yaml files
518
+ '"./__YAML_CHECK_GLOB_PATTERNS__"': yamlCheckGlobPatterns,
519
+ // Inject the glob patterns for matching model comparison yaml files
520
+ '"./__YAML_COMPARISON_GLOB_PATTERNS__"': yamlComparisonGlobPatterns
535
521
  }
536
522
  })
537
523
  ],
@@ -607,7 +593,7 @@ var CheckPlugin = class {
607
593
  await this.copyPreviousBundle(context.config);
608
594
  }
609
595
  context.log("info", "Generating model check bundle...");
610
- await this.genCurrentBundle(context.config, modelSpec);
596
+ await this.genCurrentBundle(context, modelSpec);
611
597
  }
612
598
  if (this.options?.testConfigPath === void 0) {
613
599
  if (context.config.mode === "production" || firstBuild) {
@@ -633,9 +619,8 @@ var CheckPlugin = class {
633
619
  await copyFile(currentBundleFile, previousBundleFile);
634
620
  }
635
621
  }
636
- async genCurrentBundle(config, modelSpec) {
637
- const prepDir = config.prepDir;
638
- const viteConfig = await createViteConfigForBundle(prepDir, modelSpec);
622
+ async genCurrentBundle(context, modelSpec) {
623
+ const viteConfig = await createViteConfigForBundle(context, modelSpec);
639
624
  await build(viteConfig);
640
625
  }
641
626
  async genTestConfig(config, mode) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/plugin.ts","../src/run-suite.ts","../src/vite-config-for-bundle.ts","../src/var-names.ts","../src/vite-config-for-report.ts","../src/vite-config-for-tests.ts"],"sourcesContent":["// Copyright (c) 2022 Climate Interactive / New Venture Fund\n\nimport { existsSync } from 'fs'\nimport { copyFile, mkdir } from 'fs/promises'\nimport { dirname, join as joinPath, relative } from 'path'\nimport { fileURLToPath } from 'url'\n\nimport type { InlineConfig, ViteDevServer } from 'vite'\nimport { build, createServer } from 'vite'\n\nimport chokidar from 'chokidar'\n\nimport type { BuildContext, Plugin, ResolvedConfig, ResolvedModelSpec } from '@sdeverywhere/build'\n\nimport type { Bundle, ConfigInitOptions, SuiteSummary } from '@sdeverywhere/check-core'\nimport { createConfig } from '@sdeverywhere/check-core'\n\nimport type { CheckPluginOptions } from './options'\nimport { runTestSuite } from './run-suite'\nimport { createViteConfigForBundle } from './vite-config-for-bundle'\nimport { createViteConfigForReport } from './vite-config-for-report'\nimport { createViteConfigForTests } from './vite-config-for-tests'\n\nexport function checkPlugin(options?: CheckPluginOptions): Plugin {\n return new CheckPlugin(options)\n}\n\ninterface TestOptions {\n currentBundleName: string\n currentBundlePath: string\n testConfigPath: string\n}\n\nclass CheckPlugin implements Plugin {\n private firstBuild = true\n\n constructor(private readonly options?: CheckPluginOptions) {}\n\n async watch(config: ResolvedConfig): Promise<void> {\n if (this.options?.testConfigPath === undefined) {\n // Test config was not provided, so generate a default config in watch mode.\n // The test template uses import.meta.glob so that checks are re-run\n // automatically when the *.check.yaml files are changed.\n await this.genTestConfig(config, 'watch')\n }\n\n // For development mode, run Vite in dev mode so that it serves the\n // model-check report locally (with live reload enabled). When a model\n // test file is changed, the tests will be re-run in the browser.\n const testOptions = this.resolveTestOptions(config)\n const viteConfig = this.createViteConfigForReport(config, testOptions, undefined)\n const server: ViteDevServer = await createServer(viteConfig)\n await server.listen()\n\n // XXX: Currently Vite doesn't reload the page if a file is added/removed\n // in the baselines directory (Vite's import.meta.glob handling doesn't\n // seem to do this automatically), so as a workaround, watch the baselines\n // directory and restart the server if files are added/removed\n // TODO: The same problem also applies to the glob for `.check.yaml` files\n // in the test config, so we should also reload if files match/unmatch\n // the `.check.yaml` glob\n // TODO: Use the baselines directory from the config\n const baselinesDir = 'baselines'\n const watcher = chokidar.watch(baselinesDir, {\n // Watch paths are resolved relative to the project root directory\n cwd: config.rootDir,\n // Don't send initial \"file added\" events\n ignoreInitial: true,\n // XXX: Include a delay, otherwise on macOS we sometimes get multiple\n // change events when a file is saved just once\n awaitWriteFinish: {\n stabilityThreshold: 200\n }\n })\n watcher.on('add', () => server.restart())\n watcher.on('unlink', () => server.restart())\n }\n\n // TODO: Note that this plugin runs as a `postBuild` step because it currently\n // needs to run after other plugins, and those plugins need to run after the\n // staged files are copied to their final destination(s). We should probably\n // make it configurable so that it can either be run as a `postGenerate` or a\n // `postBuild` step.\n async postBuild(context: BuildContext, modelSpec: ResolvedModelSpec): Promise<boolean> {\n const firstBuild = this.firstBuild\n this.firstBuild = false\n\n // For both production builds and local development, generate default bundle\n // in this post-build step each time a source file is changed\n // TODO: We could potentially use watch mode for the bundle similar to\n // what we do for the test config, but the bundle depends on the ModelSpec,\n // which currently isn't made available to the `watch` function\n if (this.options?.current === undefined) {\n // Path to current bundle was not provided, so generate a default bundle\n if (context.config.mode === 'development') {\n // Copy the previous bundle to the `baselines` directory so that\n // we automatically have it available as a baseline for comparison\n await this.copyPreviousBundle(context.config)\n }\n context.log('info', 'Generating model check bundle...')\n await this.genCurrentBundle(context.config, modelSpec)\n }\n\n // For production builds (and for the initial build in local development mode),\n // generate default test config in this post-build step\n if (this.options?.testConfigPath === undefined) {\n if (context.config.mode === 'production' || firstBuild) {\n // Test config was not provided, so generate a default config\n context.log('info', 'Generating model check test configuration...')\n await this.genTestConfig(context.config, 'build')\n }\n }\n\n if (context.config.mode === 'production') {\n // For production builds, run the model checks/comparisons, and then\n // inject the results into the generated report\n const testOptions = this.resolveTestOptions(context.config)\n return this.runChecks(context, testOptions)\n } else {\n // Nothing to do here in dev mode; the dev server will refresh and\n // re-run the tests in the browser when changes are detected\n return true\n }\n }\n\n private async copyPreviousBundle(config: ResolvedConfig): Promise<void> {\n // Only copy if the current bundle exists\n const currentBundleFile = joinPath(config.prepDir, 'check-bundle.js')\n if (existsSync(currentBundleFile)) {\n // TODO: Use the baselines directory from the config (not yet available)\n const baselinesDir = joinPath(config.rootDir, 'baselines')\n if (!existsSync(baselinesDir)) {\n await mkdir(baselinesDir, { recursive: true })\n }\n const previousBundleFile = joinPath(baselinesDir, 'previous.js')\n await copyFile(currentBundleFile, previousBundleFile)\n }\n }\n\n private async genCurrentBundle(config: ResolvedConfig, modelSpec: ResolvedModelSpec): Promise<void> {\n const prepDir = config.prepDir\n const viteConfig = await createViteConfigForBundle(prepDir, modelSpec)\n await build(viteConfig)\n }\n\n private async genTestConfig(config: ResolvedConfig, mode: 'build' | 'watch'): Promise<void> {\n const rootDir = config.rootDir\n const prepDir = config.prepDir\n const viteConfig = createViteConfigForTests(rootDir, prepDir, mode)\n await build(viteConfig)\n }\n\n private async runChecks(context: BuildContext, testOptions: TestOptions): Promise<boolean> {\n context.log('info', 'Running model checks...')\n\n // Load the bundles used by the model check/compare configuration. We\n // always initialize the \"current\" bundle.\n const moduleR = await import(relativeToSourcePath(testOptions.currentBundlePath))\n const bundleR = moduleR.createBundle() as Bundle\n const bundleNameR = testOptions.currentBundleName\n\n // Only initialize the \"baseline\" bundle if it is defined and the version\n // is the same as the \"current\" one. If the baseline bundle has a different\n // version, we will skip the comparison tests and only run the checks on the\n // current bundle.\n let bundleL: Bundle\n let bundleNameL: string\n if (this.options?.baseline) {\n const moduleL = await import(relativeToSourcePath(this.options.baseline.path))\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const rawBundleL: any = moduleL.createBundle() as any\n if (rawBundleL.version === bundleR.version) {\n bundleL = rawBundleL as Bundle\n bundleNameL = this.options.baseline.name\n }\n }\n\n // Get the model check/comparison configuration\n const testConfigModule = await import(relativeToSourcePath(testOptions.testConfigPath))\n const configInitOptions: ConfigInitOptions = {\n bundleNameL,\n bundleNameR\n }\n const configOptions = await testConfigModule.getConfigOptions(bundleL, bundleR, configInitOptions)\n\n // Run the suite of checks and comparisons\n const checkConfig = await createConfig(configOptions)\n const result = await runTestSuite(context, checkConfig, /*verbose=*/ false)\n\n // Build the report (using Vite)\n context.log('info', 'Building model check report')\n const viteConfig = this.createViteConfigForReport(context.config, testOptions, result.suiteSummary)\n await build(viteConfig)\n\n // context.log('info', 'Done!')\n\n return result.allChecksPassed\n }\n\n private resolveTestOptions(config: ResolvedConfig): TestOptions {\n let currentBundleName: string\n let currentBundlePath: string\n if (this.options?.current === undefined) {\n // Path to current bundle was not provided, so use a generated bundle\n currentBundleName = 'current'\n currentBundlePath = joinPath(config.prepDir, 'check-bundle.js')\n } else {\n // Use the provided bundle\n currentBundleName = this.options.current.name\n currentBundlePath = this.options.current.path\n }\n\n let testConfigPath: string\n if (this.options?.testConfigPath === undefined) {\n // Test config was not provided, so use a generated config\n testConfigPath = joinPath(config.prepDir, 'check-tests.js')\n } else {\n // Use the provided test config\n testConfigPath = this.options.testConfigPath\n }\n\n return {\n currentBundleName,\n currentBundlePath,\n testConfigPath\n }\n }\n\n private createViteConfigForReport(\n config: ResolvedConfig,\n testOptions: TestOptions,\n suiteSummary: SuiteSummary | undefined\n ): InlineConfig {\n return createViteConfigForReport(\n this.options,\n config.rootDir,\n config.prepDir,\n testOptions.currentBundleName,\n testOptions.currentBundlePath,\n testOptions.testConfigPath,\n suiteSummary\n )\n }\n}\n\n/**\n * Return a Unix-style path (e.g. '../../foo.js') that is relative to the directory of\n * the current source file. This can be used to construct a path that is safe for\n * dynamic import on either Unix or Windows.\n *\n * @param filePath The path to make relative.\n */\nfunction relativeToSourcePath(filePath: string): string {\n const srcDir = dirname(fileURLToPath(import.meta.url))\n const relPath = relative(srcDir, filePath)\n return relPath.replaceAll('\\\\', '/')\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport { performance } from 'perf_hooks'\n\nimport pico from 'picocolors'\n\nimport type { BuildContext } from '@sdeverywhere/build'\n\nimport type {\n Config,\n ComparisonReport,\n PerfReport,\n RunSuiteCallbacks,\n CheckReport,\n CheckStatus,\n ComparisonConfig,\n CheckTestReport,\n SuiteSummary\n} from '@sdeverywhere/check-core'\nimport {\n datasetMessage,\n predicateMessage,\n runSuite,\n scenarioMessage,\n suiteSummaryFromReport\n} from '@sdeverywhere/check-core'\n\nexport interface RunTestSuiteResult {\n allChecksPassed: boolean\n suiteSummary: SuiteSummary\n}\n\n/**\n * Runs the test suite.\n */\nexport async function runTestSuite(\n context: BuildContext,\n config: Config,\n verbose: boolean\n): Promise<RunTestSuiteResult> {\n return new Promise((resolve, reject) => {\n const t0 = performance.now()\n let lastPctByInc: number\n const callbacks: RunSuiteCallbacks = {\n onProgress: progress => {\n const pct = Math.round(progress * 100)\n const pctByInc = Math.floor(pct / 5) * 5\n if (lastPctByInc === undefined || pctByInc > lastPctByInc) {\n lastPctByInc = pctByInc\n context.log('info', `${pctByInc}%`)\n }\n },\n onComplete: report => {\n try {\n const t1 = performance.now()\n const elapsed = ((t1 - t0) / 1000).toFixed(1)\n context.log('info', `\\nTest suite completed in ${elapsed}s`)\n\n // Print check summary to the console\n const allChecksPassed = printCheckSummary(context, report.checkReport, verbose)\n\n if (report.comparisonReport) {\n // Print the perf stats to the console\n printPerfStats(context, config.comparison, report.comparisonReport)\n }\n\n // Convert check and compare reports to terse form that only includes\n // failed/errored checks or comparisons with differences\n // TODO: The terse form was originally used when we had to write the\n // results to a JSON file and then read them back in when building\n // the report, but we no longer use that intermediate file, so there's\n // less reason to use the terse form (since it requires the web app\n // code to reconstruct the results). But for now, we will continue\n // to use the terse form, and later we can update the app code.\n const suiteSummary = suiteSummaryFromReport(report)\n\n resolve({\n allChecksPassed,\n suiteSummary\n })\n } catch (e) {\n reject(e)\n }\n },\n onError: error => {\n reject(error)\n }\n }\n runSuite(config, callbacks)\n })\n}\n\nfunction printCheckSummary(context: BuildContext, checkReport: CheckReport, verbose: boolean): boolean {\n function printResult(indent: number, status: CheckStatus, text: string): void {\n if (!verbose && status === 'passed' && indent > 1) {\n return\n }\n let statusChar: string\n switch (status) {\n case 'passed':\n statusChar = '✓'\n break\n case 'failed':\n statusChar = '✗'\n break\n case 'error':\n statusChar = '‼'\n break\n default:\n statusChar = ''\n break\n }\n const msg = `${' '.repeat(indent)}${statusChar} ${text}`\n context.log('info', status === 'passed' ? pico.green(msg) : pico.red(msg))\n }\n\n function bold(s: string): string {\n return pico.bold(s)\n }\n\n function printTest(test: CheckTestReport): void {\n const msg = `${test.name}${verbose || test.status !== 'passed' ? ':' : ''}`\n printResult(1, test.status, msg)\n }\n\n let allPassed = true\n context.log('info', '\\nCheck results:')\n for (const group of checkReport.groups) {\n context.log('info', `\\n${group.name}`)\n\n for (const test of group.tests) {\n if (test.status !== 'passed') {\n allPassed = false\n }\n printTest(test)\n\n for (const scenario of test.scenarios) {\n printResult(3, scenario.status, scenarioMessage(scenario, bold))\n\n for (const dataset of scenario.datasets) {\n printResult(5, dataset.status, datasetMessage(dataset, bold))\n\n for (const predicate of dataset.predicates) {\n printResult(7, predicate.result.status, predicateMessage(predicate, bold))\n }\n }\n }\n }\n }\n context.log('info', '')\n\n return allPassed\n}\n\nfunction stat(label: string, n: number): string {\n return `${label}=${n.toFixed(1)}ms`\n}\n\nfunction printPerfReportLine(context: BuildContext, perfReport: PerfReport): void {\n const avg = stat('avg', perfReport.avgTime)\n const min = stat('min', perfReport.minTime)\n const max = stat('max', perfReport.maxTime)\n context.log('info', ` ${avg} ${min} ${max}`)\n}\n\nfunction printPerfStats(context: BuildContext, comparisonConfig: ComparisonConfig, report: ComparisonReport): void {\n context.log('info', '\\nPerformance stats:')\n context.log('info', ` ${comparisonConfig.bundleL.name}:`)\n printPerfReportLine(context, report.perfReportL)\n context.log('info', ` ${comparisonConfig.bundleR.name}:`)\n printPerfReportLine(context, report.perfReportR)\n context.log('info', '')\n}\n","// Copyright (c) 2022 Climate Interactive / New Venture Fund\n\nimport { existsSync, statSync } from 'fs'\nimport { basename, dirname, join as joinPath, relative, resolve as resolvePath } from 'path'\nimport { fileURLToPath } from 'url'\n\nimport type { InlineConfig, ResolvedConfig, Plugin as VitePlugin } from 'vite'\nimport { nodeResolve } from '@rollup/plugin-node-resolve'\n\nimport type { ResolvedModelSpec } from '@sdeverywhere/build'\n\nimport { sdeNameForVensimVarName } from './var-names'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\n/**\n * This is a virtual module plugin used to inject model-specific configuration\n * values into the generated worker bundle.\n *\n * This follows the \"Virtual Modules Convention\" described here:\n * https://vitejs.dev/guide/api-plugin.html#virtual-modules-convention\n *\n * TODO: This could be simplified by using `vite-plugin-virtual` but that\n * doesn't seem to be working correctly in an ESM setting\n */\nfunction injectModelSpec(prepDir: string, modelSpec: ResolvedModelSpec): VitePlugin {\n // Include the SDE variable ID with each spec\n const inputSpecs = []\n for (const modelInputSpec of modelSpec.inputs) {\n // Note that the `InputSpec` interface in the `@sdeverywhere/build` package\n // allows the default/min/max values to be undefined, which can be the case\n // if the user doesn't return full `InputSpec` instances in the `ModelSpec`.\n // We will log a warning and skip the input if these values are not defined.\n if (\n modelInputSpec.defaultValue === undefined ||\n modelInputSpec.minValue === undefined ||\n modelInputSpec.maxValue === undefined\n ) {\n let msg = ''\n msg += `WARNING: The {defaultValue,minValue,maxValue} properties are required by plugin-check, `\n msg += `but are undefined in the InputSpec for '${modelInputSpec.varName}'. `\n msg += `This input variable will be excluded from the model-check bundle until those properties `\n msg += `are defined.`\n console.warn(msg)\n continue\n }\n\n // Use the `inputId` if defined for the `InputSpec`, otherwise use `varId`. The\n // latter is less resilient if the variable is renamed between two versions of\n // the model, but will be sufficient for now. Note that `plugin-config` defines\n // a stable `inputId` for each row in the `inputs.csv`, and that is the most\n // common way to configure a `ModelSpec`, so it will be uncommon for `inputId`\n // to be undefined here.\n const varId = sdeNameForVensimVarName(modelInputSpec.varName)\n const inputId = modelInputSpec.inputId || varId\n inputSpecs.push({\n inputId,\n varId,\n ...modelInputSpec\n })\n }\n const outputSpecs = modelSpec.outputs.map(o => {\n return {\n varId: sdeNameForVensimVarName(o.varName),\n ...o\n }\n })\n\n function stagedFileSize(filename: string): number {\n const path = joinPath(prepDir, 'staged', 'model', filename)\n if (existsSync(path)) {\n return statSync(path).size\n } else {\n return 0\n }\n }\n\n // The size (in bytes) of the `generated-model.js` file\n // TODO: Ideally we would measure the size of the raw Wasm binary, but currently\n // we inline it as a base64 blob inside the JS file, so we take the size of the\n // whole JS file as the second best option\n const modelSizeInBytes = stagedFileSize('generated-model.js')\n\n // The size (in bytes) of the `static-data.ts` file\n // TODO: Ideally we would measure the size of the minified JS file here, or\n // at least ignore things like whitespace\n const dataSizeInBytes = stagedFileSize('static-data.ts')\n\n const moduleSrc = `\nexport const inputSpecs = ${JSON.stringify(inputSpecs)};\nexport const outputSpecs = ${JSON.stringify(outputSpecs)};\nexport const modelSizeInBytes = ${modelSizeInBytes};\nexport const dataSizeInBytes = ${dataSizeInBytes};\n`\n\n const virtualModuleId = 'virtual:model-spec'\n const resolvedVirtualModuleId = '\\0' + virtualModuleId\n\n return {\n name: 'vite-plugin-virtual-custom',\n resolveId(id: string) {\n if (id === virtualModuleId) {\n return resolvedVirtualModuleId\n }\n },\n load(id: string) {\n if (id === resolvedVirtualModuleId) {\n return moduleSrc\n }\n }\n }\n}\n\n/**\n * XXX: This overrides the built-in `vite:resolve` plugin so that we can intercept `resolveId`\n * calls for the threads package.\n */\nfunction overrideViteResolvePlugin(viteConfig: ResolvedConfig) {\n const resolvePlugin = viteConfig.plugins.find(p => p.name === 'vite:resolve')\n if (resolvePlugin === undefined) {\n throw new Error('Failed to locate the built-in vite:resolve plugin')\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const originalResolveId = resolvePlugin.resolveId as any\n resolvePlugin.resolveId = async function resolveId(id, importer, options) {\n if (id.startsWith('./implementation') && importer.includes('threads/dist-esm')) {\n // XXX: The default resolver behavior will look at the `browser` mappings in\n // `threads/package.json` and try to resolve `implementation.js` to\n // `implementation.browser.js` because it thinks we're in a browser-only context.\n // We don't want that. Instead we want to keep the generic implementation from\n // threads that chooses between the Node and browser implementations at runtime.\n //\n // If we get here, importer will be something like:\n // /.../node_modules/.pnpm/threads@1.7.0/node_modules/threads/dist-esm/{worker,master}/index.js\n // And id will be:\n // ./implementation\n // So resolve the ID to:\n // /.../node_modules/.pnpm/threads@1.7.0/node_modules/threads/dist-esm/{worker,master}/implementation.js\n //\n // Or, importer will be:\n // /.../node_modules/.pnpm/threads@1.7.0/node_modules/threads/dist-esm/{worker,master}/implementation.js\n // And id will be one of:\n // ./implementation.browser\n // ./implementation.node\n // ./implementation.worker_threads\n // So resolve the ID to:\n // /.../node_modules/.pnpm/threads@1.7.0/node_modules/threads/dist-esm/{worker,master}/implementation.{...}.js\n const idFileName = id.replace('./', '')\n const importerFileName = basename(importer)\n const resolvedId = importer.replace(importerFileName, `${idFileName}.js`)\n return {\n id: resolvedId,\n moduleSideEffects: false\n }\n }\n\n // For all other cases, fall back on the default resolver\n return originalResolveId.call(this, id, importer, options)\n }\n}\n\nexport async function createViteConfigForBundle(prepDir: string, modelSpec: ResolvedModelSpec): Promise<InlineConfig> {\n // Use `template-bundle` as the root directory for the bundle project\n const root = resolvePath(__dirname, '..', 'template-bundle')\n\n // Calculate output directory relative to the template root\n // TODO: For now we write it to `prepDir`; make this configurable?\n const outDir = relative(root, prepDir)\n\n // Use the model worker from the staged directory\n // TODO: Make this configurable?\n const modelWorkerPath = joinPath(prepDir, 'staged', 'model', 'worker.js?raw')\n\n return {\n // Don't use an external config file\n configFile: false,\n\n // Use the root directory configured above\n root,\n\n // Don't clear the screen in dev mode so that we can see builder output\n clearScreen: false,\n\n // TODO: Disable vite output by default?\n // logLevel: 'silent',\n\n // Configure path aliases\n resolve: {\n alias: [\n // Inject the configured model worker\n {\n find: '@_model_worker_',\n replacement: modelWorkerPath\n },\n\n // XXX: Prevent Vite from using the `browser` section of `threads/package.json`\n // since we want to force the use of the general module (under dist-esm) that chooses\n // the correct implementation (Web Worker vs worker_threads) at runtime. Currently\n // Vite's library mode is browser focused and generally chooses the right imports,\n // except in the case of the threads package where we want to use the generic\n // `implementation.js` that chooses between Web Worker and worker_threads at runtime.\n // Note that we could in theory set `resolve.browserField` to false, but that would\n // make Vite not use the browser field for all other packages, and there is not\n // currently a way to tell Vite to use the browser field on a case-by-case basis.\n // So for now we need this workaround here to make it resolve to `dist-esm`, and then\n // a second workaround in `overrideViteResolvePlugin` to prevent the resolver from\n // using the browser field when resolving the threads package.\n {\n find: 'threads',\n replacement: 'threads',\n customResolver: async function (source, importer, options) {\n // Note that we need to use `resolveId.call` here in order to provide the\n // right `this` context, which provides Rollup plugin functionality\n const customResolver = nodeResolve({ browser: false })\n const resolved = await customResolver.resolveId.call(this, source, importer, options)\n // Force the use of the `dist-esm` variant of the threads.js package\n if (source === 'threads/worker') {\n return resolved.id.replace('worker.mjs', 'dist-esm/worker/index.js')\n } else {\n return resolved.id.replace('index.mjs', 'dist-esm/index.js')\n }\n }\n }\n ]\n },\n\n plugins: [\n // Use a virtual module plugin to inject the model spec values\n injectModelSpec(prepDir, modelSpec),\n\n // XXX: Install a wrapper around the built-in `vite:resolve` plugin so that we can\n // override the default resolver behavior that tries to resolve the `browser` section\n // of the `package.json` for the threads package.\n {\n name: 'vite-plugin-override-resolve',\n configResolved(viteConfig) {\n overrideViteResolvePlugin(viteConfig)\n }\n }\n ],\n\n build: {\n // Write output files to the configured directory (instead of the default `dist`);\n // note that this must be relative to the project `root`\n outDir,\n emptyOutDir: false,\n\n // Uncomment for debugging purposes\n // minify: false,\n\n lib: {\n entry: './src/index.ts',\n formats: ['es'],\n fileName: () => 'check-bundle.js'\n },\n\n rollupOptions: {\n // Don't transform Node imports used by threads.js\n external: ['events', 'os', 'path', 'url'],\n\n // XXX: Insert custom code at the top of the generated bundle that defines\n // the special `__non_webpack_require__` function that is used by threads.js\n // in its Node implementation. This import ensures that threads.js uses\n // the native `worker_threads` implementation when using the bundle in a\n // Node environment. When importing the bundle for use in the browser,\n // Vite will transform this import into an empty module due to the empty\n // polyfill that is configured in `vite-config-for-report.ts`.\n output: {\n banner: `\nimport * as worker_threads from 'worker_threads'\nlet __non_webpack_require__ = () => {\n return worker_threads;\n};\n`\n },\n\n onwarn: (warning, warn) => {\n // XXX: Suppress \"Use of eval is strongly discouraged\" warnings that are\n // triggered by use of the following pattern in threads.js:\n // eval(\"require\")(\"worker_threads\")\n // It would be nice to avoid use of `eval` there, but it's not critical for\n // our use case so we will suppress the warnings for now\n if (warning.code !== 'EVAL') {\n warn(warning)\n }\n }\n }\n }\n }\n}\n","// Copyright (c) 2022 Climate Interactive / New Venture Fund\n\n/**\n * Helper function that converts a Vensim variable or subscript name\n * into a valid C identifier as used by SDE.\n * TODO: Import helper function from `compile` package instead\n */\nfunction sdeNameForVensimName(name: string): string {\n return (\n '_' +\n name\n .trim()\n .replace(/\"/g, '_')\n .replace(/\\s+!$/g, '!')\n .replace(/\\s/g, '_')\n .replace(/,/g, '_')\n .replace(/-/g, '_')\n .replace(/\\./g, '_')\n .replace(/\\$/g, '_')\n .replace(/'/g, '_')\n .replace(/&/g, '_')\n .replace(/%/g, '_')\n .replace(/\\//g, '_')\n .replace(/\\|/g, '_')\n .toLowerCase()\n )\n}\n\n/**\n * Helper function that converts a Vensim variable name (possibly containing\n * subscripts) into a valid C identifier as used by SDE.\n * TODO: Import helper function from `compile` package instead\n */\nexport function sdeNameForVensimVarName(varName: string): string {\n const m = varName.match(/([^[]+)(?:\\[([^\\]]+)\\])?/)\n if (!m) {\n throw new Error(`Invalid Vensim name: ${varName}`)\n }\n let id = sdeNameForVensimName(m[1])\n if (m[2]) {\n const subscripts = m[2].split(',').map(x => sdeNameForVensimName(x))\n id += `[${subscripts.join(',')}]`\n }\n\n return id\n}\n","// Copyright (c) 2022 Climate Interactive / New Venture Fund\n\nimport { dirname, relative, join as joinPath, resolve as resolvePath } from 'path'\nimport { fileURLToPath } from 'url'\n\nimport type { Alias, InlineConfig, PluginOption } from 'vite'\nimport replace from '@rollup/plugin-replace'\n\nimport type { SuiteSummary } from '@sdeverywhere/check-core'\n\nimport type { CheckPluginOptions } from './options'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\nexport function createViteConfigForReport(\n options: CheckPluginOptions | undefined,\n projDir: string,\n prepDir: string,\n currentBundleName: string,\n currentBundlePath: string,\n testConfigPath: string,\n suiteSummary: SuiteSummary | undefined\n): InlineConfig {\n // Use `template-report` as the root directory for the report project\n const root = resolvePath(__dirname, '..', 'template-report')\n\n // Include `baselines/*.js` files under the configured project root directory. This\n // glob path apparently must be a relative path (relative to the `template-report/src`\n // directory where the glob is used).\n const templateSrcDir = resolvePath(root, 'src')\n const relProjDir = relative(templateSrcDir, projDir)\n // XXX: The glob pattern must use forward slashes only, so on Windows we need to\n // convert backslashes to slashes\n const relProjDirPath = relProjDir.replaceAll('\\\\', '/')\n // TODO: Use baselinesDir from options\n const baselinesPath = `${relProjDirPath}/baselines/*.js`\n\n // Calculate output directory relative to the template root\n let reportPath: string\n if (options?.reportPath) {\n reportPath = options.reportPath\n } else {\n reportPath = joinPath(prepDir, 'check-report')\n }\n const outDir = relative(root, reportPath)\n\n // Convert the suite summary to JSON, which is what the app currently expects\n const suiteSummaryJson = suiteSummary ? JSON.stringify(suiteSummary) : ''\n\n const alias = (find: string, replacement: string) => {\n return {\n find,\n replacement\n } as Alias\n }\n\n // XXX: This provides custom handling for Node built-ins such as 'events' that are\n // referenced by the check bundle (specifically in the Node implementation of\n // threads.js). These are not actually used in the browser, so we just need\n // to provide no-op polyfills for these.\n const noopPolyfillAlias = (find: string) => {\n return {\n find,\n replacement: '/polyfills/noop-polyfills.ts'\n } as Alias\n }\n\n return {\n // Don't use an external config file\n configFile: false,\n\n // Use the root directory configured above\n root,\n\n // Use `.` as the base directory (instead of the default `/`); this controls\n // how the path to the js/css files are generated in `index.html`\n base: '',\n\n // Use a custom cache directory under `prepDir`, as otherwise Vite will use\n // `packages/plugin-check/template-report/node_modules/.vite`, and we want to\n // avoid generating files in `template-report` (which should be read-only)\n cacheDir: joinPath(prepDir, '.vite-check-report'),\n\n // Load static files from `static` (instead of the default `public`)\n // publicDir: 'static',\n\n // Don't clear the screen in dev mode so that we can see builder output\n clearScreen: false,\n\n // TODO\n // logLevel: 'silent',\n\n optimizeDeps: {\n // Prevent Vite from examining other html files when scanning entrypoints\n // for dependency optimization\n entries: ['index.html'],\n\n // XXX: When plugin-check is installed via pnpm, the Vite dev server seems\n // to have no trouble resolving other dependencies using the optimizeDeps\n // mechanism. However, this fails when the package is installed via yarn\n // or npm (probably due to the fact that the `template-report` directory\n // is located under the top-level `node_modules` directory); in the browser,\n // there will be \"import not found\" errors for the packages referenced below.\n // As a terrible workaround, explicitly include the direct dependencies so\n // that Vite optimizes them; this works for pnpm, yarn, and npm. We should\n // find a less fragile solution.\n include: [\n // from check-core\n '@sdeverywhere/check-core > assert-never',\n '@sdeverywhere/check-core > ajv',\n '@sdeverywhere/check-core > neverthrow',\n '@sdeverywhere/check-core > yaml',\n // from check-ui-shell\n '@sdeverywhere/check-ui-shell > fontfaceobserver',\n '@sdeverywhere/check-ui-shell > copy-text-to-clipboard',\n '@sdeverywhere/check-ui-shell > chart.js'\n ],\n\n exclude: [\n // XXX: The threads.js implementation references `tiny-worker` as an optional\n // dependency, but it doesn't get used at runtime, so we can just exclude it\n // so that Vite doesn't complain in dev mode\n 'tiny-worker'\n\n // XXX: Similarly, chart.js treats `moment` as an optional dependency, but we\n // don't use it at runtime; we need to exclude it here, otherwise Vite will\n // complain about missing dependencies in dev mode\n // 'moment'\n ]\n },\n\n // Configure path aliases\n resolve: {\n alias: [\n // Use the configured \"baseline\" bundle if defined, otherwise use the \"empty\" bundle\n // (which will cause comparison tests to be skipped)\n alias('@_baseline_bundle_', options?.baseline ? options.baseline.path : '/src/empty-bundle.ts'),\n\n // Use the configured \"current\" bundle\n alias('@_current_bundle_', currentBundlePath),\n\n // Use the configured test config file\n alias('@_test_config_', testConfigPath),\n\n // Make the overlay use the `messages.html` file that is written to the prep directory\n alias('@_prep_', prepDir),\n\n // XXX: Include no-op polyfills for these modules that are used in the Node-specific\n // implementation of threads.js; this allows us to use one bundle that works in both\n // Node and browser environments\n noopPolyfillAlias('events'),\n noopPolyfillAlias('fs'),\n noopPolyfillAlias('os'),\n noopPolyfillAlias('path'),\n noopPolyfillAlias('url'),\n noopPolyfillAlias('worker_threads')\n ]\n },\n\n // Inject special values into the generated JS\n define: {\n // Inject the summary JSON into the build\n __SUITE_SUMMARY_JSON__: JSON.stringify(suiteSummaryJson),\n\n // Inject the baseline branch name\n __BASELINE_NAME__: JSON.stringify(options?.baseline?.name || ''),\n\n // Inject the current branch name\n __CURRENT_NAME__: JSON.stringify(currentBundleName)\n },\n\n plugins: [\n // Inject special values into the generated JS\n // TODO: We currently have to use `@rollup/plugin-replace` instead of Vite's\n // built-in `define` feature because the latter does not seem to run before\n // the glob handler (which requires the glob to be injected as a literal)\n replace({\n preventAssignment: true,\n delimiters: ['', ''],\n values: {\n // Inject the path for baseline bundles\n './__BASELINE_BUNDLES_PATH__': baselinesPath\n }\n }) as unknown as PluginOption\n ],\n\n build: {\n // Write output files to the configured directory (instead of the default `dist`);\n // note that this must be relative to the project `root`\n outDir,\n\n // Write js/css files to `public` (instead of the default `<outDir>/assets`)\n assetsDir: '',\n\n rollupOptions: {\n output: {\n // XXX: Prevent vite from creating a separate `vendor.js` file\n manualChunks: undefined\n },\n\n onwarn: (warning, warn) => {\n // XXX: Suppress \"Use of eval is strongly discouraged\" warnings that are\n // triggered by use of the following pattern in threads.js:\n // eval(\"require\")(\"worker_threads\")\n // It would be nice to avoid use of `eval` there, but it's not critical for\n // our use case so we will suppress the warnings for now\n if (warning.code !== 'EVAL') {\n warn(warning)\n }\n }\n }\n },\n\n server: {\n // Run the dev server at `localhost:8081` by default\n port: options?.serverPort || 8081,\n\n // Open the app in the browser by default\n open: '/index.html',\n\n // XXX: Add a small delay, otherwise on macOS we sometimes get multiple\n // change events when a file is saved just once. That is a relatively\n // harmless issue except that it causes redundant messages in the console\n // and can cause extra churn when refreshing the app.\n watch: {\n awaitWriteFinish: {\n stabilityThreshold: 100\n }\n }\n }\n }\n}\n","// Copyright (c) 2022 Climate Interactive / New Venture Fund\n\nimport { dirname, relative, resolve as resolvePath } from 'path'\nimport { fileURLToPath } from 'url'\n\nimport type { InlineConfig, PluginOption } from 'vite'\nimport replace from '@rollup/plugin-replace'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\nexport function createViteConfigForTests(projDir: string, prepDir: string, mode: 'build' | 'watch'): InlineConfig {\n // Use `template-tests` as the root directory for the tests project\n const root = resolvePath(__dirname, '..', 'template-tests')\n\n // Include `*.check.yaml` files under the configured project root directory. This\n // glob path apparently must be a relative path (relative to the `template-tests/src`\n // directory where the glob is used).\n const templateSrcDir = resolvePath(root, 'src')\n const relProjDir = relative(templateSrcDir, projDir)\n // XXX: The glob pattern must use forward slashes only, so on Windows we need to\n // convert backslashes to slashes\n const relProjDirPath = relProjDir.replaceAll('\\\\', '/')\n // TODO: Use yamlPath from options\n const yamlPath = `${relProjDirPath}/**/*.check.yaml`\n\n // // Read the `package.json` for the template project\n // const pkgPath = resolvePath(root, 'package.json')\n // const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))\n\n // Calculate output directory relative to the template root\n // TODO: For now we write it to `prepDir`; make this configurable?\n const outDir = relative(root, prepDir)\n\n return {\n // Don't use an external config file\n configFile: false,\n\n // Use the root directory configured above\n root,\n\n // Don't clear the screen in dev mode so that we can see builder output\n clearScreen: false,\n\n // TODO: Disable vite output by default?\n // logLevel: 'silent',\n\n plugins: [\n // Inject special values into the generated JS\n // TODO: We currently have to use `@rollup/plugin-replace` instead of Vite's\n // built-in `define` feature because the latter does not seem to run before\n // the glob handler (which requires the glob to be injected as a literal)\n replace({\n preventAssignment: true,\n delimiters: ['', ''],\n values: {\n // Inject the glob pattern for matching check yaml files\n './__YAML_PATH__': yamlPath\n }\n }) as unknown as PluginOption\n ],\n\n build: {\n // Write output files to the configured directory (instead of the default `dist`);\n // note that this must be relative to the project `root`\n outDir,\n emptyOutDir: false,\n\n lib: {\n entry: './src/index.ts',\n formats: ['es'],\n fileName: () => 'check-tests.js'\n },\n\n // Enable watch mode if requested\n watch: mode === 'watch' && {},\n\n rollupOptions: {\n // Prevent dependencies from being included in packaged library\n // TODO: For now we include check-core in the packaged library so that its\n // dependencies are correctly resolved at runtime. Ideally this would only\n // include a couple functions that are used for defining tests, but Vite 2.x\n // does not implement tree shaking for ES libraries, which means the generated\n // library is much larger than it needs to be. Once we upgrade to Vite 3.x,\n // the generated library should be smaller; see related fix:\n // https://github.com/vitejs/vite/pull/8737\n // external: Object.keys(pkg.dependencies)\n }\n }\n }\n}\n"],"mappings":"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","names":["existsSync","dirname","joinPath","relative","fileURLToPath","__filename","__dirname","dirname","relative","joinPath","resolvePath","fileURLToPath","__filename","__dirname","dirname","relative","resolvePath","fileURLToPath","replace","__filename","__dirname","joinPath","existsSync","dirname","fileURLToPath","relative"]}
1
+ {"version":3,"sources":["../src/plugin.ts","../src/run-suite.ts","../src/vite-config-for-bundle.ts","../src/vite-config-for-report.ts","../src/vite-config-for-tests.ts"],"sourcesContent":["// Copyright (c) 2022 Climate Interactive / New Venture Fund\n\nimport { existsSync } from 'fs'\nimport { copyFile, mkdir } from 'fs/promises'\nimport { dirname, join as joinPath, relative } from 'path'\nimport { fileURLToPath } from 'url'\n\nimport type { InlineConfig, ViteDevServer } from 'vite'\nimport { build, createServer } from 'vite'\n\nimport chokidar from 'chokidar'\n\nimport type { BuildContext, Plugin, ResolvedConfig, ResolvedModelSpec } from '@sdeverywhere/build'\n\nimport type { Bundle, ConfigInitOptions, SuiteSummary } from '@sdeverywhere/check-core'\nimport { createConfig } from '@sdeverywhere/check-core'\n\nimport type { CheckPluginOptions } from './options'\nimport { runTestSuite } from './run-suite'\nimport { createViteConfigForBundle } from './vite-config-for-bundle'\nimport { createViteConfigForReport } from './vite-config-for-report'\nimport { createViteConfigForTests } from './vite-config-for-tests'\n\nexport function checkPlugin(options?: CheckPluginOptions): Plugin {\n return new CheckPlugin(options)\n}\n\ninterface TestOptions {\n currentBundleName: string\n currentBundlePath: string\n testConfigPath: string\n}\n\nclass CheckPlugin implements Plugin {\n private firstBuild = true\n\n constructor(private readonly options?: CheckPluginOptions) {}\n\n async watch(config: ResolvedConfig): Promise<void> {\n if (this.options?.testConfigPath === undefined) {\n // Test config was not provided, so generate a default config in watch mode.\n // The test template uses import.meta.glob so that checks are re-run\n // automatically when the `{checks/comparisons}/*.yaml` files are changed.\n await this.genTestConfig(config, 'watch')\n }\n\n // For development mode, run Vite in dev mode so that it serves the\n // model-check report locally (with live reload enabled). When a model\n // test file is changed, the tests will be re-run in the browser.\n const testOptions = this.resolveTestOptions(config)\n const viteConfig = this.createViteConfigForReport(config, testOptions, undefined)\n const server: ViteDevServer = await createServer(viteConfig)\n await server.listen()\n\n // XXX: Currently Vite doesn't reload the page if a file is added/removed\n // in the baselines directory (Vite's import.meta.glob handling doesn't\n // seem to do this automatically), so as a workaround, watch the baselines\n // directory and restart the server if files are added/removed\n // TODO: The same problem also applies to the glob for `checks/*.yaml` and\n // `comparisons/*.yaml` files in the test config, so we should also reload\n // if files match/unmatch those glob patterns\n // TODO: Use the baselines directory from the config\n const baselinesDir = 'baselines'\n const watcher = chokidar.watch(baselinesDir, {\n // Watch paths are resolved relative to the project root directory\n cwd: config.rootDir,\n // Don't send initial \"file added\" events\n ignoreInitial: true,\n // XXX: Include a delay, otherwise on macOS we sometimes get multiple\n // change events when a file is saved just once\n awaitWriteFinish: {\n stabilityThreshold: 200\n }\n })\n watcher.on('add', () => server.restart())\n watcher.on('unlink', () => server.restart())\n }\n\n // TODO: Note that this plugin runs as a `postBuild` step because it currently\n // needs to run after other plugins, and those plugins need to run after the\n // staged files are copied to their final destination(s). We should probably\n // make it configurable so that it can either be run as a `postGenerate` or a\n // `postBuild` step.\n async postBuild(context: BuildContext, modelSpec: ResolvedModelSpec): Promise<boolean> {\n const firstBuild = this.firstBuild\n this.firstBuild = false\n\n // For both production builds and local development, generate default bundle\n // in this post-build step each time a source file is changed\n // TODO: We could potentially use watch mode for the bundle similar to\n // what we do for the test config, but the bundle depends on the ModelSpec,\n // which currently isn't made available to the `watch` function\n if (this.options?.current === undefined) {\n // Path to current bundle was not provided, so generate a default bundle\n if (context.config.mode === 'development') {\n // Copy the previous bundle to the `baselines` directory so that\n // we automatically have it available as a baseline for comparison\n await this.copyPreviousBundle(context.config)\n }\n context.log('info', 'Generating model check bundle...')\n await this.genCurrentBundle(context, modelSpec)\n }\n\n // For production builds (and for the initial build in local development mode),\n // generate default test config in this post-build step\n if (this.options?.testConfigPath === undefined) {\n if (context.config.mode === 'production' || firstBuild) {\n // Test config was not provided, so generate a default config\n context.log('info', 'Generating model check test configuration...')\n await this.genTestConfig(context.config, 'build')\n }\n }\n\n if (context.config.mode === 'production') {\n // For production builds, run the model checks/comparisons, and then\n // inject the results into the generated report\n const testOptions = this.resolveTestOptions(context.config)\n return this.runChecks(context, testOptions)\n } else {\n // Nothing to do here in dev mode; the dev server will refresh and\n // re-run the tests in the browser when changes are detected\n return true\n }\n }\n\n private async copyPreviousBundle(config: ResolvedConfig): Promise<void> {\n // Only copy if the current bundle exists\n const currentBundleFile = joinPath(config.prepDir, 'check-bundle.js')\n if (existsSync(currentBundleFile)) {\n // TODO: Use the baselines directory from the config (not yet available)\n const baselinesDir = joinPath(config.rootDir, 'baselines')\n if (!existsSync(baselinesDir)) {\n await mkdir(baselinesDir, { recursive: true })\n }\n const previousBundleFile = joinPath(baselinesDir, 'previous.js')\n await copyFile(currentBundleFile, previousBundleFile)\n }\n }\n\n private async genCurrentBundle(context: BuildContext, modelSpec: ResolvedModelSpec): Promise<void> {\n const viteConfig = await createViteConfigForBundle(context, modelSpec)\n await build(viteConfig)\n }\n\n private async genTestConfig(config: ResolvedConfig, mode: 'build' | 'watch'): Promise<void> {\n const rootDir = config.rootDir\n const prepDir = config.prepDir\n const viteConfig = createViteConfigForTests(rootDir, prepDir, mode)\n await build(viteConfig)\n }\n\n private async runChecks(context: BuildContext, testOptions: TestOptions): Promise<boolean> {\n context.log('info', 'Running model checks...')\n\n // Load the bundles used by the model check/compare configuration. We\n // always initialize the \"current\" bundle.\n const moduleR = await import(relativeToSourcePath(testOptions.currentBundlePath))\n const bundleR = moduleR.createBundle() as Bundle\n const bundleNameR = testOptions.currentBundleName\n\n // Only initialize the \"baseline\" bundle if it is defined and the version\n // is the same as the \"current\" one. If the baseline bundle has a different\n // version, we will skip the comparison tests and only run the checks on the\n // current bundle.\n let bundleL: Bundle\n let bundleNameL: string\n if (this.options?.baseline) {\n const moduleL = await import(relativeToSourcePath(this.options.baseline.path))\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const rawBundleL: any = moduleL.createBundle() as any\n if (rawBundleL.version === bundleR.version) {\n bundleL = rawBundleL as Bundle\n bundleNameL = this.options.baseline.name\n }\n }\n\n // Get the model check/comparison configuration\n const testConfigModule = await import(relativeToSourcePath(testOptions.testConfigPath))\n const configInitOptions: ConfigInitOptions = {\n bundleNameL,\n bundleNameR\n }\n const configOptions = await testConfigModule.getConfigOptions(bundleL, bundleR, configInitOptions)\n\n // Run the suite of checks and comparisons\n const checkConfig = await createConfig(configOptions)\n const result = await runTestSuite(context, checkConfig, /*verbose=*/ false)\n\n // Build the report (using Vite)\n context.log('info', 'Building model check report')\n const viteConfig = this.createViteConfigForReport(context.config, testOptions, result.suiteSummary)\n await build(viteConfig)\n\n // context.log('info', 'Done!')\n\n return result.allChecksPassed\n }\n\n private resolveTestOptions(config: ResolvedConfig): TestOptions {\n let currentBundleName: string\n let currentBundlePath: string\n if (this.options?.current === undefined) {\n // Path to current bundle was not provided, so use a generated bundle\n currentBundleName = 'current'\n currentBundlePath = joinPath(config.prepDir, 'check-bundle.js')\n } else {\n // Use the provided bundle\n currentBundleName = this.options.current.name\n currentBundlePath = this.options.current.path\n }\n\n let testConfigPath: string\n if (this.options?.testConfigPath === undefined) {\n // Test config was not provided, so use a generated config\n testConfigPath = joinPath(config.prepDir, 'check-tests.js')\n } else {\n // Use the provided test config\n testConfigPath = this.options.testConfigPath\n }\n\n return {\n currentBundleName,\n currentBundlePath,\n testConfigPath\n }\n }\n\n private createViteConfigForReport(\n config: ResolvedConfig,\n testOptions: TestOptions,\n suiteSummary: SuiteSummary | undefined\n ): InlineConfig {\n return createViteConfigForReport(\n this.options,\n config.rootDir,\n config.prepDir,\n testOptions.currentBundleName,\n testOptions.currentBundlePath,\n testOptions.testConfigPath,\n suiteSummary\n )\n }\n}\n\n/**\n * Return a Unix-style path (e.g. '../../foo.js') that is relative to the directory of\n * the current source file. This can be used to construct a path that is safe for\n * dynamic import on either Unix or Windows.\n *\n * @param filePath The path to make relative.\n */\nfunction relativeToSourcePath(filePath: string): string {\n const srcDir = dirname(fileURLToPath(import.meta.url))\n const relPath = relative(srcDir, filePath)\n return relPath.replaceAll('\\\\', '/')\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport { performance } from 'perf_hooks'\n\nimport pico from 'picocolors'\n\nimport type { BuildContext } from '@sdeverywhere/build'\n\nimport type {\n Config,\n ComparisonReport,\n PerfReport,\n RunSuiteCallbacks,\n CheckReport,\n CheckStatus,\n ComparisonConfig,\n CheckTestReport,\n SuiteSummary\n} from '@sdeverywhere/check-core'\nimport {\n datasetMessage,\n predicateMessage,\n runSuite,\n scenarioMessage,\n suiteSummaryFromReport\n} from '@sdeverywhere/check-core'\n\nexport interface RunTestSuiteResult {\n allChecksPassed: boolean\n suiteSummary: SuiteSummary\n}\n\n/**\n * Runs the test suite.\n */\nexport async function runTestSuite(\n context: BuildContext,\n config: Config,\n verbose: boolean\n): Promise<RunTestSuiteResult> {\n return new Promise((resolve, reject) => {\n const t0 = performance.now()\n let lastPctByInc: number\n const callbacks: RunSuiteCallbacks = {\n onProgress: progress => {\n const pct = Math.round(progress * 100)\n const pctByInc = Math.floor(pct / 5) * 5\n if (lastPctByInc === undefined || pctByInc > lastPctByInc) {\n lastPctByInc = pctByInc\n context.log('info', `${pctByInc}%`)\n }\n },\n onComplete: report => {\n try {\n const t1 = performance.now()\n const elapsed = ((t1 - t0) / 1000).toFixed(1)\n context.log('info', `\\nTest suite completed in ${elapsed}s`)\n\n // Print check summary to the console\n const allChecksPassed = printCheckSummary(context, report.checkReport, verbose)\n\n if (report.comparisonReport) {\n // Print the perf stats to the console\n printPerfStats(context, config.comparison, report.comparisonReport)\n }\n\n // Convert check and compare reports to terse form that only includes\n // failed/errored checks or comparisons with differences\n // TODO: The terse form was originally used when we had to write the\n // results to a JSON file and then read them back in when building\n // the report, but we no longer use that intermediate file, so there's\n // less reason to use the terse form (since it requires the web app\n // code to reconstruct the results). But for now, we will continue\n // to use the terse form, and later we can update the app code.\n const suiteSummary = suiteSummaryFromReport(report)\n\n resolve({\n allChecksPassed,\n suiteSummary\n })\n } catch (e) {\n reject(e)\n }\n },\n onError: error => {\n reject(error)\n }\n }\n runSuite(config, callbacks)\n })\n}\n\nfunction printCheckSummary(context: BuildContext, checkReport: CheckReport, verbose: boolean): boolean {\n function printResult(indent: number, status: CheckStatus, text: string): void {\n if (!verbose && status === 'passed' && indent > 1) {\n return\n }\n let statusChar: string\n switch (status) {\n case 'passed':\n statusChar = '✓'\n break\n case 'failed':\n statusChar = '✗'\n break\n case 'error':\n statusChar = '‼'\n break\n default:\n statusChar = ''\n break\n }\n const msg = `${' '.repeat(indent)}${statusChar} ${text}`\n context.log('info', status === 'passed' ? pico.green(msg) : pico.red(msg))\n }\n\n function bold(s: string): string {\n return pico.bold(s)\n }\n\n function printTest(test: CheckTestReport): void {\n const msg = `${test.name}${verbose || test.status !== 'passed' ? ':' : ''}`\n printResult(1, test.status, msg)\n }\n\n let allPassed = true\n context.log('info', '\\nCheck results:')\n for (const group of checkReport.groups) {\n context.log('info', `\\n${group.name}`)\n\n for (const test of group.tests) {\n if (test.status !== 'passed') {\n allPassed = false\n }\n printTest(test)\n\n for (const scenario of test.scenarios) {\n printResult(3, scenario.status, scenarioMessage(scenario, bold))\n\n for (const dataset of scenario.datasets) {\n printResult(5, dataset.status, datasetMessage(dataset, bold))\n\n for (const predicate of dataset.predicates) {\n printResult(7, predicate.result.status, predicateMessage(predicate, bold))\n }\n }\n }\n }\n }\n context.log('info', '')\n\n return allPassed\n}\n\nfunction stat(label: string, n: number): string {\n return `${label}=${n.toFixed(1)}ms`\n}\n\nfunction printPerfReportLine(context: BuildContext, perfReport: PerfReport): void {\n const avg = stat('avg', perfReport.avgTime)\n const min = stat('min', perfReport.minTime)\n const max = stat('max', perfReport.maxTime)\n context.log('info', ` ${avg} ${min} ${max}`)\n}\n\nfunction printPerfStats(context: BuildContext, comparisonConfig: ComparisonConfig, report: ComparisonReport): void {\n context.log('info', '\\nPerformance stats:')\n context.log('info', ` ${comparisonConfig.bundleL.name}:`)\n printPerfReportLine(context, report.perfReportL)\n context.log('info', ` ${comparisonConfig.bundleR.name}:`)\n printPerfReportLine(context, report.perfReportR)\n context.log('info', '')\n}\n","// Copyright (c) 2022 Climate Interactive / New Venture Fund\n\nimport { existsSync, statSync } from 'fs'\nimport { basename, dirname, join as joinPath, relative, resolve as resolvePath } from 'path'\nimport { fileURLToPath } from 'url'\n\nimport type { InlineConfig, ResolvedConfig, Plugin as VitePlugin } from 'vite'\nimport { nodeResolve } from '@rollup/plugin-node-resolve'\n\nimport type { BuildContext, ResolvedModelSpec } from '@sdeverywhere/build'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\n/**\n * This is a virtual module plugin used to inject model-specific configuration\n * values into the generated worker bundle.\n *\n * This follows the \"Virtual Modules Convention\" described here:\n * https://vitejs.dev/guide/api-plugin.html#virtual-modules-convention\n *\n * TODO: This could be simplified by using `vite-plugin-virtual` but that\n * doesn't seem to be working correctly in an ESM setting\n */\nfunction injectModelSpec(context: BuildContext, modelSpec: ResolvedModelSpec): VitePlugin {\n // Include the SDE variable ID with each spec\n const inputSpecs = []\n for (const modelInputSpec of modelSpec.inputs) {\n // Note that the `InputSpec` interface in the `@sdeverywhere/build` package\n // allows the default/min/max values to be undefined, which can be the case\n // if the user doesn't return full `InputSpec` instances in the `ModelSpec`.\n // We will log a warning and skip the input if these values are not defined.\n if (\n modelInputSpec.defaultValue === undefined ||\n modelInputSpec.minValue === undefined ||\n modelInputSpec.maxValue === undefined\n ) {\n let msg = ''\n msg += `WARNING: The {defaultValue,minValue,maxValue} properties are required by plugin-check, `\n msg += `but are undefined in the InputSpec for '${modelInputSpec.varName}'. `\n msg += `This input variable will be excluded from the model-check bundle until those properties `\n msg += `are defined.`\n console.warn(msg)\n continue\n }\n\n // Use the `inputId` if defined for the `InputSpec`, otherwise use `varId`. The\n // latter is less resilient if the variable is renamed between two versions of\n // the model, but will be sufficient for now. Note that `plugin-config` defines\n // a stable `inputId` for each row in the `inputs.csv`, and that is the most\n // common way to configure a `ModelSpec`, so it will be uncommon for `inputId`\n // to be undefined here.\n const varId = context.canonicalVarId(modelInputSpec.varName)\n const inputId = modelInputSpec.inputId || varId\n inputSpecs.push({\n inputId,\n varId,\n ...modelInputSpec\n })\n }\n const outputSpecs = modelSpec.outputs.map(o => {\n return {\n varId: context.canonicalVarId(o.varName),\n ...o\n }\n })\n\n function stagedFileSize(filename: string): number {\n const prepDir = context.config.prepDir\n const path = joinPath(prepDir, 'staged', 'model', filename)\n if (existsSync(path)) {\n return statSync(path).size\n } else {\n return 0\n }\n }\n\n // The size (in bytes) of the `generated-model.js` file\n // TODO: Ideally we would measure the size of the raw Wasm binary, but currently\n // we inline it as a base64 blob inside the JS file, so we take the size of the\n // whole JS file as the second best option\n const modelSizeInBytes = stagedFileSize('generated-model.js')\n\n // The size (in bytes) of the `static-data.ts` file\n // TODO: Ideally we would measure the size of the minified JS file here, or\n // at least ignore things like whitespace\n const dataSizeInBytes = stagedFileSize('static-data.ts')\n\n const moduleSrc = `\nexport const inputSpecs = ${JSON.stringify(inputSpecs)};\nexport const outputSpecs = ${JSON.stringify(outputSpecs)};\nexport const modelSizeInBytes = ${modelSizeInBytes};\nexport const dataSizeInBytes = ${dataSizeInBytes};\n`\n\n const virtualModuleId = 'virtual:model-spec'\n const resolvedVirtualModuleId = '\\0' + virtualModuleId\n\n return {\n name: 'vite-plugin-virtual-custom',\n resolveId(id: string) {\n if (id === virtualModuleId) {\n return resolvedVirtualModuleId\n }\n },\n load(id: string) {\n if (id === resolvedVirtualModuleId) {\n return moduleSrc\n }\n }\n }\n}\n\n/**\n * XXX: This overrides the built-in `vite:resolve` plugin so that we can intercept `resolveId`\n * calls for the threads package.\n */\nfunction overrideViteResolvePlugin(viteConfig: ResolvedConfig) {\n const resolvePlugin = viteConfig.plugins.find(p => p.name === 'vite:resolve')\n if (resolvePlugin === undefined) {\n throw new Error('Failed to locate the built-in vite:resolve plugin')\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const originalResolveId = resolvePlugin.resolveId as any\n resolvePlugin.resolveId = async function resolveId(id, importer, options) {\n if (id.startsWith('./implementation') && importer.includes('threads/dist-esm')) {\n // XXX: The default resolver behavior will look at the `browser` mappings in\n // `threads/package.json` and try to resolve `implementation.js` to\n // `implementation.browser.js` because it thinks we're in a browser-only context.\n // We don't want that. Instead we want to keep the generic implementation from\n // threads that chooses between the Node and browser implementations at runtime.\n //\n // If we get here, importer will be something like:\n // /.../node_modules/.pnpm/threads@1.7.0/node_modules/threads/dist-esm/{worker,master}/index.js\n // And id will be:\n // ./implementation\n // So resolve the ID to:\n // /.../node_modules/.pnpm/threads@1.7.0/node_modules/threads/dist-esm/{worker,master}/implementation.js\n //\n // Or, importer will be:\n // /.../node_modules/.pnpm/threads@1.7.0/node_modules/threads/dist-esm/{worker,master}/implementation.js\n // And id will be one of:\n // ./implementation.browser\n // ./implementation.node\n // ./implementation.worker_threads\n // So resolve the ID to:\n // /.../node_modules/.pnpm/threads@1.7.0/node_modules/threads/dist-esm/{worker,master}/implementation.{...}.js\n const idFileName = id.replace('./', '')\n const importerFileName = basename(importer)\n const resolvedId = importer.replace(importerFileName, `${idFileName}.js`)\n return {\n id: resolvedId,\n moduleSideEffects: false\n }\n }\n\n // For all other cases, fall back on the default resolver\n return originalResolveId.call(this, id, importer, options)\n }\n}\n\nexport async function createViteConfigForBundle(\n context: BuildContext,\n modelSpec: ResolvedModelSpec\n): Promise<InlineConfig> {\n // Use `template-bundle` as the root directory for the bundle project\n const root = resolvePath(__dirname, '..', 'template-bundle')\n\n // Calculate output directory relative to the template root\n // TODO: For now we write it to `prepDir`; make this configurable?\n const prepDir = context.config.prepDir\n const outDir = relative(root, prepDir)\n\n // Use the model worker from the staged directory\n // TODO: Make this configurable?\n const modelWorkerPath = joinPath(prepDir, 'staged', 'model', 'worker.js?raw')\n\n return {\n // Don't use an external config file\n configFile: false,\n\n // Use the root directory configured above\n root,\n\n // Don't clear the screen in dev mode so that we can see builder output\n clearScreen: false,\n\n // TODO: Disable vite output by default?\n // logLevel: 'silent',\n\n // Configure path aliases\n resolve: {\n alias: [\n // Inject the configured model worker\n {\n find: '@_model_worker_',\n replacement: modelWorkerPath\n },\n\n // XXX: Prevent Vite from using the `browser` section of `threads/package.json`\n // since we want to force the use of the general module (under dist-esm) that chooses\n // the correct implementation (Web Worker vs worker_threads) at runtime. Currently\n // Vite's library mode is browser focused and generally chooses the right imports,\n // except in the case of the threads package where we want to use the generic\n // `implementation.js` that chooses between Web Worker and worker_threads at runtime.\n // Note that we could in theory set `resolve.browserField` to false, but that would\n // make Vite not use the browser field for all other packages, and there is not\n // currently a way to tell Vite to use the browser field on a case-by-case basis.\n // So for now we need this workaround here to make it resolve to `dist-esm`, and then\n // a second workaround in `overrideViteResolvePlugin` to prevent the resolver from\n // using the browser field when resolving the threads package.\n {\n find: 'threads',\n replacement: 'threads',\n customResolver: async function (source, importer, options) {\n // Note that we need to use `resolveId.call` here in order to provide the\n // right `this` context, which provides Rollup plugin functionality\n const customResolver = nodeResolve({ browser: false })\n const resolved = await customResolver.resolveId.call(this, source, importer, options)\n // Force the use of the `dist-esm` variant of the threads.js package\n if (source === 'threads/worker') {\n return resolved.id.replace('worker.mjs', 'dist-esm/worker/index.js')\n } else {\n return resolved.id.replace('index.mjs', 'dist-esm/index.js')\n }\n }\n }\n ]\n },\n\n plugins: [\n // Use a virtual module plugin to inject the model spec values\n injectModelSpec(context, modelSpec),\n\n // XXX: Install a wrapper around the built-in `vite:resolve` plugin so that we can\n // override the default resolver behavior that tries to resolve the `browser` section\n // of the `package.json` for the threads package.\n {\n name: 'vite-plugin-override-resolve',\n configResolved(viteConfig) {\n overrideViteResolvePlugin(viteConfig)\n }\n }\n ],\n\n build: {\n // Write output files to the configured directory (instead of the default `dist`);\n // note that this must be relative to the project `root`\n outDir,\n emptyOutDir: false,\n\n // Uncomment for debugging purposes\n // minify: false,\n\n lib: {\n entry: './src/index.ts',\n formats: ['es'],\n fileName: () => 'check-bundle.js'\n },\n\n rollupOptions: {\n // Don't transform Node imports used by threads.js\n external: ['events', 'os', 'path', 'url'],\n\n // XXX: Insert custom code at the top of the generated bundle that defines\n // the special `__non_webpack_require__` function that is used by threads.js\n // in its Node implementation. This import ensures that threads.js uses\n // the native `worker_threads` implementation when using the bundle in a\n // Node environment. When importing the bundle for use in the browser,\n // Vite will transform this import into an empty module due to the empty\n // polyfill that is configured in `vite-config-for-report.ts`.\n output: {\n banner: `\nimport * as worker_threads from 'worker_threads'\nlet __non_webpack_require__ = () => {\n return worker_threads;\n};\n`\n },\n\n onwarn: (warning, warn) => {\n // XXX: Suppress \"Use of eval is strongly discouraged\" warnings that are\n // triggered by use of the following pattern in threads.js:\n // eval(\"require\")(\"worker_threads\")\n // It would be nice to avoid use of `eval` there, but it's not critical for\n // our use case so we will suppress the warnings for now\n if (warning.code !== 'EVAL') {\n warn(warning)\n }\n }\n }\n }\n }\n}\n","// Copyright (c) 2022 Climate Interactive / New Venture Fund\n\nimport { dirname, relative, join as joinPath, resolve as resolvePath } from 'path'\nimport { fileURLToPath } from 'url'\n\nimport type { Alias, InlineConfig, PluginOption } from 'vite'\nimport replace from '@rollup/plugin-replace'\n\nimport type { SuiteSummary } from '@sdeverywhere/check-core'\n\nimport type { CheckPluginOptions } from './options'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\nexport function createViteConfigForReport(\n options: CheckPluginOptions | undefined,\n projDir: string,\n prepDir: string,\n currentBundleName: string,\n currentBundlePath: string,\n testConfigPath: string,\n suiteSummary: SuiteSummary | undefined\n): InlineConfig {\n // Use `template-report` as the root directory for the report project\n const root = resolvePath(__dirname, '..', 'template-report')\n\n // Include `baselines/*.js` files under the configured project root directory. This\n // glob path apparently must be a relative path (relative to the `template-report/src`\n // directory where the glob is used).\n const templateSrcDir = resolvePath(root, 'src')\n const relProjDir = relative(templateSrcDir, projDir)\n // XXX: The glob pattern must use forward slashes only, so on Windows we need to\n // convert backslashes to slashes\n const relProjDirPath = relProjDir.replaceAll('\\\\', '/')\n // TODO: Use baselinesDir from options\n const baselinesPath = `${relProjDirPath}/baselines/*.js`\n\n // Calculate output directory relative to the template root\n let reportPath: string\n if (options?.reportPath) {\n reportPath = options.reportPath\n } else {\n reportPath = joinPath(prepDir, 'check-report')\n }\n const outDir = relative(root, reportPath)\n\n // Convert the suite summary to JSON, which is what the app currently expects\n const suiteSummaryJson = suiteSummary ? JSON.stringify(suiteSummary) : ''\n\n const alias = (find: string, replacement: string) => {\n return {\n find,\n replacement\n } as Alias\n }\n\n // XXX: This provides custom handling for Node built-ins such as 'events' that are\n // referenced by the check bundle (specifically in the Node implementation of\n // threads.js). These are not actually used in the browser, so we just need\n // to provide no-op polyfills for these.\n const noopPolyfillAlias = (find: string) => {\n return {\n find,\n replacement: '/polyfills/noop-polyfills.ts'\n } as Alias\n }\n\n return {\n // Don't use an external config file\n configFile: false,\n\n // Use the root directory configured above\n root,\n\n // Use `.` as the base directory (instead of the default `/`); this controls\n // how the path to the js/css files are generated in `index.html`\n base: '',\n\n // Use a custom cache directory under `prepDir`, as otherwise Vite will use\n // `packages/plugin-check/template-report/node_modules/.vite`, and we want to\n // avoid generating files in `template-report` (which should be read-only)\n cacheDir: joinPath(prepDir, '.vite-check-report'),\n\n // Load static files from `static` (instead of the default `public`)\n // publicDir: 'static',\n\n // Don't clear the screen in dev mode so that we can see builder output\n clearScreen: false,\n\n // TODO\n // logLevel: 'silent',\n\n optimizeDeps: {\n // Prevent Vite from examining other html files when scanning entrypoints\n // for dependency optimization\n entries: ['index.html'],\n\n // XXX: When plugin-check is installed via pnpm, the Vite dev server seems\n // to have no trouble resolving other dependencies using the optimizeDeps\n // mechanism. However, this fails when the package is installed via yarn\n // or npm (probably due to the fact that the `template-report` directory\n // is located under the top-level `node_modules` directory); in the browser,\n // there will be \"import not found\" errors for the packages referenced below.\n // As a terrible workaround, explicitly include the direct dependencies so\n // that Vite optimizes them; this works for pnpm, yarn, and npm. We should\n // find a less fragile solution.\n include: [\n // from check-core\n '@sdeverywhere/check-core > assert-never',\n '@sdeverywhere/check-core > ajv',\n '@sdeverywhere/check-core > neverthrow',\n '@sdeverywhere/check-core > yaml',\n // from check-ui-shell\n '@sdeverywhere/check-ui-shell > fontfaceobserver',\n '@sdeverywhere/check-ui-shell > copy-text-to-clipboard',\n '@sdeverywhere/check-ui-shell > chart.js'\n ],\n\n exclude: [\n // XXX: The threads.js implementation references `tiny-worker` as an optional\n // dependency, but it doesn't get used at runtime, so we can just exclude it\n // so that Vite doesn't complain in dev mode\n 'tiny-worker'\n\n // XXX: Similarly, chart.js treats `moment` as an optional dependency, but we\n // don't use it at runtime; we need to exclude it here, otherwise Vite will\n // complain about missing dependencies in dev mode\n // 'moment'\n ]\n },\n\n // Configure path aliases\n resolve: {\n alias: [\n // Use the configured \"baseline\" bundle if defined, otherwise use the \"empty\" bundle\n // (which will cause comparison tests to be skipped)\n alias('@_baseline_bundle_', options?.baseline ? options.baseline.path : '/src/empty-bundle.ts'),\n\n // Use the configured \"current\" bundle\n alias('@_current_bundle_', currentBundlePath),\n\n // Use the configured test config file\n alias('@_test_config_', testConfigPath),\n\n // Make the overlay use the `messages.html` file that is written to the prep directory\n alias('@_prep_', prepDir),\n\n // XXX: Include no-op polyfills for these modules that are used in the Node-specific\n // implementation of threads.js; this allows us to use one bundle that works in both\n // Node and browser environments\n noopPolyfillAlias('events'),\n noopPolyfillAlias('fs'),\n noopPolyfillAlias('os'),\n noopPolyfillAlias('path'),\n noopPolyfillAlias('url'),\n noopPolyfillAlias('worker_threads')\n ]\n },\n\n // Inject special values into the generated JS\n define: {\n // Inject the summary JSON into the build\n __SUITE_SUMMARY_JSON__: JSON.stringify(suiteSummaryJson),\n\n // Inject the baseline branch name\n __BASELINE_NAME__: JSON.stringify(options?.baseline?.name || ''),\n\n // Inject the current branch name\n __CURRENT_NAME__: JSON.stringify(currentBundleName)\n },\n\n plugins: [\n // Inject special values into the generated JS\n // TODO: We currently have to use `@rollup/plugin-replace` instead of Vite's\n // built-in `define` feature because the latter does not seem to run before\n // the glob handler (which requires the glob to be injected as a literal)\n replace({\n preventAssignment: true,\n delimiters: ['', ''],\n values: {\n // Inject the path for baseline bundles\n './__BASELINE_BUNDLES_PATH__': baselinesPath\n }\n }) as unknown as PluginOption\n ],\n\n build: {\n // Write output files to the configured directory (instead of the default `dist`);\n // note that this must be relative to the project `root`\n outDir,\n\n // Write js/css files to `public` (instead of the default `<outDir>/assets`)\n assetsDir: '',\n\n rollupOptions: {\n output: {\n // XXX: Prevent vite from creating a separate `vendor.js` file\n manualChunks: undefined\n },\n\n onwarn: (warning, warn) => {\n // XXX: Suppress \"Use of eval is strongly discouraged\" warnings that are\n // triggered by use of the following pattern in threads.js:\n // eval(\"require\")(\"worker_threads\")\n // It would be nice to avoid use of `eval` there, but it's not critical for\n // our use case so we will suppress the warnings for now\n if (warning.code !== 'EVAL') {\n warn(warning)\n }\n }\n }\n },\n\n server: {\n // Run the dev server at `localhost:8081` by default\n port: options?.serverPort || 8081,\n\n // Open the app in the browser by default\n open: '/index.html',\n\n // XXX: Add a small delay, otherwise on macOS we sometimes get multiple\n // change events when a file is saved just once. That is a relatively\n // harmless issue except that it causes redundant messages in the console\n // and can cause extra churn when refreshing the app.\n watch: {\n awaitWriteFinish: {\n stabilityThreshold: 100\n }\n }\n }\n }\n}\n","// Copyright (c) 2022 Climate Interactive / New Venture Fund\n\nimport { dirname, relative, resolve as resolvePath } from 'path'\nimport { fileURLToPath } from 'url'\n\nimport type { InlineConfig, PluginOption } from 'vite'\nimport replace from '@rollup/plugin-replace'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\nexport function createViteConfigForTests(projDir: string, prepDir: string, mode: 'build' | 'watch'): InlineConfig {\n // Use `template-tests` as the root directory for the tests project\n const root = resolvePath(__dirname, '..', 'template-tests')\n\n // Get the base glob path; apparently this must be a relative path (relative to\n // the `template-tests/src` directory where the glob is used)\n const templateSrcDir = resolvePath(root, 'src')\n const relProjDir = relative(templateSrcDir, projDir)\n // XXX: The glob pattern must use forward slashes only, so on Windows we need to\n // convert backslashes to slashes\n const relProjDirPath = relProjDir.replaceAll('\\\\', '/')\n\n // Include check test definitions in files matching `checks/*.yaml` under\n // the configured project root directory. We also include `*.check.yaml`,\n // which was the naming used in earlier versions of the create package and\n // related examples.\n // TODO: Use yaml path/pattern from options\n const yamlCheckGlobPatterns = `['${relProjDirPath}/**/checks/*.yaml', '${relProjDirPath}/**/*.check.yaml']`\n\n // Include comparison test definitions in files matching `comparisons/*.yaml`\n // under the configured project root directory\n // TODO: Use yaml path/pattern from options\n const yamlComparisonGlobPatterns = `['${relProjDirPath}/**/comparisons/*.yaml']`\n\n // Calculate output directory relative to the template root\n // TODO: For now we write it to `prepDir`; make this configurable?\n const outDir = relative(root, prepDir)\n\n return {\n // Don't use an external config file\n configFile: false,\n\n // Use the root directory configured above\n root,\n\n // Don't clear the screen in dev mode so that we can see builder output\n clearScreen: false,\n\n // TODO: Disable vite output by default?\n // logLevel: 'silent',\n\n plugins: [\n // Inject special values into the generated JS\n // TODO: We currently have to use `@rollup/plugin-replace` instead of Vite's\n // built-in `define` feature because the latter does not seem to run before\n // the glob handler (which requires the glob to be injected as a literal)\n replace({\n preventAssignment: true,\n delimiters: ['', ''],\n values: {\n // Inject the glob patterns for matching model check yaml files\n '\"./__YAML_CHECK_GLOB_PATTERNS__\"': yamlCheckGlobPatterns,\n // Inject the glob patterns for matching model comparison yaml files\n '\"./__YAML_COMPARISON_GLOB_PATTERNS__\"': yamlComparisonGlobPatterns\n }\n }) as unknown as PluginOption\n ],\n\n build: {\n // Write output files to the configured directory (instead of the default `dist`);\n // note that this must be relative to the project `root`\n outDir,\n emptyOutDir: false,\n\n lib: {\n entry: './src/index.ts',\n formats: ['es'],\n fileName: () => 'check-tests.js'\n },\n\n // Enable watch mode if requested\n watch: mode === 'watch' && {},\n\n rollupOptions: {\n // Prevent dependencies from being included in packaged library\n // TODO: For now we include check-core in the packaged library so that its\n // dependencies are correctly resolved at runtime. Ideally this would only\n // include a couple functions that are used for defining tests, but Vite 2.x\n // does not implement tree shaking for ES libraries, which means the generated\n // library is much larger than it needs to be. Once we upgrade to Vite 3.x,\n // the generated library should be smaller; see related fix:\n // https://github.com/vitejs/vite/pull/8737\n // external: Object.keys(pkg.dependencies)\n }\n }\n }\n}\n"],"mappings":"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","names":["existsSync","dirname","joinPath","relative","fileURLToPath","__filename","__dirname","dirname","relative","joinPath","resolvePath","fileURLToPath","__filename","__dirname","dirname","relative","resolvePath","fileURLToPath","replace","__filename","__dirname","joinPath","existsSync","dirname","fileURLToPath","relative"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sdeverywhere/plugin-check",
3
- "version": "0.3.11",
3
+ "version": "0.3.13",
4
4
  "files": [
5
5
  "bin/**",
6
6
  "dist/**",
@@ -45,7 +45,7 @@
45
45
  ]
46
46
  },
47
47
  "peerDependencies": {
48
- "@sdeverywhere/build": "^0.3.6"
48
+ "@sdeverywhere/build": "^0.3.7"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@sdeverywhere/build": "*",
@@ -81,7 +81,7 @@ export function setInputsForScenario(inputs: Map<InputVarId, Input>, scenario: S
81
81
  input.value.set(input.minValue)
82
82
  }
83
83
  function setInputToMaximum(input: Input): void {
84
- input.value.set(input.minValue)
84
+ input.value.set(input.maxValue)
85
85
  }
86
86
 
87
87
  function setAllToDefault(): void {
@@ -22,6 +22,7 @@ export function getOutputVars(outputSpecs: OutputSpec[]): Map<DatasetKey, Output
22
22
  const varId = outputSpec.varId
23
23
  const datasetKey = datasetKeyForOutputVar(undefined, varId)
24
24
  outputVars.set(datasetKey, {
25
+ datasetKey,
25
26
  sourceName: undefined,
26
27
  varId,
27
28
  varName: outputSpec.varName
@@ -5,6 +5,7 @@ import type {
5
5
  ComparisonOptions,
6
6
  ComparisonScenarioSpec,
7
7
  ComparisonSpecs,
8
+ ComparisonSpecsSource,
8
9
  ConfigInitOptions,
9
10
  ConfigOptions,
10
11
  DatasetKey,
@@ -12,21 +13,36 @@ import type {
12
13
  InputVar
13
14
  } from '@sdeverywhere/check-core'
14
15
 
15
- // Load the yaml test files. The `./__YAML_PATH__` part will be replaced by Vite
16
- // (see `vite-config-for-tests.ts`). Note that we provide a placeholder here that
17
- // looks like a valid glob pattern, since Vite's dependency resolver will report
18
- // errors if it is invalid (not a literal).
19
- const yamlGlob = import.meta.glob('./__YAML_PATH__', {
16
+ // Load the yaml files containing model check test definitions. The
17
+ // `./__YAML_*_GLOB_PATTERNS__` part will be replaced by Vite (see
18
+ // `vite-config-for-tests.ts`). Note that we provide a placeholder here
19
+ // that looks like a valid glob pattern, since Vite's dependency resolver
20
+ // will report errors if it is invalid (not a literal).
21
+ const yamlCheckSpecFilesGlob = import.meta.glob('./__YAML_CHECK_GLOB_PATTERNS__', {
20
22
  eager: true,
21
23
  query: '?raw',
22
24
  import: 'default'
23
25
  })
24
- const tests: string[] = []
25
- for (const yamlKey of Object.keys(yamlGlob)) {
26
- const yaml = yamlGlob[yamlKey]
27
- tests.push(yaml as unknown as string)
26
+ const yamlCheckSpecFiles: string[] = []
27
+ for (const yamlKey of Object.keys(yamlCheckSpecFilesGlob)) {
28
+ const yaml = yamlCheckSpecFilesGlob[yamlKey]
29
+ yamlCheckSpecFiles.push(yaml as unknown as string)
28
30
  }
29
31
 
32
+ // Load the yaml files containing model comparison test definitions
33
+ const yamlComparisonFilesGlob = import.meta.glob('./__YAML_COMPARISON_GLOB_PATTERNS__', {
34
+ eager: true,
35
+ query: '?raw',
36
+ import: 'default'
37
+ })
38
+ const yamlComparisonSpecFiles: ComparisonSpecsSource[] = Object.entries(yamlComparisonFilesGlob).map(entry => {
39
+ return {
40
+ kind: 'yaml',
41
+ filename: entry[0],
42
+ content: entry[1] as string
43
+ }
44
+ })
45
+
30
46
  // If an output variable is renamed, define the mapping from the old key
31
47
  // to the new key here. If this step is omitted, the old variable will
32
48
  // appear as being removed and the new variable will appear as being added,
@@ -53,16 +69,18 @@ export async function getConfigOptions(
53
69
  // - twice for each input
54
70
  // - once with single input at its minimum
55
71
  // - once with single input at its maximum
56
- // TODO: Also read specs from comparison yaml files
57
72
  const baseComparisonSpecs = createBaseComparisonSpecs(bundleL, bundleR)
58
73
 
74
+ // Also include custom scenarios defined in the `comparisons/*.yaml` files
75
+ const comparisonSpecs = [baseComparisonSpecs, ...yamlComparisonSpecFiles]
76
+
59
77
  comparisonOptions = {
60
78
  baseline: {
61
79
  name: opts?.bundleNameL || 'baseline',
62
80
  bundle: bundleL
63
81
  },
64
82
  thresholds: [1, 5, 10],
65
- specs: [baseComparisonSpecs],
83
+ specs: comparisonSpecs,
66
84
  datasets: {
67
85
  renamedDatasetKeys
68
86
  }
@@ -75,7 +93,7 @@ export async function getConfigOptions(
75
93
  bundle: bundleR
76
94
  },
77
95
  check: {
78
- tests
96
+ tests: yamlCheckSpecFiles
79
97
  },
80
98
  comparison: comparisonOptions
81
99
  }