@sdeverywhere/plugin-check 0.3.36 → 0.3.38
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.d.ts +67 -65
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +660 -899
- package/dist/index.js.map +1 -1
- package/package.json +13 -26
- package/template-report/index.html +7 -5
- package/template-report/polyfills/noop-polyfills.ts +8 -5
- package/template-report/src/bundle-metadata.spec.ts +81 -0
- package/template-report/src/bundle-metadata.ts +66 -0
- package/template-report/src/index.ts +17 -78
- package/template-report/src/load-bundle.ts +1 -4
- package/template-report/src/resolve-bundle.spec.ts +109 -0
- package/template-report/src/resolve-bundle.ts +64 -0
- package/dist/index.cjs +0 -972
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -83
- package/template-report/src/bundles/unused.txt +0 -2
package/dist/index.js
CHANGED
|
@@ -1,938 +1,699 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
1
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { copyFile, mkdir, readFile, readdir, stat, utimes, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
6
5
|
import { build, createServer } from "vite";
|
|
7
|
-
import { createConfig } from "@sdeverywhere/check-core";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
import {
|
|
11
|
-
import { dirname, join as
|
|
6
|
+
import { createConfig, datasetMessage, encodeImplVars, predicateMessage, runSuite, scenarioMessage, suiteSummaryFromReport } from "@sdeverywhere/check-core";
|
|
7
|
+
import { performance } from "perf_hooks";
|
|
8
|
+
import pico from "picocolors";
|
|
9
|
+
import { existsSync as existsSync$1, readFileSync, statSync } from "fs";
|
|
10
|
+
import { dirname as dirname$1, join as join$1, relative as relative$1, resolve as resolve$1 } from "path";
|
|
11
|
+
import { fileURLToPath as fileURLToPath$1, pathToFileURL } from "node:url";
|
|
12
|
+
import chokidar from "chokidar";
|
|
13
|
+
//#region src/bundle-file-ops.ts
|
|
14
|
+
/**
|
|
15
|
+
* Download a bundle from a remote URL and save it to the local bundles directory.
|
|
16
|
+
*
|
|
17
|
+
* @param url The remote URL to download the bundle from.
|
|
18
|
+
* @param name The bundle name (may contain slashes for subdirectories).
|
|
19
|
+
* @param lastModified The last modified timestamp from the remote bundle.
|
|
20
|
+
* @param bundlesDir The bundles directory path.
|
|
21
|
+
* @param fetchRemoteBundle Optional custom function for fetching remote bundle files.
|
|
22
|
+
* @returns The file path where the bundle was saved.
|
|
23
|
+
*/
|
|
12
24
|
async function downloadBundle(url, name, lastModified, bundlesDir, fetchRemoteBundle) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
await utimes(filePath, mtime, mtime);
|
|
31
|
-
}
|
|
32
|
-
return filePath;
|
|
25
|
+
const fullUrl = `${url}?cb=${Date.now()}`;
|
|
26
|
+
let bundleContent;
|
|
27
|
+
if (fetchRemoteBundle) bundleContent = await fetchRemoteBundle(fullUrl);
|
|
28
|
+
else {
|
|
29
|
+
const response = await fetch(fullUrl);
|
|
30
|
+
if (!response.ok) throw new Error(`Failed to fetch bundle: HTTP ${response.status} ${response.statusText}`);
|
|
31
|
+
bundleContent = await response.text();
|
|
32
|
+
}
|
|
33
|
+
const nameParts = name.split("/");
|
|
34
|
+
const filePath = join(bundlesDir, ...nameParts) + ".js";
|
|
35
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
36
|
+
await writeFile(filePath, bundleContent, "utf8");
|
|
37
|
+
if (lastModified) {
|
|
38
|
+
const mtime = new Date(lastModified);
|
|
39
|
+
await utimes(filePath, mtime, mtime);
|
|
40
|
+
}
|
|
41
|
+
return filePath;
|
|
33
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Copy a bundle from a source URL and save it with a new name to the local bundles directory.
|
|
45
|
+
*
|
|
46
|
+
* @param url The source URL to copy the bundle from (can be 'current' or file:// URLs).
|
|
47
|
+
* @param newName The new bundle name (may contain slashes for subdirectories).
|
|
48
|
+
* @param bundlesDir The bundles directory path.
|
|
49
|
+
* @returns The file path where the bundle was saved.
|
|
50
|
+
*/
|
|
34
51
|
async function copyBundle(url, newName, bundlesDir) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
await mkdir(dirname(filePath), { recursive: true });
|
|
50
|
-
await writeFile(filePath, bundleContent, "utf8");
|
|
51
|
-
if (sourceLastModified) {
|
|
52
|
-
await utimes(filePath, sourceLastModified, sourceLastModified);
|
|
53
|
-
}
|
|
54
|
-
return filePath;
|
|
52
|
+
let srcPath;
|
|
53
|
+
if (url === "current") {
|
|
54
|
+
const sdePrepDir = join(bundlesDir, "..", "sde-prep");
|
|
55
|
+
srcPath = join(sdePrepDir, "check-bundle.js");
|
|
56
|
+
} else if (url.startsWith("file://")) srcPath = new URL(url).pathname;
|
|
57
|
+
else throw new Error(`Cannot copy bundle with URL: ${url}`);
|
|
58
|
+
const bundleContent = await readFile(srcPath, "utf8");
|
|
59
|
+
const sourceLastModified = (await stat(srcPath)).mtime;
|
|
60
|
+
const nameParts = newName.split("/");
|
|
61
|
+
const filePath = join(bundlesDir, ...nameParts) + ".js";
|
|
62
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
63
|
+
await writeFile(filePath, bundleContent, "utf8");
|
|
64
|
+
if (sourceLastModified) await utimes(filePath, sourceLastModified, sourceLastModified);
|
|
65
|
+
return filePath;
|
|
55
66
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
datasetMessage,
|
|
62
|
-
predicateMessage,
|
|
63
|
-
runSuite,
|
|
64
|
-
scenarioMessage,
|
|
65
|
-
suiteSummaryFromReport
|
|
66
|
-
} from "@sdeverywhere/check-core";
|
|
67
|
+
//#endregion
|
|
68
|
+
//#region src/run-suite.ts
|
|
69
|
+
/**
|
|
70
|
+
* Runs the test suite.
|
|
71
|
+
*/
|
|
67
72
|
async function runTestSuite(context, config, verbose) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
onError: (error) => {
|
|
101
|
-
reject(error);
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
runSuite(config, callbacks);
|
|
105
|
-
});
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
const t0 = performance.now();
|
|
75
|
+
let lastPctByInc;
|
|
76
|
+
runSuite(config, {
|
|
77
|
+
onProgress: (progress) => {
|
|
78
|
+
const pct = Math.round(progress * 100);
|
|
79
|
+
const pctByInc = Math.floor(pct / 5) * 5;
|
|
80
|
+
if (lastPctByInc === void 0 || pctByInc > lastPctByInc) {
|
|
81
|
+
lastPctByInc = pctByInc;
|
|
82
|
+
context.log("info", `${pctByInc}%`);
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
onComplete: (report) => {
|
|
86
|
+
try {
|
|
87
|
+
const elapsedMillis = performance.now() - t0;
|
|
88
|
+
const elapsedSeconds = (elapsedMillis / 1e3).toFixed(1);
|
|
89
|
+
context.log("info", `\nTest suite completed in ${elapsedSeconds}s`);
|
|
90
|
+
const allChecksPassed = printCheckSummary(context, report.checkReport, verbose);
|
|
91
|
+
if (report.comparisonReport) printPerfStats(context, config.comparison, report.comparisonReport);
|
|
92
|
+
resolve({
|
|
93
|
+
allChecksPassed,
|
|
94
|
+
suiteSummary: suiteSummaryFromReport(report, elapsedMillis)
|
|
95
|
+
});
|
|
96
|
+
} catch (e) {
|
|
97
|
+
reject(e);
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
onError: (error) => {
|
|
101
|
+
reject(error);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
});
|
|
106
105
|
}
|
|
107
106
|
function printCheckSummary(context, checkReport, verbose) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
for (const predicate of dataset.predicates) {
|
|
155
|
-
printResult(7, predicate.result.status, predicateMessage(predicate, bold));
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
context.log("info", "");
|
|
162
|
-
return allPassed;
|
|
107
|
+
function printResult(indent, status, text) {
|
|
108
|
+
if (!verbose && status === "passed" && indent > 1) return;
|
|
109
|
+
let statusChar;
|
|
110
|
+
switch (status) {
|
|
111
|
+
case "passed":
|
|
112
|
+
statusChar = "✓";
|
|
113
|
+
break;
|
|
114
|
+
case "failed":
|
|
115
|
+
statusChar = "✗";
|
|
116
|
+
break;
|
|
117
|
+
case "error":
|
|
118
|
+
statusChar = "‼";
|
|
119
|
+
break;
|
|
120
|
+
case "skipped":
|
|
121
|
+
statusChar = "–";
|
|
122
|
+
break;
|
|
123
|
+
default: statusChar = "";
|
|
124
|
+
}
|
|
125
|
+
const msg = `${" ".repeat(indent)}${statusChar} ${text}`;
|
|
126
|
+
context.log("info", status === "passed" ? pico.green(msg) : pico.red(msg));
|
|
127
|
+
}
|
|
128
|
+
function bold(s) {
|
|
129
|
+
return pico.bold(s);
|
|
130
|
+
}
|
|
131
|
+
function printTest(test) {
|
|
132
|
+
const msg = `${test.name}${verbose || test.status !== "passed" ? ":" : ""}`;
|
|
133
|
+
printResult(1, test.status, msg);
|
|
134
|
+
}
|
|
135
|
+
let allPassed = true;
|
|
136
|
+
context.log("info", "\nCheck results:");
|
|
137
|
+
for (const group of checkReport.groups) {
|
|
138
|
+
context.log("info", `\n${group.name}`);
|
|
139
|
+
for (const test of group.tests) {
|
|
140
|
+
if (test.status !== "passed") allPassed = false;
|
|
141
|
+
printTest(test);
|
|
142
|
+
for (const scenario of test.scenarios) {
|
|
143
|
+
printResult(3, scenario.status, scenarioMessage(scenario, bold));
|
|
144
|
+
for (const dataset of scenario.datasets) {
|
|
145
|
+
printResult(5, dataset.status, datasetMessage(dataset, bold));
|
|
146
|
+
for (const predicate of dataset.predicates) printResult(7, predicate.result.status, predicateMessage(predicate, bold));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
context.log("info", "");
|
|
152
|
+
return allPassed;
|
|
163
153
|
}
|
|
164
|
-
function
|
|
165
|
-
|
|
154
|
+
function stat$1(label, n) {
|
|
155
|
+
return `${label}=${n.toFixed(1)}ms`;
|
|
166
156
|
}
|
|
167
157
|
function printPerfReportLine(context, perfReport) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
158
|
+
const avg = stat$1("avg", perfReport.avgTime);
|
|
159
|
+
const min = stat$1("min", perfReport.minTime);
|
|
160
|
+
const max = stat$1("max", perfReport.maxTime);
|
|
161
|
+
context.log("info", ` ${avg} ${min} ${max}`);
|
|
172
162
|
}
|
|
173
163
|
function printPerfStats(context, comparisonConfig, report) {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
164
|
+
context.log("info", "\nPerformance stats:");
|
|
165
|
+
context.log("info", ` ${comparisonConfig.bundleL.name}:`);
|
|
166
|
+
printPerfReportLine(context, report.perfReportL);
|
|
167
|
+
context.log("info", ` ${comparisonConfig.bundleR.name}:`);
|
|
168
|
+
printPerfReportLine(context, report.perfReportR);
|
|
169
|
+
context.log("info", "");
|
|
180
170
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
171
|
+
//#endregion
|
|
172
|
+
//#region src/vite-config-for-bundle.ts
|
|
173
|
+
const __filename$2 = fileURLToPath(import.meta.url);
|
|
174
|
+
const __dirname$2 = dirname$1(__filename$2);
|
|
175
|
+
/**
|
|
176
|
+
* This is a virtual module plugin used to inject model-specific configuration
|
|
177
|
+
* values into the generated worker bundle.
|
|
178
|
+
*
|
|
179
|
+
* This follows the "Virtual Modules Convention" described here:
|
|
180
|
+
* https://vitejs.dev/guide/api-plugin.html#virtual-modules-convention
|
|
181
|
+
*
|
|
182
|
+
* TODO: This could be simplified by using `vite-plugin-virtual` but that
|
|
183
|
+
* doesn't seem to be working correctly in an ESM setting
|
|
184
|
+
*/
|
|
190
185
|
function injectModelSpec(context, modelSpec) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
return 0;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
const modelSizeInBytes = stagedFileSize("generated-model.js");
|
|
238
|
-
const dataSizeInBytes = stagedFileSize("static-data.ts");
|
|
239
|
-
const moduleSrc = `
|
|
186
|
+
const prepDir = context.config.prepDir;
|
|
187
|
+
const inputSpecs = [];
|
|
188
|
+
for (const modelInputSpec of modelSpec.inputs) {
|
|
189
|
+
if (modelInputSpec.defaultValue === void 0 || modelInputSpec.minValue === void 0 || modelInputSpec.maxValue === void 0) {
|
|
190
|
+
let msg = "";
|
|
191
|
+
msg += `WARNING: The {defaultValue,minValue,maxValue} properties are required by plugin-check, `;
|
|
192
|
+
msg += `but are undefined in the InputSpec for '${modelInputSpec.varName}'. `;
|
|
193
|
+
msg += `This input variable will be excluded from the model-check bundle until those properties `;
|
|
194
|
+
msg += `are defined.`;
|
|
195
|
+
console.warn(msg);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
const varId = context.canonicalVarId(modelInputSpec.varName);
|
|
199
|
+
const inputId = modelInputSpec.inputId || varId;
|
|
200
|
+
inputSpecs.push({
|
|
201
|
+
inputId,
|
|
202
|
+
varId,
|
|
203
|
+
...modelInputSpec
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
const outputSpecs = modelSpec.outputs.map((o) => {
|
|
207
|
+
return {
|
|
208
|
+
varId: context.canonicalVarId(o.varName),
|
|
209
|
+
...o
|
|
210
|
+
};
|
|
211
|
+
});
|
|
212
|
+
function readJsonListing() {
|
|
213
|
+
const path = join$1(prepDir, "build", "processed.json");
|
|
214
|
+
if (existsSync$1(path)) {
|
|
215
|
+
const json = readFileSync(path, "utf8");
|
|
216
|
+
return JSON.parse(json);
|
|
217
|
+
} else return {};
|
|
218
|
+
}
|
|
219
|
+
const varInstances = readJsonListing().varInstances || {};
|
|
220
|
+
const encodedImplVars = encodeImplVars(varInstances);
|
|
221
|
+
function stagedFileSize(filename) {
|
|
222
|
+
const path = join$1(prepDir, "staged", "model", filename);
|
|
223
|
+
if (existsSync$1(path)) return statSync(path).size;
|
|
224
|
+
else return 0;
|
|
225
|
+
}
|
|
226
|
+
const modelSizeInBytes = stagedFileSize("generated-model.js");
|
|
227
|
+
const dataSizeInBytes = stagedFileSize("static-data.ts");
|
|
228
|
+
const moduleSrc = `
|
|
240
229
|
export const inputSpecs = ${JSON.stringify(inputSpecs)};
|
|
241
230
|
export const outputSpecs = ${JSON.stringify(outputSpecs)};
|
|
242
231
|
export const encodedImplVars = ${JSON.stringify(encodedImplVars)};
|
|
243
232
|
export const modelSizeInBytes = ${modelSizeInBytes};
|
|
244
233
|
export const dataSizeInBytes = ${dataSizeInBytes};
|
|
245
234
|
`;
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
return moduleSrc;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
function overrideViteResolvePlugin(viteConfig) {
|
|
263
|
-
const resolvePlugin = viteConfig.plugins.find((p) => p.name === "vite:resolve");
|
|
264
|
-
if (resolvePlugin === void 0) {
|
|
265
|
-
throw new Error("Failed to locate the built-in vite:resolve plugin");
|
|
266
|
-
}
|
|
267
|
-
const originalResolveId = resolvePlugin.resolveId;
|
|
268
|
-
resolvePlugin.resolveId = async function resolveId(id, importer, options) {
|
|
269
|
-
if (id.startsWith("./implementation") && importer.includes("threads/dist-esm")) {
|
|
270
|
-
const idFileName = id.replace("./", "");
|
|
271
|
-
const importerFileName = basename(importer);
|
|
272
|
-
const resolvedId = importer.replace(importerFileName, `${idFileName}.js`);
|
|
273
|
-
return {
|
|
274
|
-
id: resolvedId,
|
|
275
|
-
moduleSideEffects: false
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
return await originalResolveId.handler.call(this, id, importer, options);
|
|
279
|
-
};
|
|
235
|
+
const virtualModuleId = "virtual:model-spec";
|
|
236
|
+
const resolvedVirtualModuleId = "\0" + virtualModuleId;
|
|
237
|
+
return {
|
|
238
|
+
name: "vite-plugin-virtual-custom",
|
|
239
|
+
resolveId(id) {
|
|
240
|
+
if (id === virtualModuleId) return resolvedVirtualModuleId;
|
|
241
|
+
},
|
|
242
|
+
load(id) {
|
|
243
|
+
if (id === resolvedVirtualModuleId) return moduleSrc;
|
|
244
|
+
}
|
|
245
|
+
};
|
|
280
246
|
}
|
|
281
247
|
async function createViteConfigForBundle(context, modelSpec) {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
// since we want to force the use of the general module (under dist-esm) that chooses
|
|
305
|
-
// the correct implementation (Web Worker vs worker_threads) at runtime. Currently
|
|
306
|
-
// Vite's library mode is browser focused and generally chooses the right imports,
|
|
307
|
-
// except in the case of the threads package where we want to use the generic
|
|
308
|
-
// `implementation.js` that chooses between Web Worker and worker_threads at runtime.
|
|
309
|
-
// Note that we could in theory set `resolve.browserField` to false, but that would
|
|
310
|
-
// make Vite not use the browser field for all other packages, and there is not
|
|
311
|
-
// currently a way to tell Vite to use the browser field on a case-by-case basis.
|
|
312
|
-
// So for now we need this workaround here to make it resolve to `dist-esm`, and then
|
|
313
|
-
// a second workaround in `overrideViteResolvePlugin` to prevent the resolver from
|
|
314
|
-
// using the browser field when resolving the threads package.
|
|
315
|
-
{
|
|
316
|
-
find: "threads",
|
|
317
|
-
replacement: "threads",
|
|
318
|
-
customResolver: async function(source, importer, options) {
|
|
319
|
-
const customResolver = nodeResolve({ browser: false });
|
|
320
|
-
const resolveIdHook = customResolver.resolveId;
|
|
321
|
-
const resolveIdFn = typeof resolveIdHook === "function" ? resolveIdHook : resolveIdHook.handler;
|
|
322
|
-
const resolved = await resolveIdFn.call(this, source, importer, options);
|
|
323
|
-
if (source === "threads/worker") {
|
|
324
|
-
return resolved.id.replace("worker.mjs", "dist-esm/worker/index.js");
|
|
325
|
-
} else {
|
|
326
|
-
return resolved.id.replace("index.mjs", "dist-esm/index.js");
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
]
|
|
331
|
-
},
|
|
332
|
-
plugins: [
|
|
333
|
-
// Use a virtual module plugin to inject the model spec values
|
|
334
|
-
injectModelSpec(context, modelSpec),
|
|
335
|
-
// XXX: Install a wrapper around the built-in `vite:resolve` plugin so that we can
|
|
336
|
-
// override the default resolver behavior that tries to resolve the `browser` section
|
|
337
|
-
// of the `package.json` for the threads package.
|
|
338
|
-
{
|
|
339
|
-
name: "vite-plugin-override-resolve",
|
|
340
|
-
configResolved(viteConfig) {
|
|
341
|
-
overrideViteResolvePlugin(viteConfig);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
],
|
|
345
|
-
build: {
|
|
346
|
-
// Write output files to the configured directory (instead of the default `dist`);
|
|
347
|
-
// note that this must be relative to the project `root`
|
|
348
|
-
outDir,
|
|
349
|
-
emptyOutDir: false,
|
|
350
|
-
// Uncomment for debugging purposes
|
|
351
|
-
// minify: false,
|
|
352
|
-
lib: {
|
|
353
|
-
entry: "./src/index.ts",
|
|
354
|
-
formats: ["es"],
|
|
355
|
-
fileName: () => "check-bundle.js"
|
|
356
|
-
},
|
|
357
|
-
rollupOptions: {
|
|
358
|
-
// Don't transform Node imports used by threads.js
|
|
359
|
-
external: ["events", "os", "path", "url"],
|
|
360
|
-
// XXX: Insert custom code at the top of the generated bundle that defines
|
|
361
|
-
// the special `__non_webpack_require__` function that is used by threads.js
|
|
362
|
-
// in its Node implementation. This import ensures that threads.js uses
|
|
363
|
-
// the native `worker_threads` implementation when using the bundle in a
|
|
364
|
-
// Node environment. When importing the bundle for use in the browser,
|
|
365
|
-
// Vite will transform this import into an empty module due to the empty
|
|
366
|
-
// polyfill that is configured in `vite-config-for-report.ts`.
|
|
367
|
-
output: {
|
|
368
|
-
banner: `
|
|
369
|
-
import * as worker_threads from 'worker_threads'
|
|
370
|
-
let __non_webpack_require__ = () => {
|
|
371
|
-
return worker_threads;
|
|
372
|
-
};
|
|
373
|
-
`
|
|
374
|
-
},
|
|
375
|
-
onwarn: (warning, warn) => {
|
|
376
|
-
if (warning.code !== "EVAL") {
|
|
377
|
-
warn(warning);
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
};
|
|
248
|
+
const root = resolve$1(__dirname$2, "..", "template-bundle");
|
|
249
|
+
const prepDir = context.config.prepDir;
|
|
250
|
+
const outDir = relative$1(root, prepDir);
|
|
251
|
+
return {
|
|
252
|
+
configFile: false,
|
|
253
|
+
root,
|
|
254
|
+
clearScreen: false,
|
|
255
|
+
resolve: { alias: [{
|
|
256
|
+
find: "@_model_worker_",
|
|
257
|
+
replacement: join$1(prepDir, "staged", "model", "worker.js?raw")
|
|
258
|
+
}] },
|
|
259
|
+
plugins: [injectModelSpec(context, modelSpec)],
|
|
260
|
+
build: {
|
|
261
|
+
outDir,
|
|
262
|
+
emptyOutDir: false,
|
|
263
|
+
lib: {
|
|
264
|
+
entry: "./src/index.ts",
|
|
265
|
+
formats: ["es"],
|
|
266
|
+
fileName: () => "check-bundle.js"
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
};
|
|
383
270
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
271
|
+
//#endregion
|
|
272
|
+
//#region src/vite-local-bundles-plugin.ts
|
|
273
|
+
/**
|
|
274
|
+
* Vite plugin that provides a bridge to the model-check report app to allow access
|
|
275
|
+
* to the local bundles directory when running in local development mode.
|
|
276
|
+
*
|
|
277
|
+
* This plugin adds an HMR (Hot Module Replacement) event handler that listens for
|
|
278
|
+
* 'list-bundles' and 'download-bundle' events from the client.
|
|
279
|
+
*
|
|
280
|
+
* Note that when a request fails, the handlers below log only the error message (along
|
|
281
|
+
* with the bundle name and URL) instead of the raw error object, since the latter can
|
|
282
|
+
* produce a long and unhelpful stack trace in the console. The full message is sent
|
|
283
|
+
* back to the client, which reports it in the browser console.
|
|
284
|
+
*
|
|
285
|
+
* @param bundlesDir The absolute path to the bundles directory.
|
|
286
|
+
* @param currentBundlePath The absolute path to the current bundle file.
|
|
287
|
+
* @param fetchRemoteBundle Optional function for fetching remote bundle files.
|
|
288
|
+
*/
|
|
396
289
|
function localBundlesPlugin(bundlesDir, currentBundlePath, fetchRemoteBundle) {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
290
|
+
return {
|
|
291
|
+
name: "sde-local-bundles",
|
|
292
|
+
configureServer(server) {
|
|
293
|
+
const watcher = chokidar.watch(bundlesDir, {
|
|
294
|
+
ignoreInitial: true,
|
|
295
|
+
awaitWriteFinish: { stabilityThreshold: 200 },
|
|
296
|
+
depth: 10
|
|
297
|
+
});
|
|
298
|
+
watcher.on("all", (event) => {
|
|
299
|
+
if (event === "add" || event === "unlink") server.ws.send("bundles-changed", {});
|
|
300
|
+
});
|
|
301
|
+
server.httpServer?.on("close", () => {
|
|
302
|
+
watcher.close();
|
|
303
|
+
});
|
|
304
|
+
server.ws.on("list-bundles", async (_, client) => {
|
|
305
|
+
try {
|
|
306
|
+
const bundles = await scanBundlesRecursively(bundlesDir, bundlesDir);
|
|
307
|
+
const currentBundleStats = await stat(currentBundlePath);
|
|
308
|
+
bundles.push({
|
|
309
|
+
name: "current",
|
|
310
|
+
url: "current",
|
|
311
|
+
lastModified: currentBundleStats.mtime.toISOString()
|
|
312
|
+
});
|
|
313
|
+
client.send("list-bundles-success", { bundles });
|
|
314
|
+
} catch (error) {
|
|
315
|
+
console.error(`[sde-local-bundles] Failed to list bundles in ${bundlesDir}: ${error.message}`);
|
|
316
|
+
client.send("list-bundles-error", { error: error.message });
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
server.ws.on("load-bundle", async (data, client) => {
|
|
320
|
+
const { url, name } = data;
|
|
321
|
+
try {
|
|
322
|
+
let sourceCode;
|
|
323
|
+
if (url.startsWith("file://")) {
|
|
324
|
+
const filePath = fileURLToPath$1(url);
|
|
325
|
+
sourceCode = await readFile(filePath, "utf-8");
|
|
326
|
+
} else if (url.startsWith("https://") || url.startsWith("http://")) {
|
|
327
|
+
const fullUrl = `${url}?cb=${Date.now()}`;
|
|
328
|
+
if (fetchRemoteBundle) sourceCode = await fetchRemoteBundle(fullUrl);
|
|
329
|
+
else {
|
|
330
|
+
const response = await fetch(fullUrl);
|
|
331
|
+
if (!response.ok) throw new Error(`Failed to fetch bundle: ${response.status} ${response.statusText}`);
|
|
332
|
+
sourceCode = await response.text();
|
|
333
|
+
}
|
|
334
|
+
} else throw new Error(`Unsupported URL scheme: ${url}`);
|
|
335
|
+
client.send("load-bundle-success", {
|
|
336
|
+
name,
|
|
337
|
+
url,
|
|
338
|
+
sourceCode
|
|
339
|
+
});
|
|
340
|
+
} catch (error) {
|
|
341
|
+
console.error(`[sde-local-bundles] Failed to load bundle '${name}' from ${url}: ${error.message}`);
|
|
342
|
+
client.send("load-bundle-error", {
|
|
343
|
+
name,
|
|
344
|
+
url,
|
|
345
|
+
error: error.message
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
server.ws.on("download-bundle", async (data, client) => {
|
|
350
|
+
const { url, name, lastModified } = data;
|
|
351
|
+
try {
|
|
352
|
+
console.log(`[sde-local-bundles] Downloading bundle: name=${name} url=${url}`);
|
|
353
|
+
const filePath = await downloadBundle(url, name, lastModified, bundlesDir, fetchRemoteBundle);
|
|
354
|
+
console.log(`[sde-local-bundles] Downloaded bundle to ${filePath}`);
|
|
355
|
+
client.send("download-bundle-success", {
|
|
356
|
+
name,
|
|
357
|
+
filePath: `${name}.js`
|
|
358
|
+
});
|
|
359
|
+
} catch (error) {
|
|
360
|
+
console.error(`[sde-local-bundles] Failed to download bundle '${name}' from ${url}: ${error.message}`);
|
|
361
|
+
client.send("download-bundle-error", {
|
|
362
|
+
name,
|
|
363
|
+
error: error.message
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
server.ws.on("copy-bundle", async (data, client) => {
|
|
368
|
+
const { url, name, newName } = data;
|
|
369
|
+
try {
|
|
370
|
+
console.log(`[sde-local-bundles] Copying bundle: src=${name} dst=${newName}`);
|
|
371
|
+
const filePath = await copyBundle(url, newName, bundlesDir);
|
|
372
|
+
console.log(`[sde-local-bundles] Copied bundle to ${filePath}`);
|
|
373
|
+
client.send("copy-bundle-success", {
|
|
374
|
+
name: newName,
|
|
375
|
+
filePath: `${newName}.js`
|
|
376
|
+
});
|
|
377
|
+
} catch (error) {
|
|
378
|
+
console.error(`[sde-local-bundles] Failed to copy bundle '${name}' to '${newName}': ${error.message}`);
|
|
379
|
+
client.send("copy-bundle-error", {
|
|
380
|
+
name,
|
|
381
|
+
error: error.message
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
};
|
|
487
387
|
}
|
|
388
|
+
/**
|
|
389
|
+
* Recursively scan a directory for .js files.
|
|
390
|
+
*
|
|
391
|
+
* @param dir The directory to scan.
|
|
392
|
+
* @param baseDir The base directory (used for calculating relative paths).
|
|
393
|
+
* @returns An array of bundle information.
|
|
394
|
+
*/
|
|
488
395
|
async function scanBundlesRecursively(dir, baseDir) {
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
return bundles;
|
|
396
|
+
const bundles = [];
|
|
397
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
398
|
+
for (const entry of entries) {
|
|
399
|
+
const fullPath = join(dir, entry.name);
|
|
400
|
+
if (entry.isDirectory()) {
|
|
401
|
+
const subBundles = await scanBundlesRecursively(fullPath, baseDir);
|
|
402
|
+
bundles.push(...subBundles);
|
|
403
|
+
} else if (entry.isFile() && entry.name.endsWith(".js")) {
|
|
404
|
+
const stats = await stat(fullPath);
|
|
405
|
+
const name = relative(baseDir, fullPath).replace(/\.js$/, "").split(sep).join("/");
|
|
406
|
+
bundles.push({
|
|
407
|
+
name,
|
|
408
|
+
url: pathToFileURL(fullPath).toString(),
|
|
409
|
+
lastModified: stats.mtime.toISOString()
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return bundles;
|
|
508
414
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
415
|
+
//#endregion
|
|
416
|
+
//#region src/vite-config-for-report.ts
|
|
417
|
+
const __filename$1 = fileURLToPath$1(import.meta.url);
|
|
418
|
+
const __dirname$1 = dirname(__filename$1);
|
|
419
|
+
/**
|
|
420
|
+
* NOTE: This function currently only supports creating a Vite config for the
|
|
421
|
+
* model-check report when the current/baseline bundles are local files. If
|
|
422
|
+
* you want to use remote bundles, you must first download them to the local
|
|
423
|
+
* `bundles` directory and then pass `LocalBundleSpec` instances that include
|
|
424
|
+
* the local bundle file paths.
|
|
425
|
+
*/
|
|
513
426
|
function createViteConfigForReport(mode, options, projDir, prepDir, currentBundleSpec, baselineBundleSpec, testConfigPath, suiteSummary) {
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
],
|
|
585
|
-
exclude: [
|
|
586
|
-
// XXX: The threads.js implementation references `tiny-worker` as an optional
|
|
587
|
-
// dependency, but it doesn't get used at runtime, so we can just exclude it
|
|
588
|
-
// so that Vite doesn't complain in dev mode
|
|
589
|
-
"tiny-worker"
|
|
590
|
-
// XXX: Similarly, chart.js treats `moment` as an optional dependency, but we
|
|
591
|
-
// don't use it at runtime; we need to exclude it here, otherwise Vite will
|
|
592
|
-
// complain about missing dependencies in dev mode
|
|
593
|
-
// 'moment'
|
|
594
|
-
]
|
|
595
|
-
},
|
|
596
|
-
// Configure path aliases
|
|
597
|
-
resolve: {
|
|
598
|
-
alias: [
|
|
599
|
-
// Use the configured "baseline" bundle if defined, otherwise use the "empty" bundle
|
|
600
|
-
// (which will cause comparison tests to be skipped)
|
|
601
|
-
alias("@_baseline_bundle_", baselineBundleSpec?.path || "/src/empty-bundle.ts"),
|
|
602
|
-
// Use the configured "current" bundle
|
|
603
|
-
alias("@_current_bundle_", currentBundleSpec.path),
|
|
604
|
-
// Use the configured test config file
|
|
605
|
-
alias("@_test_config_", testConfigPath),
|
|
606
|
-
// Make the overlay use the `messages.html` file that is written to the prep directory
|
|
607
|
-
alias("@_prep_", prepDir),
|
|
608
|
-
// XXX: Include no-op polyfills for these modules that are used in the Node-specific
|
|
609
|
-
// implementation of threads.js; this allows us to use one bundle that works in both
|
|
610
|
-
// Node and browser environments
|
|
611
|
-
noopPolyfillAlias("events"),
|
|
612
|
-
noopPolyfillAlias("fs"),
|
|
613
|
-
noopPolyfillAlias("os"),
|
|
614
|
-
noopPolyfillAlias("path"),
|
|
615
|
-
noopPolyfillAlias("url"),
|
|
616
|
-
noopPolyfillAlias("worker_threads")
|
|
617
|
-
]
|
|
618
|
-
},
|
|
619
|
-
// Inject special values into the generated JS
|
|
620
|
-
define: {
|
|
621
|
-
// Inject the summary JSON into the build
|
|
622
|
-
__SUITE_SUMMARY_JSON__: JSON.stringify(suiteSummaryJson),
|
|
623
|
-
// Inject the baseline bundle name
|
|
624
|
-
__BASELINE_NAME__: JSON.stringify(baselineBundleSpec?.name || ""),
|
|
625
|
-
// Inject the current bundle name
|
|
626
|
-
__CURRENT_NAME__: JSON.stringify(currentBundleSpec.name),
|
|
627
|
-
// Inject the remote bundles URL
|
|
628
|
-
__REMOTE_BUNDLES_URL__: JSON.stringify(options?.remoteBundlesUrl || "")
|
|
629
|
-
},
|
|
630
|
-
plugins: [
|
|
631
|
-
// Inject special values into the generated JS
|
|
632
|
-
// TODO: We currently have to use `@rollup/plugin-replace` instead of Vite's
|
|
633
|
-
// built-in `define` feature because the latter does not seem to run before
|
|
634
|
-
// the glob handler (which requires the glob to be injected as a literal)
|
|
635
|
-
replace({
|
|
636
|
-
preventAssignment: true,
|
|
637
|
-
delimiters: ["", ""],
|
|
638
|
-
values: {
|
|
639
|
-
// Inject the path for baseline bundles
|
|
640
|
-
// XXX: Note that we use './bundles/**/*.txt' instead of something special
|
|
641
|
-
// like './__BASELINE_BUNDLES_PATH__' because sometimes Vite's dependency
|
|
642
|
-
// scanner sees the latter (instead of the injected path) and reports
|
|
643
|
-
// an error since the path does not exist. As a workaround, we use
|
|
644
|
-
// './bundles/**/*.txt', which gets interpreted as the valid path
|
|
645
|
-
// '.../template-report/src/bundles/**/*.txt' (see `bundles/unused.txt`).
|
|
646
|
-
"./bundles/**/*.txt": bundlesPath
|
|
647
|
-
}
|
|
648
|
-
}),
|
|
649
|
-
// When local development mode is active, enable the local bundles plugin that
|
|
650
|
-
// allows the report app to access the local bundles directory
|
|
651
|
-
...mode === "watch" ? [localBundlesPlugin(bundlesDir, currentBundleSpec.path, options?.fetchRemoteBundle)] : []
|
|
652
|
-
],
|
|
653
|
-
build: {
|
|
654
|
-
// Write output files to the configured directory (instead of the default `dist`);
|
|
655
|
-
// note that this must be relative to the project `root`
|
|
656
|
-
outDir,
|
|
657
|
-
// Write js/css files to `public` (instead of the default `<outDir>/assets`)
|
|
658
|
-
assetsDir: "",
|
|
659
|
-
rollupOptions: {
|
|
660
|
-
output: {
|
|
661
|
-
// XXX: Prevent vite from creating a separate `vendor.js` file
|
|
662
|
-
manualChunks: void 0
|
|
663
|
-
},
|
|
664
|
-
onwarn: (warning, warn) => {
|
|
665
|
-
if (warning.code !== "EVAL") {
|
|
666
|
-
warn(warning);
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
},
|
|
671
|
-
server: {
|
|
672
|
-
// Run the dev server at `localhost:8081` by default
|
|
673
|
-
port: options?.serverPort || 8081,
|
|
674
|
-
// Open the app in the browser by default
|
|
675
|
-
open: "/index.html",
|
|
676
|
-
// XXX: Add a small delay, otherwise on macOS we sometimes get multiple
|
|
677
|
-
// change events when a file is saved just once. That is a relatively
|
|
678
|
-
// harmless issue except that it causes redundant messages in the console
|
|
679
|
-
// and can cause extra churn when refreshing the app.
|
|
680
|
-
watch: {
|
|
681
|
-
awaitWriteFinish: {
|
|
682
|
-
stabilityThreshold: 100
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
};
|
|
427
|
+
const root = resolve(__dirname$1, "..", "template-report");
|
|
428
|
+
const bundlesDir = resolve(projDir, "bundles");
|
|
429
|
+
if (!existsSync(bundlesDir)) mkdirSync(bundlesDir, { recursive: true });
|
|
430
|
+
let reportPath;
|
|
431
|
+
if (options?.reportPath) reportPath = options.reportPath;
|
|
432
|
+
else reportPath = join(prepDir, "check-report");
|
|
433
|
+
const outDir = relative(root, reportPath);
|
|
434
|
+
const suiteSummaryJson = suiteSummary ? JSON.stringify(suiteSummary) : "";
|
|
435
|
+
const alias = (find, replacement) => {
|
|
436
|
+
return {
|
|
437
|
+
find,
|
|
438
|
+
replacement
|
|
439
|
+
};
|
|
440
|
+
};
|
|
441
|
+
const noopPolyfillAlias = (find) => {
|
|
442
|
+
return {
|
|
443
|
+
find,
|
|
444
|
+
replacement: "/polyfills/noop-polyfills.ts"
|
|
445
|
+
};
|
|
446
|
+
};
|
|
447
|
+
return {
|
|
448
|
+
configFile: false,
|
|
449
|
+
root,
|
|
450
|
+
base: "",
|
|
451
|
+
cacheDir: join(prepDir, ".vite-check-report"),
|
|
452
|
+
clearScreen: false,
|
|
453
|
+
optimizeDeps: {
|
|
454
|
+
entries: ["index.html"],
|
|
455
|
+
include: [
|
|
456
|
+
"@sdeverywhere/check-core > assert-never",
|
|
457
|
+
"@sdeverywhere/check-core > ajv",
|
|
458
|
+
"@sdeverywhere/check-core > neverthrow",
|
|
459
|
+
"@sdeverywhere/check-core > yaml",
|
|
460
|
+
"@sdeverywhere/check-ui-shell > fontfaceobserver",
|
|
461
|
+
"@sdeverywhere/check-ui-shell > copy-text-to-clipboard",
|
|
462
|
+
"@sdeverywhere/check-ui-shell > chart.js"
|
|
463
|
+
],
|
|
464
|
+
exclude: []
|
|
465
|
+
},
|
|
466
|
+
resolve: { alias: [
|
|
467
|
+
alias("@_baseline_bundle_", baselineBundleSpec?.path || "/src/empty-bundle.ts"),
|
|
468
|
+
alias("@_current_bundle_", currentBundleSpec.path),
|
|
469
|
+
alias("@_test_config_", testConfigPath),
|
|
470
|
+
alias("@_prep_", prepDir),
|
|
471
|
+
noopPolyfillAlias("events"),
|
|
472
|
+
noopPolyfillAlias("fs"),
|
|
473
|
+
noopPolyfillAlias("os"),
|
|
474
|
+
noopPolyfillAlias("path"),
|
|
475
|
+
noopPolyfillAlias("url"),
|
|
476
|
+
noopPolyfillAlias("worker_threads"),
|
|
477
|
+
noopPolyfillAlias("tiny-worker")
|
|
478
|
+
] },
|
|
479
|
+
define: {
|
|
480
|
+
__SUITE_SUMMARY_JSON__: JSON.stringify(suiteSummaryJson),
|
|
481
|
+
__BASELINE_NAME__: JSON.stringify(baselineBundleSpec?.name || ""),
|
|
482
|
+
__CURRENT_NAME__: JSON.stringify(currentBundleSpec.name),
|
|
483
|
+
__REMOTE_BUNDLES_URL__: JSON.stringify(options?.remoteBundlesUrl || "")
|
|
484
|
+
},
|
|
485
|
+
plugins: [...mode === "watch" ? [localBundlesPlugin(bundlesDir, currentBundleSpec.path, options?.fetchRemoteBundle)] : []],
|
|
486
|
+
build: {
|
|
487
|
+
outDir,
|
|
488
|
+
assetsDir: "",
|
|
489
|
+
rolldownOptions: { checks: { eval: false } }
|
|
490
|
+
},
|
|
491
|
+
server: {
|
|
492
|
+
port: options?.serverPort || 8081,
|
|
493
|
+
open: "/index.html",
|
|
494
|
+
watch: { awaitWriteFinish: { stabilityThreshold: 100 } }
|
|
495
|
+
}
|
|
496
|
+
};
|
|
687
497
|
}
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
498
|
+
//#endregion
|
|
499
|
+
//#region src/vite-inject-literals-plugin.ts
|
|
500
|
+
/**
|
|
501
|
+
* Return a Vite plugin that replaces literal strings in source files before
|
|
502
|
+
* Vite's own transforms run.
|
|
503
|
+
*
|
|
504
|
+
* This is a minimal replacement for `@rollup/plugin-replace` (as previously
|
|
505
|
+
* configured with empty delimiters, i.e., plain string substitution). We use
|
|
506
|
+
* this instead of Vite's built-in `define` feature because `define`
|
|
507
|
+
* replacements are not applied before Vite's `import.meta.glob` handler runs,
|
|
508
|
+
* and the glob handler requires the glob pattern to appear as a literal in
|
|
509
|
+
* the source.
|
|
510
|
+
*
|
|
511
|
+
* @param values A map of literal search strings to their replacement strings.
|
|
512
|
+
*/
|
|
513
|
+
function injectLiteralsPlugin(values) {
|
|
514
|
+
const entries = Object.entries(values);
|
|
515
|
+
return {
|
|
516
|
+
name: "vite-plugin-inject-literals",
|
|
517
|
+
transform(code) {
|
|
518
|
+
let transformed = code;
|
|
519
|
+
let changed = false;
|
|
520
|
+
for (const [find, replacement] of entries) if (transformed.includes(find)) {
|
|
521
|
+
transformed = transformed.replaceAll(find, replacement);
|
|
522
|
+
changed = true;
|
|
523
|
+
}
|
|
524
|
+
if (changed) return {
|
|
525
|
+
code: transformed,
|
|
526
|
+
map: null
|
|
527
|
+
};
|
|
528
|
+
else return;
|
|
529
|
+
}
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
//#endregion
|
|
533
|
+
//#region src/vite-config-for-tests.ts
|
|
534
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
535
|
+
const __dirname = dirname$1(__filename);
|
|
695
536
|
function createViteConfigForTests(mode, projDir, prepDir) {
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
// Inject the glob patterns for matching model check yaml files
|
|
722
|
-
'"./__YAML_CHECK_GLOB_PATTERNS__"': yamlCheckGlobPatterns,
|
|
723
|
-
// Inject the glob patterns for matching model comparison yaml files
|
|
724
|
-
'"./__YAML_COMPARISON_GLOB_PATTERNS__"': yamlComparisonGlobPatterns
|
|
725
|
-
}
|
|
726
|
-
})
|
|
727
|
-
],
|
|
728
|
-
build: {
|
|
729
|
-
// Write output files to the configured directory (instead of the default `dist`);
|
|
730
|
-
// note that this must be relative to the project `root`
|
|
731
|
-
outDir,
|
|
732
|
-
emptyOutDir: false,
|
|
733
|
-
lib: {
|
|
734
|
-
entry: "./src/index.ts",
|
|
735
|
-
formats: ["es"],
|
|
736
|
-
fileName: () => "check-tests.js"
|
|
737
|
-
},
|
|
738
|
-
// Enable watch mode if requested
|
|
739
|
-
watch: mode === "watch" && {},
|
|
740
|
-
rollupOptions: {
|
|
741
|
-
// Prevent dependencies from being included in packaged library
|
|
742
|
-
// TODO: For now we include check-core in the packaged library so that its
|
|
743
|
-
// dependencies are correctly resolved at runtime. Ideally this would only
|
|
744
|
-
// include a couple functions that are used for defining tests, but Vite 2.x
|
|
745
|
-
// does not implement tree shaking for ES libraries, which means the generated
|
|
746
|
-
// library is much larger than it needs to be. Once we upgrade to Vite 3.x,
|
|
747
|
-
// the generated library should be smaller; see related fix:
|
|
748
|
-
// https://github.com/vitejs/vite/pull/8737
|
|
749
|
-
// external: Object.keys(pkg.dependencies)
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
};
|
|
537
|
+
const root = resolve$1(__dirname, "..", "template-tests");
|
|
538
|
+
const templateSrcDir = resolve$1(root, "src");
|
|
539
|
+
const relProjDirPath = relative$1(templateSrcDir, projDir).replaceAll("\\", "/");
|
|
540
|
+
const yamlCheckGlobPatterns = `['${relProjDirPath}/**/checks/*.yaml', '${relProjDirPath}/**/*.check.yaml']`;
|
|
541
|
+
const yamlComparisonGlobPatterns = `['${relProjDirPath}/**/comparisons/*.yaml']`;
|
|
542
|
+
const outDir = relative$1(root, prepDir);
|
|
543
|
+
return {
|
|
544
|
+
configFile: false,
|
|
545
|
+
root,
|
|
546
|
+
clearScreen: false,
|
|
547
|
+
plugins: [injectLiteralsPlugin({
|
|
548
|
+
"\"./__YAML_CHECK_GLOB_PATTERNS__\"": yamlCheckGlobPatterns,
|
|
549
|
+
"\"./__YAML_COMPARISON_GLOB_PATTERNS__\"": yamlComparisonGlobPatterns
|
|
550
|
+
})],
|
|
551
|
+
build: {
|
|
552
|
+
outDir,
|
|
553
|
+
emptyOutDir: false,
|
|
554
|
+
lib: {
|
|
555
|
+
entry: "./src/index.ts",
|
|
556
|
+
formats: ["es"],
|
|
557
|
+
fileName: () => "check-tests.js"
|
|
558
|
+
},
|
|
559
|
+
watch: mode === "watch" && {}
|
|
560
|
+
}
|
|
561
|
+
};
|
|
753
562
|
}
|
|
754
|
-
|
|
755
|
-
|
|
563
|
+
//#endregion
|
|
564
|
+
//#region src/plugin.ts
|
|
756
565
|
function checkPlugin(options) {
|
|
757
|
-
|
|
566
|
+
return new CheckPlugin(options);
|
|
758
567
|
}
|
|
759
568
|
var CheckPlugin = class {
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
return {
|
|
876
|
-
name: bundle.name,
|
|
877
|
-
path: localBundlePath
|
|
878
|
-
};
|
|
879
|
-
} else if (bundle?.path !== void 0) {
|
|
880
|
-
return {
|
|
881
|
-
name: bundle.name,
|
|
882
|
-
path: bundle.path
|
|
883
|
-
};
|
|
884
|
-
} else {
|
|
885
|
-
return {
|
|
886
|
-
name: bundle?.name || "current",
|
|
887
|
-
path: joinPath5(config.prepDir, "check-bundle.js")
|
|
888
|
-
};
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
const currentBundleSpec = await resolveBundle(this.options?.current);
|
|
892
|
-
let baselineBundleSpec;
|
|
893
|
-
if (mode === "bundle" && this.options?.baseline) {
|
|
894
|
-
try {
|
|
895
|
-
baselineBundleSpec = await resolveBundle(this.options.baseline);
|
|
896
|
-
} catch (e) {
|
|
897
|
-
const name = this.options.baseline.name;
|
|
898
|
-
const loc = this.options.baseline.url || this.options.baseline.path;
|
|
899
|
-
console.warn(
|
|
900
|
-
`WARNING: Failed to load '${name}' bundle from '${loc}'; check tests will be run but comparisons will be skipped. Cause:`,
|
|
901
|
-
e
|
|
902
|
-
);
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
let testConfigPath;
|
|
906
|
-
if (this.options?.testConfigPath === void 0) {
|
|
907
|
-
testConfigPath = joinPath5(config.prepDir, "check-tests.js");
|
|
908
|
-
} else {
|
|
909
|
-
testConfigPath = this.options.testConfigPath;
|
|
910
|
-
}
|
|
911
|
-
return {
|
|
912
|
-
currentBundleSpec,
|
|
913
|
-
baselineBundleSpec,
|
|
914
|
-
testConfigPath
|
|
915
|
-
};
|
|
916
|
-
}
|
|
917
|
-
async createViteConfigForReport(mode, config, testOptions, suiteSummary) {
|
|
918
|
-
return createViteConfigForReport(
|
|
919
|
-
mode,
|
|
920
|
-
this.options,
|
|
921
|
-
config.rootDir,
|
|
922
|
-
config.prepDir,
|
|
923
|
-
testOptions.currentBundleSpec,
|
|
924
|
-
testOptions.baselineBundleSpec,
|
|
925
|
-
testOptions.testConfigPath,
|
|
926
|
-
suiteSummary
|
|
927
|
-
);
|
|
928
|
-
}
|
|
569
|
+
constructor(options) {
|
|
570
|
+
this.options = options;
|
|
571
|
+
this.firstBuild = true;
|
|
572
|
+
}
|
|
573
|
+
async watch(config) {
|
|
574
|
+
if (this.options?.testConfigPath === void 0) await this.genTestConfig("watch", config);
|
|
575
|
+
const testOptions = await this.resolveTestOptions("watch", config);
|
|
576
|
+
const viteConfig = await this.createViteConfigForReport("watch", config, testOptions, void 0);
|
|
577
|
+
await (await createServer(viteConfig)).listen();
|
|
578
|
+
}
|
|
579
|
+
async postBuild(context, modelSpec) {
|
|
580
|
+
const firstBuild = this.firstBuild;
|
|
581
|
+
this.firstBuild = false;
|
|
582
|
+
if (this.options?.current?.path === void 0 && this.options?.current?.url === void 0) {
|
|
583
|
+
if (context.config.mode === "development") await this.copyPreviousBundle(context.config);
|
|
584
|
+
context.log("info", "Generating model check bundle...");
|
|
585
|
+
await this.genCurrentBundle(context, modelSpec);
|
|
586
|
+
}
|
|
587
|
+
if (this.options?.testConfigPath === void 0) {
|
|
588
|
+
if (context.config.mode === "production" || firstBuild) {
|
|
589
|
+
context.log("info", "Generating model check test configuration...");
|
|
590
|
+
await this.genTestConfig("bundle", context.config);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
if (context.config.mode === "production") {
|
|
594
|
+
const testOptions = await this.resolveTestOptions("bundle", context.config);
|
|
595
|
+
return this.runChecks(context, testOptions);
|
|
596
|
+
} else return true;
|
|
597
|
+
}
|
|
598
|
+
async copyPreviousBundle(config) {
|
|
599
|
+
const currentBundleFile = join(config.prepDir, "check-bundle.js");
|
|
600
|
+
if (existsSync(currentBundleFile)) {
|
|
601
|
+
const bundlesDir = join(config.rootDir, "bundles");
|
|
602
|
+
if (!existsSync(bundlesDir)) await mkdir(bundlesDir, { recursive: true });
|
|
603
|
+
const previousBundleFile = join(bundlesDir, "previous.js");
|
|
604
|
+
await copyFile(currentBundleFile, previousBundleFile);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
async genCurrentBundle(context, modelSpec) {
|
|
608
|
+
const viteConfig = await createViteConfigForBundle(context, modelSpec);
|
|
609
|
+
await build(viteConfig);
|
|
610
|
+
}
|
|
611
|
+
async genTestConfig(mode, config) {
|
|
612
|
+
const rootDir = config.rootDir;
|
|
613
|
+
const prepDir = config.prepDir;
|
|
614
|
+
const viteConfig = createViteConfigForTests(mode, rootDir, prepDir);
|
|
615
|
+
await build(viteConfig);
|
|
616
|
+
}
|
|
617
|
+
async runChecks(context, testOptions) {
|
|
618
|
+
context.log("info", "Running model checks...");
|
|
619
|
+
async function importBundleModule(bundleSpec) {
|
|
620
|
+
return import(relativeToSourcePath(bundleSpec.path));
|
|
621
|
+
}
|
|
622
|
+
const bundleR = (await importBundleModule(testOptions.currentBundleSpec)).createBundle();
|
|
623
|
+
const bundleNameR = testOptions.currentBundleSpec.name;
|
|
624
|
+
let bundleL;
|
|
625
|
+
let bundleNameL;
|
|
626
|
+
if (testOptions.baselineBundleSpec !== void 0) {
|
|
627
|
+
const rawBundleL = (await importBundleModule(testOptions.baselineBundleSpec)).createBundle();
|
|
628
|
+
if (rawBundleL.version === bundleR.version) {
|
|
629
|
+
bundleL = rawBundleL;
|
|
630
|
+
bundleNameL = testOptions.baselineBundleSpec.name || "base";
|
|
631
|
+
} else console.warn(`WARNING: Bundle version mismatch (baseline=${rawBundleL.version} current=${bundleR.version}); check tests will be run but comparisons will be skipped`);
|
|
632
|
+
}
|
|
633
|
+
const testConfigModule = await import(relativeToSourcePath(testOptions.testConfigPath));
|
|
634
|
+
const configInitOptions = {
|
|
635
|
+
bundleNameL,
|
|
636
|
+
bundleNameR
|
|
637
|
+
};
|
|
638
|
+
const configOptions = await testConfigModule.getConfigOptions(bundleL, bundleR, configInitOptions);
|
|
639
|
+
const result = await runTestSuite(context, await createConfig(configOptions), false);
|
|
640
|
+
context.log("info", "Building model check report");
|
|
641
|
+
const viteConfig = await this.createViteConfigForReport("bundle", context.config, testOptions, result.suiteSummary);
|
|
642
|
+
await build(viteConfig);
|
|
643
|
+
return result.allChecksPassed;
|
|
644
|
+
}
|
|
645
|
+
async resolveTestOptions(mode, config) {
|
|
646
|
+
const fetchRemoteBundle = this.options?.fetchRemoteBundle;
|
|
647
|
+
async function resolveBundle(bundle) {
|
|
648
|
+
if (bundle?.url !== void 0) {
|
|
649
|
+
const localBundlePath = await downloadBundle(bundle.url, bundle.name, void 0, join(config.rootDir, "bundles"), fetchRemoteBundle);
|
|
650
|
+
return {
|
|
651
|
+
name: bundle.name,
|
|
652
|
+
path: localBundlePath
|
|
653
|
+
};
|
|
654
|
+
} else if (bundle?.path !== void 0) return {
|
|
655
|
+
name: bundle.name,
|
|
656
|
+
path: bundle.path
|
|
657
|
+
};
|
|
658
|
+
else return {
|
|
659
|
+
name: bundle?.name || "current",
|
|
660
|
+
path: join(config.prepDir, "check-bundle.js")
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
const currentBundleSpec = await resolveBundle(this.options?.current);
|
|
664
|
+
let baselineBundleSpec;
|
|
665
|
+
if (mode === "bundle" && this.options?.baseline) try {
|
|
666
|
+
baselineBundleSpec = await resolveBundle(this.options.baseline);
|
|
667
|
+
} catch (e) {
|
|
668
|
+
const name = this.options.baseline.name;
|
|
669
|
+
const loc = this.options.baseline.url || this.options.baseline.path;
|
|
670
|
+
console.warn(`WARNING: Failed to load '${name}' bundle from '${loc}'; check tests will be run but comparisons will be skipped. Cause:`, e);
|
|
671
|
+
}
|
|
672
|
+
let testConfigPath;
|
|
673
|
+
if (this.options?.testConfigPath === void 0) testConfigPath = join(config.prepDir, "check-tests.js");
|
|
674
|
+
else testConfigPath = this.options.testConfigPath;
|
|
675
|
+
return {
|
|
676
|
+
currentBundleSpec,
|
|
677
|
+
baselineBundleSpec,
|
|
678
|
+
testConfigPath
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
async createViteConfigForReport(mode, config, testOptions, suiteSummary) {
|
|
682
|
+
return createViteConfigForReport(mode, this.options, config.rootDir, config.prepDir, testOptions.currentBundleSpec, testOptions.baselineBundleSpec, testOptions.testConfigPath, suiteSummary);
|
|
683
|
+
}
|
|
929
684
|
};
|
|
685
|
+
/**
|
|
686
|
+
* Return a Unix-style path (e.g. '../../foo.js') that is relative to the directory of
|
|
687
|
+
* the current source file. This can be used to construct a path that is safe for
|
|
688
|
+
* dynamic import on either Unix or Windows.
|
|
689
|
+
*
|
|
690
|
+
* @param filePath The path to make relative.
|
|
691
|
+
*/
|
|
930
692
|
function relativeToSourcePath(filePath) {
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
return relPath.replaceAll("\\", "/");
|
|
693
|
+
const srcDir = dirname(fileURLToPath(import.meta.url));
|
|
694
|
+
return relative(srcDir, filePath).replaceAll("\\", "/");
|
|
934
695
|
}
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
696
|
+
//#endregion
|
|
697
|
+
export { checkPlugin };
|
|
698
|
+
|
|
938
699
|
//# sourceMappingURL=index.js.map
|