@nasti-toolchain/nasti 2.2.0 → 2.3.1
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/README.md +37 -1
- package/dist/cli.cjs +552 -123
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +552 -123
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +554 -121
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +109 -11
- package/dist/index.d.ts +109 -11
- package/dist/index.js +551 -121
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/dist/cli.cjs
CHANGED
|
@@ -226,6 +226,92 @@ var init_defaults = __esm({
|
|
|
226
226
|
}
|
|
227
227
|
});
|
|
228
228
|
|
|
229
|
+
// src/core/plugin-api.ts
|
|
230
|
+
function orderPlugins(plugins) {
|
|
231
|
+
const baseline = plugins.map((plugin, index2) => ({ plugin, index: index2 })).sort((a, b) => enforceRank(a.plugin) - enforceRank(b.plugin) || a.index - b.index).map(({ plugin }) => plugin);
|
|
232
|
+
const indexesByName = /* @__PURE__ */ new Map();
|
|
233
|
+
baseline.forEach((plugin, index2) => {
|
|
234
|
+
const indexes = indexesByName.get(plugin.name) ?? [];
|
|
235
|
+
indexes.push(index2);
|
|
236
|
+
indexesByName.set(plugin.name, indexes);
|
|
237
|
+
});
|
|
238
|
+
const edges = baseline.map(() => /* @__PURE__ */ new Set());
|
|
239
|
+
const indegree = baseline.map(() => 0);
|
|
240
|
+
const addEdge = (from, to) => {
|
|
241
|
+
if (from === to || edges[from].has(to)) return;
|
|
242
|
+
edges[from].add(to);
|
|
243
|
+
indegree[to]++;
|
|
244
|
+
};
|
|
245
|
+
baseline.forEach((plugin, current) => {
|
|
246
|
+
for (const dependency of plugin.pre ?? []) {
|
|
247
|
+
for (const before of indexesByName.get(dependency) ?? []) addEdge(before, current);
|
|
248
|
+
}
|
|
249
|
+
for (const dependency of plugin.post ?? []) {
|
|
250
|
+
for (const after of indexesByName.get(dependency) ?? []) addEdge(current, after);
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
const ready = indegree.map((degree, index2) => ({ degree, index: index2 })).filter(({ degree }) => degree === 0).map(({ index: index2 }) => index2);
|
|
254
|
+
const ordered = [];
|
|
255
|
+
while (ready.length > 0) {
|
|
256
|
+
ready.sort((a, b) => a - b);
|
|
257
|
+
const current = ready.shift();
|
|
258
|
+
ordered.push(baseline[current]);
|
|
259
|
+
for (const next of edges[current]) {
|
|
260
|
+
indegree[next]--;
|
|
261
|
+
if (indegree[next] === 0) ready.push(next);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (ordered.length !== baseline.length) {
|
|
265
|
+
const cyclic = baseline.filter((_, index2) => indegree[index2] > 0).map((plugin) => plugin.name);
|
|
266
|
+
throw new Error(
|
|
267
|
+
`[nasti] circular plugin setup dependency: ${[...new Set(cyclic)].join(", ")}`
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
return ordered;
|
|
271
|
+
}
|
|
272
|
+
async function setupPluginApi(config, plugins) {
|
|
273
|
+
const exposed = /* @__PURE__ */ new Map();
|
|
274
|
+
const api = {
|
|
275
|
+
config,
|
|
276
|
+
logger: config.logger,
|
|
277
|
+
expose(key, value) {
|
|
278
|
+
if (exposed.has(key) && exposed.get(key) !== value) {
|
|
279
|
+
throw new Error(`[nasti] plugin API key already exposed: ${String(key)}`);
|
|
280
|
+
}
|
|
281
|
+
exposed.set(key, value);
|
|
282
|
+
},
|
|
283
|
+
useExposed(key) {
|
|
284
|
+
return exposed.get(key);
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
apiByConfig.set(config, api);
|
|
288
|
+
for (const plugin of plugins) {
|
|
289
|
+
await plugin.setup?.(api);
|
|
290
|
+
}
|
|
291
|
+
return api;
|
|
292
|
+
}
|
|
293
|
+
function getPluginApi(config) {
|
|
294
|
+
const api = apiByConfig.get(config);
|
|
295
|
+
if (!api) {
|
|
296
|
+
throw new Error(
|
|
297
|
+
"[nasti] internal: plugin API requested before setup; getPluginApi requires the original ResolvedConfig reference registered by setupPluginApi, not a shallow copy"
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
return api;
|
|
301
|
+
}
|
|
302
|
+
function enforceRank(plugin) {
|
|
303
|
+
if (plugin.enforce === "pre") return 0;
|
|
304
|
+
if (plugin.enforce === "post") return 2;
|
|
305
|
+
return 1;
|
|
306
|
+
}
|
|
307
|
+
var apiByConfig;
|
|
308
|
+
var init_plugin_api = __esm({
|
|
309
|
+
"src/core/plugin-api.ts"() {
|
|
310
|
+
"use strict";
|
|
311
|
+
apiByConfig = /* @__PURE__ */ new WeakMap();
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
|
|
229
315
|
// src/config/index.ts
|
|
230
316
|
function loadTsconfigPaths(root) {
|
|
231
317
|
const tsconfigPath = import_node_path.default.resolve(root, "tsconfig.json");
|
|
@@ -260,6 +346,43 @@ async function loadConfigFromFile(root) {
|
|
|
260
346
|
}
|
|
261
347
|
return {};
|
|
262
348
|
}
|
|
349
|
+
function detectFramework(root) {
|
|
350
|
+
const sourceRoot = import_node_path.default.resolve(root, "src");
|
|
351
|
+
if (containsVueFile(sourceRoot)) return "vue";
|
|
352
|
+
const packagePath = import_node_path.default.resolve(root, "package.json");
|
|
353
|
+
if (import_node_fs.default.existsSync(packagePath)) {
|
|
354
|
+
try {
|
|
355
|
+
const pkg = JSON.parse(import_node_fs.default.readFileSync(packagePath, "utf-8"));
|
|
356
|
+
const dependencies = {
|
|
357
|
+
...pkg.dependencies ?? {},
|
|
358
|
+
...pkg.devDependencies ?? {},
|
|
359
|
+
...pkg.peerDependencies ?? {},
|
|
360
|
+
...pkg.optionalDependencies ?? {}
|
|
361
|
+
};
|
|
362
|
+
const hasVue = "vue" in dependencies || "@vue/runtime-dom" in dependencies;
|
|
363
|
+
const hasReact = "react" in dependencies || "react-dom" in dependencies;
|
|
364
|
+
if (hasVue && !hasReact) return "vue";
|
|
365
|
+
if (hasReact) return "react";
|
|
366
|
+
if (hasVue) return "vue";
|
|
367
|
+
} catch {
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return "react";
|
|
371
|
+
}
|
|
372
|
+
function containsVueFile(dir, depth = 0) {
|
|
373
|
+
if (depth > 5 || !import_node_fs.default.existsSync(dir)) return false;
|
|
374
|
+
try {
|
|
375
|
+
for (const entry of import_node_fs.default.readdirSync(dir, { withFileTypes: true })) {
|
|
376
|
+
if (entry.isFile() && entry.name.endsWith(".vue")) return true;
|
|
377
|
+
if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== "dist" && containsVueFile(import_node_path.default.join(dir, entry.name), depth + 1)) {
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
} catch {
|
|
382
|
+
return false;
|
|
383
|
+
}
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
263
386
|
async function loadTsConfig(filePath) {
|
|
264
387
|
const { transformSync: transformSync2 } = await import("oxc-transform");
|
|
265
388
|
const code = import_node_fs.default.readFileSync(filePath, "utf-8");
|
|
@@ -306,7 +429,7 @@ async function resolveConfig(inlineConfig = {}, command) {
|
|
|
306
429
|
base: merged.base ?? defaults.base,
|
|
307
430
|
mode,
|
|
308
431
|
target: merged.target ?? defaults.target,
|
|
309
|
-
framework: merged.framework ?? defaults.framework,
|
|
432
|
+
framework: (merged.framework ?? defaults.framework) === "auto" ? detectFramework(root) : merged.framework,
|
|
310
433
|
command,
|
|
311
434
|
resolve: {
|
|
312
435
|
// tsconfig paths 优先级最低:tsconfig < defaults < user config
|
|
@@ -353,7 +476,12 @@ async function resolveConfig(inlineConfig = {}, command) {
|
|
|
353
476
|
if (envOptions.build) Object.assign(resolved.build, envOptions.build);
|
|
354
477
|
resolved.environments.client = {
|
|
355
478
|
consumer,
|
|
356
|
-
entry:
|
|
479
|
+
entry: normalizeEnvironmentEntries(envOptions.entry, root),
|
|
480
|
+
html: import_node_path.default.resolve(
|
|
481
|
+
root,
|
|
482
|
+
envOptions.html ?? (resolved.target === "electron" ? resolved.electron.renderer : "index.html")
|
|
483
|
+
),
|
|
484
|
+
driver: envOptions.driver,
|
|
357
485
|
// 同引用 —— 精确镜像(assertClientEnvironmentMirror 校验)
|
|
358
486
|
resolve: resolved.resolve,
|
|
359
487
|
build: resolved.build
|
|
@@ -362,7 +490,9 @@ async function resolveConfig(inlineConfig = {}, command) {
|
|
|
362
490
|
}
|
|
363
491
|
resolved.environments[name] = {
|
|
364
492
|
consumer,
|
|
365
|
-
entry: (
|
|
493
|
+
entry: normalizeEnvironmentEntries(envOptions.entry, root),
|
|
494
|
+
html: envOptions.consumer === "client" && envOptions.html ? import_node_path.default.resolve(root, envOptions.html) : void 0,
|
|
495
|
+
driver: envOptions.driver,
|
|
366
496
|
resolve: {
|
|
367
497
|
alias: { ...resolved.resolve.alias, ...envOptions.resolve?.alias },
|
|
368
498
|
extensions: envOptions.resolve?.extensions ?? [...resolved.resolve.extensions],
|
|
@@ -381,12 +511,13 @@ async function resolveConfig(inlineConfig = {}, command) {
|
|
|
381
511
|
};
|
|
382
512
|
}
|
|
383
513
|
assertClientEnvironmentMirror(resolved);
|
|
384
|
-
const filteredPlugins = rawPlugins.filter((p) => {
|
|
514
|
+
const filteredPlugins = orderPlugins(rawPlugins.filter((p) => {
|
|
385
515
|
if (!p.apply) return true;
|
|
386
516
|
if (typeof p.apply === "function") return p.apply(resolved, env);
|
|
387
517
|
return p.apply === command;
|
|
388
|
-
});
|
|
518
|
+
}));
|
|
389
519
|
resolved.plugins = filteredPlugins;
|
|
520
|
+
await setupPluginApi(resolved, filteredPlugins);
|
|
390
521
|
if (resolved.target === "electron") {
|
|
391
522
|
const autoExternal = detectNativeDeps(root);
|
|
392
523
|
if (autoExternal.length > 0) {
|
|
@@ -402,6 +533,10 @@ async function resolveConfig(inlineConfig = {}, command) {
|
|
|
402
533
|
}
|
|
403
534
|
return resolved;
|
|
404
535
|
}
|
|
536
|
+
function normalizeEnvironmentEntries(entry, root) {
|
|
537
|
+
const entries = Array.isArray(entry) ? entry : entry ? [entry] : [];
|
|
538
|
+
return entries.map((item) => import_node_path.default.resolve(root, item));
|
|
539
|
+
}
|
|
405
540
|
function detectNativeDeps(root) {
|
|
406
541
|
const result = /* @__PURE__ */ new Set();
|
|
407
542
|
const pkgJsonPath = import_node_path.default.resolve(root, "package.json");
|
|
@@ -522,6 +657,7 @@ var init_config = __esm({
|
|
|
522
657
|
import_node_fs = __toESM(require("fs"), 1);
|
|
523
658
|
init_defaults();
|
|
524
659
|
init_logger();
|
|
660
|
+
init_plugin_api();
|
|
525
661
|
CONFIG_FILES = [
|
|
526
662
|
"nasti.config.ts",
|
|
527
663
|
"nasti.config.js",
|
|
@@ -532,21 +668,11 @@ var init_config = __esm({
|
|
|
532
668
|
});
|
|
533
669
|
|
|
534
670
|
// src/core/plugin-container.ts
|
|
535
|
-
function sortPlugins(plugins) {
|
|
536
|
-
const pre = [];
|
|
537
|
-
const normal = [];
|
|
538
|
-
const post = [];
|
|
539
|
-
for (const plugin of plugins) {
|
|
540
|
-
if (plugin.enforce === "pre") pre.push(plugin);
|
|
541
|
-
else if (plugin.enforce === "post") post.push(plugin);
|
|
542
|
-
else normal.push(plugin);
|
|
543
|
-
}
|
|
544
|
-
return [...pre, ...normal, ...post];
|
|
545
|
-
}
|
|
546
671
|
var PluginContainer;
|
|
547
672
|
var init_plugin_container = __esm({
|
|
548
673
|
"src/core/plugin-container.ts"() {
|
|
549
674
|
"use strict";
|
|
675
|
+
init_plugin_api();
|
|
550
676
|
PluginContainer = class {
|
|
551
677
|
plugins;
|
|
552
678
|
config;
|
|
@@ -557,7 +683,7 @@ var init_plugin_container = __esm({
|
|
|
557
683
|
constructor(config, environment) {
|
|
558
684
|
this.config = config;
|
|
559
685
|
this.environment = environment;
|
|
560
|
-
this.plugins =
|
|
686
|
+
this.plugins = orderPlugins(config.plugins);
|
|
561
687
|
this.ctx = this.createContext();
|
|
562
688
|
}
|
|
563
689
|
createContext() {
|
|
@@ -896,6 +1022,7 @@ var init_environment = __esm({
|
|
|
896
1022
|
init_module_graph();
|
|
897
1023
|
init_hot_channel();
|
|
898
1024
|
init_debug();
|
|
1025
|
+
init_plugin_api();
|
|
899
1026
|
debug = createDebugger("nasti:environment");
|
|
900
1027
|
NastiEnvironment = class {
|
|
901
1028
|
name;
|
|
@@ -904,6 +1031,7 @@ var init_environment = __esm({
|
|
|
904
1031
|
config;
|
|
905
1032
|
options;
|
|
906
1033
|
hot;
|
|
1034
|
+
driver;
|
|
907
1035
|
/** applyToEnvironment 过滤后的插件(init() 后可用) */
|
|
908
1036
|
plugins = [];
|
|
909
1037
|
/** per-env 插件容器(init() 后可用;dev 管线使用) */
|
|
@@ -911,6 +1039,7 @@ var init_environment = __esm({
|
|
|
911
1039
|
/** per-env 模块图(dev 管线使用) */
|
|
912
1040
|
moduleGraph;
|
|
913
1041
|
candidatePlugins;
|
|
1042
|
+
pluginApi;
|
|
914
1043
|
initialized = false;
|
|
915
1044
|
constructor(name, config, init = {}) {
|
|
916
1045
|
const options = config.environments[name];
|
|
@@ -927,6 +1056,7 @@ var init_environment = __esm({
|
|
|
927
1056
|
this.hot = init.hot ?? createNoopHotChannel();
|
|
928
1057
|
this.moduleGraph = new ModuleGraph();
|
|
929
1058
|
this.candidatePlugins = init.plugins ?? config.plugins;
|
|
1059
|
+
this.pluginApi = init.pluginApi ?? getPluginApi(config);
|
|
930
1060
|
}
|
|
931
1061
|
/** 过滤插件并建 per-env PluginContainer */
|
|
932
1062
|
async init() {
|
|
@@ -937,10 +1067,41 @@ var init_environment = __esm({
|
|
|
937
1067
|
{ ...this.config, plugins: this.plugins },
|
|
938
1068
|
this
|
|
939
1069
|
);
|
|
1070
|
+
if (this.options.driver) {
|
|
1071
|
+
const claimed = [];
|
|
1072
|
+
for (const plugin of this.plugins) {
|
|
1073
|
+
const driver = await plugin.createEnvironmentDriver?.(this, this.pluginApi);
|
|
1074
|
+
if (driver) claimed.push({ plugin, driver });
|
|
1075
|
+
}
|
|
1076
|
+
if (claimed.length === 0) {
|
|
1077
|
+
throw new Error(
|
|
1078
|
+
`[nasti] environment "${this.name}" requested driver "${this.options.driver}", but no plugin provided it`
|
|
1079
|
+
);
|
|
1080
|
+
}
|
|
1081
|
+
if (claimed.length > 1) {
|
|
1082
|
+
throw new Error(
|
|
1083
|
+
`[nasti] environment "${this.name}" was claimed by multiple drivers: ` + claimed.map(({ plugin, driver }) => `${plugin.name} (${driver.name})`).join(", ")
|
|
1084
|
+
);
|
|
1085
|
+
}
|
|
1086
|
+
this.driver = claimed[0].driver;
|
|
1087
|
+
debug?.(`env "${this.name}" uses driver "${this.driver.name}"`);
|
|
1088
|
+
}
|
|
940
1089
|
debug?.(`env "${this.name}" initialized (${this.plugins.length} plugins)`);
|
|
941
1090
|
}
|
|
1091
|
+
getDriverContext() {
|
|
1092
|
+
return {
|
|
1093
|
+
environment: this,
|
|
1094
|
+
config: this.config,
|
|
1095
|
+
api: this.pluginApi,
|
|
1096
|
+
logger: this.config.logger
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
942
1099
|
async close() {
|
|
943
|
-
|
|
1100
|
+
try {
|
|
1101
|
+
await this.driver?.close?.(this.getDriverContext());
|
|
1102
|
+
} finally {
|
|
1103
|
+
await this.hot.close?.();
|
|
1104
|
+
}
|
|
944
1105
|
}
|
|
945
1106
|
};
|
|
946
1107
|
}
|
|
@@ -1005,7 +1166,8 @@ function transformCode(filename, code, options = {}) {
|
|
|
1005
1166
|
importSource: options.jsxImportSource ?? "react",
|
|
1006
1167
|
refresh: options.reactRefresh ?? false
|
|
1007
1168
|
} : void 0,
|
|
1008
|
-
sourcemap: options.sourcemap ?? true
|
|
1169
|
+
sourcemap: options.sourcemap ?? true,
|
|
1170
|
+
target: options.target
|
|
1009
1171
|
});
|
|
1010
1172
|
if (result.errors && result.errors.length > 0) {
|
|
1011
1173
|
const msg = result.errors.map((e) => e.message ?? String(e)).join("\n");
|
|
@@ -1036,7 +1198,7 @@ function htmlPlugin(config) {
|
|
|
1036
1198
|
transformIndexHtml(html) {
|
|
1037
1199
|
const tags = [];
|
|
1038
1200
|
if (config.command === "serve") {
|
|
1039
|
-
const isReactLike = config.framework === "react"
|
|
1201
|
+
const isReactLike = config.framework === "react";
|
|
1040
1202
|
if (isReactLike) {
|
|
1041
1203
|
tags.push({
|
|
1042
1204
|
tag: "script",
|
|
@@ -1090,8 +1252,8 @@ function serializeTag(tag) {
|
|
|
1090
1252
|
}
|
|
1091
1253
|
return ` <${tag.tag}${attrs}>${children}</${tag.tag}>`;
|
|
1092
1254
|
}
|
|
1093
|
-
async function readHtmlFile(root) {
|
|
1094
|
-
const htmlPath = import_node_path2.default.resolve(root,
|
|
1255
|
+
async function readHtmlFile(root, htmlFile = "index.html") {
|
|
1256
|
+
const htmlPath = import_node_path2.default.isAbsolute(htmlFile) ? htmlFile : import_node_path2.default.resolve(root, htmlFile);
|
|
1095
1257
|
if (!import_node_fs2.default.existsSync(htmlPath)) return null;
|
|
1096
1258
|
return import_node_fs2.default.readFileSync(htmlPath, "utf-8");
|
|
1097
1259
|
}
|
|
@@ -1288,7 +1450,10 @@ function transformMiddleware(ctx) {
|
|
|
1288
1450
|
return;
|
|
1289
1451
|
}
|
|
1290
1452
|
if (url === "/" || url.endsWith(".html")) {
|
|
1291
|
-
const html = await readHtmlFile(
|
|
1453
|
+
const html = await readHtmlFile(
|
|
1454
|
+
ctx.config.root,
|
|
1455
|
+
ctx.config.environments.client?.html
|
|
1456
|
+
);
|
|
1292
1457
|
if (html) {
|
|
1293
1458
|
let processedHtml = html;
|
|
1294
1459
|
for (const plugin of ctx.config.plugins) {
|
|
@@ -3923,6 +4088,7 @@ var build_exports = {};
|
|
|
3923
4088
|
__export(build_exports, {
|
|
3924
4089
|
build: () => build,
|
|
3925
4090
|
getRolldownOptions: () => getRolldownOptions,
|
|
4091
|
+
replaceEntryScript: () => replaceEntryScript,
|
|
3926
4092
|
resolveClientEntries: () => resolveClientEntries,
|
|
3927
4093
|
toRolldownPlugins: () => toRolldownPlugins
|
|
3928
4094
|
});
|
|
@@ -4001,13 +4167,20 @@ function toRolldownPlugins(plugins) {
|
|
|
4001
4167
|
}));
|
|
4002
4168
|
}
|
|
4003
4169
|
function resolveClientEntries(config, html) {
|
|
4170
|
+
const configuredEntries = config.environments.client?.entry ?? [];
|
|
4171
|
+
if (configuredEntries.length > 0) return configuredEntries;
|
|
4004
4172
|
const entryPoints = [];
|
|
4173
|
+
const htmlFile = config.environments.client?.html;
|
|
4174
|
+
const htmlDir = htmlFile ? import_node_path12.default.dirname(htmlFile) : config.root;
|
|
4005
4175
|
if (html) {
|
|
4006
4176
|
const scriptMatches = html.matchAll(/<script[^>]+src=["']([^"']+)["'][^>]*>/gi);
|
|
4007
4177
|
for (const match of scriptMatches) {
|
|
4008
4178
|
const src = match[1];
|
|
4009
4179
|
if (src && !src.startsWith("http")) {
|
|
4010
|
-
|
|
4180
|
+
const cleanSrc = src.split(/[?#]/, 1)[0];
|
|
4181
|
+
entryPoints.push(
|
|
4182
|
+
cleanSrc.startsWith("/") ? import_node_path12.default.resolve(config.root, cleanSrc.replace(/^\//, "")) : import_node_path12.default.resolve(htmlDir, cleanSrc)
|
|
4183
|
+
);
|
|
4011
4184
|
}
|
|
4012
4185
|
}
|
|
4013
4186
|
}
|
|
@@ -4043,21 +4216,55 @@ async function build(inlineConfig = {}) {
|
|
|
4043
4216
|
const startTime = performance.now();
|
|
4044
4217
|
logger.info(
|
|
4045
4218
|
import_picocolors6.default.cyan(`
|
|
4046
|
-
nasti v${"2.
|
|
4219
|
+
nasti v${"2.3.1"} `) + import_picocolors6.default.green(`building for ${config.mode}...`)
|
|
4047
4220
|
);
|
|
4048
4221
|
debug5?.(`root: ${config.root}`);
|
|
4049
4222
|
const buildableNames = Object.keys(config.environments).filter(
|
|
4050
|
-
(name) => name === "client" || config.environments[name].entry.length > 0
|
|
4223
|
+
(name) => name === "client" || config.environments[name].entry.length > 0 || !!config.environments[name].driver
|
|
4051
4224
|
);
|
|
4052
4225
|
buildableNames.sort((a, b) => a === "client" ? -1 : b === "client" ? 1 : 0);
|
|
4053
4226
|
const environments = {};
|
|
4227
|
+
const environmentResults = {};
|
|
4228
|
+
const initializedEnvironments = [];
|
|
4054
4229
|
let clientOutput = [];
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4230
|
+
let buildFailed = false;
|
|
4231
|
+
try {
|
|
4232
|
+
for (const name of buildableNames) {
|
|
4233
|
+
const built = name === "client" ? await buildClientEnvironment(config) : await buildServerEnvironment(config, name);
|
|
4234
|
+
initializedEnvironments.push(built.environment);
|
|
4235
|
+
environments[name] = built.result.output;
|
|
4236
|
+
environmentResults[name] = built.result;
|
|
4237
|
+
if (name === "client") clientOutput = built.result.output;
|
|
4238
|
+
if (buildableNames.length > 1) {
|
|
4239
|
+
debug5?.(`environment "${name}" built (${built.result.output.length} files)`);
|
|
4240
|
+
}
|
|
4241
|
+
}
|
|
4242
|
+
const pluginApi = getPluginApi(config);
|
|
4243
|
+
for (const plugin of config.plugins) {
|
|
4244
|
+
await plugin.afterBuildApp?.(environmentResults, pluginApi);
|
|
4245
|
+
}
|
|
4246
|
+
} catch (error) {
|
|
4247
|
+
buildFailed = true;
|
|
4248
|
+
throw error;
|
|
4249
|
+
} finally {
|
|
4250
|
+
let closeFailed = false;
|
|
4251
|
+
let firstCloseError;
|
|
4252
|
+
for (const environment of [...initializedEnvironments].reverse()) {
|
|
4253
|
+
try {
|
|
4254
|
+
await environment.close();
|
|
4255
|
+
} catch (error) {
|
|
4256
|
+
if (!closeFailed) {
|
|
4257
|
+
closeFailed = true;
|
|
4258
|
+
firstCloseError = error;
|
|
4259
|
+
}
|
|
4260
|
+
const closeError = error instanceof Error ? error : new Error(String(error));
|
|
4261
|
+
logger.error(`[nasti] failed to close environment "${environment.name}"`, {
|
|
4262
|
+
error: closeError
|
|
4263
|
+
});
|
|
4264
|
+
}
|
|
4265
|
+
}
|
|
4266
|
+
if (closeFailed && !buildFailed) {
|
|
4267
|
+
throw firstCloseError;
|
|
4061
4268
|
}
|
|
4062
4269
|
}
|
|
4063
4270
|
const elapsed = ((performance.now() - startTime) / 1e3).toFixed(2);
|
|
@@ -4070,83 +4277,130 @@ nasti v${"2.2.0"} `) + import_picocolors6.default.green(`building for ${config.m
|
|
|
4070
4277
|
const envSuffix = buildableNames.length > 1 ? ` (${buildableNames.join(" + ")})` : "";
|
|
4071
4278
|
logger.info(import_picocolors6.default.green(`\u2713 built in ${elapsed}s`) + import_picocolors6.default.dim(envSuffix));
|
|
4072
4279
|
logger.info(import_picocolors6.default.dim(` ${fileCount} files, ${displaySize(totalSize)} total \u2192 ${config.build.outDir}/`));
|
|
4073
|
-
return { output: clientOutput, environments };
|
|
4280
|
+
return { output: clientOutput, environments, environmentResults };
|
|
4074
4281
|
}
|
|
4075
4282
|
async function buildClientEnvironment(config) {
|
|
4076
4283
|
const logger = config.logger;
|
|
4077
4284
|
const outDir = import_node_path12.default.resolve(config.root, config.build.outDir);
|
|
4078
|
-
if (config.build.emptyOutDir && import_node_fs9.default.existsSync(outDir)) {
|
|
4079
|
-
import_node_fs9.default.rmSync(outDir, { recursive: true, force: true });
|
|
4080
|
-
}
|
|
4081
|
-
import_node_fs9.default.mkdirSync(outDir, { recursive: true });
|
|
4082
|
-
const html = await readHtmlFile(config.root);
|
|
4083
|
-
const entryPoints = resolveClientEntries(config, html);
|
|
4084
|
-
if (entryPoints.length === 0) {
|
|
4085
|
-
throw new Error("No entry point found. Add a <script> tag to index.html or create src/main.ts");
|
|
4086
|
-
}
|
|
4087
4285
|
const cssEngine = createCssEngine();
|
|
4088
4286
|
const pluginList = resolvePluginList(config, config.plugins, { cssEngine });
|
|
4089
|
-
const clientEnv = new NastiEnvironment("client",
|
|
4287
|
+
const clientEnv = new NastiEnvironment("client", config, {
|
|
4090
4288
|
mode: "build",
|
|
4091
|
-
plugins: pluginList
|
|
4289
|
+
plugins: pluginList,
|
|
4290
|
+
pluginApi: getPluginApi(config)
|
|
4092
4291
|
});
|
|
4093
4292
|
await clientEnv.init();
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
...nativeReporter ? [nativeReporter] : []
|
|
4100
|
-
];
|
|
4101
|
-
const { inputOptions, outputOptions } = getRolldownOptions(clientEnv, entryPoints, rolldownPlugins);
|
|
4102
|
-
const bundle2 = await (0, import_rolldown.rolldown)(inputOptions);
|
|
4103
|
-
const { output } = await bundle2.write(outputOptions);
|
|
4104
|
-
await bundle2.close();
|
|
4105
|
-
if (html) {
|
|
4106
|
-
let processedHtml = html;
|
|
4107
|
-
const htmlPlugins = [...allPlugins.filter((p) => p.transformIndexHtml), htmlPlugin(config)];
|
|
4108
|
-
for (const p of htmlPlugins) {
|
|
4109
|
-
const result = await p.transformIndexHtml(processedHtml);
|
|
4110
|
-
if (typeof result === "string") {
|
|
4111
|
-
processedHtml = result;
|
|
4112
|
-
} else if (result && "html" in result) {
|
|
4113
|
-
processedHtml = processHtml(result.html, result.tags);
|
|
4114
|
-
} else if (Array.isArray(result)) {
|
|
4115
|
-
processedHtml = processHtml(processedHtml, result);
|
|
4116
|
-
}
|
|
4117
|
-
}
|
|
4118
|
-
processedHtml = injectCssLinks(processedHtml, cssEngine, config);
|
|
4119
|
-
for (const chunk of output) {
|
|
4120
|
-
if (chunk.type === "chunk" && chunk.isEntry && chunk.facadeModuleId) {
|
|
4121
|
-
const originalEntry = import_node_path12.default.relative(config.root, chunk.facadeModuleId);
|
|
4122
|
-
processedHtml = processedHtml.replace(
|
|
4123
|
-
new RegExp(`(src=["'])/?(${escapeRegExp(originalEntry)})(["'])`, "g"),
|
|
4124
|
-
`$1${config.base}${chunk.fileName}$3`
|
|
4293
|
+
try {
|
|
4294
|
+
if (clientEnv.driver) {
|
|
4295
|
+
if (!clientEnv.driver.build) {
|
|
4296
|
+
throw new Error(
|
|
4297
|
+
`[nasti] environment "client" driver "${clientEnv.driver.name}" does not implement build()`
|
|
4125
4298
|
);
|
|
4126
4299
|
}
|
|
4300
|
+
const result = await clientEnv.driver.build(clientEnv.getDriverContext());
|
|
4301
|
+
return { environment: clientEnv, result };
|
|
4127
4302
|
}
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4303
|
+
if (config.build.emptyOutDir && import_node_fs9.default.existsSync(outDir)) {
|
|
4304
|
+
import_node_fs9.default.rmSync(outDir, { recursive: true, force: true });
|
|
4305
|
+
}
|
|
4306
|
+
import_node_fs9.default.mkdirSync(outDir, { recursive: true });
|
|
4307
|
+
const htmlFile = config.environments.client.html ?? import_node_path12.default.resolve(config.root, "index.html");
|
|
4308
|
+
const html = await readHtmlFile(config.root, htmlFile);
|
|
4309
|
+
const entryPoints = resolveClientEntries(config, html);
|
|
4310
|
+
if (entryPoints.length === 0) {
|
|
4311
|
+
throw new Error("No entry point found. Add a <script> tag to index.html or create src/main.ts");
|
|
4312
|
+
}
|
|
4313
|
+
const allPlugins = clientEnv.plugins;
|
|
4314
|
+
const nativeReporter = config.logLevel === "silent" ? null : await tryNativeReporterPlugin(config, logger);
|
|
4315
|
+
const rolldownPlugins = [
|
|
4316
|
+
createOxcTransformPlugin(config, clientEnv),
|
|
4317
|
+
...toRolldownPlugins(allPlugins),
|
|
4318
|
+
...nativeReporter ? [nativeReporter] : []
|
|
4319
|
+
];
|
|
4320
|
+
const { inputOptions, outputOptions } = getRolldownOptions(
|
|
4321
|
+
clientEnv,
|
|
4322
|
+
entryPoints,
|
|
4323
|
+
rolldownPlugins
|
|
4324
|
+
);
|
|
4325
|
+
const bundle2 = await (0, import_rolldown.rolldown)(inputOptions);
|
|
4326
|
+
const { output } = await bundle2.write(outputOptions);
|
|
4327
|
+
await bundle2.close();
|
|
4328
|
+
if (html) {
|
|
4329
|
+
let processedHtml = html;
|
|
4330
|
+
const htmlPlugins = [...allPlugins.filter((p) => p.transformIndexHtml), htmlPlugin(config)];
|
|
4331
|
+
for (const p of htmlPlugins) {
|
|
4332
|
+
const result = await p.transformIndexHtml(processedHtml);
|
|
4333
|
+
if (typeof result === "string") {
|
|
4334
|
+
processedHtml = result;
|
|
4335
|
+
} else if (result && "html" in result) {
|
|
4336
|
+
processedHtml = processHtml(result.html, result.tags);
|
|
4337
|
+
} else if (Array.isArray(result)) {
|
|
4338
|
+
processedHtml = processHtml(processedHtml, result);
|
|
4339
|
+
}
|
|
4340
|
+
}
|
|
4341
|
+
processedHtml = injectCssLinks(processedHtml, cssEngine, config);
|
|
4342
|
+
for (const chunk of output) {
|
|
4343
|
+
if (chunk.type === "chunk" && chunk.isEntry && chunk.facadeModuleId) {
|
|
4344
|
+
processedHtml = replaceEntryScript(
|
|
4345
|
+
processedHtml,
|
|
4346
|
+
chunk.facadeModuleId,
|
|
4347
|
+
chunk.fileName,
|
|
4348
|
+
config,
|
|
4349
|
+
htmlFile,
|
|
4350
|
+
config.base
|
|
4351
|
+
);
|
|
4352
|
+
}
|
|
4353
|
+
}
|
|
4354
|
+
import_node_fs9.default.writeFileSync(import_node_path12.default.resolve(outDir, "index.html"), processedHtml);
|
|
4355
|
+
}
|
|
4356
|
+
if (!nativeReporter && config.logLevel !== "silent") {
|
|
4357
|
+
reportBuildOutput(output, config, logger);
|
|
4358
|
+
}
|
|
4359
|
+
warnLargeChunks(output, config, logger);
|
|
4360
|
+
return { environment: clientEnv, result: { output } };
|
|
4361
|
+
} catch (error) {
|
|
4362
|
+
try {
|
|
4363
|
+
await clientEnv.close();
|
|
4364
|
+
} catch (closeError) {
|
|
4365
|
+
const normalized = closeError instanceof Error ? closeError : new Error(String(closeError));
|
|
4366
|
+
logger.error("[nasti] failed to close client environment after build failure", {
|
|
4367
|
+
error: normalized
|
|
4368
|
+
});
|
|
4369
|
+
}
|
|
4370
|
+
throw error;
|
|
4132
4371
|
}
|
|
4133
|
-
warnLargeChunks(output, config, logger);
|
|
4134
|
-
return output;
|
|
4135
4372
|
}
|
|
4136
4373
|
async function buildServerEnvironment(config, name) {
|
|
4137
4374
|
const envOptions = config.environments[name];
|
|
4138
4375
|
const logger = config.logger;
|
|
4376
|
+
const pluginList = resolvePluginList(config, config.plugins, { consumer: envOptions.consumer });
|
|
4377
|
+
const environment = new NastiEnvironment(name, config, {
|
|
4378
|
+
mode: "build",
|
|
4379
|
+
plugins: pluginList,
|
|
4380
|
+
pluginApi: getPluginApi(config)
|
|
4381
|
+
});
|
|
4382
|
+
await environment.init();
|
|
4383
|
+
if (environment.driver) {
|
|
4384
|
+
if (!environment.driver.build) {
|
|
4385
|
+
await environment.close();
|
|
4386
|
+
throw new Error(
|
|
4387
|
+
`[nasti] environment "${name}" driver "${environment.driver.name}" does not implement build()`
|
|
4388
|
+
);
|
|
4389
|
+
}
|
|
4390
|
+
try {
|
|
4391
|
+
const result = await environment.driver.build(environment.getDriverContext());
|
|
4392
|
+
return { environment, result };
|
|
4393
|
+
} catch (error) {
|
|
4394
|
+
await environment.close();
|
|
4395
|
+
throw error;
|
|
4396
|
+
}
|
|
4397
|
+
}
|
|
4139
4398
|
for (const entry of envOptions.entry) {
|
|
4140
4399
|
if (!import_node_fs9.default.existsSync(entry)) {
|
|
4400
|
+
await environment.close();
|
|
4141
4401
|
throw new Error(`[nasti] environment "${name}" entry not found: ${entry}`);
|
|
4142
4402
|
}
|
|
4143
4403
|
}
|
|
4144
|
-
const pluginList = resolvePluginList(config, config.plugins, { consumer: envOptions.consumer });
|
|
4145
|
-
const environment = new NastiEnvironment(name, { ...config, plugins: pluginList }, {
|
|
4146
|
-
mode: "build",
|
|
4147
|
-
plugins: pluginList
|
|
4148
|
-
});
|
|
4149
|
-
await environment.init();
|
|
4150
4404
|
const rolldownPlugins = [
|
|
4151
4405
|
createOxcTransformPlugin(config, environment),
|
|
4152
4406
|
...toRolldownPlugins(environment.plugins)
|
|
@@ -4166,7 +4420,7 @@ async function buildServerEnvironment(config, name) {
|
|
|
4166
4420
|
logger.info(
|
|
4167
4421
|
import_picocolors6.default.dim(` [${name}] `) + output.map((o) => import_node_path12.default.join(envOptions.build.outDir, o.fileName)).join(import_picocolors6.default.dim(", "))
|
|
4168
4422
|
);
|
|
4169
|
-
return output;
|
|
4423
|
+
return { environment, result: { output } };
|
|
4170
4424
|
}
|
|
4171
4425
|
function injectCssLinks(html, cssEngine, config) {
|
|
4172
4426
|
const cssLinkTags = [];
|
|
@@ -4192,6 +4446,25 @@ function injectCssLinks(html, cssEngine, config) {
|
|
|
4192
4446
|
function escapeRegExp(string) {
|
|
4193
4447
|
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4194
4448
|
}
|
|
4449
|
+
function replaceEntryScript(html, facadeModuleId, fileName, config, htmlFile, urlPrefix) {
|
|
4450
|
+
const rootRelative = import_node_path12.default.relative(config.root, facadeModuleId).split(import_node_path12.default.sep).join("/");
|
|
4451
|
+
const resolvedHtmlFile = import_node_path12.default.resolve(config.root, htmlFile);
|
|
4452
|
+
const htmlRelative = import_node_path12.default.relative(import_node_path12.default.dirname(resolvedHtmlFile), facadeModuleId).split(import_node_path12.default.sep).join("/");
|
|
4453
|
+
const candidates = /* @__PURE__ */ new Set([
|
|
4454
|
+
rootRelative,
|
|
4455
|
+
`/${rootRelative}`,
|
|
4456
|
+
htmlRelative,
|
|
4457
|
+
`./${htmlRelative}`
|
|
4458
|
+
]);
|
|
4459
|
+
let processed = html;
|
|
4460
|
+
for (const candidate of candidates) {
|
|
4461
|
+
processed = processed.replace(
|
|
4462
|
+
new RegExp(`(src=["'])${escapeRegExp(candidate)}([?#][^"']*)?(["'])`, "g"),
|
|
4463
|
+
`$1${urlPrefix}${fileName}$3`
|
|
4464
|
+
);
|
|
4465
|
+
}
|
|
4466
|
+
return processed;
|
|
4467
|
+
}
|
|
4195
4468
|
var import_node_path12, import_node_fs9, import_node_module5, import_rolldown, import_picocolors6, debug5, NODE_BUILTINS2;
|
|
4196
4469
|
var init_build = __esm({
|
|
4197
4470
|
"src/build/index.ts"() {
|
|
@@ -4209,6 +4482,7 @@ var init_build = __esm({
|
|
|
4209
4482
|
init_env();
|
|
4210
4483
|
init_reporter();
|
|
4211
4484
|
init_debug();
|
|
4485
|
+
init_plugin_api();
|
|
4212
4486
|
import_picocolors6 = __toESM(require("picocolors"), 1);
|
|
4213
4487
|
debug5 = createDebugger("nasti:build");
|
|
4214
4488
|
NODE_BUILTINS2 = /* @__PURE__ */ new Set([...import_node_module5.builtinModules, ...import_node_module5.builtinModules.map((m) => `node:${m}`)]);
|
|
@@ -4237,7 +4511,7 @@ async function createBundledDevServer(opts) {
|
|
|
4237
4511
|
`[nasti] experimental.bundledDev requires rolldown's experimental dev() API (locked to the installed rc; got: ${err.message}). Remove --bundle / experimental.bundledDev to use the default unbundled dev server.`
|
|
4238
4512
|
);
|
|
4239
4513
|
}
|
|
4240
|
-
const html = await readHtmlFile(config.root);
|
|
4514
|
+
const html = await readHtmlFile(config.root, config.environments.client?.html);
|
|
4241
4515
|
const entryPoints = resolveClientEntries(config, html);
|
|
4242
4516
|
if (entryPoints.length === 0) {
|
|
4243
4517
|
throw new Error("No entry point found. Add a <script> tag to index.html or create src/main.ts");
|
|
@@ -4443,7 +4717,7 @@ async function createBundledDevServer(opts) {
|
|
|
4443
4717
|
return;
|
|
4444
4718
|
}
|
|
4445
4719
|
if (pathname === "/" || pathname.endsWith(".html")) {
|
|
4446
|
-
const rawHtml = await readHtmlFile(config.root);
|
|
4720
|
+
const rawHtml = await readHtmlFile(config.root, config.environments.client?.html);
|
|
4447
4721
|
if (rawHtml) {
|
|
4448
4722
|
res.setHeader("Content-Type", "text/html");
|
|
4449
4723
|
res.setHeader("Cache-Control", "no-store");
|
|
@@ -4527,10 +4801,13 @@ async function renderBundledIndexHtml(html, config, entryFileNames) {
|
|
|
4527
4801
|
}
|
|
4528
4802
|
}
|
|
4529
4803
|
for (const [facadeModuleId, fileName] of entryFileNames) {
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4804
|
+
processed = replaceEntryScript(
|
|
4805
|
+
processed,
|
|
4806
|
+
facadeModuleId,
|
|
4807
|
+
fileName,
|
|
4808
|
+
config,
|
|
4809
|
+
config.environments.client?.html ?? "index.html",
|
|
4810
|
+
"/"
|
|
4534
4811
|
);
|
|
4535
4812
|
}
|
|
4536
4813
|
return processed;
|
|
@@ -4671,10 +4948,12 @@ async function createServer(inlineConfig = {}) {
|
|
|
4671
4948
|
const app = (0, import_connect.default)();
|
|
4672
4949
|
const httpServer = import_node_http.default.createServer(app);
|
|
4673
4950
|
const ws = createWebSocketServer(httpServer);
|
|
4674
|
-
const
|
|
4951
|
+
const pluginApi = getPluginApi(config);
|
|
4952
|
+
const clientEnv = new NastiEnvironment("client", config, {
|
|
4675
4953
|
hot: createWsHotChannel(ws),
|
|
4676
4954
|
mode: "dev",
|
|
4677
|
-
plugins: allPlugins
|
|
4955
|
+
plugins: allPlugins,
|
|
4956
|
+
pluginApi
|
|
4678
4957
|
});
|
|
4679
4958
|
await clientEnv.init();
|
|
4680
4959
|
const environments = { client: clientEnv };
|
|
@@ -4682,11 +4961,15 @@ async function createServer(inlineConfig = {}) {
|
|
|
4682
4961
|
if (name === "client") continue;
|
|
4683
4962
|
const consumer = config.environments[name].consumer;
|
|
4684
4963
|
const envPlugins = resolvePluginList(config, config.plugins, { consumer });
|
|
4685
|
-
environments[name] = new NastiEnvironment(name,
|
|
4964
|
+
environments[name] = new NastiEnvironment(name, config, {
|
|
4686
4965
|
mode: "dev",
|
|
4687
|
-
plugins: envPlugins
|
|
4966
|
+
plugins: envPlugins,
|
|
4967
|
+
pluginApi
|
|
4688
4968
|
});
|
|
4689
4969
|
}
|
|
4970
|
+
for (const [name, environment] of Object.entries(environments)) {
|
|
4971
|
+
if (name !== "client" && environment.options.driver) await environment.init();
|
|
4972
|
+
}
|
|
4690
4973
|
let ssrRunner = null;
|
|
4691
4974
|
async function getSsrRunner() {
|
|
4692
4975
|
if (ssrRunner) return ssrRunner;
|
|
@@ -4711,14 +4994,6 @@ async function createServer(inlineConfig = {}) {
|
|
|
4711
4994
|
});
|
|
4712
4995
|
app.use(bundledServer.middleware);
|
|
4713
4996
|
}
|
|
4714
|
-
app.use(transformMiddleware({
|
|
4715
|
-
config: configWithPlugins,
|
|
4716
|
-
pluginContainer,
|
|
4717
|
-
moduleGraph
|
|
4718
|
-
}));
|
|
4719
|
-
const publicDir = import_node_path14.default.resolve(config.root, "public");
|
|
4720
|
-
app.use((0, import_sirv.default)(publicDir, { dev: true, etag: true }));
|
|
4721
|
-
app.use((0, import_sirv.default)(config.root, { dev: true, etag: true }));
|
|
4722
4997
|
const ignoredSegments = /* @__PURE__ */ new Set(["node_modules", ".git", ".nasti"]);
|
|
4723
4998
|
const outDirAbs = import_node_path14.default.resolve(config.root, config.build.outDir);
|
|
4724
4999
|
const watcher = (0, import_chokidar.watch)(config.root, {
|
|
@@ -4735,13 +5010,72 @@ async function createServer(inlineConfig = {}) {
|
|
|
4735
5010
|
ignoreInitial: true
|
|
4736
5011
|
});
|
|
4737
5012
|
let server;
|
|
5013
|
+
const environmentServices = {};
|
|
5014
|
+
let environmentDriversStarted = false;
|
|
5015
|
+
const logCloseError = (target, error) => {
|
|
5016
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
5017
|
+
logger.error(`[nasti] failed to close ${target}`, { error: normalized });
|
|
5018
|
+
};
|
|
5019
|
+
const startEnvironmentDrivers = async () => {
|
|
5020
|
+
if (environmentDriversStarted) return;
|
|
5021
|
+
environmentDriversStarted = true;
|
|
5022
|
+
const started = [];
|
|
5023
|
+
const attempted = [];
|
|
5024
|
+
try {
|
|
5025
|
+
for (const [name, environment] of Object.entries(environments)) {
|
|
5026
|
+
if (!environment.driver?.serve) continue;
|
|
5027
|
+
attempted.push(environment);
|
|
5028
|
+
const result = await environment.driver.serve({
|
|
5029
|
+
...environment.getDriverContext(),
|
|
5030
|
+
server
|
|
5031
|
+
});
|
|
5032
|
+
started.push({ name, environment, service: result ?? {} });
|
|
5033
|
+
}
|
|
5034
|
+
for (const { name, service } of started) {
|
|
5035
|
+
environmentServices[name] = service;
|
|
5036
|
+
if (service.middleware) app.use(service.middleware);
|
|
5037
|
+
}
|
|
5038
|
+
} catch (error) {
|
|
5039
|
+
environmentDriversStarted = false;
|
|
5040
|
+
for (const { name } of started) {
|
|
5041
|
+
delete environmentServices[name];
|
|
5042
|
+
}
|
|
5043
|
+
for (const environment of attempted.reverse()) {
|
|
5044
|
+
try {
|
|
5045
|
+
await environment.driver?.close?.(environment.getDriverContext());
|
|
5046
|
+
} catch (closeError) {
|
|
5047
|
+
logCloseError(`environment driver "${environment.driver.name}"`, closeError);
|
|
5048
|
+
}
|
|
5049
|
+
}
|
|
5050
|
+
throw error;
|
|
5051
|
+
}
|
|
5052
|
+
};
|
|
5053
|
+
const notifyEnvironmentDrivers = (file, event) => {
|
|
5054
|
+
for (const environment of Object.values(environments)) {
|
|
5055
|
+
if (!environment.driver?.watchChange) continue;
|
|
5056
|
+
void Promise.resolve(
|
|
5057
|
+
environment.driver.watchChange(file, event, environment.getDriverContext())
|
|
5058
|
+
).catch((error) => {
|
|
5059
|
+
logger.error(
|
|
5060
|
+
`[nasti] environment driver "${environment.driver.name}" watchChange failed`,
|
|
5061
|
+
{ error }
|
|
5062
|
+
);
|
|
5063
|
+
});
|
|
5064
|
+
}
|
|
5065
|
+
};
|
|
4738
5066
|
watcher.on("change", (file) => {
|
|
4739
5067
|
ssrRunner?.invalidateFile(file);
|
|
4740
5068
|
handleFileChange(file, server);
|
|
5069
|
+
notifyEnvironmentDrivers(file, "change");
|
|
4741
5070
|
});
|
|
4742
5071
|
watcher.on("add", (file) => {
|
|
4743
5072
|
ssrRunner?.invalidateFile(file);
|
|
4744
5073
|
handleFileChange(file, server);
|
|
5074
|
+
notifyEnvironmentDrivers(file, "add");
|
|
5075
|
+
});
|
|
5076
|
+
watcher.on("unlink", (file) => {
|
|
5077
|
+
ssrRunner?.invalidateFile(file);
|
|
5078
|
+
notifyEnvironmentDrivers(file, "unlink");
|
|
4745
5079
|
});
|
|
4746
5080
|
server = {
|
|
4747
5081
|
config: configWithPlugins,
|
|
@@ -4750,10 +5084,12 @@ async function createServer(inlineConfig = {}) {
|
|
|
4750
5084
|
watcher,
|
|
4751
5085
|
ws,
|
|
4752
5086
|
environments,
|
|
5087
|
+
environmentServices,
|
|
4753
5088
|
async listen(port) {
|
|
4754
5089
|
const finalPort = port ?? config.server.port;
|
|
4755
5090
|
const host = config.server.host === true ? "0.0.0.0" : config.server.host;
|
|
4756
5091
|
await pluginContainer.buildStart();
|
|
5092
|
+
await startEnvironmentDrivers();
|
|
4757
5093
|
return new Promise((resolve, reject) => {
|
|
4758
5094
|
let currentPort = finalPort;
|
|
4759
5095
|
const onListening = () => {
|
|
@@ -4761,15 +5097,20 @@ async function createServer(inlineConfig = {}) {
|
|
|
4761
5097
|
config.server.port = actualPort;
|
|
4762
5098
|
const localUrl = `http://localhost:${actualPort}/`;
|
|
4763
5099
|
const networkUrl = host === "0.0.0.0" ? `http://${getNetworkAddress()}:${actualPort}/` : null;
|
|
5100
|
+
const driverLocalUrls = Object.values(environmentServices).flatMap((service) => service.localUrls ?? []);
|
|
5101
|
+
const driverNetworkUrls = Object.values(environmentServices).flatMap((service) => service.networkUrls ?? []);
|
|
4764
5102
|
logger.clearScreen("info");
|
|
4765
5103
|
const readyIn = Math.ceil(performance.now() - startTime);
|
|
4766
5104
|
logger.info(
|
|
4767
5105
|
`
|
|
4768
|
-
${import_picocolors8.default.cyan(import_picocolors8.default.bold("NASTI"))} ${import_picocolors8.default.cyan(`v${"2.
|
|
5106
|
+
${import_picocolors8.default.cyan(import_picocolors8.default.bold("NASTI"))} ${import_picocolors8.default.cyan(`v${"2.3.1"}`)} ${import_picocolors8.default.dim("ready in")} ${import_picocolors8.default.bold(readyIn)} ${import_picocolors8.default.dim("ms")}
|
|
4769
5107
|
`
|
|
4770
5108
|
);
|
|
4771
5109
|
printServerUrls(
|
|
4772
|
-
{
|
|
5110
|
+
{
|
|
5111
|
+
local: [localUrl, ...driverLocalUrls],
|
|
5112
|
+
network: [...networkUrl ? [networkUrl] : [], ...driverNetworkUrls]
|
|
5113
|
+
},
|
|
4773
5114
|
logger.info
|
|
4774
5115
|
);
|
|
4775
5116
|
logger.info("");
|
|
@@ -4799,11 +5140,62 @@ async function createServer(inlineConfig = {}) {
|
|
|
4799
5140
|
async close() {
|
|
4800
5141
|
await pluginContainer.buildEnd();
|
|
4801
5142
|
await bundledServer?.close();
|
|
4802
|
-
|
|
5143
|
+
let environmentCloseFailed = false;
|
|
5144
|
+
let firstEnvironmentCloseError;
|
|
5145
|
+
for (const environment of Object.values(environments).reverse()) {
|
|
5146
|
+
try {
|
|
5147
|
+
await environment.close();
|
|
5148
|
+
} catch (error) {
|
|
5149
|
+
if (!environmentCloseFailed) {
|
|
5150
|
+
environmentCloseFailed = true;
|
|
5151
|
+
firstEnvironmentCloseError = error;
|
|
5152
|
+
}
|
|
5153
|
+
logCloseError(`environment "${environment.name}"`, error);
|
|
5154
|
+
}
|
|
5155
|
+
}
|
|
5156
|
+
await watcher.close();
|
|
4803
5157
|
ws.close();
|
|
4804
5158
|
httpServer.close();
|
|
5159
|
+
if (environmentCloseFailed) {
|
|
5160
|
+
throw firstEnvironmentCloseError;
|
|
5161
|
+
}
|
|
4805
5162
|
}
|
|
4806
5163
|
};
|
|
5164
|
+
try {
|
|
5165
|
+
await startEnvironmentDrivers();
|
|
5166
|
+
} catch (error) {
|
|
5167
|
+
if (bundledServer) {
|
|
5168
|
+
try {
|
|
5169
|
+
await bundledServer.close();
|
|
5170
|
+
} catch (closeError) {
|
|
5171
|
+
logCloseError("bundled dev server after driver startup failure", closeError);
|
|
5172
|
+
}
|
|
5173
|
+
}
|
|
5174
|
+
try {
|
|
5175
|
+
await watcher.close();
|
|
5176
|
+
} catch (closeError) {
|
|
5177
|
+
logCloseError("file watcher after driver startup failure", closeError);
|
|
5178
|
+
}
|
|
5179
|
+
try {
|
|
5180
|
+
ws.close();
|
|
5181
|
+
} catch (closeError) {
|
|
5182
|
+
logCloseError("WebSocket server after driver startup failure", closeError);
|
|
5183
|
+
}
|
|
5184
|
+
try {
|
|
5185
|
+
httpServer.close();
|
|
5186
|
+
} catch (closeError) {
|
|
5187
|
+
logCloseError("HTTP server after driver startup failure", closeError);
|
|
5188
|
+
}
|
|
5189
|
+
throw error;
|
|
5190
|
+
}
|
|
5191
|
+
app.use(transformMiddleware({
|
|
5192
|
+
config: configWithPlugins,
|
|
5193
|
+
pluginContainer,
|
|
5194
|
+
moduleGraph
|
|
5195
|
+
}));
|
|
5196
|
+
const publicDir = import_node_path14.default.resolve(config.root, "public");
|
|
5197
|
+
app.use((0, import_sirv.default)(publicDir, { dev: true, etag: true }));
|
|
5198
|
+
app.use((0, import_sirv.default)(config.root, { dev: true, etag: true }));
|
|
4807
5199
|
const postMiddlewares = [];
|
|
4808
5200
|
for (const plugin of allPlugins) {
|
|
4809
5201
|
if (plugin.configureServer) {
|
|
@@ -4846,6 +5238,7 @@ var init_server = __esm({
|
|
|
4846
5238
|
init_middleware();
|
|
4847
5239
|
init_hmr();
|
|
4848
5240
|
init_builtins();
|
|
5241
|
+
init_plugin_api();
|
|
4849
5242
|
}
|
|
4850
5243
|
});
|
|
4851
5244
|
|
|
@@ -4892,6 +5285,7 @@ var init_electron = __esm({
|
|
|
4892
5285
|
var electron_exports = {};
|
|
4893
5286
|
__export(electron_exports, {
|
|
4894
5287
|
buildElectron: () => buildElectron,
|
|
5288
|
+
createElectronRendererConfig: () => createElectronRendererConfig,
|
|
4895
5289
|
detectInstalledElectron: () => detectInstalledElectron,
|
|
4896
5290
|
normalizePreload: () => normalizePreload
|
|
4897
5291
|
});
|
|
@@ -4899,7 +5293,7 @@ async function buildElectron(inlineConfig = {}) {
|
|
|
4899
5293
|
const config = await resolveConfig({ ...inlineConfig, target: "electron" }, "build");
|
|
4900
5294
|
const startTime = performance.now();
|
|
4901
5295
|
assertElectronVersion(config);
|
|
4902
|
-
console.log(import_picocolors9.default.cyan("\n\u26A1 nasti build (electron)") + import_picocolors9.default.dim(` v${"2.
|
|
5296
|
+
console.log(import_picocolors9.default.cyan("\n\u26A1 nasti build (electron)") + import_picocolors9.default.dim(` v${"2.3.1"}`));
|
|
4903
5297
|
console.log(import_picocolors9.default.dim(` root: ${config.root}`));
|
|
4904
5298
|
console.log(import_picocolors9.default.dim(` mode: ${config.mode}`));
|
|
4905
5299
|
console.log(import_picocolors9.default.dim(` target: electron (\u2265 ${config.electron.minVersion})`));
|
|
@@ -4910,15 +5304,13 @@ async function buildElectron(inlineConfig = {}) {
|
|
|
4910
5304
|
import_node_fs10.default.mkdirSync(outDir, { recursive: true });
|
|
4911
5305
|
const rendererOutDir = import_node_path15.default.join(outDir, "renderer");
|
|
4912
5306
|
const { build: build2 } = await Promise.resolve().then(() => (init_build(), build_exports));
|
|
4913
|
-
await build2({
|
|
4914
|
-
...inlineConfig,
|
|
4915
|
-
target: "web",
|
|
5307
|
+
await build2(createElectronRendererConfig(config, inlineConfig, {
|
|
4916
5308
|
build: {
|
|
4917
5309
|
...inlineConfig.build,
|
|
4918
5310
|
outDir: rendererOutDir,
|
|
4919
5311
|
emptyOutDir: false
|
|
4920
5312
|
}
|
|
4921
|
-
});
|
|
5313
|
+
}));
|
|
4922
5314
|
const mainEntry = import_node_path15.default.resolve(config.root, config.electron.main);
|
|
4923
5315
|
if (!import_node_fs10.default.existsSync(mainEntry)) {
|
|
4924
5316
|
throw new Error(
|
|
@@ -4972,7 +5364,8 @@ async function bundleNode(config, entry, opts) {
|
|
|
4972
5364
|
const result = transformCode(id, code, {
|
|
4973
5365
|
sourcemap: !!config.build.sourcemap,
|
|
4974
5366
|
jsxRuntime: "automatic",
|
|
4975
|
-
jsxImportSource: config.framework === "vue" ? "vue" : "react"
|
|
5367
|
+
jsxImportSource: config.framework === "vue" ? "vue" : "react",
|
|
5368
|
+
target: config.electron.nodeTarget
|
|
4976
5369
|
});
|
|
4977
5370
|
return { code: result.code, map: result.map ? JSON.parse(result.map) : void 0 };
|
|
4978
5371
|
}
|
|
@@ -4983,7 +5376,11 @@ async function bundleNode(config, entry, opts) {
|
|
|
4983
5376
|
...restInputOptions,
|
|
4984
5377
|
input: entry,
|
|
4985
5378
|
platform: "node",
|
|
4986
|
-
transform: {
|
|
5379
|
+
transform: {
|
|
5380
|
+
...userTransform,
|
|
5381
|
+
target: config.electron.nodeTarget,
|
|
5382
|
+
define: mergedDefine
|
|
5383
|
+
},
|
|
4987
5384
|
plugins: [oxcTransformPlugin, electronPlugin(config), resolvePlugin(config)]
|
|
4988
5385
|
});
|
|
4989
5386
|
import_node_fs10.default.mkdirSync(import_node_path15.default.dirname(opts.outFile), { recursive: true });
|
|
@@ -5000,6 +5397,25 @@ async function bundleNode(config, entry, opts) {
|
|
|
5000
5397
|
console.log(import_picocolors9.default.dim(` \u2713 ${opts.label} \u2192 ${import_node_path15.default.relative(config.root, opts.outFile)}`));
|
|
5001
5398
|
return opts.outFile;
|
|
5002
5399
|
}
|
|
5400
|
+
function createElectronRendererConfig(config, inlineConfig = {}, overrides = {}) {
|
|
5401
|
+
const inlineClient = inlineConfig.environments?.client ?? {};
|
|
5402
|
+
return {
|
|
5403
|
+
...inlineConfig,
|
|
5404
|
+
...overrides,
|
|
5405
|
+
root: config.root,
|
|
5406
|
+
mode: config.mode,
|
|
5407
|
+
target: "web",
|
|
5408
|
+
framework: config.framework,
|
|
5409
|
+
base: config.base === "/" ? "./" : config.base,
|
|
5410
|
+
environments: {
|
|
5411
|
+
...inlineConfig.environments ?? {},
|
|
5412
|
+
client: {
|
|
5413
|
+
...inlineClient,
|
|
5414
|
+
html: config.electron.renderer
|
|
5415
|
+
}
|
|
5416
|
+
}
|
|
5417
|
+
};
|
|
5418
|
+
}
|
|
5003
5419
|
function outFileName(outDir, base, format) {
|
|
5004
5420
|
const ext = format === "cjs" ? ".cjs" : ".mjs";
|
|
5005
5421
|
return import_node_path15.default.join(outDir, base + ext);
|
|
@@ -5049,17 +5465,22 @@ var init_electron2 = __esm({
|
|
|
5049
5465
|
// src/server/electron-dev.ts
|
|
5050
5466
|
var electron_dev_exports = {};
|
|
5051
5467
|
__export(electron_dev_exports, {
|
|
5468
|
+
electronRendererDevPath: () => electronRendererDevPath,
|
|
5052
5469
|
startElectronDev: () => startElectronDev
|
|
5053
5470
|
});
|
|
5054
5471
|
async function startElectronDev(inlineConfig = {}) {
|
|
5055
5472
|
const { noSpawn, ...rest } = inlineConfig;
|
|
5056
5473
|
const config = await resolveConfig({ ...rest, target: "electron" }, "serve");
|
|
5057
5474
|
warnElectronVersion(config);
|
|
5058
|
-
console.log(import_picocolors10.default.cyan("\n\u26A1 nasti electron dev") + import_picocolors10.default.dim(` v${"2.
|
|
5475
|
+
console.log(import_picocolors10.default.cyan("\n\u26A1 nasti electron dev") + import_picocolors10.default.dim(` v${"2.3.1"}`));
|
|
5059
5476
|
const { createServer: createServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
|
|
5060
|
-
const server = await createServer2({
|
|
5477
|
+
const server = await createServer2({
|
|
5478
|
+
...rest,
|
|
5479
|
+
target: "electron",
|
|
5480
|
+
framework: config.framework
|
|
5481
|
+
});
|
|
5061
5482
|
await server.listen();
|
|
5062
|
-
const devUrl = `http://localhost:${server.config.server.port}
|
|
5483
|
+
const devUrl = `http://localhost:${server.config.server.port}` + electronRendererDevPath(config.electron.renderer);
|
|
5063
5484
|
console.log(import_picocolors10.default.dim(` renderer: ${devUrl}`));
|
|
5064
5485
|
const stageDir = import_node_path16.default.resolve(config.root, ".nasti");
|
|
5065
5486
|
import_node_fs11.default.mkdirSync(stageDir, { recursive: true });
|
|
@@ -5181,14 +5602,18 @@ async function compileNode(config, entry, opts) {
|
|
|
5181
5602
|
const result = transformCode(id, code, {
|
|
5182
5603
|
sourcemap: true,
|
|
5183
5604
|
jsxRuntime: "automatic",
|
|
5184
|
-
jsxImportSource: config.framework === "vue" ? "vue" : "react"
|
|
5605
|
+
jsxImportSource: config.framework === "vue" ? "vue" : "react",
|
|
5606
|
+
target: config.electron.nodeTarget
|
|
5185
5607
|
});
|
|
5186
5608
|
return { code: result.code, map: result.map ? JSON.parse(result.map) : void 0 };
|
|
5187
5609
|
}
|
|
5188
5610
|
};
|
|
5189
5611
|
const bundle2 = await (0, import_rolldown3.rolldown)({
|
|
5190
5612
|
input: entry,
|
|
5191
|
-
transform: {
|
|
5613
|
+
transform: {
|
|
5614
|
+
target: config.electron.nodeTarget,
|
|
5615
|
+
define: envDefine
|
|
5616
|
+
},
|
|
5192
5617
|
platform: "node",
|
|
5193
5618
|
plugins: [oxcTransformPlugin, electronPlugin(config), resolvePlugin(config)]
|
|
5194
5619
|
});
|
|
@@ -5204,6 +5629,10 @@ async function compileNode(config, entry, opts) {
|
|
|
5204
5629
|
});
|
|
5205
5630
|
await bundle2.close();
|
|
5206
5631
|
}
|
|
5632
|
+
function electronRendererDevPath(renderer) {
|
|
5633
|
+
const normalized = renderer.split(import_node_path16.default.sep).join("/").replace(/^\.?\//, "");
|
|
5634
|
+
return normalized === "index.html" ? "/" : `/${normalized}`;
|
|
5635
|
+
}
|
|
5207
5636
|
function resolveElectronBinary(config) {
|
|
5208
5637
|
if (config.electron.electronPath && import_node_fs11.default.existsSync(config.electron.electronPath)) {
|
|
5209
5638
|
return config.electron.electronPath;
|
|
@@ -5408,7 +5837,7 @@ cli.command("preview [root]", "Preview production build").option("--port <port>"
|
|
|
5408
5837
|
const host = options.host === true ? "0.0.0.0" : options.host ?? "localhost";
|
|
5409
5838
|
http2.createServer(app).listen(port, host, () => {
|
|
5410
5839
|
logger.info(`
|
|
5411
|
-
${import_picocolors11.default.cyan(import_picocolors11.default.bold("NASTI"))} ${import_picocolors11.default.cyan(`v${"2.
|
|
5840
|
+
${import_picocolors11.default.cyan(import_picocolors11.default.bold("NASTI"))} ${import_picocolors11.default.cyan(`v${"2.3.1"}`)} ${import_picocolors11.default.dim("preview")}
|
|
5412
5841
|
`);
|
|
5413
5842
|
printServerUrls2(
|
|
5414
5843
|
{
|
|
@@ -5425,6 +5854,6 @@ cli.command("preview [root]", "Preview production build").option("--port <port>"
|
|
|
5425
5854
|
}
|
|
5426
5855
|
});
|
|
5427
5856
|
cli.help();
|
|
5428
|
-
cli.version("2.
|
|
5857
|
+
cli.version("2.3.1");
|
|
5429
5858
|
cli.parse();
|
|
5430
5859
|
//# sourceMappingURL=cli.cjs.map
|