webanvil 0.0.6 → 0.0.7
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/LICENSE +21 -21
- package/README.md +490 -38
- package/bin/webanvil +6 -0
- package/bin/webanvil.cmd +2 -0
- package/dist/_chunks/commands.mjs +1867 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +30 -0
- package/dist/index.d.mts +646 -0
- package/dist/index.mjs +5 -0
- package/package.json +96 -60
- package/bin/run +0 -3
- package/bin/run.cmd +0 -3
- package/lib/commands/build.d.ts +0 -3
- package/lib/commands/build.d.ts.map +0 -1
- package/lib/commands/build.js +0 -34
- package/lib/commands/serve.d.ts +0 -3
- package/lib/commands/serve.d.ts.map +0 -1
- package/lib/commands/serve.js +0 -44
- package/lib/commons/io/core/File.d.ts +0 -11
- package/lib/commons/io/core/File.d.ts.map +0 -1
- package/lib/commons/io/core/File.js +0 -23
- package/lib/commons/io/core/Path.d.ts +0 -30
- package/lib/commons/io/core/Path.d.ts.map +0 -1
- package/lib/commons/io/core/Path.js +0 -122
- package/lib/commons/io/sync/fs.d.ts +0 -11
- package/lib/commons/io/sync/fs.d.ts.map +0 -1
- package/lib/commons/io/sync/fs.js +0 -46
- package/lib/commons/io/sync/index.d.ts +0 -5
- package/lib/commons/io/sync/index.d.ts.map +0 -1
- package/lib/commons/io/sync/index.js +0 -9
- package/lib/commons/io/sync/path.d.ts +0 -4
- package/lib/commons/io/sync/path.d.ts.map +0 -1
- package/lib/commons/io/sync/path.js +0 -13
- package/lib/core/Configuration.d.ts +0 -37
- package/lib/core/Configuration.d.ts.map +0 -1
- package/lib/core/Configuration.js +0 -56
- package/lib/core/EventEmitter.d.ts +0 -14
- package/lib/core/EventEmitter.d.ts.map +0 -1
- package/lib/core/EventEmitter.js +0 -42
- package/lib/core/Page.d.ts +0 -14
- package/lib/core/Page.d.ts.map +0 -1
- package/lib/core/Page.js +0 -34
- package/lib/core/Plugin.d.ts +0 -13
- package/lib/core/Plugin.d.ts.map +0 -1
- package/lib/core/Plugin.js +0 -12
- package/lib/core/Renderer/Renderer.d.ts +0 -8
- package/lib/core/Renderer/Renderer.d.ts.map +0 -1
- package/lib/core/Renderer/Renderer.js +0 -9
- package/lib/core/Renderer/index.d.ts +0 -8
- package/lib/core/Renderer/index.d.ts.map +0 -1
- package/lib/core/Renderer/index.js +0 -18
- package/lib/index.d.ts +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js +0 -1
- package/lib/main.d.ts +0 -2
- package/lib/main.d.ts.map +0 -1
- package/lib/main.js +0 -36
- package/lib/plugins/outlinecss.d.ts +0 -4
- package/lib/plugins/outlinecss.d.ts.map +0 -1
- package/lib/plugins/outlinecss.js +0 -34
- package/lib/renderers/EJSRenderer.d.ts +0 -7
- package/lib/renderers/EJSRenderer.d.ts.map +0 -1
- package/lib/renderers/EJSRenderer.js +0 -34
|
@@ -0,0 +1,1867 @@
|
|
|
1
|
+
import { Module, createRequire, isBuiltin } from "node:module";
|
|
2
|
+
import { defineArgument, defineCommand, defineOption } from "cmdore";
|
|
3
|
+
import { dirname, isAbsolute, join, relative, resolve } from "pathe";
|
|
4
|
+
import { glob } from "tinyglobby";
|
|
5
|
+
import { access, copyFile, lstat, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, rmdir, writeFile } from "node:fs/promises";
|
|
6
|
+
import { loadConfig } from "c12";
|
|
7
|
+
import { defu } from "defu";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { dirname as dirname$1, isAbsolute as isAbsolute$1, join as join$1, relative as relative$1, resolve as resolve$1 } from "node:path";
|
|
10
|
+
import { getTsconfig, readTsconfig } from "get-tsconfig";
|
|
11
|
+
import { dts } from "rolldown-plugin-dts";
|
|
12
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
13
|
+
import { resolvePath } from "mlly";
|
|
14
|
+
import { parse } from "yaml";
|
|
15
|
+
import { resolvePackageJSON } from "pkg-types";
|
|
16
|
+
import { consola } from "consola";
|
|
17
|
+
import { randomUUID } from "node:crypto";
|
|
18
|
+
import { execa } from "execa";
|
|
19
|
+
const entry = defineArgument({
|
|
20
|
+
name: "entry",
|
|
21
|
+
description: "Web entry or Node public root; unbundled Node builds emit its reachable graph with preserveModules."
|
|
22
|
+
});
|
|
23
|
+
const filters = defineArgument({
|
|
24
|
+
name: "filters",
|
|
25
|
+
description: "Test files or names to run.",
|
|
26
|
+
variadic: true
|
|
27
|
+
});
|
|
28
|
+
const paths = defineArgument({
|
|
29
|
+
name: "paths",
|
|
30
|
+
description: "Files or directories to check.",
|
|
31
|
+
variadic: true
|
|
32
|
+
});
|
|
33
|
+
const hasFile = async (path) => access(path).then(() => true).catch(() => false);
|
|
34
|
+
const hasToolConfig = async (name, cwd = process.cwd()) => {
|
|
35
|
+
const files = await readdir(cwd).catch(() => []);
|
|
36
|
+
return [
|
|
37
|
+
"js",
|
|
38
|
+
"mjs",
|
|
39
|
+
"cjs",
|
|
40
|
+
"ts",
|
|
41
|
+
"mts",
|
|
42
|
+
"cts"
|
|
43
|
+
].some((extension) => files.includes(`${name}.config.${extension}`));
|
|
44
|
+
};
|
|
45
|
+
const hasOxcConfig = (name, cwd = process.cwd()) => hasFile(join(cwd, name === "oxfmt" ? ".oxfmtrc.json" : ".oxlintrc.json"));
|
|
46
|
+
const NODE_PLUGIN_ERROR = "Node builds require plugins created with definePlugin()";
|
|
47
|
+
const definePlugin = (plugin, options) => ({
|
|
48
|
+
rolldown: () => plugin.rolldown(options),
|
|
49
|
+
vite: () => plugin.vite(options)
|
|
50
|
+
});
|
|
51
|
+
const isUnpluginAdapter = (plugin) => typeof plugin === "object" && plugin !== null && "rolldown" in plugin && typeof plugin.rolldown === "function" && "vite" in plugin && typeof plugin.vite === "function";
|
|
52
|
+
const isWebAnvilPlugin = (plugin) => Array.isArray(plugin) || typeof plugin === "function" || typeof plugin === "object" && plugin !== null && "name" in plugin || isUnpluginAdapter(plugin);
|
|
53
|
+
const resolveRolldownPlugins = (plugins) => plugins.flatMap((plugin) => {
|
|
54
|
+
if (!isUnpluginAdapter(plugin)) throw new Error(NODE_PLUGIN_ERROR);
|
|
55
|
+
return plugin.rolldown();
|
|
56
|
+
});
|
|
57
|
+
const resolveVitePlugins = (plugins) => plugins.map((plugin) => isUnpluginAdapter(plugin) ? plugin.vite() : plugin);
|
|
58
|
+
const copyMappingSchema = z.strictObject({
|
|
59
|
+
from: z.string().min(1),
|
|
60
|
+
to: z.string().min(1)
|
|
61
|
+
});
|
|
62
|
+
const legacyPlatformTarget = (target) => (typeof target === "string" ? [target] : target).find((value) => value === "browser" || value === "neutral");
|
|
63
|
+
const assertSyntaxTarget = (target) => {
|
|
64
|
+
if (target === void 0) return;
|
|
65
|
+
const legacy = legacyPlatformTarget(target);
|
|
66
|
+
if (legacy !== void 0) throw new Error(`build.target no longer selects a platform; use build.platform: "${legacy}" instead`);
|
|
67
|
+
};
|
|
68
|
+
const syntaxTargetSchema = z.union([z.string().min(1), z.array(z.string().min(1)).min(1)]).superRefine((target, context) => {
|
|
69
|
+
const legacy = legacyPlatformTarget(target);
|
|
70
|
+
if (legacy !== void 0) context.addIssue({
|
|
71
|
+
code: "custom",
|
|
72
|
+
message: `build.target no longer selects a platform; use build.platform: "${legacy}" instead`
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
const nativeConfigSchema = () => z.custom((value) => typeof value === "object" && value !== null && !Array.isArray(value), "Expected a configuration object");
|
|
76
|
+
const buildConfigSchema = z.strictObject({
|
|
77
|
+
bundle: z.boolean().optional(),
|
|
78
|
+
mode: z.enum(["web", "node"]).optional(),
|
|
79
|
+
entry: z.string().min(1).optional(),
|
|
80
|
+
entries: z.record(z.string().min(1), z.string().min(1)).optional(),
|
|
81
|
+
outDir: z.string().min(1).optional(),
|
|
82
|
+
declaration: z.union([z.boolean(), nativeConfigSchema()]).optional(),
|
|
83
|
+
sourcemap: z.boolean().optional(),
|
|
84
|
+
minify: z.boolean().optional(),
|
|
85
|
+
copy: z.array(copyMappingSchema).optional(),
|
|
86
|
+
formats: z.array(z.enum(["esm", "cjs"])).min(1).optional(),
|
|
87
|
+
platform: z.enum([
|
|
88
|
+
"node",
|
|
89
|
+
"browser",
|
|
90
|
+
"neutral"
|
|
91
|
+
]).optional(),
|
|
92
|
+
target: syntaxTargetSchema.optional()
|
|
93
|
+
});
|
|
94
|
+
const formatConfigSchema = nativeConfigSchema();
|
|
95
|
+
const lintConfigSchema = nativeConfigSchema();
|
|
96
|
+
const rolldownConfigSchema = nativeConfigSchema();
|
|
97
|
+
const testConfigSchema = nativeConfigSchema();
|
|
98
|
+
const viteConfigSchema = nativeConfigSchema();
|
|
99
|
+
const pluginSchema = z.custom(isWebAnvilPlugin, "Expected a Vite plugin or a WebAnvil plugin created with definePlugin()");
|
|
100
|
+
const userConfigSchema = z.strictObject({
|
|
101
|
+
build: buildConfigSchema.optional(),
|
|
102
|
+
format: formatConfigSchema.optional(),
|
|
103
|
+
lint: lintConfigSchema.optional(),
|
|
104
|
+
rolldown: rolldownConfigSchema.optional(),
|
|
105
|
+
test: testConfigSchema.optional(),
|
|
106
|
+
vite: viteConfigSchema.optional(),
|
|
107
|
+
plugins: z.array(pluginSchema).optional()
|
|
108
|
+
});
|
|
109
|
+
const effectiveUserConfigSchema = userConfigSchema.superRefine((config, context) => {
|
|
110
|
+
const build = config.build ?? {};
|
|
111
|
+
if (build.entries !== void 0 && build.mode !== "node") context.addIssue({
|
|
112
|
+
code: "custom",
|
|
113
|
+
path: ["build", "entries"],
|
|
114
|
+
message: "build.entries is only available in Node mode"
|
|
115
|
+
});
|
|
116
|
+
if (build.mode === "web" && build.platform !== void 0) context.addIssue({
|
|
117
|
+
code: "custom",
|
|
118
|
+
path: ["build", "platform"],
|
|
119
|
+
message: "Web builds do not accept build.platform"
|
|
120
|
+
});
|
|
121
|
+
if (build.mode === "node") {
|
|
122
|
+
for (const [index, plugin] of (config.plugins ?? []).entries()) if (!isUnpluginAdapter(plugin)) context.addIssue({
|
|
123
|
+
code: "custom",
|
|
124
|
+
path: ["plugins", index],
|
|
125
|
+
message: NODE_PLUGIN_ERROR
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
const defaultConfig = {
|
|
130
|
+
build: {
|
|
131
|
+
mode: "node",
|
|
132
|
+
entry: "src/index.ts",
|
|
133
|
+
outDir: "dist"
|
|
134
|
+
},
|
|
135
|
+
test: { environment: "node" }
|
|
136
|
+
};
|
|
137
|
+
const toCommandArguments = (config) => Object.fromEntries(Object.entries(config).filter(([, value]) => value !== void 0).map(([key, value]) => [key.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`), value]));
|
|
138
|
+
const defined = (arguments_) => Object.fromEntries(Object.entries(arguments_).filter(([, value]) => value !== void 0));
|
|
139
|
+
const defineConfig = (config) => config;
|
|
140
|
+
const loadConfig$1 = async (cwd = process.cwd()) => {
|
|
141
|
+
const { config, configFile } = await loadConfig({
|
|
142
|
+
name: "webanvil",
|
|
143
|
+
cwd,
|
|
144
|
+
configFile: "webanvil.config",
|
|
145
|
+
packageJson: false,
|
|
146
|
+
rcFile: false
|
|
147
|
+
});
|
|
148
|
+
return {
|
|
149
|
+
config: userConfigSchema.parse(defu(config, defaultConfig)),
|
|
150
|
+
configFile
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
const resolveEffectiveBuildConfig = (config, overrides, explicitEntry) => {
|
|
154
|
+
const build = {
|
|
155
|
+
...config.build,
|
|
156
|
+
...defined(overrides)
|
|
157
|
+
};
|
|
158
|
+
if (explicitEntry) delete build.entries;
|
|
159
|
+
return effectiveUserConfigSchema.parse({
|
|
160
|
+
...config,
|
|
161
|
+
build
|
|
162
|
+
}).build ?? {};
|
|
163
|
+
};
|
|
164
|
+
const withConfig = (select, run) => async (arguments_) => {
|
|
165
|
+
const { config } = await loadConfig$1();
|
|
166
|
+
const selectedConfig = select(config) ?? {};
|
|
167
|
+
return run({
|
|
168
|
+
...toCommandArguments(selectedConfig),
|
|
169
|
+
...defined(arguments_)
|
|
170
|
+
}, selectedConfig, config, arguments_);
|
|
171
|
+
};
|
|
172
|
+
const path = (cwd) => resolve(cwd, ".webanvil", "buildinfo.json");
|
|
173
|
+
const relativeOutput = (file, cwd) => {
|
|
174
|
+
const output = relative(cwd, resolve(cwd, file));
|
|
175
|
+
if (output === "" || output === ".." || output.startsWith("../") || isAbsolute(output)) throw new Error(`Invalid build output: ${file}`);
|
|
176
|
+
return output;
|
|
177
|
+
};
|
|
178
|
+
const assertSafeRemoval = async (file, cwd) => {
|
|
179
|
+
let directory = dirname(resolve(cwd, relativeOutput(file, cwd)));
|
|
180
|
+
while (directory !== resolve(cwd)) {
|
|
181
|
+
try {
|
|
182
|
+
if ((await lstat(directory)).isSymbolicLink()) throw new Error(`Refusing to remove output through symbolic link: ${file}`);
|
|
183
|
+
} catch (error) {
|
|
184
|
+
if (error.code !== "ENOENT") throw error;
|
|
185
|
+
}
|
|
186
|
+
directory = dirname(directory);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
const removeBuildOutputs = async (output, cwd = process.cwd()) => {
|
|
190
|
+
await Promise.all(output.map(async (file) => {
|
|
191
|
+
await assertSafeRemoval(file, cwd);
|
|
192
|
+
await rm(resolve(cwd, file), { force: true });
|
|
193
|
+
}));
|
|
194
|
+
};
|
|
195
|
+
const readBuildInfo = async (cwd = process.cwd()) => {
|
|
196
|
+
try {
|
|
197
|
+
const value = JSON.parse(await readFile(path(cwd), "utf8"));
|
|
198
|
+
if (typeof value !== "object" || value === null || !("output" in value) || !Array.isArray(value.output) || !value.output.every((file) => typeof file === "string")) throw new Error("Expected { output: string[] }");
|
|
199
|
+
return { output: value.output.map((file) => relativeOutput(file, cwd)) };
|
|
200
|
+
} catch (error) {
|
|
201
|
+
if (error.code === "ENOENT") return { output: [] };
|
|
202
|
+
throw new Error(`Invalid .webanvil/buildinfo.json: ${error instanceof Error ? error.message : String(error)}`);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
const writeBuildInfo = async (output, cwd = process.cwd()) => {
|
|
206
|
+
const file = path(cwd);
|
|
207
|
+
const value = { output: [...new Set(output.map((entry) => relativeOutput(entry, cwd)))].sort() };
|
|
208
|
+
await mkdir(dirname(file), { recursive: true });
|
|
209
|
+
await writeFile(`${file}.tmp`, `${JSON.stringify(value, null, 2)}\n`);
|
|
210
|
+
await rename(`${file}.tmp`, file);
|
|
211
|
+
};
|
|
212
|
+
const removeOutputsIn = async (outDir, cwd = process.cwd()) => {
|
|
213
|
+
const info = await readBuildInfo(cwd);
|
|
214
|
+
const directory = resolve(cwd, outDir);
|
|
215
|
+
const output = info.output.filter((file) => {
|
|
216
|
+
const target = resolve(cwd, file);
|
|
217
|
+
return target !== directory && !relative(directory, target).startsWith("../");
|
|
218
|
+
});
|
|
219
|
+
await removeBuildOutputs(output, cwd);
|
|
220
|
+
await Promise.all([...new Set(output.map((file) => dirname(resolve(cwd, file))))].map(async (directory) => {
|
|
221
|
+
while (directory !== resolve(cwd) && directory !== resolve(cwd, outDir)) {
|
|
222
|
+
try {
|
|
223
|
+
await rmdir(directory);
|
|
224
|
+
} catch (error) {
|
|
225
|
+
if (!["ENOENT", "ENOTEMPTY"].includes(error.code ?? "")) throw error;
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
directory = dirname(directory);
|
|
229
|
+
}
|
|
230
|
+
}));
|
|
231
|
+
return { output: info.output.filter((file) => !output.includes(file)) };
|
|
232
|
+
};
|
|
233
|
+
const clearBuildInfo = (cwd = process.cwd()) => writeBuildInfo([], cwd);
|
|
234
|
+
const { satisfies } = createRequire(import.meta.url)("semver");
|
|
235
|
+
const supportedTools = {
|
|
236
|
+
vite: {
|
|
237
|
+
packageName: "vite",
|
|
238
|
+
range: ">=8.1.5 <9"
|
|
239
|
+
},
|
|
240
|
+
vitest: {
|
|
241
|
+
packageName: "vitest",
|
|
242
|
+
range: ">=4.1.10 <5"
|
|
243
|
+
},
|
|
244
|
+
rolldown: {
|
|
245
|
+
packageName: "rolldown",
|
|
246
|
+
range: ">=1.2.0 <2"
|
|
247
|
+
},
|
|
248
|
+
oxlint: {
|
|
249
|
+
packageName: "oxlint",
|
|
250
|
+
range: ">=1.75.0 <2",
|
|
251
|
+
bin: "oxlint"
|
|
252
|
+
},
|
|
253
|
+
oxfmt: {
|
|
254
|
+
packageName: "oxfmt",
|
|
255
|
+
range: ">=0.60.0 <0.61",
|
|
256
|
+
bin: "oxfmt"
|
|
257
|
+
},
|
|
258
|
+
typescript: {
|
|
259
|
+
packageName: "typescript",
|
|
260
|
+
range: ">=5 <7"
|
|
261
|
+
},
|
|
262
|
+
"typescript-native": {
|
|
263
|
+
packageName: "@typescript/native-preview",
|
|
264
|
+
range: ">=7.0.0-dev.20260707.2 <7.0.0",
|
|
265
|
+
bin: "tsgo"
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
const dependencyFields = [
|
|
269
|
+
"dependencies",
|
|
270
|
+
"devDependencies",
|
|
271
|
+
"optionalDependencies",
|
|
272
|
+
"peerDependencies"
|
|
273
|
+
];
|
|
274
|
+
const readManifest = async (path) => {
|
|
275
|
+
const contents = await readFile(path, "utf8");
|
|
276
|
+
const manifest = JSON.parse(contents);
|
|
277
|
+
if (manifest === null || typeof manifest !== "object" || manifest instanceof Array) throw new Error(`Invalid package manifest at ${path}`);
|
|
278
|
+
return manifest;
|
|
279
|
+
};
|
|
280
|
+
const hasOwnDeclaration = (manifest, packageName) => dependencyFields.some((field) => {
|
|
281
|
+
const dependencies = manifest[field];
|
|
282
|
+
return dependencies !== null && typeof dependencies === "object" && !Array.isArray(dependencies) && Object.hasOwn(dependencies, packageName);
|
|
283
|
+
});
|
|
284
|
+
const parentDirectories = function* (start) {
|
|
285
|
+
let directory = resolve$1(start);
|
|
286
|
+
while (true) {
|
|
287
|
+
yield directory;
|
|
288
|
+
const parent = dirname$1(directory);
|
|
289
|
+
if (parent === directory) return;
|
|
290
|
+
directory = parent;
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
const findNearestManifest = async (cwd) => {
|
|
294
|
+
for (const directory of parentDirectories(cwd)) {
|
|
295
|
+
const path = join$1(directory, "package.json");
|
|
296
|
+
try {
|
|
297
|
+
await access(path);
|
|
298
|
+
return {
|
|
299
|
+
directory,
|
|
300
|
+
path
|
|
301
|
+
};
|
|
302
|
+
} catch {}
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
const manifestWorkspacePatterns = (manifest) => {
|
|
306
|
+
if (Array.isArray(manifest.workspaces)) return manifest.workspaces.filter((pattern) => typeof pattern === "string");
|
|
307
|
+
if (manifest.workspaces !== null && typeof manifest.workspaces === "object" && !Array.isArray(manifest.workspaces) && Array.isArray(manifest.workspaces.packages)) return manifest.workspaces.packages.filter((pattern) => typeof pattern === "string");
|
|
308
|
+
return [];
|
|
309
|
+
};
|
|
310
|
+
const pnpmWorkspacePatterns = async (directory) => {
|
|
311
|
+
const path = join$1(directory, "pnpm-workspace.yaml");
|
|
312
|
+
let contents;
|
|
313
|
+
try {
|
|
314
|
+
contents = await readFile(path, "utf8");
|
|
315
|
+
} catch (error) {
|
|
316
|
+
if (error.code === "ENOENT") return [];
|
|
317
|
+
throw error;
|
|
318
|
+
}
|
|
319
|
+
let manifest;
|
|
320
|
+
try {
|
|
321
|
+
manifest = parse(contents);
|
|
322
|
+
} catch (error) {
|
|
323
|
+
throw new Error(`Invalid pnpm workspace manifest at ${path}`, { cause: error });
|
|
324
|
+
}
|
|
325
|
+
if (manifest === null || typeof manifest !== "object" || Array.isArray(manifest)) throw new Error(`Invalid pnpm workspace manifest at ${path}: expected a mapping`);
|
|
326
|
+
const packages = manifest.packages;
|
|
327
|
+
if (packages === void 0) return [];
|
|
328
|
+
if (!Array.isArray(packages) || packages.some((pattern) => typeof pattern !== "string")) throw new Error(`Invalid pnpm workspace manifest at ${path}: packages must be an array of strings`);
|
|
329
|
+
return packages;
|
|
330
|
+
};
|
|
331
|
+
const isWorkspaceMember = async (manifest, workspaceDirectory, projectDirectory) => {
|
|
332
|
+
const patterns = [...manifestWorkspacePatterns(manifest), ...await pnpmWorkspacePatterns(workspaceDirectory)];
|
|
333
|
+
if (patterns.length === 0) return false;
|
|
334
|
+
const members = await glob(patterns, {
|
|
335
|
+
absolute: true,
|
|
336
|
+
cwd: workspaceDirectory,
|
|
337
|
+
onlyDirectories: true
|
|
338
|
+
});
|
|
339
|
+
const project = resolve$1(projectDirectory);
|
|
340
|
+
return members.some((member) => resolve$1(member) === project);
|
|
341
|
+
};
|
|
342
|
+
const findDeclaration = async (cwd, packageName) => {
|
|
343
|
+
const project = await findNearestManifest(cwd);
|
|
344
|
+
if (project !== void 0) {
|
|
345
|
+
const manifest = await readManifest(project.path);
|
|
346
|
+
if (hasOwnDeclaration(manifest, packageName)) return project;
|
|
347
|
+
}
|
|
348
|
+
const workspaceSearchRoot = project === void 0 ? cwd : dirname$1(project.directory);
|
|
349
|
+
for (const directory of parentDirectories(workspaceSearchRoot)) {
|
|
350
|
+
const path = join$1(directory, "package.json");
|
|
351
|
+
let manifest;
|
|
352
|
+
try {
|
|
353
|
+
manifest = await readManifest(path);
|
|
354
|
+
} catch (error) {
|
|
355
|
+
if (error.code === "ENOENT") continue;
|
|
356
|
+
throw error;
|
|
357
|
+
}
|
|
358
|
+
if (project !== void 0 && await isWorkspaceMember(manifest, directory, project.directory) && hasOwnDeclaration(manifest, packageName)) return {
|
|
359
|
+
directory,
|
|
360
|
+
path
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
const findContainingManifest = async (entryPath) => {
|
|
365
|
+
for (const directory of parentDirectories(dirname$1(entryPath))) {
|
|
366
|
+
const path = join$1(directory, "package.json");
|
|
367
|
+
try {
|
|
368
|
+
await access(path);
|
|
369
|
+
return path;
|
|
370
|
+
} catch {}
|
|
371
|
+
}
|
|
372
|
+
throw new Error(`Could not find the package manifest containing ${entryPath}`);
|
|
373
|
+
};
|
|
374
|
+
const resolveInstalledManifest = (packageName, anchor) => {
|
|
375
|
+
const require = createRequire(join$1(anchor, "package.json"));
|
|
376
|
+
try {
|
|
377
|
+
return require.resolve(`${packageName}/package.json`);
|
|
378
|
+
} catch (error) {
|
|
379
|
+
if (error.code !== "ERR_PACKAGE_PATH_NOT_EXPORTED") throw error;
|
|
380
|
+
return require.resolve(packageName);
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
const resolveDeclaredPackage = async (packageName, cwd = process.cwd()) => {
|
|
384
|
+
const declaration = await findDeclaration(cwd, packageName);
|
|
385
|
+
if (declaration === void 0) throw new Error(`${packageName} must be declared in the active project or workspace package.json before WebAnvil can use it`);
|
|
386
|
+
let resolvedPath;
|
|
387
|
+
try {
|
|
388
|
+
resolvedPath = resolveInstalledManifest(packageName, declaration.directory);
|
|
389
|
+
} catch (error) {
|
|
390
|
+
if (error.code === "MODULE_NOT_FOUND") throw new Error(`${packageName} is declared by ${declaration.path} but is not installed`, { cause: error });
|
|
391
|
+
throw error;
|
|
392
|
+
}
|
|
393
|
+
const manifestPath = resolvedPath.endsWith("package.json") ? resolvedPath : await findContainingManifest(resolvedPath);
|
|
394
|
+
const manifest = await readManifest(manifestPath);
|
|
395
|
+
if (manifest.name !== packageName || typeof manifest.version !== "string") throw new Error(`The resolved ${packageName} package at ${dirname$1(manifestPath)} has invalid package metadata`);
|
|
396
|
+
return {
|
|
397
|
+
declarationDirectory: declaration.directory,
|
|
398
|
+
packageRoot: dirname$1(manifestPath),
|
|
399
|
+
version: manifest.version
|
|
400
|
+
};
|
|
401
|
+
};
|
|
402
|
+
const normalizeSubpath = (subpath) => subpath.replace(/^\.?\//, "");
|
|
403
|
+
const resolveExecutable = async (definition, manifest, packageRoot) => {
|
|
404
|
+
if (definition.bin === void 0) return;
|
|
405
|
+
const relativeExecutable = typeof manifest.bin === "string" ? manifest.bin : manifest.bin !== null && typeof manifest.bin === "object" && !Array.isArray(manifest.bin) ? manifest.bin[definition.bin] : void 0;
|
|
406
|
+
if (typeof relativeExecutable !== "string") throw new Error(`${definition.packageName} does not provide its expected ${definition.bin} executable`);
|
|
407
|
+
const executable = resolve$1(packageRoot, relativeExecutable);
|
|
408
|
+
const packageRelativePath = relative$1(packageRoot, executable);
|
|
409
|
+
if (packageRelativePath.startsWith("..") || isAbsolute$1(packageRelativePath)) throw new Error(`${definition.packageName} has an invalid ${definition.bin} executable path`);
|
|
410
|
+
try {
|
|
411
|
+
await access(executable);
|
|
412
|
+
} catch {
|
|
413
|
+
throw new Error(`${definition.packageName}'s ${definition.bin} executable is missing at ${executable}`);
|
|
414
|
+
}
|
|
415
|
+
return executable;
|
|
416
|
+
};
|
|
417
|
+
const loadResolvedTool = async (name, definition, anchor, source) => {
|
|
418
|
+
let resolvedPath;
|
|
419
|
+
try {
|
|
420
|
+
resolvedPath = resolveInstalledManifest(definition.packageName, anchor);
|
|
421
|
+
} catch (error) {
|
|
422
|
+
if (error.code === "MODULE_NOT_FOUND") throw new Error(`${definition.packageName} is declared by ${join$1(anchor, "package.json")} but is not installed`, { cause: error });
|
|
423
|
+
throw error;
|
|
424
|
+
}
|
|
425
|
+
const manifestPath = resolvedPath.endsWith("package.json") ? resolvedPath : await findContainingManifest(resolvedPath);
|
|
426
|
+
const packageRoot = dirname$1(manifestPath);
|
|
427
|
+
const manifest = await readManifest(manifestPath);
|
|
428
|
+
if (manifest.name !== definition.packageName) throw new Error(`Expected ${definition.packageName} at ${packageRoot}, but its package manifest identifies as ${String(manifest.name)}`);
|
|
429
|
+
if (typeof manifest.version !== "string") throw new Error(`${definition.packageName} at ${packageRoot} does not declare a valid version`);
|
|
430
|
+
if (!satisfies(manifest.version, definition.range)) throw new Error(`${definition.packageName} ${manifest.version} is incompatible with WebAnvil; supported versions are ${definition.range}`);
|
|
431
|
+
const importPackage = async (subpath) => {
|
|
432
|
+
return import(pathToFileURL(await resolvePath(subpath === void 0 || subpath === "" ? definition.packageName : `${definition.packageName}/${normalizeSubpath(subpath)}`, {
|
|
433
|
+
conditions: [
|
|
434
|
+
"node",
|
|
435
|
+
"import",
|
|
436
|
+
"default"
|
|
437
|
+
],
|
|
438
|
+
url: pathToFileURL(manifestPath)
|
|
439
|
+
})).href);
|
|
440
|
+
};
|
|
441
|
+
return {
|
|
442
|
+
name,
|
|
443
|
+
packageName: definition.packageName,
|
|
444
|
+
version: manifest.version,
|
|
445
|
+
source,
|
|
446
|
+
packageRoot,
|
|
447
|
+
import: importPackage,
|
|
448
|
+
executable: await resolveExecutable(definition, manifest, packageRoot)
|
|
449
|
+
};
|
|
450
|
+
};
|
|
451
|
+
var Toolchain = class {
|
|
452
|
+
cwd;
|
|
453
|
+
#tools = /* @__PURE__ */ new Map();
|
|
454
|
+
constructor(cwd = process.cwd()) {
|
|
455
|
+
this.cwd = resolve$1(cwd);
|
|
456
|
+
}
|
|
457
|
+
resolve(name) {
|
|
458
|
+
const existing = this.#tools.get(name);
|
|
459
|
+
if (existing !== void 0) return existing;
|
|
460
|
+
const selected = this.#resolve(name);
|
|
461
|
+
this.#tools.set(name, selected);
|
|
462
|
+
return selected;
|
|
463
|
+
}
|
|
464
|
+
async #resolve(name) {
|
|
465
|
+
const definition = supportedTools[name];
|
|
466
|
+
const declaration = await findDeclaration(this.cwd, definition.packageName);
|
|
467
|
+
if (declaration !== void 0) return loadResolvedTool(name, definition, declaration.directory, "project");
|
|
468
|
+
const webanvilPackageRoot = dirname$1(await findContainingManifest(fileURLToPath(import.meta.url)));
|
|
469
|
+
return loadResolvedTool(name, definition, webanvilPackageRoot, "webanvil");
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
const formatResolvedTool = (tool) => `${tool.packageName} ${tool.version} (${tool.source})`;
|
|
473
|
+
const declarationDefaults = {
|
|
474
|
+
generator: "tsc",
|
|
475
|
+
incremental: false,
|
|
476
|
+
newContext: true,
|
|
477
|
+
parallel: false
|
|
478
|
+
};
|
|
479
|
+
let compilerIdentity;
|
|
480
|
+
let setupTail = Promise.resolve();
|
|
481
|
+
const serializeSetup = async (setup) => {
|
|
482
|
+
const previous = setupTail;
|
|
483
|
+
let release;
|
|
484
|
+
setupTail = new Promise((resolvePromise) => {
|
|
485
|
+
release = resolvePromise;
|
|
486
|
+
});
|
|
487
|
+
await previous;
|
|
488
|
+
try {
|
|
489
|
+
return await setup();
|
|
490
|
+
} finally {
|
|
491
|
+
release();
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
const compilerPlugins = (cwd, config) => {
|
|
495
|
+
if (config.tsconfig === false) return { plugins: (config.compilerOptions?.plugins ?? []).filter((plugin) => typeof plugin.transform === "string") };
|
|
496
|
+
const result = typeof config.tsconfig === "string" ? readTsconfig(resolve$1(cwd, config.tsconfig)) : getTsconfig(cwd);
|
|
497
|
+
const configured = (result?.config.compilerOptions)?.plugins ?? [];
|
|
498
|
+
return {
|
|
499
|
+
plugins: (config.compilerOptions?.plugins ?? configured).filter((plugin) => typeof plugin.transform === "string"),
|
|
500
|
+
...result === void 0 ? {} : { tsconfigPath: result.path }
|
|
501
|
+
};
|
|
502
|
+
};
|
|
503
|
+
const packageName = (specifier) => {
|
|
504
|
+
if (specifier.startsWith(".") || isAbsolute$1(specifier)) return;
|
|
505
|
+
const parts = specifier.split("/");
|
|
506
|
+
return specifier.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
|
|
507
|
+
};
|
|
508
|
+
const assertTransformPackages = async (plugins, cwd) => {
|
|
509
|
+
for (const plugin of plugins) {
|
|
510
|
+
if (typeof plugin.transform !== "string") continue;
|
|
511
|
+
const name = packageName(plugin.transform);
|
|
512
|
+
if (name === void 0) continue;
|
|
513
|
+
const declared = await resolveDeclaredPackage(name, cwd);
|
|
514
|
+
const require = createRequire(join$1(declared.declarationDirectory, "package.json"));
|
|
515
|
+
let resolvedTransform;
|
|
516
|
+
try {
|
|
517
|
+
resolvedTransform = require.resolve(plugin.transform);
|
|
518
|
+
} catch (error) {
|
|
519
|
+
throw new Error(`TypeScript declaration transform ${plugin.transform} is declared but cannot be resolved`, { cause: error });
|
|
520
|
+
}
|
|
521
|
+
const packageRelative = relative$1(declared.packageRoot, resolvedTransform);
|
|
522
|
+
if (packageRelative.startsWith("..") || isAbsolute$1(packageRelative)) throw new Error(`TypeScript declaration transform ${plugin.transform} resolved outside its declared ${name} package`);
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
const typescriptEntry = (tool) => createRequire(join$1(tool.packageRoot, "package.json")).resolve("typescript");
|
|
526
|
+
const patchedCompilerEntry = async (typescript, cwd, plugins) => {
|
|
527
|
+
if (plugins.length === 0) return typescriptEntry(typescript);
|
|
528
|
+
await assertTransformPackages(plugins, cwd);
|
|
529
|
+
const tsPatch = await resolveDeclaredPackage("ts-patch", cwd);
|
|
530
|
+
const require = createRequire(join$1(tsPatch.declarationDirectory, "package.json"));
|
|
531
|
+
let patchedEntry;
|
|
532
|
+
try {
|
|
533
|
+
patchedEntry = require.resolve("ts-patch/compiler");
|
|
534
|
+
} catch (error) {
|
|
535
|
+
throw new Error("ts-patch is declared but its patched compiler entry cannot be resolved", { cause: error });
|
|
536
|
+
}
|
|
537
|
+
const patchTypescript = await realpath(createRequire(join$1(tsPatch.packageRoot, "package.json")).resolve("typescript"));
|
|
538
|
+
const selectedTypescript = await realpath(typescriptEntry(typescript));
|
|
539
|
+
if (patchTypescript !== selectedTypescript) throw new Error(`ts-patch resolves TypeScript at ${patchTypescript}, but WebAnvil selected ${selectedTypescript}; install both from the same project or workspace`);
|
|
540
|
+
const patched = require(patchedEntry);
|
|
541
|
+
if (patched.version !== typescript.version) throw new Error(`ts-patch compiler ${String(patched.version)} does not match selected TypeScript ${typescript.version}`);
|
|
542
|
+
return patchedEntry;
|
|
543
|
+
};
|
|
544
|
+
const withCompilerResolution = (entry, run) => {
|
|
545
|
+
const module = Module;
|
|
546
|
+
const original = module._resolveFilename;
|
|
547
|
+
module._resolveFilename = function(request, parent, isMain, options) {
|
|
548
|
+
if (request === "typescript" && parent?.filename?.includes("rolldown-plugin-dts")) return entry;
|
|
549
|
+
return original.call(this, request, parent, isMain, options);
|
|
550
|
+
};
|
|
551
|
+
try {
|
|
552
|
+
return run();
|
|
553
|
+
} finally {
|
|
554
|
+
module._resolveFilename = original;
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
const assertCompilerIdentity = (selected) => {
|
|
558
|
+
if (compilerIdentity === void 0) {
|
|
559
|
+
compilerIdentity = selected;
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
if (compilerIdentity.realpath === selected.realpath && compilerIdentity.version === selected.version) return;
|
|
563
|
+
throw new Error(`rolldown-plugin-dts already initialized TypeScript ${compilerIdentity.version} from ${compilerIdentity.realpath}; this process cannot switch to TypeScript ${selected.version} from ${selected.realpath}. Run builds that require different TypeScript compilers in separate processes.`);
|
|
564
|
+
};
|
|
565
|
+
const withoutImplicitIncremental = (config) => {
|
|
566
|
+
const compilerOptions = config.compilerOptions;
|
|
567
|
+
if (config.incremental === true || compilerOptions?.incremental === true || typeof compilerOptions?.tsBuildInfoFile === "string") return config;
|
|
568
|
+
return {
|
|
569
|
+
...config,
|
|
570
|
+
incremental: false,
|
|
571
|
+
compilerOptions: {
|
|
572
|
+
...compilerOptions,
|
|
573
|
+
incremental: false,
|
|
574
|
+
tsBuildInfoFile: void 0
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
};
|
|
578
|
+
const createDeclarationPlugins = async (declaration, cwd, toolchain, emitDtsOnly) => serializeSetup(async () => {
|
|
579
|
+
const configured = withoutImplicitIncremental(declaration === true ? {} : declaration);
|
|
580
|
+
const generator = configured.generator ?? declarationDefaults.generator;
|
|
581
|
+
const { plugins, tsconfigPath } = compilerPlugins(cwd, configured);
|
|
582
|
+
if (plugins.length > 0 && generator !== "tsc") throw new Error(`TypeScript declaration transforms from ${tsconfigPath ?? "compilerOptions"} require build.declaration.generator "tsc"; ${generator} cannot apply TypeScript emit transforms`);
|
|
583
|
+
const options = {
|
|
584
|
+
...declarationDefaults,
|
|
585
|
+
...configured,
|
|
586
|
+
cwd,
|
|
587
|
+
emitDtsOnly,
|
|
588
|
+
generator,
|
|
589
|
+
parallel: false
|
|
590
|
+
};
|
|
591
|
+
if (generator === "tsgo") {
|
|
592
|
+
const tsgo = await toolchain.resolve("typescript-native");
|
|
593
|
+
if (tsgo.executable === void 0) throw new Error(`${tsgo.packageName} does not provide the tsgo executable required for declarations`);
|
|
594
|
+
options.tsgo = {
|
|
595
|
+
...typeof configured.tsgo === "object" ? configured.tsgo : {},
|
|
596
|
+
path: tsgo.executable
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
if (generator !== "tsc") return dts(options);
|
|
600
|
+
const typescript = await toolchain.resolve("typescript");
|
|
601
|
+
const compilerEntry = await patchedCompilerEntry(typescript, cwd, plugins);
|
|
602
|
+
const selected = {
|
|
603
|
+
realpath: await realpath(compilerEntry),
|
|
604
|
+
version: typescript.version
|
|
605
|
+
};
|
|
606
|
+
assertCompilerIdentity(selected);
|
|
607
|
+
return withCompilerResolution(compilerEntry, () => dts(options));
|
|
608
|
+
});
|
|
609
|
+
const dependencyStoreSegments = [
|
|
610
|
+
"/node_modules/",
|
|
611
|
+
"/.pnpm/",
|
|
612
|
+
"/.yarn/cache/",
|
|
613
|
+
"/.yarn/berry/cache/",
|
|
614
|
+
"/.bun/install/cache/"
|
|
615
|
+
];
|
|
616
|
+
const cleanId = (id) => id.replaceAll("\\", "/").split(/[?#]/, 1)[0];
|
|
617
|
+
const isInstalledPackagePath = (id, cwd = process.cwd()) => {
|
|
618
|
+
const path = cleanId(id);
|
|
619
|
+
if (dependencyStoreSegments.some((segment) => path.includes(segment))) return true;
|
|
620
|
+
const projectRelative = relative(cwd, path);
|
|
621
|
+
return (projectRelative === ".." || projectRelative.startsWith("../")) && (path.includes("/pnpm/store/") || path.includes("/bun/install/"));
|
|
622
|
+
};
|
|
623
|
+
const cacheKey = (source, importer) => `${importer ?? ""}\0${source}`;
|
|
624
|
+
const projectExternalPlugin = (cwd = process.cwd()) => {
|
|
625
|
+
const resolutionCache = /* @__PURE__ */ new Map();
|
|
626
|
+
return {
|
|
627
|
+
name: "webanvil-project-externals",
|
|
628
|
+
buildStart() {
|
|
629
|
+
resolutionCache.clear();
|
|
630
|
+
},
|
|
631
|
+
resolveId: {
|
|
632
|
+
order: "pre",
|
|
633
|
+
async handler(source, importer, options) {
|
|
634
|
+
if (isBuiltin(source)) return {
|
|
635
|
+
id: source,
|
|
636
|
+
external: true
|
|
637
|
+
};
|
|
638
|
+
if (source.startsWith(".") || isAbsolute(source) || source.startsWith("\0")) return null;
|
|
639
|
+
const key = cacheKey(source, importer);
|
|
640
|
+
let pending = resolutionCache.get(key);
|
|
641
|
+
if (pending === void 0) {
|
|
642
|
+
pending = this.resolve(source, importer, {
|
|
643
|
+
...options,
|
|
644
|
+
skipSelf: true
|
|
645
|
+
});
|
|
646
|
+
resolutionCache.set(key, pending);
|
|
647
|
+
}
|
|
648
|
+
const resolved = await pending;
|
|
649
|
+
if (resolved === null) this.error(`Could not resolve "${source}"${importer === void 0 ? "" : ` from ${importer}`}`);
|
|
650
|
+
if (resolved.external) return resolved;
|
|
651
|
+
return isInstalledPackagePath(resolved.id, cwd) ? {
|
|
652
|
+
id: source,
|
|
653
|
+
external: true
|
|
654
|
+
} : resolved;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
};
|
|
659
|
+
const sourceExtensions = [
|
|
660
|
+
".cts",
|
|
661
|
+
".mts",
|
|
662
|
+
".tsx",
|
|
663
|
+
".jsx",
|
|
664
|
+
".ts",
|
|
665
|
+
".js"
|
|
666
|
+
];
|
|
667
|
+
const rolldownRuntimePlugin = () => ({
|
|
668
|
+
name: "webanvil-rolldown-runtime",
|
|
669
|
+
resolveId: {
|
|
670
|
+
order: "pre",
|
|
671
|
+
handler(source, importer) {
|
|
672
|
+
if (source !== "node:module" || importer !== "\0rolldown/runtime.js") return null;
|
|
673
|
+
return {
|
|
674
|
+
id: source,
|
|
675
|
+
external: true,
|
|
676
|
+
moduleSideEffects: false
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
});
|
|
681
|
+
const withoutExtension = (path) => {
|
|
682
|
+
const extension = sourceExtensions.find((candidate) => path.endsWith(candidate));
|
|
683
|
+
return extension === void 0 ? path : path.slice(0, -extension.length);
|
|
684
|
+
};
|
|
685
|
+
const entryName = (subpath) => subpath === "." ? "index" : subpath.replace(/^\.\//, "");
|
|
686
|
+
const defaultEntryName = (entry, cwd) => withoutExtension(relative(cwd, resolve(cwd, entry))).replace(/^src\//, "");
|
|
687
|
+
const resolvePublicInputs = (cwd, entry, entries) => {
|
|
688
|
+
if (entries === void 0) return { [defaultEntryName(entry, cwd)]: resolve(cwd, entry) };
|
|
689
|
+
const inputs = {};
|
|
690
|
+
const names = /* @__PURE__ */ new Map();
|
|
691
|
+
const sources = /* @__PURE__ */ new Map();
|
|
692
|
+
for (const [subpath, source] of Object.entries(entries)) {
|
|
693
|
+
const name = entryName(subpath);
|
|
694
|
+
const existingName = names.get(name);
|
|
695
|
+
if (existingName !== void 0) throw new Error(`Node entries ${existingName} and ${subpath} normalize to the same public name: ${name}`);
|
|
696
|
+
names.set(name, subpath);
|
|
697
|
+
const resolved = resolve(cwd, source);
|
|
698
|
+
const existing = sources.get(resolved);
|
|
699
|
+
if (existing !== void 0) throw new Error(`Node entries ${existing} and ${subpath} resolve to the same source file`);
|
|
700
|
+
sources.set(resolved, subpath);
|
|
701
|
+
inputs[name] = resolved;
|
|
702
|
+
}
|
|
703
|
+
if (Object.keys(inputs).length === 0) throw new Error("Node build entries cannot be empty");
|
|
704
|
+
return inputs;
|
|
705
|
+
};
|
|
706
|
+
const commonSourceRoot = (inputs) => {
|
|
707
|
+
const directories = Object.values(inputs).map(dirname);
|
|
708
|
+
let root = directories[0];
|
|
709
|
+
for (const directory of directories.slice(1)) while (relative(root, directory).startsWith("../") || isAbsolute(relative(root, directory))) {
|
|
710
|
+
const parent = dirname(root);
|
|
711
|
+
if (parent === root) return root;
|
|
712
|
+
root = parent;
|
|
713
|
+
}
|
|
714
|
+
return root;
|
|
715
|
+
};
|
|
716
|
+
const outputForFormat = (format, native, owned) => ({
|
|
717
|
+
entryFileNames: format === "esm" ? "[name].js" : "[name].cjs",
|
|
718
|
+
chunkFileNames: format === "esm" ? "[name]-[hash].js" : "[name]-[hash].cjs",
|
|
719
|
+
polyfillRequire: false,
|
|
720
|
+
...native,
|
|
721
|
+
...owned,
|
|
722
|
+
cleanDir: false,
|
|
723
|
+
format: format === "esm" ? "es" : "cjs"
|
|
724
|
+
});
|
|
725
|
+
const nodeOutputPlan = ({ bundle = false, cwd = process.cwd(), declarationPlugins = [], entry, entries, formats = ["esm"], minify, native, outDir, platform = "node", plugins = [], sourcemap, target = "node20" }) => {
|
|
726
|
+
assertSyntaxTarget(target);
|
|
727
|
+
const inputs = resolvePublicInputs(cwd, entry, entries);
|
|
728
|
+
const preserveModulesRoot = commonSourceRoot(inputs);
|
|
729
|
+
const nativeInput = native?.input ?? {};
|
|
730
|
+
const nativePlugins = nativeInput.plugins ?? [];
|
|
731
|
+
const resolveOptions = platform === "neutral" && nativeInput.resolve?.mainFields === void 0 ? {
|
|
732
|
+
...nativeInput.resolve,
|
|
733
|
+
mainFields: ["module", "main"]
|
|
734
|
+
} : nativeInput.resolve;
|
|
735
|
+
const input = {
|
|
736
|
+
tsconfig: true,
|
|
737
|
+
...nativeInput,
|
|
738
|
+
input: inputs,
|
|
739
|
+
platform,
|
|
740
|
+
...resolveOptions === void 0 ? {} : { resolve: resolveOptions },
|
|
741
|
+
transform: {
|
|
742
|
+
...typeof nativeInput.transform === "object" ? nativeInput.transform : {},
|
|
743
|
+
target
|
|
744
|
+
},
|
|
745
|
+
plugins: [
|
|
746
|
+
rolldownRuntimePlugin(),
|
|
747
|
+
projectExternalPlugin(cwd),
|
|
748
|
+
...nativePlugins,
|
|
749
|
+
...plugins,
|
|
750
|
+
...declarationPlugins
|
|
751
|
+
]
|
|
752
|
+
};
|
|
753
|
+
return {
|
|
754
|
+
authoredInputs: Object.values(inputs),
|
|
755
|
+
input,
|
|
756
|
+
outDir,
|
|
757
|
+
output: formats.map((format) => outputForFormat(format, native?.output?.[format], {
|
|
758
|
+
dir: outDir,
|
|
759
|
+
...minify === void 0 ? {} : { minify },
|
|
760
|
+
preserveModules: !bundle,
|
|
761
|
+
...!bundle ? { preserveModulesRoot } : {},
|
|
762
|
+
...sourcemap === void 0 ? {} : { sourcemap }
|
|
763
|
+
}))
|
|
764
|
+
};
|
|
765
|
+
};
|
|
766
|
+
const authoredNodeSources = (plan, outputs) => {
|
|
767
|
+
const sources = new Set(plan.authoredInputs);
|
|
768
|
+
for (const output of outputs) for (const file of output.output) {
|
|
769
|
+
if (file.type !== "chunk") continue;
|
|
770
|
+
for (const source of Object.keys(file.modules)) if (isAbsolute(source)) sources.add(source);
|
|
771
|
+
if (file.facadeModuleId !== null && isAbsolute(file.facadeModuleId)) sources.add(file.facadeModuleId);
|
|
772
|
+
}
|
|
773
|
+
return [...sources];
|
|
774
|
+
};
|
|
775
|
+
const sourceBytes = (file) => file.type === "chunk" ? file.code : file.source;
|
|
776
|
+
const generatedNodeFiles = (outputs) => {
|
|
777
|
+
const generated = /* @__PURE__ */ new Map();
|
|
778
|
+
for (const output of outputs) for (const file of output.output) {
|
|
779
|
+
const fileName = file.fileName;
|
|
780
|
+
const source = sourceBytes(file);
|
|
781
|
+
if (generated.get(fileName) !== void 0) throw new Error(`Rolldown outputs collide at ${fileName}`);
|
|
782
|
+
generated.set(fileName, {
|
|
783
|
+
fileName,
|
|
784
|
+
source
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
return [...generated.values()];
|
|
788
|
+
};
|
|
789
|
+
const collectExportConditions = (exports, conditions = /* @__PURE__ */ new Set()) => {
|
|
790
|
+
if (exports === void 0 || exports === null || typeof exports === "string") return conditions;
|
|
791
|
+
if (Array.isArray(exports)) {
|
|
792
|
+
for (const entry of exports) collectExportConditions(entry, conditions);
|
|
793
|
+
return conditions;
|
|
794
|
+
}
|
|
795
|
+
for (const [condition, value] of Object.entries(exports)) {
|
|
796
|
+
conditions.add(condition);
|
|
797
|
+
collectExportConditions(value, conditions);
|
|
798
|
+
}
|
|
799
|
+
return conditions;
|
|
800
|
+
};
|
|
801
|
+
const inferPackageOutputOptions = (packageJson) => {
|
|
802
|
+
const conditions = collectExportConditions(packageJson.exports);
|
|
803
|
+
const formats = [];
|
|
804
|
+
if (conditions.has("import")) formats.push("esm");
|
|
805
|
+
if (conditions.has("require")) formats.push("cjs");
|
|
806
|
+
return {
|
|
807
|
+
...packageJson.types || conditions.has("types") ? { declaration: true } : {},
|
|
808
|
+
...formats.length > 0 ? { formats } : {}
|
|
809
|
+
};
|
|
810
|
+
};
|
|
811
|
+
const resolvePackageOutputOptions = async (options, cwd = process.cwd()) => {
|
|
812
|
+
const packagePath = await resolvePackageJSON(cwd).catch(() => void 0);
|
|
813
|
+
const packageJson = packagePath === void 0 ? void 0 : JSON.parse(await readFile(packagePath, "utf8"));
|
|
814
|
+
const resolved = packageJson === void 0 ? {} : inferPackageOutputOptions(packageJson);
|
|
815
|
+
if (options.declaration !== void 0) resolved.declaration = options.declaration;
|
|
816
|
+
if (options.formats !== void 0) resolved.formats = options.formats;
|
|
817
|
+
return resolved;
|
|
818
|
+
};
|
|
819
|
+
const isInside$1 = (directory, target) => {
|
|
820
|
+
const path = relative(directory, target);
|
|
821
|
+
return path === "" || path !== ".." && !path.startsWith("../") && !isAbsolute(path);
|
|
822
|
+
};
|
|
823
|
+
const staticBase = (pattern) => {
|
|
824
|
+
const parts = pattern.replaceAll("\\", "/").split("/");
|
|
825
|
+
const index = parts.findIndex((part) => /[*?[\]{}()!]/.test(part));
|
|
826
|
+
return index === -1 ? dirname(pattern) : parts.slice(0, index).join("/") || ".";
|
|
827
|
+
};
|
|
828
|
+
const staticCopyWatchPaths = (mappings, cwd = process.cwd()) => {
|
|
829
|
+
if (mappings == null || mappings.length === 0) return [];
|
|
830
|
+
return [...new Set(mappings.map(({ from }) => {
|
|
831
|
+
assertRelative(from, "Copy source", cwd);
|
|
832
|
+
const base = resolve(cwd, staticBase(from));
|
|
833
|
+
if (!isInside$1(cwd, base)) throw new Error(`Copy source is outside the project root: ${from}`);
|
|
834
|
+
return base;
|
|
835
|
+
}))];
|
|
836
|
+
};
|
|
837
|
+
const assertRelative = (path, label, cwd = process.cwd()) => {
|
|
838
|
+
if (path.length === 0 || isAbsolute(path) || !isInside$1(cwd, resolve(cwd, path))) throw new Error(`${label} must be relative to the project root: ${path}`);
|
|
839
|
+
};
|
|
840
|
+
const assertNoSymlink = async (path, directory) => {
|
|
841
|
+
const target = dirname(path);
|
|
842
|
+
if (!isInside$1(directory, target)) throw new Error(`Copy destination is outside the build output directory: ${path}`);
|
|
843
|
+
let current = directory;
|
|
844
|
+
for (const segment of relative(directory, target).split("/").filter(Boolean)) {
|
|
845
|
+
current = resolve(current, segment);
|
|
846
|
+
try {
|
|
847
|
+
if ((await lstat(current)).isSymbolicLink()) throw new Error(`Refusing to copy through symbolic link: ${path}`);
|
|
848
|
+
} catch (error) {
|
|
849
|
+
if (error.code !== "ENOENT") throw error;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
};
|
|
853
|
+
const assertSourceInsideProject = async (file, cwd) => {
|
|
854
|
+
if (!isInside$1(await realpath(cwd), await realpath(file))) throw new Error(`Copy source is outside the project root: ${file}`);
|
|
855
|
+
};
|
|
856
|
+
const planStaticCopies = async (mappings, outDir, cwd = process.cwd()) => {
|
|
857
|
+
if (mappings == null || mappings.length === 0) return [];
|
|
858
|
+
const output = resolve(cwd, outDir);
|
|
859
|
+
if (!isInside$1(cwd, output)) throw new Error(`Build output directory must be within the project root: ${outDir}`);
|
|
860
|
+
await assertNoSymlink(resolve(output, ".webanvil-static-copy"), cwd);
|
|
861
|
+
const files = (await Promise.all(mappings.map(async ({ from, to }) => {
|
|
862
|
+
assertRelative(from, "Copy source", cwd);
|
|
863
|
+
assertRelative(to, "Copy destination", cwd);
|
|
864
|
+
const base = resolve(cwd, staticBase(from));
|
|
865
|
+
if (!isInside$1(cwd, base)) throw new Error(`Copy source is outside the project root: ${from}`);
|
|
866
|
+
const files = await glob(from, {
|
|
867
|
+
cwd,
|
|
868
|
+
dot: true,
|
|
869
|
+
onlyFiles: true
|
|
870
|
+
});
|
|
871
|
+
return Promise.all(files.map(async (file) => {
|
|
872
|
+
const source = resolve(cwd, file);
|
|
873
|
+
await assertSourceInsideProject(source, cwd);
|
|
874
|
+
const destination = resolve(output, to, relative(base, source));
|
|
875
|
+
if (!isInside$1(output, destination) || !isInside$1(cwd, destination)) throw new Error(`Copy destination is outside the build output directory: ${to}`);
|
|
876
|
+
return {
|
|
877
|
+
from: source,
|
|
878
|
+
to: destination
|
|
879
|
+
};
|
|
880
|
+
}));
|
|
881
|
+
}))).flat();
|
|
882
|
+
const destinations = /* @__PURE__ */ new Set();
|
|
883
|
+
for (const file of files) {
|
|
884
|
+
if (destinations.has(file.to)) throw new Error(`Duplicate copy destination: ${relative(output, file.to)}`);
|
|
885
|
+
destinations.add(file.to);
|
|
886
|
+
}
|
|
887
|
+
return files;
|
|
888
|
+
};
|
|
889
|
+
const copyStaticFiles = async (copies, generated, cwd = process.cwd()) => {
|
|
890
|
+
if (copies.length === 0) return [];
|
|
891
|
+
await assertStaticCopyDestinationsAvailable(copies, generated);
|
|
892
|
+
for (const { from, to } of copies) {
|
|
893
|
+
await assertNoSymlink(to, cwd);
|
|
894
|
+
await mkdir(dirname(to), { recursive: true });
|
|
895
|
+
await copyFile(from, to);
|
|
896
|
+
}
|
|
897
|
+
return copies.map(({ to }) => to);
|
|
898
|
+
};
|
|
899
|
+
const assertStaticCopyDestinationsAvailable = async (copies, generated = [], checkExisting = true) => {
|
|
900
|
+
const generatedPaths = new Set(generated.map((file) => resolve(process.cwd(), file)));
|
|
901
|
+
for (const { to } of copies) {
|
|
902
|
+
if (generatedPaths.has(to)) throw new Error(`Copy destination collides with generated output: ${to}`);
|
|
903
|
+
if (!checkExisting) continue;
|
|
904
|
+
try {
|
|
905
|
+
await lstat(to);
|
|
906
|
+
throw new Error(`Copy destination already exists: ${to}`);
|
|
907
|
+
} catch (error) {
|
|
908
|
+
if (error.code !== "ENOENT") throw error;
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
};
|
|
912
|
+
var StaleNodeWatchBuild = class extends Error {};
|
|
913
|
+
const createNodeBuildPlan = async (entry, outDir, options, plugins, native = {}, toolchain = new Toolchain(process.cwd())) => {
|
|
914
|
+
const cwd = process.cwd();
|
|
915
|
+
const packageOptions = await resolvePackageOutputOptions(options, cwd);
|
|
916
|
+
const resolvedOptions = {
|
|
917
|
+
...options,
|
|
918
|
+
...packageOptions
|
|
919
|
+
};
|
|
920
|
+
const formats = resolvedOptions.formats ?? ["esm"];
|
|
921
|
+
const target = resolve(cwd, outDir);
|
|
922
|
+
const declaration = resolvedOptions.declaration;
|
|
923
|
+
const combinedDeclarations = declaration !== false && declaration !== void 0 && formats.length === 1 && formats[0] === "esm";
|
|
924
|
+
const declarationPlugins = declaration === false || declaration === void 0 ? [] : await createDeclarationPlugins(declaration, cwd, toolchain, !combinedDeclarations);
|
|
925
|
+
const output = nodeOutputPlan({
|
|
926
|
+
bundle: resolvedOptions.bundle,
|
|
927
|
+
cwd,
|
|
928
|
+
declarationPlugins: combinedDeclarations ? declarationPlugins : [],
|
|
929
|
+
entry,
|
|
930
|
+
entries: resolvedOptions.entries,
|
|
931
|
+
formats,
|
|
932
|
+
minify: resolvedOptions.minify,
|
|
933
|
+
native,
|
|
934
|
+
outDir: target,
|
|
935
|
+
platform: resolvedOptions.platform,
|
|
936
|
+
plugins,
|
|
937
|
+
sourcemap: resolvedOptions.sourcemap,
|
|
938
|
+
target: resolvedOptions.target
|
|
939
|
+
});
|
|
940
|
+
const declarationOutput = declarationPlugins.length === 0 || combinedDeclarations ? void 0 : nodeOutputPlan({
|
|
941
|
+
bundle: resolvedOptions.bundle,
|
|
942
|
+
cwd,
|
|
943
|
+
declarationPlugins,
|
|
944
|
+
entry,
|
|
945
|
+
entries: resolvedOptions.entries,
|
|
946
|
+
formats: ["esm"],
|
|
947
|
+
native,
|
|
948
|
+
outDir: target,
|
|
949
|
+
platform: resolvedOptions.platform,
|
|
950
|
+
plugins,
|
|
951
|
+
target: resolvedOptions.target
|
|
952
|
+
});
|
|
953
|
+
return {
|
|
954
|
+
cwd,
|
|
955
|
+
...declarationOutput === void 0 ? {} : { declarationOutput },
|
|
956
|
+
options: resolvedOptions,
|
|
957
|
+
outDir: target,
|
|
958
|
+
output
|
|
959
|
+
};
|
|
960
|
+
};
|
|
961
|
+
const bundleOutput = (bundle) => ({ output: Object.values(bundle) });
|
|
962
|
+
const generatedPaths = (plan, files) => files.map(({ fileName }) => resolve(plan.outDir, fileName));
|
|
963
|
+
const isInside = (directory, target) => {
|
|
964
|
+
const path = relative(directory, target);
|
|
965
|
+
return path === "" || path !== ".." && !path.startsWith("../") && !isAbsolute(path);
|
|
966
|
+
};
|
|
967
|
+
const displayPath = (path, cwd) => relative(cwd, path) || ".";
|
|
968
|
+
const isDeclarationOutput = (path) => /\.d\.[cm]?ts$/.test(path);
|
|
969
|
+
const canonicalPath = async (path) => {
|
|
970
|
+
try {
|
|
971
|
+
return await realpath(path);
|
|
972
|
+
} catch (error) {
|
|
973
|
+
if (error.code === "ENOENT") return resolve(path);
|
|
974
|
+
throw error;
|
|
975
|
+
}
|
|
976
|
+
};
|
|
977
|
+
const assertNoSymlinkDestination = async (path, cwd) => {
|
|
978
|
+
let directory = dirname(path);
|
|
979
|
+
while (directory !== resolve(cwd)) {
|
|
980
|
+
if (!isInside(cwd, directory)) throw new Error(`Node output is outside the project root: ${path}`);
|
|
981
|
+
try {
|
|
982
|
+
if ((await lstat(directory)).isSymbolicLink()) throw new Error(`Refusing to write Node output through symbolic link: ${path}`);
|
|
983
|
+
} catch (error) {
|
|
984
|
+
if (error.code !== "ENOENT") throw error;
|
|
985
|
+
}
|
|
986
|
+
directory = dirname(directory);
|
|
987
|
+
}
|
|
988
|
+
};
|
|
989
|
+
const preflightBuild = async (plan, files, copies, sources) => {
|
|
990
|
+
const info = await readBuildInfo(plan.cwd);
|
|
991
|
+
const tracked = new Set(info.output.map((file) => resolve(plan.cwd, file)));
|
|
992
|
+
const authoredInputs = new Set([...plan.output.authoredInputs, ...plan.declarationOutput?.authoredInputs ?? []].map((file) => resolve(file)));
|
|
993
|
+
const canonicalSources = /* @__PURE__ */ new Map();
|
|
994
|
+
for (const source of sources) canonicalSources.set(await canonicalPath(source), source);
|
|
995
|
+
const destinations = [...generatedPaths(plan, files).map((path) => ({
|
|
996
|
+
kind: "generated",
|
|
997
|
+
path
|
|
998
|
+
})), ...copies.map(({ to }) => ({
|
|
999
|
+
kind: "copied",
|
|
1000
|
+
path: to
|
|
1001
|
+
}))];
|
|
1002
|
+
for (const destination of destinations) {
|
|
1003
|
+
if (!isInside(plan.outDir, destination.path) || !isInside(plan.cwd, destination.path)) throw new Error(`Node output is outside the build output directory: ${destination.path}`);
|
|
1004
|
+
const source = canonicalSources.get(await canonicalPath(destination.path));
|
|
1005
|
+
await assertNoSymlinkDestination(destination.path, plan.cwd);
|
|
1006
|
+
let exists = false;
|
|
1007
|
+
try {
|
|
1008
|
+
await lstat(destination.path);
|
|
1009
|
+
exists = true;
|
|
1010
|
+
} catch (error) {
|
|
1011
|
+
if (error.code !== "ENOENT") throw error;
|
|
1012
|
+
}
|
|
1013
|
+
const generatedDeclarationAlias = destination.kind === "generated" && source !== void 0 && resolve(source) === resolve(destination.path) && isDeclarationOutput(destination.path) && !authoredInputs.has(resolve(source)) && (!exists || tracked.has(destination.path));
|
|
1014
|
+
if (source !== void 0 && !generatedDeclarationAlias) throw new Error(`Node ${destination.kind} output ${displayPath(destination.path, plan.cwd)} aliases authored source ${displayPath(source, plan.cwd)}`);
|
|
1015
|
+
if (exists && !tracked.has(destination.path)) throw new Error(`Node ${destination.kind} output already exists and is not tracked by WebAnvil: ${displayPath(destination.path, plan.cwd)}`);
|
|
1016
|
+
}
|
|
1017
|
+
return {
|
|
1018
|
+
previous: info.output.filter((file) => isInside(plan.outDir, resolve(plan.cwd, file))),
|
|
1019
|
+
retained: info.output.filter((file) => !isInside(plan.outDir, resolve(plan.cwd, file)))
|
|
1020
|
+
};
|
|
1021
|
+
};
|
|
1022
|
+
const stageBuild = async (plan, files, copies) => {
|
|
1023
|
+
await mkdir(resolve(plan.cwd, ".webanvil"), { recursive: true });
|
|
1024
|
+
const directory = await mkdtemp(resolve(plan.cwd, ".webanvil", "node-output-"));
|
|
1025
|
+
const next = resolve(directory, "next");
|
|
1026
|
+
const generated = generatedPaths(plan, files);
|
|
1027
|
+
try {
|
|
1028
|
+
await assertStaticCopyDestinationsAvailable(copies, generated, false);
|
|
1029
|
+
for (const file of files) {
|
|
1030
|
+
const target = resolve(next, file.fileName);
|
|
1031
|
+
if (!isInside(next, target)) throw new Error(`Node output is outside the build output directory: ${file.fileName}`);
|
|
1032
|
+
await mkdir(dirname(target), { recursive: true });
|
|
1033
|
+
await writeFile(target, file.source);
|
|
1034
|
+
}
|
|
1035
|
+
for (const copy of copies) {
|
|
1036
|
+
const target = resolve(next, relative(plan.outDir, copy.to));
|
|
1037
|
+
await mkdir(dirname(target), { recursive: true });
|
|
1038
|
+
await copyFile(copy.from, target);
|
|
1039
|
+
}
|
|
1040
|
+
return {
|
|
1041
|
+
directory,
|
|
1042
|
+
next,
|
|
1043
|
+
output: [...generated, ...copies.map(({ to }) => to)]
|
|
1044
|
+
};
|
|
1045
|
+
} catch (error) {
|
|
1046
|
+
await rm(directory, {
|
|
1047
|
+
force: true,
|
|
1048
|
+
recursive: true
|
|
1049
|
+
});
|
|
1050
|
+
throw error;
|
|
1051
|
+
}
|
|
1052
|
+
};
|
|
1053
|
+
const restoreBuildInfo = async (cwd, contents) => {
|
|
1054
|
+
const path = resolve(cwd, ".webanvil", "buildinfo.json");
|
|
1055
|
+
await rm(`${path}.tmp`, { force: true });
|
|
1056
|
+
if (contents === void 0) {
|
|
1057
|
+
await rm(path, { force: true });
|
|
1058
|
+
return;
|
|
1059
|
+
}
|
|
1060
|
+
const temporary = `${path}.rollback`;
|
|
1061
|
+
await writeFile(temporary, contents);
|
|
1062
|
+
await rename(temporary, path);
|
|
1063
|
+
};
|
|
1064
|
+
const readBuildInfoContents = async (cwd) => {
|
|
1065
|
+
try {
|
|
1066
|
+
return await readFile(resolve(cwd, ".webanvil", "buildinfo.json"), "utf8");
|
|
1067
|
+
} catch (error) {
|
|
1068
|
+
if (error.code === "ENOENT") return void 0;
|
|
1069
|
+
throw error;
|
|
1070
|
+
}
|
|
1071
|
+
};
|
|
1072
|
+
const commitBuild = async (plan, files, copies, sources, fileSystem = { rename }, isCurrent = () => true) => {
|
|
1073
|
+
const assertCurrent = () => {
|
|
1074
|
+
if (!isCurrent()) throw new StaleNodeWatchBuild();
|
|
1075
|
+
};
|
|
1076
|
+
assertCurrent();
|
|
1077
|
+
const { previous, retained } = await preflightBuild(plan, files, copies, sources);
|
|
1078
|
+
assertCurrent();
|
|
1079
|
+
const previousBuildInfo = await readBuildInfoContents(plan.cwd);
|
|
1080
|
+
const staged = await stageBuild(plan, files, copies);
|
|
1081
|
+
const moved = [];
|
|
1082
|
+
const installed = [];
|
|
1083
|
+
try {
|
|
1084
|
+
assertCurrent();
|
|
1085
|
+
for (const file of previous) {
|
|
1086
|
+
assertCurrent();
|
|
1087
|
+
const target = resolve(plan.cwd, file);
|
|
1088
|
+
const backup = resolve(staged.directory, "previous", file);
|
|
1089
|
+
await mkdir(dirname(backup), { recursive: true });
|
|
1090
|
+
try {
|
|
1091
|
+
await fileSystem.rename(target, backup);
|
|
1092
|
+
moved.push({
|
|
1093
|
+
backup,
|
|
1094
|
+
target
|
|
1095
|
+
});
|
|
1096
|
+
assertCurrent();
|
|
1097
|
+
} catch (error) {
|
|
1098
|
+
if (error.code !== "ENOENT") throw error;
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
for (const output of staged.output) {
|
|
1102
|
+
assertCurrent();
|
|
1103
|
+
const target = resolve(output);
|
|
1104
|
+
const source = resolve(staged.next, relative(plan.outDir, target));
|
|
1105
|
+
await mkdir(dirname(target), { recursive: true });
|
|
1106
|
+
await fileSystem.rename(source, target);
|
|
1107
|
+
installed.push(target);
|
|
1108
|
+
assertCurrent();
|
|
1109
|
+
}
|
|
1110
|
+
assertCurrent();
|
|
1111
|
+
await writeBuildInfo([...retained, ...staged.output], plan.cwd);
|
|
1112
|
+
assertCurrent();
|
|
1113
|
+
return staged.output;
|
|
1114
|
+
} catch (error) {
|
|
1115
|
+
const rollbackErrors = [];
|
|
1116
|
+
for (const target of installed.reverse()) try {
|
|
1117
|
+
await rm(target, { force: true });
|
|
1118
|
+
} catch (rollbackError) {
|
|
1119
|
+
rollbackErrors.push(rollbackError);
|
|
1120
|
+
}
|
|
1121
|
+
for (const previous of moved.reverse()) try {
|
|
1122
|
+
await mkdir(dirname(previous.target), { recursive: true });
|
|
1123
|
+
await fileSystem.rename(previous.backup, previous.target);
|
|
1124
|
+
} catch (rollbackError) {
|
|
1125
|
+
rollbackErrors.push(rollbackError);
|
|
1126
|
+
}
|
|
1127
|
+
try {
|
|
1128
|
+
await restoreBuildInfo(plan.cwd, previousBuildInfo);
|
|
1129
|
+
} catch (rollbackError) {
|
|
1130
|
+
rollbackErrors.push(rollbackError);
|
|
1131
|
+
}
|
|
1132
|
+
if (rollbackErrors.length > 0) throw new AggregateError([error, ...rollbackErrors], "Node build commit failed and rollback was incomplete");
|
|
1133
|
+
throw error;
|
|
1134
|
+
} finally {
|
|
1135
|
+
await rm(staged.directory, {
|
|
1136
|
+
force: true,
|
|
1137
|
+
recursive: true
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1140
|
+
};
|
|
1141
|
+
const generateOutput = async (plan, rolldown) => {
|
|
1142
|
+
const bundle = await rolldown(plan.input);
|
|
1143
|
+
try {
|
|
1144
|
+
return await Promise.all(plan.output.map((output) => bundle.generate(output)));
|
|
1145
|
+
} finally {
|
|
1146
|
+
await bundle.close();
|
|
1147
|
+
}
|
|
1148
|
+
};
|
|
1149
|
+
const generatedBuild = (plan, outputs, declarationOutputs = []) => ({
|
|
1150
|
+
files: generatedNodeFiles([...outputs, ...declarationOutputs]),
|
|
1151
|
+
sources: [.../* @__PURE__ */ new Set([...authoredNodeSources(plan.output, outputs), ...plan.declarationOutput === void 0 ? [] : authoredNodeSources(plan.declarationOutput, declarationOutputs)])]
|
|
1152
|
+
});
|
|
1153
|
+
const runNodeBuild = async (plan, rolldown, fileSystem = { rename }) => {
|
|
1154
|
+
const outputs = await generateOutput(plan.output, rolldown);
|
|
1155
|
+
const declarationOutputs = plan.declarationOutput === void 0 ? [] : await generateOutput(plan.declarationOutput, rolldown);
|
|
1156
|
+
const { files, sources } = generatedBuild(plan, outputs, declarationOutputs);
|
|
1157
|
+
const copies = await planStaticCopies(plan.options.copy, plan.outDir, plan.cwd);
|
|
1158
|
+
return commitBuild(plan, files, copies, sources, fileSystem);
|
|
1159
|
+
};
|
|
1160
|
+
const nodeWatchLifecycle = (plan, rolldown) => {
|
|
1161
|
+
let generation = 0;
|
|
1162
|
+
let outputs = [];
|
|
1163
|
+
let copies = [];
|
|
1164
|
+
let commits = Promise.resolve();
|
|
1165
|
+
return {
|
|
1166
|
+
abort: () => {
|
|
1167
|
+
generation += 1;
|
|
1168
|
+
outputs = [];
|
|
1169
|
+
copies = [];
|
|
1170
|
+
},
|
|
1171
|
+
plugin: {
|
|
1172
|
+
name: "webanvil-node-watch",
|
|
1173
|
+
async buildStart() {
|
|
1174
|
+
copies = await planStaticCopies(plan.options.copy, plan.outDir, plan.cwd);
|
|
1175
|
+
for (const path of [...staticCopyWatchPaths(plan.options.copy, plan.cwd), ...copies.map(({ from }) => from)]) this.addWatchFile(path);
|
|
1176
|
+
},
|
|
1177
|
+
generateBundle(_options, bundle) {
|
|
1178
|
+
outputs.push(bundleOutput(bundle));
|
|
1179
|
+
}
|
|
1180
|
+
},
|
|
1181
|
+
complete: async () => {
|
|
1182
|
+
const completedGeneration = generation;
|
|
1183
|
+
const completedOutputs = outputs;
|
|
1184
|
+
const completedCopies = copies;
|
|
1185
|
+
if (plan.declarationOutput !== void 0 && rolldown === void 0) throw new Error("The declaration-only watch graph requires the selected Rolldown API");
|
|
1186
|
+
const declarationOutputs = plan.declarationOutput === void 0 ? [] : await generateOutput(plan.declarationOutput, rolldown);
|
|
1187
|
+
const build = generatedBuild(plan, completedOutputs, declarationOutputs);
|
|
1188
|
+
const commit = async () => {
|
|
1189
|
+
if (completedGeneration !== generation) return void 0;
|
|
1190
|
+
try {
|
|
1191
|
+
return await commitBuild(plan, build.files, completedCopies, build.sources, void 0, () => completedGeneration === generation);
|
|
1192
|
+
} catch (error) {
|
|
1193
|
+
if (error instanceof StaleNodeWatchBuild) return void 0;
|
|
1194
|
+
throw error;
|
|
1195
|
+
}
|
|
1196
|
+
};
|
|
1197
|
+
const pending = commits.then(commit, commit);
|
|
1198
|
+
commits = pending.then(() => void 0, () => void 0);
|
|
1199
|
+
return pending;
|
|
1200
|
+
}
|
|
1201
|
+
};
|
|
1202
|
+
};
|
|
1203
|
+
const logger = consola.withTag("webanvil");
|
|
1204
|
+
const announced = /* @__PURE__ */ new Set();
|
|
1205
|
+
const useTool = async (name, toolchain = new Toolchain(process.cwd())) => {
|
|
1206
|
+
const tool = await toolchain.resolve(name);
|
|
1207
|
+
const identity = `${tool.packageRoot}:${tool.version}`;
|
|
1208
|
+
if (!announced.has(identity)) {
|
|
1209
|
+
announced.add(identity);
|
|
1210
|
+
logger.info(`Using ${formatResolvedTool(tool)}`);
|
|
1211
|
+
}
|
|
1212
|
+
return tool;
|
|
1213
|
+
};
|
|
1214
|
+
const useToolApi = async (name, subpath, toolchain = new Toolchain(process.cwd())) => (await useTool(name, toolchain)).import(subpath);
|
|
1215
|
+
const useToolExecutable = async (name, toolchain = new Toolchain(process.cwd())) => {
|
|
1216
|
+
const tool = await useTool(name, toolchain);
|
|
1217
|
+
if (tool.executable === void 0) throw new Error(`${tool.packageName} does not expose an executable`);
|
|
1218
|
+
return tool.executable;
|
|
1219
|
+
};
|
|
1220
|
+
const bundle = defineOption({
|
|
1221
|
+
name: "bundle",
|
|
1222
|
+
description: "Bundle the Node public roots; without it, emit their reachable graph with preserveModules.",
|
|
1223
|
+
arity: 0
|
|
1224
|
+
});
|
|
1225
|
+
const check = defineOption({
|
|
1226
|
+
name: "check",
|
|
1227
|
+
description: "Check formatting without writing files.",
|
|
1228
|
+
arity: 0
|
|
1229
|
+
});
|
|
1230
|
+
const parseMapping = (value) => {
|
|
1231
|
+
const separator = value.indexOf("=");
|
|
1232
|
+
if (separator <= 0 || separator === value.length - 1) throw new Error(`Invalid copy mapping: ${value}. Expected source=destination.`);
|
|
1233
|
+
return {
|
|
1234
|
+
from: value.slice(0, separator),
|
|
1235
|
+
to: value.slice(separator + 1)
|
|
1236
|
+
};
|
|
1237
|
+
};
|
|
1238
|
+
const copy = defineOption({
|
|
1239
|
+
name: "copy",
|
|
1240
|
+
description: "Copy source=destination mappings after the build.",
|
|
1241
|
+
schema: z.array(z.string()).transform((values) => values.map(parseMapping))
|
|
1242
|
+
});
|
|
1243
|
+
const coverage = defineOption({
|
|
1244
|
+
name: "coverage",
|
|
1245
|
+
description: "Collect test coverage with V8.",
|
|
1246
|
+
arity: 0
|
|
1247
|
+
});
|
|
1248
|
+
const declaration = defineOption({
|
|
1249
|
+
name: "declaration",
|
|
1250
|
+
description: "Emit TypeScript declarations for a Node build.",
|
|
1251
|
+
arity: 1,
|
|
1252
|
+
schema: z.enum(["true", "false"]).transform((value) => value === "true")
|
|
1253
|
+
});
|
|
1254
|
+
const environment = defineOption({
|
|
1255
|
+
name: "environment",
|
|
1256
|
+
description: "Vitest environment to use for this run.",
|
|
1257
|
+
arity: 1
|
|
1258
|
+
});
|
|
1259
|
+
const formats = defineOption({
|
|
1260
|
+
name: "formats",
|
|
1261
|
+
description: "Comma-separated Node output formats: esm,cjs.",
|
|
1262
|
+
arity: 1,
|
|
1263
|
+
schema: z.string().transform((value) => value.split(",")).pipe(z.array(z.enum(["esm", "cjs"])).min(1))
|
|
1264
|
+
});
|
|
1265
|
+
const fix$1 = defineOption({
|
|
1266
|
+
name: "fix",
|
|
1267
|
+
description: "Apply safe lint fixes.",
|
|
1268
|
+
arity: 0
|
|
1269
|
+
});
|
|
1270
|
+
const host = defineOption({
|
|
1271
|
+
name: "host",
|
|
1272
|
+
description: "Host interface for the web server.",
|
|
1273
|
+
arity: 1
|
|
1274
|
+
});
|
|
1275
|
+
const minify = defineOption({
|
|
1276
|
+
name: "minify",
|
|
1277
|
+
description: "Minify the build output: true or false.",
|
|
1278
|
+
arity: 1,
|
|
1279
|
+
schema: z.enum(["true", "false"]).transform((value) => value === "true")
|
|
1280
|
+
});
|
|
1281
|
+
const mode = defineOption({
|
|
1282
|
+
name: "mode",
|
|
1283
|
+
description: "Build mode: web uses Vite and node uses Rolldown.",
|
|
1284
|
+
arity: 1,
|
|
1285
|
+
schema: z.enum(["web", "node"])
|
|
1286
|
+
});
|
|
1287
|
+
const open = defineOption({
|
|
1288
|
+
name: "open",
|
|
1289
|
+
description: "Open the preview in the browser.",
|
|
1290
|
+
arity: 0
|
|
1291
|
+
});
|
|
1292
|
+
const outDir = defineOption({
|
|
1293
|
+
name: "out-dir",
|
|
1294
|
+
description: "Directory where the build output is written.",
|
|
1295
|
+
arity: 1
|
|
1296
|
+
});
|
|
1297
|
+
const platform = defineOption({
|
|
1298
|
+
name: "platform",
|
|
1299
|
+
description: "Node build platform: node, browser, or neutral.",
|
|
1300
|
+
arity: 1,
|
|
1301
|
+
schema: z.enum([
|
|
1302
|
+
"node",
|
|
1303
|
+
"browser",
|
|
1304
|
+
"neutral"
|
|
1305
|
+
])
|
|
1306
|
+
});
|
|
1307
|
+
const port = defineOption({
|
|
1308
|
+
name: "port",
|
|
1309
|
+
description: "Port for the web server.",
|
|
1310
|
+
arity: 1,
|
|
1311
|
+
schema: z.coerce.number().int().min(1).max(65535)
|
|
1312
|
+
});
|
|
1313
|
+
const sourcemap = defineOption({
|
|
1314
|
+
name: "sourcemap",
|
|
1315
|
+
description: "Generate source maps: true or false.",
|
|
1316
|
+
arity: 1,
|
|
1317
|
+
schema: z.enum(["true", "false"]).transform((value) => value === "true")
|
|
1318
|
+
});
|
|
1319
|
+
const target = defineOption({
|
|
1320
|
+
name: "target",
|
|
1321
|
+
description: "Production syntax target, or comma-separated targets.",
|
|
1322
|
+
arity: 1,
|
|
1323
|
+
schema: z.string().min(1).transform((value) => {
|
|
1324
|
+
const values = value.split(",").map((part) => part.trim());
|
|
1325
|
+
return values.length === 1 ? values[0] : values;
|
|
1326
|
+
}).pipe(syntaxTargetSchema)
|
|
1327
|
+
});
|
|
1328
|
+
const ui = defineOption({
|
|
1329
|
+
name: "ui",
|
|
1330
|
+
description: "Start the Vitest user interface.",
|
|
1331
|
+
arity: 0
|
|
1332
|
+
});
|
|
1333
|
+
const uiPort = defineOption({
|
|
1334
|
+
name: "ui-port",
|
|
1335
|
+
description: "Port for the Vitest user interface.",
|
|
1336
|
+
arity: 1,
|
|
1337
|
+
schema: z.coerce.number().int().min(1).max(65535)
|
|
1338
|
+
});
|
|
1339
|
+
const watch = defineOption({
|
|
1340
|
+
name: "watch",
|
|
1341
|
+
description: "Watch test files and rerun affected tests.",
|
|
1342
|
+
arity: 0
|
|
1343
|
+
});
|
|
1344
|
+
const noBundle$1 = defineOption({
|
|
1345
|
+
name: "no-bundle",
|
|
1346
|
+
description: "Emit the reachable Node graph with preserveModules, overriding configuration that enables bundling.",
|
|
1347
|
+
arity: 0
|
|
1348
|
+
});
|
|
1349
|
+
const outputFiles = (result, outDir) => {
|
|
1350
|
+
if ("on" in result) throw new Error("Web builds cannot use watch mode");
|
|
1351
|
+
return (Array.isArray(result) ? result : [result]).flatMap((output) => output.output.map((file) => resolve(outDir, file.fileName)));
|
|
1352
|
+
};
|
|
1353
|
+
const build = async (mode, entry, outDir, options = {}, plugins = [], viteConfig = {}, rolldownConfig = {}, toolchain = new Toolchain(process.cwd()), explicit = {}) => {
|
|
1354
|
+
assertSyntaxTarget(options.target);
|
|
1355
|
+
if (mode === "web" && options.platform !== void 0) throw new Error("Web builds do not accept platform; platform applies only to Node builds");
|
|
1356
|
+
logger.start(`Building ${entry}`);
|
|
1357
|
+
if (mode === "node") {
|
|
1358
|
+
const rolldown = await useToolApi("rolldown", void 0, toolchain);
|
|
1359
|
+
await runNodeBuild(await createNodeBuildPlan(entry, outDir, options, resolveRolldownPlugins(plugins), rolldownConfig, toolchain), rolldown.rolldown);
|
|
1360
|
+
logger.success(`Built ${entry} to ${outDir}`);
|
|
1361
|
+
return;
|
|
1362
|
+
}
|
|
1363
|
+
const web = await build.webConfig(entry, outDir, options, plugins, viteConfig, toolchain, explicit);
|
|
1364
|
+
const target = web.outDir;
|
|
1365
|
+
const copies = await planStaticCopies(options.copy, target);
|
|
1366
|
+
if (web.emptyOutDir && copies.length > 0) throw new Error("Vite build.emptyOutDir must be false when using static copy mappings");
|
|
1367
|
+
await assertStaticCopyDestinationsAvailable(copies, await build.publicOutputFiles(web), false);
|
|
1368
|
+
const existing = await removeOutputsIn(target);
|
|
1369
|
+
await assertStaticCopyDestinationsAvailable(copies);
|
|
1370
|
+
const output = await build.web(web);
|
|
1371
|
+
const copied = await copyStaticFiles(copies, output);
|
|
1372
|
+
await writeBuildInfo([
|
|
1373
|
+
...existing.output,
|
|
1374
|
+
...output,
|
|
1375
|
+
...copied
|
|
1376
|
+
]);
|
|
1377
|
+
logger.success(`Built ${entry} to ${outDir}`);
|
|
1378
|
+
};
|
|
1379
|
+
build.webConfig = async (entry, outDir, options, plugins, viteConfig = {}, toolchain = new Toolchain(process.cwd()), explicit = {}) => {
|
|
1380
|
+
assertSyntaxTarget(options.target);
|
|
1381
|
+
if (options.platform !== void 0) throw new Error("Web builds do not accept platform; platform applies only to Node builds");
|
|
1382
|
+
if (options.formats?.some((format) => format !== "esm")) throw new Error("Web builds only support the esm format");
|
|
1383
|
+
const preserveOutput = options.copy != null && options.copy.length > 0;
|
|
1384
|
+
const vite = await useToolApi("vite", void 0, toolchain);
|
|
1385
|
+
const webanvilDefaults = {
|
|
1386
|
+
root: process.cwd(),
|
|
1387
|
+
plugins: resolveVitePlugins(plugins),
|
|
1388
|
+
build: {
|
|
1389
|
+
...preserveOutput ? { emptyOutDir: false } : {},
|
|
1390
|
+
outDir: resolve(process.cwd(), outDir),
|
|
1391
|
+
minify: options.minify,
|
|
1392
|
+
sourcemap: options.sourcemap,
|
|
1393
|
+
...options.target === void 0 ? {} : { target: options.target },
|
|
1394
|
+
rolldownOptions: { input: resolve(process.cwd(), entry) }
|
|
1395
|
+
}
|
|
1396
|
+
};
|
|
1397
|
+
const explicitBuild = {
|
|
1398
|
+
...explicit.outDir === void 0 ? {} : { outDir: resolve(process.cwd(), explicit.outDir) },
|
|
1399
|
+
...explicit.minify === void 0 ? {} : { minify: explicit.minify },
|
|
1400
|
+
...explicit.sourcemap === void 0 ? {} : { sourcemap: explicit.sourcemap },
|
|
1401
|
+
...explicit.target === void 0 ? {} : { target: explicit.target },
|
|
1402
|
+
...explicit.entry === void 0 ? {} : { rolldownOptions: { input: resolve(process.cwd(), explicit.entry) } },
|
|
1403
|
+
...preserveOutput ? { emptyOutDir: false } : {}
|
|
1404
|
+
};
|
|
1405
|
+
const config = await hasToolConfig("vite") ? {
|
|
1406
|
+
root: process.cwd(),
|
|
1407
|
+
...Object.keys(explicitBuild).length === 0 ? {} : { build: explicitBuild }
|
|
1408
|
+
} : vite.mergeConfig(vite.mergeConfig(webanvilDefaults, viteConfig), Object.keys(explicitBuild).length === 0 ? {} : { build: explicitBuild });
|
|
1409
|
+
const resolved = await vite.resolveConfig(config, "build", "production", "production");
|
|
1410
|
+
return {
|
|
1411
|
+
config,
|
|
1412
|
+
emptyOutDir: resolved.build.emptyOutDir === true,
|
|
1413
|
+
outDir: resolved.build.outDir,
|
|
1414
|
+
publicDir: resolved.build.copyPublicDir ? resolved.publicDir : void 0,
|
|
1415
|
+
vite
|
|
1416
|
+
};
|
|
1417
|
+
};
|
|
1418
|
+
build.publicOutputFiles = async ({ outDir, publicDir }) => publicDir ? (await glob("**/*", {
|
|
1419
|
+
cwd: publicDir,
|
|
1420
|
+
onlyFiles: true,
|
|
1421
|
+
dot: true
|
|
1422
|
+
})).map((file) => resolve(outDir, file)) : [];
|
|
1423
|
+
build.web = async (web) => [...outputFiles(await web.vite.build(web.config), web.outDir), ...await build.publicOutputFiles(web)];
|
|
1424
|
+
const commandRun$1 = (toolchain) => withConfig((config) => config.build, ({ copy, declaration, formats, minify, mode, entry, "out-dir": outDir, platform, sourcemap, target }, buildConfig, resolvedConfig, explicit) => {
|
|
1425
|
+
if (explicit.bundle && explicit["no-bundle"]) throw new Error("--bundle and --no-bundle cannot be used together");
|
|
1426
|
+
const effective = resolveEffectiveBuildConfig(resolvedConfig, {
|
|
1427
|
+
bundle: explicit["no-bundle"] ? false : explicit.bundle ? true : buildConfig.bundle,
|
|
1428
|
+
copy,
|
|
1429
|
+
declaration,
|
|
1430
|
+
entries: buildConfig.entries,
|
|
1431
|
+
entry,
|
|
1432
|
+
formats,
|
|
1433
|
+
minify,
|
|
1434
|
+
mode,
|
|
1435
|
+
outDir,
|
|
1436
|
+
platform,
|
|
1437
|
+
sourcemap,
|
|
1438
|
+
target
|
|
1439
|
+
}, explicit.entry !== void 0);
|
|
1440
|
+
return build(effective.mode, effective.entry, effective.outDir, effective, resolvedConfig.plugins ?? [], resolvedConfig.vite, resolvedConfig.rolldown, toolchain, {
|
|
1441
|
+
...explicit.entry === void 0 ? {} : { entry },
|
|
1442
|
+
...explicit["out-dir"] === void 0 ? {} : { outDir },
|
|
1443
|
+
...explicit.minify === void 0 ? {} : { minify },
|
|
1444
|
+
...explicit.sourcemap === void 0 ? {} : { sourcemap },
|
|
1445
|
+
...explicit.target === void 0 ? {} : { target }
|
|
1446
|
+
});
|
|
1447
|
+
});
|
|
1448
|
+
var build_default = defineCommand({
|
|
1449
|
+
name: "build",
|
|
1450
|
+
arguments: [entry],
|
|
1451
|
+
options: [
|
|
1452
|
+
mode,
|
|
1453
|
+
outDir,
|
|
1454
|
+
bundle,
|
|
1455
|
+
noBundle$1,
|
|
1456
|
+
copy,
|
|
1457
|
+
declaration,
|
|
1458
|
+
sourcemap,
|
|
1459
|
+
minify,
|
|
1460
|
+
formats,
|
|
1461
|
+
platform,
|
|
1462
|
+
target
|
|
1463
|
+
],
|
|
1464
|
+
run: async (arguments_) => {
|
|
1465
|
+
const toolchain = new Toolchain(process.cwd());
|
|
1466
|
+
await Promise.all([toolchain.resolve("vite"), toolchain.resolve("rolldown")]);
|
|
1467
|
+
return commandRun$1(toolchain)(arguments_);
|
|
1468
|
+
}
|
|
1469
|
+
});
|
|
1470
|
+
const rebasePath = (path, cwd, configDirectory) => {
|
|
1471
|
+
if (isAbsolute(path)) return path;
|
|
1472
|
+
const rebased = relative(configDirectory, resolve(cwd, path));
|
|
1473
|
+
return rebased.startsWith(".") ? rebased : `./${rebased}`;
|
|
1474
|
+
};
|
|
1475
|
+
const rebaseTailwind = (value, cwd, configDirectory) => {
|
|
1476
|
+
if (typeof value !== "object" || value === null || value instanceof Array) return value;
|
|
1477
|
+
const tailwind = { ...value };
|
|
1478
|
+
for (const key of ["config", "stylesheet"]) if (typeof tailwind[key] === "string") tailwind[key] = rebasePath(tailwind[key], cwd, configDirectory);
|
|
1479
|
+
return tailwind;
|
|
1480
|
+
};
|
|
1481
|
+
const rebaseOxfmtConfig = (config, cwd, configDirectory) => ({
|
|
1482
|
+
...config,
|
|
1483
|
+
...config.sortTailwindcss === void 0 ? {} : { sortTailwindcss: rebaseTailwind(config.sortTailwindcss, cwd, configDirectory) },
|
|
1484
|
+
...config.overrides instanceof Array ? { overrides: config.overrides.map((override) => {
|
|
1485
|
+
if (typeof override !== "object" || override === null || override instanceof Array) return override;
|
|
1486
|
+
const value = override;
|
|
1487
|
+
const overrideOptions = typeof value.options === "object" && value.options !== null && !(value.options instanceof Array) ? value.options : void 0;
|
|
1488
|
+
const options = overrideOptions === void 0 ? value.options : {
|
|
1489
|
+
...overrideOptions,
|
|
1490
|
+
...overrideOptions.sortTailwindcss === void 0 ? {} : { sortTailwindcss: rebaseTailwind(overrideOptions.sortTailwindcss, cwd, configDirectory) }
|
|
1491
|
+
};
|
|
1492
|
+
return {
|
|
1493
|
+
...value,
|
|
1494
|
+
...options === void 0 ? {} : { options }
|
|
1495
|
+
};
|
|
1496
|
+
}) } : {}
|
|
1497
|
+
});
|
|
1498
|
+
const rebasePlugin = (plugin, cwd, configDirectory) => {
|
|
1499
|
+
if (typeof plugin === "string") return plugin.startsWith(".") ? rebasePath(plugin, cwd, configDirectory) : plugin;
|
|
1500
|
+
if (typeof plugin !== "object" || plugin === null || plugin instanceof Array) return plugin;
|
|
1501
|
+
const value = plugin;
|
|
1502
|
+
return typeof value.specifier === "string" && value.specifier.startsWith(".") ? {
|
|
1503
|
+
...value,
|
|
1504
|
+
specifier: rebasePath(value.specifier, cwd, configDirectory)
|
|
1505
|
+
} : value;
|
|
1506
|
+
};
|
|
1507
|
+
const rebaseOxlintConfig = (config, cwd, configDirectory) => ({
|
|
1508
|
+
...config,
|
|
1509
|
+
...config.extends instanceof Array ? { extends: config.extends.map((path) => typeof path === "string" ? rebasePath(path, cwd, configDirectory) : path) } : {},
|
|
1510
|
+
...config.jsPlugins instanceof Array ? { jsPlugins: config.jsPlugins.map((plugin) => rebasePlugin(plugin, cwd, configDirectory)) } : {},
|
|
1511
|
+
...config.overrides instanceof Array ? { overrides: config.overrides.map((override) => {
|
|
1512
|
+
if (typeof override !== "object" || override === null || override instanceof Array) return override;
|
|
1513
|
+
const value = override;
|
|
1514
|
+
return {
|
|
1515
|
+
...value,
|
|
1516
|
+
...value.jsPlugins instanceof Array ? { jsPlugins: value.jsPlugins.map((plugin) => rebasePlugin(plugin, cwd, configDirectory)) } : {}
|
|
1517
|
+
};
|
|
1518
|
+
}) } : {}
|
|
1519
|
+
});
|
|
1520
|
+
const runTool = async (name, arguments_, config) => {
|
|
1521
|
+
if (name !== "tsgo" && await hasOxcConfig(name)) config = void 0;
|
|
1522
|
+
const executable = await useToolExecutable(name === "tsgo" ? "typescript-native" : name);
|
|
1523
|
+
const cwd = process.cwd();
|
|
1524
|
+
const configDirectory = join(cwd, ".webanvil");
|
|
1525
|
+
const configPath = config === void 0 ? void 0 : join(configDirectory, `${name}-${randomUUID()}.json`);
|
|
1526
|
+
const sourceConfig = config;
|
|
1527
|
+
const ignorePatterns = sourceConfig?.ignorePatterns instanceof Array ? sourceConfig.ignorePatterns.filter((pattern) => typeof pattern === "string") : [];
|
|
1528
|
+
const configWithoutIgnores = sourceConfig === void 0 ? void 0 : Object.fromEntries(Object.entries(sourceConfig).filter(([key]) => key !== "ignorePatterns"));
|
|
1529
|
+
const generatedConfig = configWithoutIgnores === void 0 || configPath === void 0 ? configWithoutIgnores : name === "oxfmt" ? rebaseOxfmtConfig(configWithoutIgnores, cwd, configDirectory) : rebaseOxlintConfig(configWithoutIgnores, cwd, configDirectory);
|
|
1530
|
+
const ignoreArguments = name === "oxfmt" ? ignorePatterns.map((pattern) => pattern.startsWith("!") ? pattern.slice(1) : `!${pattern}`) : ignorePatterns.flatMap((pattern) => ["--ignore-pattern", pattern]);
|
|
1531
|
+
const internalIgnoreArguments = name === "oxfmt" ? ["!**/.webanvil/**"] : ["--ignore-pattern", ".webanvil/**"];
|
|
1532
|
+
const toolArguments = name === "tsgo" ? arguments_ : [
|
|
1533
|
+
...configPath === void 0 ? [] : ["--config", configPath],
|
|
1534
|
+
...ignoreArguments,
|
|
1535
|
+
...internalIgnoreArguments,
|
|
1536
|
+
...name === "oxfmt" && arguments_.length === 0 ? ["."] : arguments_
|
|
1537
|
+
];
|
|
1538
|
+
if (configPath !== void 0) {
|
|
1539
|
+
await mkdir(configDirectory, { recursive: true });
|
|
1540
|
+
await writeFile(configPath, `${JSON.stringify(generatedConfig)}\n`);
|
|
1541
|
+
}
|
|
1542
|
+
try {
|
|
1543
|
+
const result = await execa(executable, toolArguments, {
|
|
1544
|
+
reject: false,
|
|
1545
|
+
stdio: "inherit"
|
|
1546
|
+
});
|
|
1547
|
+
if (result.exitCode !== 0) throw new Error(`${name} exited with code ${result.exitCode ?? "unknown"}`);
|
|
1548
|
+
} finally {
|
|
1549
|
+
if (configPath !== void 0) await rm(configPath, { force: true });
|
|
1550
|
+
}
|
|
1551
|
+
};
|
|
1552
|
+
const format = async (paths, check = false, config) => {
|
|
1553
|
+
logger.start(check ? "Checking formatting" : "Formatting");
|
|
1554
|
+
await runTool("oxfmt", [...check ? ["--check"] : [], ...paths], config);
|
|
1555
|
+
logger.success(check ? "Formatting passed" : "Formatted");
|
|
1556
|
+
};
|
|
1557
|
+
const runFormat = withConfig((config) => config.format, ({ paths, check }, config) => format(paths, check, config));
|
|
1558
|
+
var format_default = defineCommand({
|
|
1559
|
+
name: "format",
|
|
1560
|
+
arguments: [paths],
|
|
1561
|
+
options: [check],
|
|
1562
|
+
run: async (arguments_) => {
|
|
1563
|
+
await useTool("oxfmt");
|
|
1564
|
+
return runFormat(arguments_);
|
|
1565
|
+
}
|
|
1566
|
+
});
|
|
1567
|
+
const lint = async (paths, fix = false, config) => {
|
|
1568
|
+
logger.start("Linting");
|
|
1569
|
+
await runTool("oxlint", [
|
|
1570
|
+
...fix ? ["--fix"] : [],
|
|
1571
|
+
"--deny-warnings",
|
|
1572
|
+
...paths
|
|
1573
|
+
], config);
|
|
1574
|
+
logger.success("Lint passed");
|
|
1575
|
+
};
|
|
1576
|
+
const runLint = withConfig((config) => config.lint, ({ paths, fix }, config) => lint(paths, fix, config));
|
|
1577
|
+
var lint_default = defineCommand({
|
|
1578
|
+
name: "lint",
|
|
1579
|
+
arguments: [paths],
|
|
1580
|
+
options: [fix$1],
|
|
1581
|
+
run: async (arguments_) => {
|
|
1582
|
+
await useTool("oxlint");
|
|
1583
|
+
return runLint(arguments_);
|
|
1584
|
+
}
|
|
1585
|
+
});
|
|
1586
|
+
const typecheckArguments = async (paths) => {
|
|
1587
|
+
if (paths.length > 0) return [
|
|
1588
|
+
"--noEmit",
|
|
1589
|
+
"--ignoreConfig",
|
|
1590
|
+
...paths
|
|
1591
|
+
];
|
|
1592
|
+
return getTsconfig(process.cwd(), { typescriptVersion: false })?.config.references?.length ? ["-b", "--noEmit"] : ["--noEmit"];
|
|
1593
|
+
};
|
|
1594
|
+
const typecheck = async (paths) => {
|
|
1595
|
+
logger.start("Type checking");
|
|
1596
|
+
await runTool("tsgo", await typecheckArguments(paths));
|
|
1597
|
+
logger.success("Type check passed");
|
|
1598
|
+
};
|
|
1599
|
+
var typecheck_default = defineCommand({
|
|
1600
|
+
name: "typecheck",
|
|
1601
|
+
arguments: [paths],
|
|
1602
|
+
run: ({ paths }) => typecheck(paths)
|
|
1603
|
+
});
|
|
1604
|
+
const fix = defineOption({
|
|
1605
|
+
name: "fix",
|
|
1606
|
+
description: "Format files and apply safe lint fixes.",
|
|
1607
|
+
arity: 0
|
|
1608
|
+
});
|
|
1609
|
+
const checkProject = async (fixFiles = false, config = {}) => {
|
|
1610
|
+
await format([], !fixFiles, config.format);
|
|
1611
|
+
await lint([], fixFiles, config.lint);
|
|
1612
|
+
await typecheck([]);
|
|
1613
|
+
};
|
|
1614
|
+
var check_default = defineCommand({
|
|
1615
|
+
name: "check",
|
|
1616
|
+
description: "Check formatting, linting, and types, stopping at the first failure.",
|
|
1617
|
+
options: [fix],
|
|
1618
|
+
run: async ({ fix }) => {
|
|
1619
|
+
await Promise.all([
|
|
1620
|
+
useTool("oxfmt"),
|
|
1621
|
+
useTool("oxlint"),
|
|
1622
|
+
useTool("typescript-native")
|
|
1623
|
+
]);
|
|
1624
|
+
const { config } = await loadConfig$1();
|
|
1625
|
+
await checkProject(fix, config);
|
|
1626
|
+
}
|
|
1627
|
+
});
|
|
1628
|
+
const clean = async () => {
|
|
1629
|
+
const info = await readBuildInfo();
|
|
1630
|
+
await removeBuildOutputs(info.output);
|
|
1631
|
+
await clearBuildInfo();
|
|
1632
|
+
logger.success(`Removed ${info.output.length} build output${info.output.length === 1 ? "" : "s"}`);
|
|
1633
|
+
};
|
|
1634
|
+
var clean_default = defineCommand({
|
|
1635
|
+
name: "clean",
|
|
1636
|
+
run: clean
|
|
1637
|
+
});
|
|
1638
|
+
const untilTerminated = () => new Promise((resolve) => {
|
|
1639
|
+
const terminate = () => {
|
|
1640
|
+
process.off("SIGINT", terminate);
|
|
1641
|
+
process.off("SIGTERM", terminate);
|
|
1642
|
+
resolve();
|
|
1643
|
+
};
|
|
1644
|
+
process.once("SIGINT", terminate);
|
|
1645
|
+
process.once("SIGTERM", terminate);
|
|
1646
|
+
});
|
|
1647
|
+
const noBundle = defineOption({
|
|
1648
|
+
name: "no-bundle",
|
|
1649
|
+
description: "Emit the reachable Node graph with preserveModules, overriding configuration that enables bundling.",
|
|
1650
|
+
arity: 0
|
|
1651
|
+
});
|
|
1652
|
+
const dev = async (mode, entry, outDir, host, port, plugins = [], options = {}, viteConfig = {}, rolldownConfig = {}, toolchain = new Toolchain(process.cwd())) => {
|
|
1653
|
+
assertSyntaxTarget(options.target);
|
|
1654
|
+
if (mode === "web" && options.platform !== void 0) throw new Error("Web development does not accept platform; platform applies only to Node builds");
|
|
1655
|
+
logger.start(`Starting ${mode} development mode`);
|
|
1656
|
+
if (mode === "node" && (host !== void 0 || port !== void 0)) throw new Error("--host and --port are only available in web development mode");
|
|
1657
|
+
if (mode === "web") await dev.web(host, port, plugins, untilTerminated, viteConfig, toolchain);
|
|
1658
|
+
else await dev.node(entry, outDir, plugins, untilTerminated, options, rolldownConfig, toolchain);
|
|
1659
|
+
};
|
|
1660
|
+
dev.web = async (host, port, plugins = [], waitForTermination = untilTerminated, viteConfig = {}, toolchain = new Toolchain(process.cwd())) => {
|
|
1661
|
+
const vite = await useToolApi("vite", void 0, toolchain);
|
|
1662
|
+
const webanvilDefaults = {
|
|
1663
|
+
root: process.cwd(),
|
|
1664
|
+
plugins: resolveVitePlugins(plugins)
|
|
1665
|
+
};
|
|
1666
|
+
const explicit = {
|
|
1667
|
+
root: process.cwd(),
|
|
1668
|
+
...host === void 0 && port === void 0 ? {} : { server: {
|
|
1669
|
+
host,
|
|
1670
|
+
port
|
|
1671
|
+
} }
|
|
1672
|
+
};
|
|
1673
|
+
const config = await hasToolConfig("vite") ? explicit : vite.mergeConfig(vite.mergeConfig(webanvilDefaults, viteConfig), explicit);
|
|
1674
|
+
const server = await vite.createServer(config);
|
|
1675
|
+
try {
|
|
1676
|
+
await server.listen();
|
|
1677
|
+
server.printUrls();
|
|
1678
|
+
await waitForTermination();
|
|
1679
|
+
} finally {
|
|
1680
|
+
await server.close();
|
|
1681
|
+
}
|
|
1682
|
+
};
|
|
1683
|
+
dev.node = async (entry, outDir, plugins = [], waitForTermination = untilTerminated, options = {}, rolldownConfig = {}, toolchain = new Toolchain(process.cwd())) => {
|
|
1684
|
+
assertSyntaxTarget(options.target);
|
|
1685
|
+
const rolldown = await useToolApi("rolldown", void 0, toolchain);
|
|
1686
|
+
const plan = await createNodeBuildPlan(entry, outDir, options, resolveRolldownPlugins(plugins), rolldownConfig, toolchain);
|
|
1687
|
+
const lifecycle = nodeWatchLifecycle(plan, rolldown.rolldown);
|
|
1688
|
+
const watcher = rolldown.watch({
|
|
1689
|
+
...plan.output.input,
|
|
1690
|
+
plugins: [...plan.output.input.plugins ?? [], lifecycle.plugin],
|
|
1691
|
+
watch: {
|
|
1692
|
+
...typeof plan.output.input.watch === "object" ? plan.output.input.watch : {},
|
|
1693
|
+
skipWrite: true
|
|
1694
|
+
},
|
|
1695
|
+
output: plan.output.output
|
|
1696
|
+
});
|
|
1697
|
+
let failed = false;
|
|
1698
|
+
watcher.on("event", async (event) => {
|
|
1699
|
+
if (event.code === "START") {
|
|
1700
|
+
failed = false;
|
|
1701
|
+
lifecycle.abort();
|
|
1702
|
+
}
|
|
1703
|
+
if (event.code === "BUNDLE_END") await event.result.close();
|
|
1704
|
+
if (event.code === "END" && !failed) try {
|
|
1705
|
+
if (await lifecycle.complete() !== void 0) logger.success(`Built ${entry} to ${outDir}`);
|
|
1706
|
+
} catch (error) {
|
|
1707
|
+
logger.error(error);
|
|
1708
|
+
}
|
|
1709
|
+
if (event.code === "ERROR") {
|
|
1710
|
+
failed = true;
|
|
1711
|
+
lifecycle.abort();
|
|
1712
|
+
await event.result.close();
|
|
1713
|
+
logger.error(event.error);
|
|
1714
|
+
}
|
|
1715
|
+
});
|
|
1716
|
+
try {
|
|
1717
|
+
await waitForTermination();
|
|
1718
|
+
} finally {
|
|
1719
|
+
lifecycle.abort();
|
|
1720
|
+
await watcher.close();
|
|
1721
|
+
}
|
|
1722
|
+
};
|
|
1723
|
+
const commandRun = (toolchain) => withConfig((config) => config.build, ({ copy, declaration, formats, minify, mode, entry, "out-dir": outDir, host, platform, port, sourcemap, target }, buildConfig, resolvedConfig, explicit) => {
|
|
1724
|
+
if (explicit.bundle && explicit["no-bundle"]) throw new Error("--bundle and --no-bundle cannot be used together");
|
|
1725
|
+
const effective = resolveEffectiveBuildConfig(resolvedConfig, {
|
|
1726
|
+
bundle: explicit["no-bundle"] ? false : explicit.bundle ? true : buildConfig.bundle,
|
|
1727
|
+
copy,
|
|
1728
|
+
declaration,
|
|
1729
|
+
entries: buildConfig.entries,
|
|
1730
|
+
entry,
|
|
1731
|
+
formats,
|
|
1732
|
+
minify,
|
|
1733
|
+
mode,
|
|
1734
|
+
outDir,
|
|
1735
|
+
platform,
|
|
1736
|
+
sourcemap,
|
|
1737
|
+
target
|
|
1738
|
+
}, explicit.entry !== void 0);
|
|
1739
|
+
return dev(effective.mode, effective.entry, effective.outDir, host, port, resolvedConfig.plugins ?? [], effective, resolvedConfig.vite, resolvedConfig.rolldown, toolchain);
|
|
1740
|
+
});
|
|
1741
|
+
var dev_default = defineCommand({
|
|
1742
|
+
name: "dev",
|
|
1743
|
+
arguments: [entry],
|
|
1744
|
+
options: [
|
|
1745
|
+
mode,
|
|
1746
|
+
outDir,
|
|
1747
|
+
host,
|
|
1748
|
+
port,
|
|
1749
|
+
bundle,
|
|
1750
|
+
noBundle,
|
|
1751
|
+
copy,
|
|
1752
|
+
declaration,
|
|
1753
|
+
sourcemap,
|
|
1754
|
+
minify,
|
|
1755
|
+
formats,
|
|
1756
|
+
platform,
|
|
1757
|
+
target
|
|
1758
|
+
],
|
|
1759
|
+
run: async (arguments_) => {
|
|
1760
|
+
const toolchain = new Toolchain(process.cwd());
|
|
1761
|
+
await Promise.all([toolchain.resolve("vite"), toolchain.resolve("rolldown")]);
|
|
1762
|
+
return commandRun(toolchain)(arguments_);
|
|
1763
|
+
}
|
|
1764
|
+
});
|
|
1765
|
+
const preview = async (outDir, host, port, useOutDir = false, waitForTermination = untilTerminated, openBrowser, viteConfig = {}) => {
|
|
1766
|
+
logger.start("Starting web preview");
|
|
1767
|
+
const vite = await useToolApi("vite");
|
|
1768
|
+
const hasViteConfig = await hasToolConfig("vite");
|
|
1769
|
+
const defaults = {
|
|
1770
|
+
root: process.cwd(),
|
|
1771
|
+
...hasViteConfig ? {} : { build: { outDir: resolve(process.cwd(), outDir) } }
|
|
1772
|
+
};
|
|
1773
|
+
const native = hasViteConfig ? defaults : vite.mergeConfig(defaults, viteConfig);
|
|
1774
|
+
const config = vite.mergeConfig(native, {
|
|
1775
|
+
...useOutDir ? { build: { outDir: resolve(process.cwd(), outDir) } } : {},
|
|
1776
|
+
preview: {
|
|
1777
|
+
...host === void 0 ? {} : { host },
|
|
1778
|
+
...port === void 0 ? {} : { port },
|
|
1779
|
+
...openBrowser === void 0 ? {} : { open: openBrowser }
|
|
1780
|
+
}
|
|
1781
|
+
});
|
|
1782
|
+
const server = await vite.preview(config);
|
|
1783
|
+
try {
|
|
1784
|
+
server.printUrls();
|
|
1785
|
+
await waitForTermination();
|
|
1786
|
+
} finally {
|
|
1787
|
+
await server.close();
|
|
1788
|
+
}
|
|
1789
|
+
};
|
|
1790
|
+
var preview_default = defineCommand({
|
|
1791
|
+
name: "preview",
|
|
1792
|
+
options: [
|
|
1793
|
+
outDir,
|
|
1794
|
+
host,
|
|
1795
|
+
port,
|
|
1796
|
+
open
|
|
1797
|
+
],
|
|
1798
|
+
run: async ({ "out-dir": outDir, host, port, open }) => {
|
|
1799
|
+
await useTool("vite");
|
|
1800
|
+
const { config } = await loadConfig$1();
|
|
1801
|
+
return preview(outDir ?? config.build?.outDir ?? "dist", host, port, outDir !== void 0, untilTerminated, open, config.vite);
|
|
1802
|
+
}
|
|
1803
|
+
});
|
|
1804
|
+
const test = async (filters, config = {}, options = {}, waitForTermination = untilTerminated) => {
|
|
1805
|
+
if (options.uiPort !== void 0 && options.ui !== true) throw new Error("--ui-port requires --ui");
|
|
1806
|
+
logger.start("Running tests");
|
|
1807
|
+
const hasVitestConfig = await hasToolConfig("vitest");
|
|
1808
|
+
const persistent = options.watch === true || options.ui === true;
|
|
1809
|
+
const { startVitest } = await useToolApi("vitest", "node");
|
|
1810
|
+
const nativeConfig = hasVitestConfig ? {} : config;
|
|
1811
|
+
const nativeCoverage = typeof nativeConfig.coverage === "object" && nativeConfig.coverage !== null ? nativeConfig.coverage : {};
|
|
1812
|
+
const nativeApi = typeof nativeConfig.api === "object" && nativeConfig.api !== null ? nativeConfig.api : {};
|
|
1813
|
+
const vitest = await startVitest("test", filters, {
|
|
1814
|
+
...nativeConfig,
|
|
1815
|
+
passWithNoTests: true,
|
|
1816
|
+
run: !persistent,
|
|
1817
|
+
watch: persistent,
|
|
1818
|
+
...options.environment === void 0 ? {} : { environment: options.environment },
|
|
1819
|
+
...options.coverage ? { coverage: {
|
|
1820
|
+
...nativeCoverage,
|
|
1821
|
+
enabled: true,
|
|
1822
|
+
provider: "v8"
|
|
1823
|
+
} } : {},
|
|
1824
|
+
...options.ui ? { ui: true } : {},
|
|
1825
|
+
...options.uiPort === void 0 ? {} : { api: {
|
|
1826
|
+
...nativeApi,
|
|
1827
|
+
host: "127.0.0.1",
|
|
1828
|
+
port: options.uiPort,
|
|
1829
|
+
strictPort: true
|
|
1830
|
+
} }
|
|
1831
|
+
});
|
|
1832
|
+
if (persistent) {
|
|
1833
|
+
try {
|
|
1834
|
+
await waitForTermination();
|
|
1835
|
+
} finally {
|
|
1836
|
+
await vitest.close();
|
|
1837
|
+
}
|
|
1838
|
+
return;
|
|
1839
|
+
}
|
|
1840
|
+
const failed = vitest.state.getFiles().some((file) => file.result?.state === "fail") || vitest.state.getUnhandledErrors().length > 0;
|
|
1841
|
+
await vitest.close();
|
|
1842
|
+
if (failed) throw new Error("Tests failed");
|
|
1843
|
+
logger.success("Tests passed");
|
|
1844
|
+
};
|
|
1845
|
+
const runTest = withConfig((config) => config.test, ({ filters, environment, coverage, ui, "ui-port": uiPort, watch }, config, _resolvedConfig, explicitArguments) => test(filters, config, {
|
|
1846
|
+
coverage,
|
|
1847
|
+
environment: explicitArguments.environment === void 0 ? void 0 : environment,
|
|
1848
|
+
ui,
|
|
1849
|
+
uiPort,
|
|
1850
|
+
watch
|
|
1851
|
+
}));
|
|
1852
|
+
var test_default = defineCommand({
|
|
1853
|
+
name: "test",
|
|
1854
|
+
arguments: [filters],
|
|
1855
|
+
options: [
|
|
1856
|
+
environment,
|
|
1857
|
+
watch,
|
|
1858
|
+
coverage,
|
|
1859
|
+
ui,
|
|
1860
|
+
uiPort
|
|
1861
|
+
],
|
|
1862
|
+
run: async (arguments_) => {
|
|
1863
|
+
await useTool("vitest");
|
|
1864
|
+
return runTest(arguments_);
|
|
1865
|
+
}
|
|
1866
|
+
});
|
|
1867
|
+
export { assertSyntaxTarget, build, buildConfigSchema, build_default, bundle, check, checkProject, check_default, clean, clean_default, copy, copyMappingSchema, coverage, declaration, defaultConfig, defineConfig, definePlugin, dev, dev_default, effectiveUserConfigSchema, entry, environment, filters, fix$1 as fix, format, formatConfigSchema, format_default, formats, host, isUnpluginAdapter, isWebAnvilPlugin, lint, lintConfigSchema, lint_default, loadConfig$1 as loadConfig, logger, minify, mode, open, outDir, paths, platform, port, preview, preview_default, resolveEffectiveBuildConfig, resolveRolldownPlugins, resolveVitePlugins, rolldownConfigSchema, sourcemap, syntaxTargetSchema, target, test, testConfigSchema, test_default, typecheck, typecheck_default, ui, uiPort, userConfigSchema, viteConfigSchema, watch, withConfig };
|