@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.js
CHANGED
|
@@ -231,6 +231,92 @@ var init_defaults = __esm({
|
|
|
231
231
|
}
|
|
232
232
|
});
|
|
233
233
|
|
|
234
|
+
// src/core/plugin-api.ts
|
|
235
|
+
function orderPlugins(plugins) {
|
|
236
|
+
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);
|
|
237
|
+
const indexesByName = /* @__PURE__ */ new Map();
|
|
238
|
+
baseline.forEach((plugin, index2) => {
|
|
239
|
+
const indexes = indexesByName.get(plugin.name) ?? [];
|
|
240
|
+
indexes.push(index2);
|
|
241
|
+
indexesByName.set(plugin.name, indexes);
|
|
242
|
+
});
|
|
243
|
+
const edges = baseline.map(() => /* @__PURE__ */ new Set());
|
|
244
|
+
const indegree = baseline.map(() => 0);
|
|
245
|
+
const addEdge = (from, to) => {
|
|
246
|
+
if (from === to || edges[from].has(to)) return;
|
|
247
|
+
edges[from].add(to);
|
|
248
|
+
indegree[to]++;
|
|
249
|
+
};
|
|
250
|
+
baseline.forEach((plugin, current) => {
|
|
251
|
+
for (const dependency of plugin.pre ?? []) {
|
|
252
|
+
for (const before of indexesByName.get(dependency) ?? []) addEdge(before, current);
|
|
253
|
+
}
|
|
254
|
+
for (const dependency of plugin.post ?? []) {
|
|
255
|
+
for (const after of indexesByName.get(dependency) ?? []) addEdge(current, after);
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
const ready = indegree.map((degree, index2) => ({ degree, index: index2 })).filter(({ degree }) => degree === 0).map(({ index: index2 }) => index2);
|
|
259
|
+
const ordered = [];
|
|
260
|
+
while (ready.length > 0) {
|
|
261
|
+
ready.sort((a, b) => a - b);
|
|
262
|
+
const current = ready.shift();
|
|
263
|
+
ordered.push(baseline[current]);
|
|
264
|
+
for (const next of edges[current]) {
|
|
265
|
+
indegree[next]--;
|
|
266
|
+
if (indegree[next] === 0) ready.push(next);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (ordered.length !== baseline.length) {
|
|
270
|
+
const cyclic = baseline.filter((_, index2) => indegree[index2] > 0).map((plugin) => plugin.name);
|
|
271
|
+
throw new Error(
|
|
272
|
+
`[nasti] circular plugin setup dependency: ${[...new Set(cyclic)].join(", ")}`
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
return ordered;
|
|
276
|
+
}
|
|
277
|
+
async function setupPluginApi(config, plugins) {
|
|
278
|
+
const exposed = /* @__PURE__ */ new Map();
|
|
279
|
+
const api = {
|
|
280
|
+
config,
|
|
281
|
+
logger: config.logger,
|
|
282
|
+
expose(key, value) {
|
|
283
|
+
if (exposed.has(key) && exposed.get(key) !== value) {
|
|
284
|
+
throw new Error(`[nasti] plugin API key already exposed: ${String(key)}`);
|
|
285
|
+
}
|
|
286
|
+
exposed.set(key, value);
|
|
287
|
+
},
|
|
288
|
+
useExposed(key) {
|
|
289
|
+
return exposed.get(key);
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
apiByConfig.set(config, api);
|
|
293
|
+
for (const plugin of plugins) {
|
|
294
|
+
await plugin.setup?.(api);
|
|
295
|
+
}
|
|
296
|
+
return api;
|
|
297
|
+
}
|
|
298
|
+
function getPluginApi(config) {
|
|
299
|
+
const api = apiByConfig.get(config);
|
|
300
|
+
if (!api) {
|
|
301
|
+
throw new Error(
|
|
302
|
+
"[nasti] internal: plugin API requested before setup; getPluginApi requires the original ResolvedConfig reference registered by setupPluginApi, not a shallow copy"
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
return api;
|
|
306
|
+
}
|
|
307
|
+
function enforceRank(plugin) {
|
|
308
|
+
if (plugin.enforce === "pre") return 0;
|
|
309
|
+
if (plugin.enforce === "post") return 2;
|
|
310
|
+
return 1;
|
|
311
|
+
}
|
|
312
|
+
var apiByConfig;
|
|
313
|
+
var init_plugin_api = __esm({
|
|
314
|
+
"src/core/plugin-api.ts"() {
|
|
315
|
+
"use strict";
|
|
316
|
+
apiByConfig = /* @__PURE__ */ new WeakMap();
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
|
|
234
320
|
// src/config/index.ts
|
|
235
321
|
import { pathToFileURL } from "url";
|
|
236
322
|
import path from "path";
|
|
@@ -268,6 +354,43 @@ async function loadConfigFromFile(root) {
|
|
|
268
354
|
}
|
|
269
355
|
return {};
|
|
270
356
|
}
|
|
357
|
+
function detectFramework(root) {
|
|
358
|
+
const sourceRoot = path.resolve(root, "src");
|
|
359
|
+
if (containsVueFile(sourceRoot)) return "vue";
|
|
360
|
+
const packagePath = path.resolve(root, "package.json");
|
|
361
|
+
if (fs.existsSync(packagePath)) {
|
|
362
|
+
try {
|
|
363
|
+
const pkg = JSON.parse(fs.readFileSync(packagePath, "utf-8"));
|
|
364
|
+
const dependencies = {
|
|
365
|
+
...pkg.dependencies ?? {},
|
|
366
|
+
...pkg.devDependencies ?? {},
|
|
367
|
+
...pkg.peerDependencies ?? {},
|
|
368
|
+
...pkg.optionalDependencies ?? {}
|
|
369
|
+
};
|
|
370
|
+
const hasVue = "vue" in dependencies || "@vue/runtime-dom" in dependencies;
|
|
371
|
+
const hasReact = "react" in dependencies || "react-dom" in dependencies;
|
|
372
|
+
if (hasVue && !hasReact) return "vue";
|
|
373
|
+
if (hasReact) return "react";
|
|
374
|
+
if (hasVue) return "vue";
|
|
375
|
+
} catch {
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return "react";
|
|
379
|
+
}
|
|
380
|
+
function containsVueFile(dir, depth = 0) {
|
|
381
|
+
if (depth > 5 || !fs.existsSync(dir)) return false;
|
|
382
|
+
try {
|
|
383
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
384
|
+
if (entry.isFile() && entry.name.endsWith(".vue")) return true;
|
|
385
|
+
if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== "dist" && containsVueFile(path.join(dir, entry.name), depth + 1)) {
|
|
386
|
+
return true;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
} catch {
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
271
394
|
async function loadTsConfig(filePath) {
|
|
272
395
|
const { transformSync: transformSync2 } = await import("oxc-transform");
|
|
273
396
|
const code = fs.readFileSync(filePath, "utf-8");
|
|
@@ -314,7 +437,7 @@ async function resolveConfig(inlineConfig = {}, command) {
|
|
|
314
437
|
base: merged.base ?? defaults.base,
|
|
315
438
|
mode,
|
|
316
439
|
target: merged.target ?? defaults.target,
|
|
317
|
-
framework: merged.framework ?? defaults.framework,
|
|
440
|
+
framework: (merged.framework ?? defaults.framework) === "auto" ? detectFramework(root) : merged.framework,
|
|
318
441
|
command,
|
|
319
442
|
resolve: {
|
|
320
443
|
// tsconfig paths 优先级最低:tsconfig < defaults < user config
|
|
@@ -361,7 +484,12 @@ async function resolveConfig(inlineConfig = {}, command) {
|
|
|
361
484
|
if (envOptions.build) Object.assign(resolved.build, envOptions.build);
|
|
362
485
|
resolved.environments.client = {
|
|
363
486
|
consumer,
|
|
364
|
-
entry:
|
|
487
|
+
entry: normalizeEnvironmentEntries(envOptions.entry, root),
|
|
488
|
+
html: path.resolve(
|
|
489
|
+
root,
|
|
490
|
+
envOptions.html ?? (resolved.target === "electron" ? resolved.electron.renderer : "index.html")
|
|
491
|
+
),
|
|
492
|
+
driver: envOptions.driver,
|
|
365
493
|
// 同引用 —— 精确镜像(assertClientEnvironmentMirror 校验)
|
|
366
494
|
resolve: resolved.resolve,
|
|
367
495
|
build: resolved.build
|
|
@@ -370,7 +498,9 @@ async function resolveConfig(inlineConfig = {}, command) {
|
|
|
370
498
|
}
|
|
371
499
|
resolved.environments[name] = {
|
|
372
500
|
consumer,
|
|
373
|
-
entry: (
|
|
501
|
+
entry: normalizeEnvironmentEntries(envOptions.entry, root),
|
|
502
|
+
html: envOptions.consumer === "client" && envOptions.html ? path.resolve(root, envOptions.html) : void 0,
|
|
503
|
+
driver: envOptions.driver,
|
|
374
504
|
resolve: {
|
|
375
505
|
alias: { ...resolved.resolve.alias, ...envOptions.resolve?.alias },
|
|
376
506
|
extensions: envOptions.resolve?.extensions ?? [...resolved.resolve.extensions],
|
|
@@ -389,12 +519,13 @@ async function resolveConfig(inlineConfig = {}, command) {
|
|
|
389
519
|
};
|
|
390
520
|
}
|
|
391
521
|
assertClientEnvironmentMirror(resolved);
|
|
392
|
-
const filteredPlugins = rawPlugins.filter((p) => {
|
|
522
|
+
const filteredPlugins = orderPlugins(rawPlugins.filter((p) => {
|
|
393
523
|
if (!p.apply) return true;
|
|
394
524
|
if (typeof p.apply === "function") return p.apply(resolved, env);
|
|
395
525
|
return p.apply === command;
|
|
396
|
-
});
|
|
526
|
+
}));
|
|
397
527
|
resolved.plugins = filteredPlugins;
|
|
528
|
+
await setupPluginApi(resolved, filteredPlugins);
|
|
398
529
|
if (resolved.target === "electron") {
|
|
399
530
|
const autoExternal = detectNativeDeps(root);
|
|
400
531
|
if (autoExternal.length > 0) {
|
|
@@ -410,6 +541,10 @@ async function resolveConfig(inlineConfig = {}, command) {
|
|
|
410
541
|
}
|
|
411
542
|
return resolved;
|
|
412
543
|
}
|
|
544
|
+
function normalizeEnvironmentEntries(entry, root) {
|
|
545
|
+
const entries = Array.isArray(entry) ? entry : entry ? [entry] : [];
|
|
546
|
+
return entries.map((item) => path.resolve(root, item));
|
|
547
|
+
}
|
|
413
548
|
function detectNativeDeps(root) {
|
|
414
549
|
const result = /* @__PURE__ */ new Set();
|
|
415
550
|
const pkgJsonPath = path.resolve(root, "package.json");
|
|
@@ -527,6 +662,7 @@ var init_config = __esm({
|
|
|
527
662
|
"use strict";
|
|
528
663
|
init_defaults();
|
|
529
664
|
init_logger();
|
|
665
|
+
init_plugin_api();
|
|
530
666
|
CONFIG_FILES = [
|
|
531
667
|
"nasti.config.ts",
|
|
532
668
|
"nasti.config.js",
|
|
@@ -537,21 +673,11 @@ var init_config = __esm({
|
|
|
537
673
|
});
|
|
538
674
|
|
|
539
675
|
// src/core/plugin-container.ts
|
|
540
|
-
function sortPlugins(plugins) {
|
|
541
|
-
const pre = [];
|
|
542
|
-
const normal = [];
|
|
543
|
-
const post = [];
|
|
544
|
-
for (const plugin of plugins) {
|
|
545
|
-
if (plugin.enforce === "pre") pre.push(plugin);
|
|
546
|
-
else if (plugin.enforce === "post") post.push(plugin);
|
|
547
|
-
else normal.push(plugin);
|
|
548
|
-
}
|
|
549
|
-
return [...pre, ...normal, ...post];
|
|
550
|
-
}
|
|
551
676
|
var PluginContainer;
|
|
552
677
|
var init_plugin_container = __esm({
|
|
553
678
|
"src/core/plugin-container.ts"() {
|
|
554
679
|
"use strict";
|
|
680
|
+
init_plugin_api();
|
|
555
681
|
PluginContainer = class {
|
|
556
682
|
plugins;
|
|
557
683
|
config;
|
|
@@ -562,7 +688,7 @@ var init_plugin_container = __esm({
|
|
|
562
688
|
constructor(config, environment) {
|
|
563
689
|
this.config = config;
|
|
564
690
|
this.environment = environment;
|
|
565
|
-
this.plugins =
|
|
691
|
+
this.plugins = orderPlugins(config.plugins);
|
|
566
692
|
this.ctx = this.createContext();
|
|
567
693
|
}
|
|
568
694
|
createContext() {
|
|
@@ -901,6 +1027,7 @@ var init_environment = __esm({
|
|
|
901
1027
|
init_module_graph();
|
|
902
1028
|
init_hot_channel();
|
|
903
1029
|
init_debug();
|
|
1030
|
+
init_plugin_api();
|
|
904
1031
|
debug = createDebugger("nasti:environment");
|
|
905
1032
|
NastiEnvironment = class {
|
|
906
1033
|
name;
|
|
@@ -909,6 +1036,7 @@ var init_environment = __esm({
|
|
|
909
1036
|
config;
|
|
910
1037
|
options;
|
|
911
1038
|
hot;
|
|
1039
|
+
driver;
|
|
912
1040
|
/** applyToEnvironment 过滤后的插件(init() 后可用) */
|
|
913
1041
|
plugins = [];
|
|
914
1042
|
/** per-env 插件容器(init() 后可用;dev 管线使用) */
|
|
@@ -916,6 +1044,7 @@ var init_environment = __esm({
|
|
|
916
1044
|
/** per-env 模块图(dev 管线使用) */
|
|
917
1045
|
moduleGraph;
|
|
918
1046
|
candidatePlugins;
|
|
1047
|
+
pluginApi;
|
|
919
1048
|
initialized = false;
|
|
920
1049
|
constructor(name, config, init = {}) {
|
|
921
1050
|
const options = config.environments[name];
|
|
@@ -932,6 +1061,7 @@ var init_environment = __esm({
|
|
|
932
1061
|
this.hot = init.hot ?? createNoopHotChannel();
|
|
933
1062
|
this.moduleGraph = new ModuleGraph();
|
|
934
1063
|
this.candidatePlugins = init.plugins ?? config.plugins;
|
|
1064
|
+
this.pluginApi = init.pluginApi ?? getPluginApi(config);
|
|
935
1065
|
}
|
|
936
1066
|
/** 过滤插件并建 per-env PluginContainer */
|
|
937
1067
|
async init() {
|
|
@@ -942,10 +1072,41 @@ var init_environment = __esm({
|
|
|
942
1072
|
{ ...this.config, plugins: this.plugins },
|
|
943
1073
|
this
|
|
944
1074
|
);
|
|
1075
|
+
if (this.options.driver) {
|
|
1076
|
+
const claimed = [];
|
|
1077
|
+
for (const plugin of this.plugins) {
|
|
1078
|
+
const driver = await plugin.createEnvironmentDriver?.(this, this.pluginApi);
|
|
1079
|
+
if (driver) claimed.push({ plugin, driver });
|
|
1080
|
+
}
|
|
1081
|
+
if (claimed.length === 0) {
|
|
1082
|
+
throw new Error(
|
|
1083
|
+
`[nasti] environment "${this.name}" requested driver "${this.options.driver}", but no plugin provided it`
|
|
1084
|
+
);
|
|
1085
|
+
}
|
|
1086
|
+
if (claimed.length > 1) {
|
|
1087
|
+
throw new Error(
|
|
1088
|
+
`[nasti] environment "${this.name}" was claimed by multiple drivers: ` + claimed.map(({ plugin, driver }) => `${plugin.name} (${driver.name})`).join(", ")
|
|
1089
|
+
);
|
|
1090
|
+
}
|
|
1091
|
+
this.driver = claimed[0].driver;
|
|
1092
|
+
debug?.(`env "${this.name}" uses driver "${this.driver.name}"`);
|
|
1093
|
+
}
|
|
945
1094
|
debug?.(`env "${this.name}" initialized (${this.plugins.length} plugins)`);
|
|
946
1095
|
}
|
|
1096
|
+
getDriverContext() {
|
|
1097
|
+
return {
|
|
1098
|
+
environment: this,
|
|
1099
|
+
config: this.config,
|
|
1100
|
+
api: this.pluginApi,
|
|
1101
|
+
logger: this.config.logger
|
|
1102
|
+
};
|
|
1103
|
+
}
|
|
947
1104
|
async close() {
|
|
948
|
-
|
|
1105
|
+
try {
|
|
1106
|
+
await this.driver?.close?.(this.getDriverContext());
|
|
1107
|
+
} finally {
|
|
1108
|
+
await this.hot.close?.();
|
|
1109
|
+
}
|
|
949
1110
|
}
|
|
950
1111
|
};
|
|
951
1112
|
}
|
|
@@ -1010,7 +1171,8 @@ function transformCode(filename, code, options = {}) {
|
|
|
1010
1171
|
importSource: options.jsxImportSource ?? "react",
|
|
1011
1172
|
refresh: options.reactRefresh ?? false
|
|
1012
1173
|
} : void 0,
|
|
1013
|
-
sourcemap: options.sourcemap ?? true
|
|
1174
|
+
sourcemap: options.sourcemap ?? true,
|
|
1175
|
+
target: options.target
|
|
1014
1176
|
});
|
|
1015
1177
|
if (result.errors && result.errors.length > 0) {
|
|
1016
1178
|
const msg = result.errors.map((e) => e.message ?? String(e)).join("\n");
|
|
@@ -1042,7 +1204,7 @@ function htmlPlugin(config) {
|
|
|
1042
1204
|
transformIndexHtml(html) {
|
|
1043
1205
|
const tags = [];
|
|
1044
1206
|
if (config.command === "serve") {
|
|
1045
|
-
const isReactLike = config.framework === "react"
|
|
1207
|
+
const isReactLike = config.framework === "react";
|
|
1046
1208
|
if (isReactLike) {
|
|
1047
1209
|
tags.push({
|
|
1048
1210
|
tag: "script",
|
|
@@ -1096,8 +1258,8 @@ function serializeTag(tag) {
|
|
|
1096
1258
|
}
|
|
1097
1259
|
return ` <${tag.tag}${attrs}>${children}</${tag.tag}>`;
|
|
1098
1260
|
}
|
|
1099
|
-
async function readHtmlFile(root) {
|
|
1100
|
-
const htmlPath = path2.resolve(root,
|
|
1261
|
+
async function readHtmlFile(root, htmlFile = "index.html") {
|
|
1262
|
+
const htmlPath = path2.isAbsolute(htmlFile) ? htmlFile : path2.resolve(root, htmlFile);
|
|
1101
1263
|
if (!fs2.existsSync(htmlPath)) return null;
|
|
1102
1264
|
return fs2.readFileSync(htmlPath, "utf-8");
|
|
1103
1265
|
}
|
|
@@ -1296,7 +1458,10 @@ function transformMiddleware(ctx) {
|
|
|
1296
1458
|
return;
|
|
1297
1459
|
}
|
|
1298
1460
|
if (url === "/" || url.endsWith(".html")) {
|
|
1299
|
-
const html = await readHtmlFile(
|
|
1461
|
+
const html = await readHtmlFile(
|
|
1462
|
+
ctx.config.root,
|
|
1463
|
+
ctx.config.environments.client?.html
|
|
1464
|
+
);
|
|
1300
1465
|
if (html) {
|
|
1301
1466
|
let processedHtml = html;
|
|
1302
1467
|
for (const plugin of ctx.config.plugins) {
|
|
@@ -3922,6 +4087,7 @@ var build_exports = {};
|
|
|
3922
4087
|
__export(build_exports, {
|
|
3923
4088
|
build: () => build,
|
|
3924
4089
|
getRolldownOptions: () => getRolldownOptions,
|
|
4090
|
+
replaceEntryScript: () => replaceEntryScript,
|
|
3925
4091
|
resolveClientEntries: () => resolveClientEntries,
|
|
3926
4092
|
toRolldownPlugins: () => toRolldownPlugins
|
|
3927
4093
|
});
|
|
@@ -4005,13 +4171,20 @@ function toRolldownPlugins(plugins) {
|
|
|
4005
4171
|
}));
|
|
4006
4172
|
}
|
|
4007
4173
|
function resolveClientEntries(config, html) {
|
|
4174
|
+
const configuredEntries = config.environments.client?.entry ?? [];
|
|
4175
|
+
if (configuredEntries.length > 0) return configuredEntries;
|
|
4008
4176
|
const entryPoints = [];
|
|
4177
|
+
const htmlFile = config.environments.client?.html;
|
|
4178
|
+
const htmlDir = htmlFile ? path12.dirname(htmlFile) : config.root;
|
|
4009
4179
|
if (html) {
|
|
4010
4180
|
const scriptMatches = html.matchAll(/<script[^>]+src=["']([^"']+)["'][^>]*>/gi);
|
|
4011
4181
|
for (const match of scriptMatches) {
|
|
4012
4182
|
const src = match[1];
|
|
4013
4183
|
if (src && !src.startsWith("http")) {
|
|
4014
|
-
|
|
4184
|
+
const cleanSrc = src.split(/[?#]/, 1)[0];
|
|
4185
|
+
entryPoints.push(
|
|
4186
|
+
cleanSrc.startsWith("/") ? path12.resolve(config.root, cleanSrc.replace(/^\//, "")) : path12.resolve(htmlDir, cleanSrc)
|
|
4187
|
+
);
|
|
4015
4188
|
}
|
|
4016
4189
|
}
|
|
4017
4190
|
}
|
|
@@ -4047,21 +4220,55 @@ async function build(inlineConfig = {}) {
|
|
|
4047
4220
|
const startTime = performance.now();
|
|
4048
4221
|
logger.info(
|
|
4049
4222
|
pc6.cyan(`
|
|
4050
|
-
nasti v${"2.
|
|
4223
|
+
nasti v${"2.3.1"} `) + pc6.green(`building for ${config.mode}...`)
|
|
4051
4224
|
);
|
|
4052
4225
|
debug5?.(`root: ${config.root}`);
|
|
4053
4226
|
const buildableNames = Object.keys(config.environments).filter(
|
|
4054
|
-
(name) => name === "client" || config.environments[name].entry.length > 0
|
|
4227
|
+
(name) => name === "client" || config.environments[name].entry.length > 0 || !!config.environments[name].driver
|
|
4055
4228
|
);
|
|
4056
4229
|
buildableNames.sort((a, b) => a === "client" ? -1 : b === "client" ? 1 : 0);
|
|
4057
4230
|
const environments = {};
|
|
4231
|
+
const environmentResults = {};
|
|
4232
|
+
const initializedEnvironments = [];
|
|
4058
4233
|
let clientOutput = [];
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4234
|
+
let buildFailed = false;
|
|
4235
|
+
try {
|
|
4236
|
+
for (const name of buildableNames) {
|
|
4237
|
+
const built = name === "client" ? await buildClientEnvironment(config) : await buildServerEnvironment(config, name);
|
|
4238
|
+
initializedEnvironments.push(built.environment);
|
|
4239
|
+
environments[name] = built.result.output;
|
|
4240
|
+
environmentResults[name] = built.result;
|
|
4241
|
+
if (name === "client") clientOutput = built.result.output;
|
|
4242
|
+
if (buildableNames.length > 1) {
|
|
4243
|
+
debug5?.(`environment "${name}" built (${built.result.output.length} files)`);
|
|
4244
|
+
}
|
|
4245
|
+
}
|
|
4246
|
+
const pluginApi = getPluginApi(config);
|
|
4247
|
+
for (const plugin of config.plugins) {
|
|
4248
|
+
await plugin.afterBuildApp?.(environmentResults, pluginApi);
|
|
4249
|
+
}
|
|
4250
|
+
} catch (error) {
|
|
4251
|
+
buildFailed = true;
|
|
4252
|
+
throw error;
|
|
4253
|
+
} finally {
|
|
4254
|
+
let closeFailed = false;
|
|
4255
|
+
let firstCloseError;
|
|
4256
|
+
for (const environment of [...initializedEnvironments].reverse()) {
|
|
4257
|
+
try {
|
|
4258
|
+
await environment.close();
|
|
4259
|
+
} catch (error) {
|
|
4260
|
+
if (!closeFailed) {
|
|
4261
|
+
closeFailed = true;
|
|
4262
|
+
firstCloseError = error;
|
|
4263
|
+
}
|
|
4264
|
+
const closeError = error instanceof Error ? error : new Error(String(error));
|
|
4265
|
+
logger.error(`[nasti] failed to close environment "${environment.name}"`, {
|
|
4266
|
+
error: closeError
|
|
4267
|
+
});
|
|
4268
|
+
}
|
|
4269
|
+
}
|
|
4270
|
+
if (closeFailed && !buildFailed) {
|
|
4271
|
+
throw firstCloseError;
|
|
4065
4272
|
}
|
|
4066
4273
|
}
|
|
4067
4274
|
const elapsed = ((performance.now() - startTime) / 1e3).toFixed(2);
|
|
@@ -4074,83 +4281,130 @@ nasti v${"2.2.0"} `) + pc6.green(`building for ${config.mode}...`)
|
|
|
4074
4281
|
const envSuffix = buildableNames.length > 1 ? ` (${buildableNames.join(" + ")})` : "";
|
|
4075
4282
|
logger.info(pc6.green(`\u2713 built in ${elapsed}s`) + pc6.dim(envSuffix));
|
|
4076
4283
|
logger.info(pc6.dim(` ${fileCount} files, ${displaySize(totalSize)} total \u2192 ${config.build.outDir}/`));
|
|
4077
|
-
return { output: clientOutput, environments };
|
|
4284
|
+
return { output: clientOutput, environments, environmentResults };
|
|
4078
4285
|
}
|
|
4079
4286
|
async function buildClientEnvironment(config) {
|
|
4080
4287
|
const logger = config.logger;
|
|
4081
4288
|
const outDir = path12.resolve(config.root, config.build.outDir);
|
|
4082
|
-
if (config.build.emptyOutDir && fs9.existsSync(outDir)) {
|
|
4083
|
-
fs9.rmSync(outDir, { recursive: true, force: true });
|
|
4084
|
-
}
|
|
4085
|
-
fs9.mkdirSync(outDir, { recursive: true });
|
|
4086
|
-
const html = await readHtmlFile(config.root);
|
|
4087
|
-
const entryPoints = resolveClientEntries(config, html);
|
|
4088
|
-
if (entryPoints.length === 0) {
|
|
4089
|
-
throw new Error("No entry point found. Add a <script> tag to index.html or create src/main.ts");
|
|
4090
|
-
}
|
|
4091
4289
|
const cssEngine = createCssEngine();
|
|
4092
4290
|
const pluginList = resolvePluginList(config, config.plugins, { cssEngine });
|
|
4093
|
-
const clientEnv = new NastiEnvironment("client",
|
|
4291
|
+
const clientEnv = new NastiEnvironment("client", config, {
|
|
4094
4292
|
mode: "build",
|
|
4095
|
-
plugins: pluginList
|
|
4293
|
+
plugins: pluginList,
|
|
4294
|
+
pluginApi: getPluginApi(config)
|
|
4096
4295
|
});
|
|
4097
4296
|
await clientEnv.init();
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
...nativeReporter ? [nativeReporter] : []
|
|
4104
|
-
];
|
|
4105
|
-
const { inputOptions, outputOptions } = getRolldownOptions(clientEnv, entryPoints, rolldownPlugins);
|
|
4106
|
-
const bundle2 = await rolldown(inputOptions);
|
|
4107
|
-
const { output } = await bundle2.write(outputOptions);
|
|
4108
|
-
await bundle2.close();
|
|
4109
|
-
if (html) {
|
|
4110
|
-
let processedHtml = html;
|
|
4111
|
-
const htmlPlugins = [...allPlugins.filter((p) => p.transformIndexHtml), htmlPlugin(config)];
|
|
4112
|
-
for (const p of htmlPlugins) {
|
|
4113
|
-
const result = await p.transformIndexHtml(processedHtml);
|
|
4114
|
-
if (typeof result === "string") {
|
|
4115
|
-
processedHtml = result;
|
|
4116
|
-
} else if (result && "html" in result) {
|
|
4117
|
-
processedHtml = processHtml(result.html, result.tags);
|
|
4118
|
-
} else if (Array.isArray(result)) {
|
|
4119
|
-
processedHtml = processHtml(processedHtml, result);
|
|
4120
|
-
}
|
|
4121
|
-
}
|
|
4122
|
-
processedHtml = injectCssLinks(processedHtml, cssEngine, config);
|
|
4123
|
-
for (const chunk of output) {
|
|
4124
|
-
if (chunk.type === "chunk" && chunk.isEntry && chunk.facadeModuleId) {
|
|
4125
|
-
const originalEntry = path12.relative(config.root, chunk.facadeModuleId);
|
|
4126
|
-
processedHtml = processedHtml.replace(
|
|
4127
|
-
new RegExp(`(src=["'])/?(${escapeRegExp(originalEntry)})(["'])`, "g"),
|
|
4128
|
-
`$1${config.base}${chunk.fileName}$3`
|
|
4297
|
+
try {
|
|
4298
|
+
if (clientEnv.driver) {
|
|
4299
|
+
if (!clientEnv.driver.build) {
|
|
4300
|
+
throw new Error(
|
|
4301
|
+
`[nasti] environment "client" driver "${clientEnv.driver.name}" does not implement build()`
|
|
4129
4302
|
);
|
|
4130
4303
|
}
|
|
4304
|
+
const result = await clientEnv.driver.build(clientEnv.getDriverContext());
|
|
4305
|
+
return { environment: clientEnv, result };
|
|
4131
4306
|
}
|
|
4132
|
-
fs9.
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4307
|
+
if (config.build.emptyOutDir && fs9.existsSync(outDir)) {
|
|
4308
|
+
fs9.rmSync(outDir, { recursive: true, force: true });
|
|
4309
|
+
}
|
|
4310
|
+
fs9.mkdirSync(outDir, { recursive: true });
|
|
4311
|
+
const htmlFile = config.environments.client.html ?? path12.resolve(config.root, "index.html");
|
|
4312
|
+
const html = await readHtmlFile(config.root, htmlFile);
|
|
4313
|
+
const entryPoints = resolveClientEntries(config, html);
|
|
4314
|
+
if (entryPoints.length === 0) {
|
|
4315
|
+
throw new Error("No entry point found. Add a <script> tag to index.html or create src/main.ts");
|
|
4316
|
+
}
|
|
4317
|
+
const allPlugins = clientEnv.plugins;
|
|
4318
|
+
const nativeReporter = config.logLevel === "silent" ? null : await tryNativeReporterPlugin(config, logger);
|
|
4319
|
+
const rolldownPlugins = [
|
|
4320
|
+
createOxcTransformPlugin(config, clientEnv),
|
|
4321
|
+
...toRolldownPlugins(allPlugins),
|
|
4322
|
+
...nativeReporter ? [nativeReporter] : []
|
|
4323
|
+
];
|
|
4324
|
+
const { inputOptions, outputOptions } = getRolldownOptions(
|
|
4325
|
+
clientEnv,
|
|
4326
|
+
entryPoints,
|
|
4327
|
+
rolldownPlugins
|
|
4328
|
+
);
|
|
4329
|
+
const bundle2 = await rolldown(inputOptions);
|
|
4330
|
+
const { output } = await bundle2.write(outputOptions);
|
|
4331
|
+
await bundle2.close();
|
|
4332
|
+
if (html) {
|
|
4333
|
+
let processedHtml = html;
|
|
4334
|
+
const htmlPlugins = [...allPlugins.filter((p) => p.transformIndexHtml), htmlPlugin(config)];
|
|
4335
|
+
for (const p of htmlPlugins) {
|
|
4336
|
+
const result = await p.transformIndexHtml(processedHtml);
|
|
4337
|
+
if (typeof result === "string") {
|
|
4338
|
+
processedHtml = result;
|
|
4339
|
+
} else if (result && "html" in result) {
|
|
4340
|
+
processedHtml = processHtml(result.html, result.tags);
|
|
4341
|
+
} else if (Array.isArray(result)) {
|
|
4342
|
+
processedHtml = processHtml(processedHtml, result);
|
|
4343
|
+
}
|
|
4344
|
+
}
|
|
4345
|
+
processedHtml = injectCssLinks(processedHtml, cssEngine, config);
|
|
4346
|
+
for (const chunk of output) {
|
|
4347
|
+
if (chunk.type === "chunk" && chunk.isEntry && chunk.facadeModuleId) {
|
|
4348
|
+
processedHtml = replaceEntryScript(
|
|
4349
|
+
processedHtml,
|
|
4350
|
+
chunk.facadeModuleId,
|
|
4351
|
+
chunk.fileName,
|
|
4352
|
+
config,
|
|
4353
|
+
htmlFile,
|
|
4354
|
+
config.base
|
|
4355
|
+
);
|
|
4356
|
+
}
|
|
4357
|
+
}
|
|
4358
|
+
fs9.writeFileSync(path12.resolve(outDir, "index.html"), processedHtml);
|
|
4359
|
+
}
|
|
4360
|
+
if (!nativeReporter && config.logLevel !== "silent") {
|
|
4361
|
+
reportBuildOutput(output, config, logger);
|
|
4362
|
+
}
|
|
4363
|
+
warnLargeChunks(output, config, logger);
|
|
4364
|
+
return { environment: clientEnv, result: { output } };
|
|
4365
|
+
} catch (error) {
|
|
4366
|
+
try {
|
|
4367
|
+
await clientEnv.close();
|
|
4368
|
+
} catch (closeError) {
|
|
4369
|
+
const normalized = closeError instanceof Error ? closeError : new Error(String(closeError));
|
|
4370
|
+
logger.error("[nasti] failed to close client environment after build failure", {
|
|
4371
|
+
error: normalized
|
|
4372
|
+
});
|
|
4373
|
+
}
|
|
4374
|
+
throw error;
|
|
4136
4375
|
}
|
|
4137
|
-
warnLargeChunks(output, config, logger);
|
|
4138
|
-
return output;
|
|
4139
4376
|
}
|
|
4140
4377
|
async function buildServerEnvironment(config, name) {
|
|
4141
4378
|
const envOptions = config.environments[name];
|
|
4142
4379
|
const logger = config.logger;
|
|
4380
|
+
const pluginList = resolvePluginList(config, config.plugins, { consumer: envOptions.consumer });
|
|
4381
|
+
const environment = new NastiEnvironment(name, config, {
|
|
4382
|
+
mode: "build",
|
|
4383
|
+
plugins: pluginList,
|
|
4384
|
+
pluginApi: getPluginApi(config)
|
|
4385
|
+
});
|
|
4386
|
+
await environment.init();
|
|
4387
|
+
if (environment.driver) {
|
|
4388
|
+
if (!environment.driver.build) {
|
|
4389
|
+
await environment.close();
|
|
4390
|
+
throw new Error(
|
|
4391
|
+
`[nasti] environment "${name}" driver "${environment.driver.name}" does not implement build()`
|
|
4392
|
+
);
|
|
4393
|
+
}
|
|
4394
|
+
try {
|
|
4395
|
+
const result = await environment.driver.build(environment.getDriverContext());
|
|
4396
|
+
return { environment, result };
|
|
4397
|
+
} catch (error) {
|
|
4398
|
+
await environment.close();
|
|
4399
|
+
throw error;
|
|
4400
|
+
}
|
|
4401
|
+
}
|
|
4143
4402
|
for (const entry of envOptions.entry) {
|
|
4144
4403
|
if (!fs9.existsSync(entry)) {
|
|
4404
|
+
await environment.close();
|
|
4145
4405
|
throw new Error(`[nasti] environment "${name}" entry not found: ${entry}`);
|
|
4146
4406
|
}
|
|
4147
4407
|
}
|
|
4148
|
-
const pluginList = resolvePluginList(config, config.plugins, { consumer: envOptions.consumer });
|
|
4149
|
-
const environment = new NastiEnvironment(name, { ...config, plugins: pluginList }, {
|
|
4150
|
-
mode: "build",
|
|
4151
|
-
plugins: pluginList
|
|
4152
|
-
});
|
|
4153
|
-
await environment.init();
|
|
4154
4408
|
const rolldownPlugins = [
|
|
4155
4409
|
createOxcTransformPlugin(config, environment),
|
|
4156
4410
|
...toRolldownPlugins(environment.plugins)
|
|
@@ -4170,7 +4424,7 @@ async function buildServerEnvironment(config, name) {
|
|
|
4170
4424
|
logger.info(
|
|
4171
4425
|
pc6.dim(` [${name}] `) + output.map((o) => path12.join(envOptions.build.outDir, o.fileName)).join(pc6.dim(", "))
|
|
4172
4426
|
);
|
|
4173
|
-
return output;
|
|
4427
|
+
return { environment, result: { output } };
|
|
4174
4428
|
}
|
|
4175
4429
|
function injectCssLinks(html, cssEngine, config) {
|
|
4176
4430
|
const cssLinkTags = [];
|
|
@@ -4196,6 +4450,25 @@ function injectCssLinks(html, cssEngine, config) {
|
|
|
4196
4450
|
function escapeRegExp(string) {
|
|
4197
4451
|
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4198
4452
|
}
|
|
4453
|
+
function replaceEntryScript(html, facadeModuleId, fileName, config, htmlFile, urlPrefix) {
|
|
4454
|
+
const rootRelative = path12.relative(config.root, facadeModuleId).split(path12.sep).join("/");
|
|
4455
|
+
const resolvedHtmlFile = path12.resolve(config.root, htmlFile);
|
|
4456
|
+
const htmlRelative = path12.relative(path12.dirname(resolvedHtmlFile), facadeModuleId).split(path12.sep).join("/");
|
|
4457
|
+
const candidates = /* @__PURE__ */ new Set([
|
|
4458
|
+
rootRelative,
|
|
4459
|
+
`/${rootRelative}`,
|
|
4460
|
+
htmlRelative,
|
|
4461
|
+
`./${htmlRelative}`
|
|
4462
|
+
]);
|
|
4463
|
+
let processed = html;
|
|
4464
|
+
for (const candidate of candidates) {
|
|
4465
|
+
processed = processed.replace(
|
|
4466
|
+
new RegExp(`(src=["'])${escapeRegExp(candidate)}([?#][^"']*)?(["'])`, "g"),
|
|
4467
|
+
`$1${urlPrefix}${fileName}$3`
|
|
4468
|
+
);
|
|
4469
|
+
}
|
|
4470
|
+
return processed;
|
|
4471
|
+
}
|
|
4199
4472
|
var debug5, NODE_BUILTINS2;
|
|
4200
4473
|
var init_build = __esm({
|
|
4201
4474
|
"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
|
debug5 = createDebugger("nasti:build");
|
|
4213
4487
|
NODE_BUILTINS2 = /* @__PURE__ */ new Set([...builtinModules2, ...builtinModules2.map((m) => `node:${m}`)]);
|
|
4214
4488
|
}
|
|
@@ -4240,7 +4514,7 @@ async function createBundledDevServer(opts) {
|
|
|
4240
4514
|
`[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.`
|
|
4241
4515
|
);
|
|
4242
4516
|
}
|
|
4243
|
-
const html = await readHtmlFile(config.root);
|
|
4517
|
+
const html = await readHtmlFile(config.root, config.environments.client?.html);
|
|
4244
4518
|
const entryPoints = resolveClientEntries(config, html);
|
|
4245
4519
|
if (entryPoints.length === 0) {
|
|
4246
4520
|
throw new Error("No entry point found. Add a <script> tag to index.html or create src/main.ts");
|
|
@@ -4446,7 +4720,7 @@ async function createBundledDevServer(opts) {
|
|
|
4446
4720
|
return;
|
|
4447
4721
|
}
|
|
4448
4722
|
if (pathname === "/" || pathname.endsWith(".html")) {
|
|
4449
|
-
const rawHtml = await readHtmlFile(config.root);
|
|
4723
|
+
const rawHtml = await readHtmlFile(config.root, config.environments.client?.html);
|
|
4450
4724
|
if (rawHtml) {
|
|
4451
4725
|
res.setHeader("Content-Type", "text/html");
|
|
4452
4726
|
res.setHeader("Cache-Control", "no-store");
|
|
@@ -4530,10 +4804,13 @@ async function renderBundledIndexHtml(html, config, entryFileNames) {
|
|
|
4530
4804
|
}
|
|
4531
4805
|
}
|
|
4532
4806
|
for (const [facadeModuleId, fileName] of entryFileNames) {
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4807
|
+
processed = replaceEntryScript(
|
|
4808
|
+
processed,
|
|
4809
|
+
facadeModuleId,
|
|
4810
|
+
fileName,
|
|
4811
|
+
config,
|
|
4812
|
+
config.environments.client?.html ?? "index.html",
|
|
4813
|
+
"/"
|
|
4537
4814
|
);
|
|
4538
4815
|
}
|
|
4539
4816
|
return processed;
|
|
@@ -4677,10 +4954,12 @@ async function createServer(inlineConfig = {}) {
|
|
|
4677
4954
|
const app = connect();
|
|
4678
4955
|
const httpServer = http.createServer(app);
|
|
4679
4956
|
const ws = createWebSocketServer(httpServer);
|
|
4680
|
-
const
|
|
4957
|
+
const pluginApi = getPluginApi(config);
|
|
4958
|
+
const clientEnv = new NastiEnvironment("client", config, {
|
|
4681
4959
|
hot: createWsHotChannel(ws),
|
|
4682
4960
|
mode: "dev",
|
|
4683
|
-
plugins: allPlugins
|
|
4961
|
+
plugins: allPlugins,
|
|
4962
|
+
pluginApi
|
|
4684
4963
|
});
|
|
4685
4964
|
await clientEnv.init();
|
|
4686
4965
|
const environments = { client: clientEnv };
|
|
@@ -4688,11 +4967,15 @@ async function createServer(inlineConfig = {}) {
|
|
|
4688
4967
|
if (name === "client") continue;
|
|
4689
4968
|
const consumer = config.environments[name].consumer;
|
|
4690
4969
|
const envPlugins = resolvePluginList(config, config.plugins, { consumer });
|
|
4691
|
-
environments[name] = new NastiEnvironment(name,
|
|
4970
|
+
environments[name] = new NastiEnvironment(name, config, {
|
|
4692
4971
|
mode: "dev",
|
|
4693
|
-
plugins: envPlugins
|
|
4972
|
+
plugins: envPlugins,
|
|
4973
|
+
pluginApi
|
|
4694
4974
|
});
|
|
4695
4975
|
}
|
|
4976
|
+
for (const [name, environment] of Object.entries(environments)) {
|
|
4977
|
+
if (name !== "client" && environment.options.driver) await environment.init();
|
|
4978
|
+
}
|
|
4696
4979
|
let ssrRunner = null;
|
|
4697
4980
|
async function getSsrRunner() {
|
|
4698
4981
|
if (ssrRunner) return ssrRunner;
|
|
@@ -4717,14 +5000,6 @@ async function createServer(inlineConfig = {}) {
|
|
|
4717
5000
|
});
|
|
4718
5001
|
app.use(bundledServer.middleware);
|
|
4719
5002
|
}
|
|
4720
|
-
app.use(transformMiddleware({
|
|
4721
|
-
config: configWithPlugins,
|
|
4722
|
-
pluginContainer,
|
|
4723
|
-
moduleGraph
|
|
4724
|
-
}));
|
|
4725
|
-
const publicDir = path14.resolve(config.root, "public");
|
|
4726
|
-
app.use(sirv(publicDir, { dev: true, etag: true }));
|
|
4727
|
-
app.use(sirv(config.root, { dev: true, etag: true }));
|
|
4728
5003
|
const ignoredSegments = /* @__PURE__ */ new Set(["node_modules", ".git", ".nasti"]);
|
|
4729
5004
|
const outDirAbs = path14.resolve(config.root, config.build.outDir);
|
|
4730
5005
|
const watcher = watch(config.root, {
|
|
@@ -4741,13 +5016,72 @@ async function createServer(inlineConfig = {}) {
|
|
|
4741
5016
|
ignoreInitial: true
|
|
4742
5017
|
});
|
|
4743
5018
|
let server;
|
|
5019
|
+
const environmentServices = {};
|
|
5020
|
+
let environmentDriversStarted = false;
|
|
5021
|
+
const logCloseError = (target, error) => {
|
|
5022
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
5023
|
+
logger.error(`[nasti] failed to close ${target}`, { error: normalized });
|
|
5024
|
+
};
|
|
5025
|
+
const startEnvironmentDrivers = async () => {
|
|
5026
|
+
if (environmentDriversStarted) return;
|
|
5027
|
+
environmentDriversStarted = true;
|
|
5028
|
+
const started = [];
|
|
5029
|
+
const attempted = [];
|
|
5030
|
+
try {
|
|
5031
|
+
for (const [name, environment] of Object.entries(environments)) {
|
|
5032
|
+
if (!environment.driver?.serve) continue;
|
|
5033
|
+
attempted.push(environment);
|
|
5034
|
+
const result = await environment.driver.serve({
|
|
5035
|
+
...environment.getDriverContext(),
|
|
5036
|
+
server
|
|
5037
|
+
});
|
|
5038
|
+
started.push({ name, environment, service: result ?? {} });
|
|
5039
|
+
}
|
|
5040
|
+
for (const { name, service } of started) {
|
|
5041
|
+
environmentServices[name] = service;
|
|
5042
|
+
if (service.middleware) app.use(service.middleware);
|
|
5043
|
+
}
|
|
5044
|
+
} catch (error) {
|
|
5045
|
+
environmentDriversStarted = false;
|
|
5046
|
+
for (const { name } of started) {
|
|
5047
|
+
delete environmentServices[name];
|
|
5048
|
+
}
|
|
5049
|
+
for (const environment of attempted.reverse()) {
|
|
5050
|
+
try {
|
|
5051
|
+
await environment.driver?.close?.(environment.getDriverContext());
|
|
5052
|
+
} catch (closeError) {
|
|
5053
|
+
logCloseError(`environment driver "${environment.driver.name}"`, closeError);
|
|
5054
|
+
}
|
|
5055
|
+
}
|
|
5056
|
+
throw error;
|
|
5057
|
+
}
|
|
5058
|
+
};
|
|
5059
|
+
const notifyEnvironmentDrivers = (file, event) => {
|
|
5060
|
+
for (const environment of Object.values(environments)) {
|
|
5061
|
+
if (!environment.driver?.watchChange) continue;
|
|
5062
|
+
void Promise.resolve(
|
|
5063
|
+
environment.driver.watchChange(file, event, environment.getDriverContext())
|
|
5064
|
+
).catch((error) => {
|
|
5065
|
+
logger.error(
|
|
5066
|
+
`[nasti] environment driver "${environment.driver.name}" watchChange failed`,
|
|
5067
|
+
{ error }
|
|
5068
|
+
);
|
|
5069
|
+
});
|
|
5070
|
+
}
|
|
5071
|
+
};
|
|
4744
5072
|
watcher.on("change", (file) => {
|
|
4745
5073
|
ssrRunner?.invalidateFile(file);
|
|
4746
5074
|
handleFileChange(file, server);
|
|
5075
|
+
notifyEnvironmentDrivers(file, "change");
|
|
4747
5076
|
});
|
|
4748
5077
|
watcher.on("add", (file) => {
|
|
4749
5078
|
ssrRunner?.invalidateFile(file);
|
|
4750
5079
|
handleFileChange(file, server);
|
|
5080
|
+
notifyEnvironmentDrivers(file, "add");
|
|
5081
|
+
});
|
|
5082
|
+
watcher.on("unlink", (file) => {
|
|
5083
|
+
ssrRunner?.invalidateFile(file);
|
|
5084
|
+
notifyEnvironmentDrivers(file, "unlink");
|
|
4751
5085
|
});
|
|
4752
5086
|
server = {
|
|
4753
5087
|
config: configWithPlugins,
|
|
@@ -4756,10 +5090,12 @@ async function createServer(inlineConfig = {}) {
|
|
|
4756
5090
|
watcher,
|
|
4757
5091
|
ws,
|
|
4758
5092
|
environments,
|
|
5093
|
+
environmentServices,
|
|
4759
5094
|
async listen(port) {
|
|
4760
5095
|
const finalPort = port ?? config.server.port;
|
|
4761
5096
|
const host = config.server.host === true ? "0.0.0.0" : config.server.host;
|
|
4762
5097
|
await pluginContainer.buildStart();
|
|
5098
|
+
await startEnvironmentDrivers();
|
|
4763
5099
|
return new Promise((resolve, reject) => {
|
|
4764
5100
|
let currentPort = finalPort;
|
|
4765
5101
|
const onListening = () => {
|
|
@@ -4767,15 +5103,20 @@ async function createServer(inlineConfig = {}) {
|
|
|
4767
5103
|
config.server.port = actualPort;
|
|
4768
5104
|
const localUrl = `http://localhost:${actualPort}/`;
|
|
4769
5105
|
const networkUrl = host === "0.0.0.0" ? `http://${getNetworkAddress()}:${actualPort}/` : null;
|
|
5106
|
+
const driverLocalUrls = Object.values(environmentServices).flatMap((service) => service.localUrls ?? []);
|
|
5107
|
+
const driverNetworkUrls = Object.values(environmentServices).flatMap((service) => service.networkUrls ?? []);
|
|
4770
5108
|
logger.clearScreen("info");
|
|
4771
5109
|
const readyIn = Math.ceil(performance.now() - startTime);
|
|
4772
5110
|
logger.info(
|
|
4773
5111
|
`
|
|
4774
|
-
${pc8.cyan(pc8.bold("NASTI"))} ${pc8.cyan(`v${"2.
|
|
5112
|
+
${pc8.cyan(pc8.bold("NASTI"))} ${pc8.cyan(`v${"2.3.1"}`)} ${pc8.dim("ready in")} ${pc8.bold(readyIn)} ${pc8.dim("ms")}
|
|
4775
5113
|
`
|
|
4776
5114
|
);
|
|
4777
5115
|
printServerUrls(
|
|
4778
|
-
{
|
|
5116
|
+
{
|
|
5117
|
+
local: [localUrl, ...driverLocalUrls],
|
|
5118
|
+
network: [...networkUrl ? [networkUrl] : [], ...driverNetworkUrls]
|
|
5119
|
+
},
|
|
4779
5120
|
logger.info
|
|
4780
5121
|
);
|
|
4781
5122
|
logger.info("");
|
|
@@ -4805,11 +5146,62 @@ async function createServer(inlineConfig = {}) {
|
|
|
4805
5146
|
async close() {
|
|
4806
5147
|
await pluginContainer.buildEnd();
|
|
4807
5148
|
await bundledServer?.close();
|
|
4808
|
-
|
|
5149
|
+
let environmentCloseFailed = false;
|
|
5150
|
+
let firstEnvironmentCloseError;
|
|
5151
|
+
for (const environment of Object.values(environments).reverse()) {
|
|
5152
|
+
try {
|
|
5153
|
+
await environment.close();
|
|
5154
|
+
} catch (error) {
|
|
5155
|
+
if (!environmentCloseFailed) {
|
|
5156
|
+
environmentCloseFailed = true;
|
|
5157
|
+
firstEnvironmentCloseError = error;
|
|
5158
|
+
}
|
|
5159
|
+
logCloseError(`environment "${environment.name}"`, error);
|
|
5160
|
+
}
|
|
5161
|
+
}
|
|
5162
|
+
await watcher.close();
|
|
4809
5163
|
ws.close();
|
|
4810
5164
|
httpServer.close();
|
|
5165
|
+
if (environmentCloseFailed) {
|
|
5166
|
+
throw firstEnvironmentCloseError;
|
|
5167
|
+
}
|
|
4811
5168
|
}
|
|
4812
5169
|
};
|
|
5170
|
+
try {
|
|
5171
|
+
await startEnvironmentDrivers();
|
|
5172
|
+
} catch (error) {
|
|
5173
|
+
if (bundledServer) {
|
|
5174
|
+
try {
|
|
5175
|
+
await bundledServer.close();
|
|
5176
|
+
} catch (closeError) {
|
|
5177
|
+
logCloseError("bundled dev server after driver startup failure", closeError);
|
|
5178
|
+
}
|
|
5179
|
+
}
|
|
5180
|
+
try {
|
|
5181
|
+
await watcher.close();
|
|
5182
|
+
} catch (closeError) {
|
|
5183
|
+
logCloseError("file watcher after driver startup failure", closeError);
|
|
5184
|
+
}
|
|
5185
|
+
try {
|
|
5186
|
+
ws.close();
|
|
5187
|
+
} catch (closeError) {
|
|
5188
|
+
logCloseError("WebSocket server after driver startup failure", closeError);
|
|
5189
|
+
}
|
|
5190
|
+
try {
|
|
5191
|
+
httpServer.close();
|
|
5192
|
+
} catch (closeError) {
|
|
5193
|
+
logCloseError("HTTP server after driver startup failure", closeError);
|
|
5194
|
+
}
|
|
5195
|
+
throw error;
|
|
5196
|
+
}
|
|
5197
|
+
app.use(transformMiddleware({
|
|
5198
|
+
config: configWithPlugins,
|
|
5199
|
+
pluginContainer,
|
|
5200
|
+
moduleGraph
|
|
5201
|
+
}));
|
|
5202
|
+
const publicDir = path14.resolve(config.root, "public");
|
|
5203
|
+
app.use(sirv(publicDir, { dev: true, etag: true }));
|
|
5204
|
+
app.use(sirv(config.root, { dev: true, etag: true }));
|
|
4813
5205
|
const postMiddlewares = [];
|
|
4814
5206
|
for (const plugin of allPlugins) {
|
|
4815
5207
|
if (plugin.configureServer) {
|
|
@@ -4844,6 +5236,7 @@ var init_server = __esm({
|
|
|
4844
5236
|
init_middleware();
|
|
4845
5237
|
init_hmr();
|
|
4846
5238
|
init_builtins();
|
|
5239
|
+
init_plugin_api();
|
|
4847
5240
|
}
|
|
4848
5241
|
});
|
|
4849
5242
|
|
|
@@ -4890,6 +5283,7 @@ var init_electron = __esm({
|
|
|
4890
5283
|
var electron_exports = {};
|
|
4891
5284
|
__export(electron_exports, {
|
|
4892
5285
|
buildElectron: () => buildElectron,
|
|
5286
|
+
createElectronRendererConfig: () => createElectronRendererConfig,
|
|
4893
5287
|
detectInstalledElectron: () => detectInstalledElectron,
|
|
4894
5288
|
normalizePreload: () => normalizePreload
|
|
4895
5289
|
});
|
|
@@ -4901,7 +5295,7 @@ async function buildElectron(inlineConfig = {}) {
|
|
|
4901
5295
|
const config = await resolveConfig({ ...inlineConfig, target: "electron" }, "build");
|
|
4902
5296
|
const startTime = performance.now();
|
|
4903
5297
|
assertElectronVersion(config);
|
|
4904
|
-
console.log(pc9.cyan("\n\u26A1 nasti build (electron)") + pc9.dim(` v${"2.
|
|
5298
|
+
console.log(pc9.cyan("\n\u26A1 nasti build (electron)") + pc9.dim(` v${"2.3.1"}`));
|
|
4905
5299
|
console.log(pc9.dim(` root: ${config.root}`));
|
|
4906
5300
|
console.log(pc9.dim(` mode: ${config.mode}`));
|
|
4907
5301
|
console.log(pc9.dim(` target: electron (\u2265 ${config.electron.minVersion})`));
|
|
@@ -4912,15 +5306,13 @@ async function buildElectron(inlineConfig = {}) {
|
|
|
4912
5306
|
fs10.mkdirSync(outDir, { recursive: true });
|
|
4913
5307
|
const rendererOutDir = path15.join(outDir, "renderer");
|
|
4914
5308
|
const { build: build2 } = await Promise.resolve().then(() => (init_build(), build_exports));
|
|
4915
|
-
await build2({
|
|
4916
|
-
...inlineConfig,
|
|
4917
|
-
target: "web",
|
|
5309
|
+
await build2(createElectronRendererConfig(config, inlineConfig, {
|
|
4918
5310
|
build: {
|
|
4919
5311
|
...inlineConfig.build,
|
|
4920
5312
|
outDir: rendererOutDir,
|
|
4921
5313
|
emptyOutDir: false
|
|
4922
5314
|
}
|
|
4923
|
-
});
|
|
5315
|
+
}));
|
|
4924
5316
|
const mainEntry = path15.resolve(config.root, config.electron.main);
|
|
4925
5317
|
if (!fs10.existsSync(mainEntry)) {
|
|
4926
5318
|
throw new Error(
|
|
@@ -4974,7 +5366,8 @@ async function bundleNode(config, entry, opts) {
|
|
|
4974
5366
|
const result = transformCode(id, code, {
|
|
4975
5367
|
sourcemap: !!config.build.sourcemap,
|
|
4976
5368
|
jsxRuntime: "automatic",
|
|
4977
|
-
jsxImportSource: config.framework === "vue" ? "vue" : "react"
|
|
5369
|
+
jsxImportSource: config.framework === "vue" ? "vue" : "react",
|
|
5370
|
+
target: config.electron.nodeTarget
|
|
4978
5371
|
});
|
|
4979
5372
|
return { code: result.code, map: result.map ? JSON.parse(result.map) : void 0 };
|
|
4980
5373
|
}
|
|
@@ -4985,7 +5378,11 @@ async function bundleNode(config, entry, opts) {
|
|
|
4985
5378
|
...restInputOptions,
|
|
4986
5379
|
input: entry,
|
|
4987
5380
|
platform: "node",
|
|
4988
|
-
transform: {
|
|
5381
|
+
transform: {
|
|
5382
|
+
...userTransform,
|
|
5383
|
+
target: config.electron.nodeTarget,
|
|
5384
|
+
define: mergedDefine
|
|
5385
|
+
},
|
|
4989
5386
|
plugins: [oxcTransformPlugin, electronPlugin(config), resolvePlugin(config)]
|
|
4990
5387
|
});
|
|
4991
5388
|
fs10.mkdirSync(path15.dirname(opts.outFile), { recursive: true });
|
|
@@ -5002,6 +5399,25 @@ async function bundleNode(config, entry, opts) {
|
|
|
5002
5399
|
console.log(pc9.dim(` \u2713 ${opts.label} \u2192 ${path15.relative(config.root, opts.outFile)}`));
|
|
5003
5400
|
return opts.outFile;
|
|
5004
5401
|
}
|
|
5402
|
+
function createElectronRendererConfig(config, inlineConfig = {}, overrides = {}) {
|
|
5403
|
+
const inlineClient = inlineConfig.environments?.client ?? {};
|
|
5404
|
+
return {
|
|
5405
|
+
...inlineConfig,
|
|
5406
|
+
...overrides,
|
|
5407
|
+
root: config.root,
|
|
5408
|
+
mode: config.mode,
|
|
5409
|
+
target: "web",
|
|
5410
|
+
framework: config.framework,
|
|
5411
|
+
base: config.base === "/" ? "./" : config.base,
|
|
5412
|
+
environments: {
|
|
5413
|
+
...inlineConfig.environments ?? {},
|
|
5414
|
+
client: {
|
|
5415
|
+
...inlineClient,
|
|
5416
|
+
html: config.electron.renderer
|
|
5417
|
+
}
|
|
5418
|
+
}
|
|
5419
|
+
};
|
|
5420
|
+
}
|
|
5005
5421
|
function outFileName(outDir, base, format) {
|
|
5006
5422
|
const ext = format === "cjs" ? ".cjs" : ".mjs";
|
|
5007
5423
|
return path15.join(outDir, base + ext);
|
|
@@ -5046,6 +5462,7 @@ var init_electron2 = __esm({
|
|
|
5046
5462
|
// src/server/electron-dev.ts
|
|
5047
5463
|
var electron_dev_exports = {};
|
|
5048
5464
|
__export(electron_dev_exports, {
|
|
5465
|
+
electronRendererDevPath: () => electronRendererDevPath,
|
|
5049
5466
|
startElectronDev: () => startElectronDev
|
|
5050
5467
|
});
|
|
5051
5468
|
import path16 from "path";
|
|
@@ -5059,11 +5476,15 @@ async function startElectronDev(inlineConfig = {}) {
|
|
|
5059
5476
|
const { noSpawn, ...rest } = inlineConfig;
|
|
5060
5477
|
const config = await resolveConfig({ ...rest, target: "electron" }, "serve");
|
|
5061
5478
|
warnElectronVersion(config);
|
|
5062
|
-
console.log(pc10.cyan("\n\u26A1 nasti electron dev") + pc10.dim(` v${"2.
|
|
5479
|
+
console.log(pc10.cyan("\n\u26A1 nasti electron dev") + pc10.dim(` v${"2.3.1"}`));
|
|
5063
5480
|
const { createServer: createServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
|
|
5064
|
-
const server = await createServer2({
|
|
5481
|
+
const server = await createServer2({
|
|
5482
|
+
...rest,
|
|
5483
|
+
target: "electron",
|
|
5484
|
+
framework: config.framework
|
|
5485
|
+
});
|
|
5065
5486
|
await server.listen();
|
|
5066
|
-
const devUrl = `http://localhost:${server.config.server.port}
|
|
5487
|
+
const devUrl = `http://localhost:${server.config.server.port}` + electronRendererDevPath(config.electron.renderer);
|
|
5067
5488
|
console.log(pc10.dim(` renderer: ${devUrl}`));
|
|
5068
5489
|
const stageDir = path16.resolve(config.root, ".nasti");
|
|
5069
5490
|
fs11.mkdirSync(stageDir, { recursive: true });
|
|
@@ -5185,14 +5606,18 @@ async function compileNode(config, entry, opts) {
|
|
|
5185
5606
|
const result = transformCode(id, code, {
|
|
5186
5607
|
sourcemap: true,
|
|
5187
5608
|
jsxRuntime: "automatic",
|
|
5188
|
-
jsxImportSource: config.framework === "vue" ? "vue" : "react"
|
|
5609
|
+
jsxImportSource: config.framework === "vue" ? "vue" : "react",
|
|
5610
|
+
target: config.electron.nodeTarget
|
|
5189
5611
|
});
|
|
5190
5612
|
return { code: result.code, map: result.map ? JSON.parse(result.map) : void 0 };
|
|
5191
5613
|
}
|
|
5192
5614
|
};
|
|
5193
5615
|
const bundle2 = await rolldown3({
|
|
5194
5616
|
input: entry,
|
|
5195
|
-
transform: {
|
|
5617
|
+
transform: {
|
|
5618
|
+
target: config.electron.nodeTarget,
|
|
5619
|
+
define: envDefine
|
|
5620
|
+
},
|
|
5196
5621
|
platform: "node",
|
|
5197
5622
|
plugins: [oxcTransformPlugin, electronPlugin(config), resolvePlugin(config)]
|
|
5198
5623
|
});
|
|
@@ -5208,6 +5633,10 @@ async function compileNode(config, entry, opts) {
|
|
|
5208
5633
|
});
|
|
5209
5634
|
await bundle2.close();
|
|
5210
5635
|
}
|
|
5636
|
+
function electronRendererDevPath(renderer) {
|
|
5637
|
+
const normalized = renderer.split(path16.sep).join("/").replace(/^\.?\//, "");
|
|
5638
|
+
return normalized === "index.html" ? "/" : `/${normalized}`;
|
|
5639
|
+
}
|
|
5211
5640
|
function resolveElectronBinary(config) {
|
|
5212
5641
|
if (config.electron.electronPath && fs11.existsSync(config.electron.electronPath)) {
|
|
5213
5642
|
return config.electron.electronPath;
|
|
@@ -5404,7 +5833,7 @@ cli.command("preview [root]", "Preview production build").option("--port <port>"
|
|
|
5404
5833
|
const host = options.host === true ? "0.0.0.0" : options.host ?? "localhost";
|
|
5405
5834
|
http2.createServer(app).listen(port, host, () => {
|
|
5406
5835
|
logger.info(`
|
|
5407
|
-
${pc11.cyan(pc11.bold("NASTI"))} ${pc11.cyan(`v${"2.
|
|
5836
|
+
${pc11.cyan(pc11.bold("NASTI"))} ${pc11.cyan(`v${"2.3.1"}`)} ${pc11.dim("preview")}
|
|
5408
5837
|
`);
|
|
5409
5838
|
printServerUrls2(
|
|
5410
5839
|
{
|
|
@@ -5421,6 +5850,6 @@ cli.command("preview [root]", "Preview production build").option("--port <port>"
|
|
|
5421
5850
|
}
|
|
5422
5851
|
});
|
|
5423
5852
|
cli.help();
|
|
5424
|
-
cli.version("2.
|
|
5853
|
+
cli.version("2.3.1");
|
|
5425
5854
|
cli.parse();
|
|
5426
5855
|
//# sourceMappingURL=cli.js.map
|