@nasti-toolchain/nasti 2.1.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 +573 -134
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +573 -134
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +575 -132
- 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 +572 -132
- 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) {
|
|
@@ -1859,7 +2024,7 @@ const hotModulesMap = new Map();
|
|
|
1859
2024
|
const disposeMap = new Map();
|
|
1860
2025
|
const pruneMap = new Map();
|
|
1861
2026
|
|
|
1862
|
-
socket.addEventListener('message', ({ data }) => {
|
|
2027
|
+
socket.addEventListener('message', async ({ data }) => {
|
|
1863
2028
|
const payload = JSON.parse(data);
|
|
1864
2029
|
switch (payload.type) {
|
|
1865
2030
|
case 'connected':
|
|
@@ -1867,14 +2032,21 @@ socket.addEventListener('message', ({ data }) => {
|
|
|
1867
2032
|
clearErrorOverlay();
|
|
1868
2033
|
break;
|
|
1869
2034
|
case 'update':
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
2035
|
+
try {
|
|
2036
|
+
await Promise.all(payload.updates.map((update) => {
|
|
2037
|
+
if (update.type === 'js-update') {
|
|
2038
|
+
return fetchUpdate(update);
|
|
2039
|
+
} else if (update.type === 'css-update') {
|
|
2040
|
+
return updateCss(update.path);
|
|
2041
|
+
}
|
|
2042
|
+
}));
|
|
2043
|
+
clearErrorOverlay();
|
|
2044
|
+
console.log('[nasti] HMR update complete, reloading page');
|
|
2045
|
+
location.reload();
|
|
2046
|
+
} catch (err) {
|
|
2047
|
+
console.error('[nasti] HMR update failed:', err);
|
|
2048
|
+
showErrorOverlay(err);
|
|
2049
|
+
}
|
|
1878
2050
|
break;
|
|
1879
2051
|
case 'full-reload':
|
|
1880
2052
|
console.log('[nasti] full reload');
|
|
@@ -1916,7 +2088,7 @@ async function fetchUpdate(update) {
|
|
|
1916
2088
|
function updateCss(path) {
|
|
1917
2089
|
const el = document.querySelector(\`style[data-nasti-css="\${path}"]\`);
|
|
1918
2090
|
if (el) {
|
|
1919
|
-
fetch(path + '?t=' + Date.now())
|
|
2091
|
+
return fetch(path + '?t=' + Date.now())
|
|
1920
2092
|
.then(r => r.text())
|
|
1921
2093
|
.then(css => { el.textContent = css; });
|
|
1922
2094
|
}
|
|
@@ -3916,6 +4088,7 @@ var build_exports = {};
|
|
|
3916
4088
|
__export(build_exports, {
|
|
3917
4089
|
build: () => build,
|
|
3918
4090
|
getRolldownOptions: () => getRolldownOptions,
|
|
4091
|
+
replaceEntryScript: () => replaceEntryScript,
|
|
3919
4092
|
resolveClientEntries: () => resolveClientEntries,
|
|
3920
4093
|
toRolldownPlugins: () => toRolldownPlugins
|
|
3921
4094
|
});
|
|
@@ -3994,13 +4167,20 @@ function toRolldownPlugins(plugins) {
|
|
|
3994
4167
|
}));
|
|
3995
4168
|
}
|
|
3996
4169
|
function resolveClientEntries(config, html) {
|
|
4170
|
+
const configuredEntries = config.environments.client?.entry ?? [];
|
|
4171
|
+
if (configuredEntries.length > 0) return configuredEntries;
|
|
3997
4172
|
const entryPoints = [];
|
|
4173
|
+
const htmlFile = config.environments.client?.html;
|
|
4174
|
+
const htmlDir = htmlFile ? import_node_path12.default.dirname(htmlFile) : config.root;
|
|
3998
4175
|
if (html) {
|
|
3999
4176
|
const scriptMatches = html.matchAll(/<script[^>]+src=["']([^"']+)["'][^>]*>/gi);
|
|
4000
4177
|
for (const match of scriptMatches) {
|
|
4001
4178
|
const src = match[1];
|
|
4002
4179
|
if (src && !src.startsWith("http")) {
|
|
4003
|
-
|
|
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
|
+
);
|
|
4004
4184
|
}
|
|
4005
4185
|
}
|
|
4006
4186
|
}
|
|
@@ -4036,21 +4216,55 @@ async function build(inlineConfig = {}) {
|
|
|
4036
4216
|
const startTime = performance.now();
|
|
4037
4217
|
logger.info(
|
|
4038
4218
|
import_picocolors6.default.cyan(`
|
|
4039
|
-
nasti v${"2.1
|
|
4219
|
+
nasti v${"2.3.1"} `) + import_picocolors6.default.green(`building for ${config.mode}...`)
|
|
4040
4220
|
);
|
|
4041
4221
|
debug5?.(`root: ${config.root}`);
|
|
4042
4222
|
const buildableNames = Object.keys(config.environments).filter(
|
|
4043
|
-
(name) => name === "client" || config.environments[name].entry.length > 0
|
|
4223
|
+
(name) => name === "client" || config.environments[name].entry.length > 0 || !!config.environments[name].driver
|
|
4044
4224
|
);
|
|
4045
4225
|
buildableNames.sort((a, b) => a === "client" ? -1 : b === "client" ? 1 : 0);
|
|
4046
4226
|
const environments = {};
|
|
4227
|
+
const environmentResults = {};
|
|
4228
|
+
const initializedEnvironments = [];
|
|
4047
4229
|
let clientOutput = [];
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
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;
|
|
4054
4268
|
}
|
|
4055
4269
|
}
|
|
4056
4270
|
const elapsed = ((performance.now() - startTime) / 1e3).toFixed(2);
|
|
@@ -4063,83 +4277,130 @@ nasti v${"2.1.0"} `) + import_picocolors6.default.green(`building for ${config.m
|
|
|
4063
4277
|
const envSuffix = buildableNames.length > 1 ? ` (${buildableNames.join(" + ")})` : "";
|
|
4064
4278
|
logger.info(import_picocolors6.default.green(`\u2713 built in ${elapsed}s`) + import_picocolors6.default.dim(envSuffix));
|
|
4065
4279
|
logger.info(import_picocolors6.default.dim(` ${fileCount} files, ${displaySize(totalSize)} total \u2192 ${config.build.outDir}/`));
|
|
4066
|
-
return { output: clientOutput, environments };
|
|
4280
|
+
return { output: clientOutput, environments, environmentResults };
|
|
4067
4281
|
}
|
|
4068
4282
|
async function buildClientEnvironment(config) {
|
|
4069
4283
|
const logger = config.logger;
|
|
4070
4284
|
const outDir = import_node_path12.default.resolve(config.root, config.build.outDir);
|
|
4071
|
-
if (config.build.emptyOutDir && import_node_fs9.default.existsSync(outDir)) {
|
|
4072
|
-
import_node_fs9.default.rmSync(outDir, { recursive: true, force: true });
|
|
4073
|
-
}
|
|
4074
|
-
import_node_fs9.default.mkdirSync(outDir, { recursive: true });
|
|
4075
|
-
const html = await readHtmlFile(config.root);
|
|
4076
|
-
const entryPoints = resolveClientEntries(config, html);
|
|
4077
|
-
if (entryPoints.length === 0) {
|
|
4078
|
-
throw new Error("No entry point found. Add a <script> tag to index.html or create src/main.ts");
|
|
4079
|
-
}
|
|
4080
4285
|
const cssEngine = createCssEngine();
|
|
4081
4286
|
const pluginList = resolvePluginList(config, config.plugins, { cssEngine });
|
|
4082
|
-
const clientEnv = new NastiEnvironment("client",
|
|
4287
|
+
const clientEnv = new NastiEnvironment("client", config, {
|
|
4083
4288
|
mode: "build",
|
|
4084
|
-
plugins: pluginList
|
|
4289
|
+
plugins: pluginList,
|
|
4290
|
+
pluginApi: getPluginApi(config)
|
|
4085
4291
|
});
|
|
4086
4292
|
await clientEnv.init();
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
...nativeReporter ? [nativeReporter] : []
|
|
4093
|
-
];
|
|
4094
|
-
const { inputOptions, outputOptions } = getRolldownOptions(clientEnv, entryPoints, rolldownPlugins);
|
|
4095
|
-
const bundle2 = await (0, import_rolldown.rolldown)(inputOptions);
|
|
4096
|
-
const { output } = await bundle2.write(outputOptions);
|
|
4097
|
-
await bundle2.close();
|
|
4098
|
-
if (html) {
|
|
4099
|
-
let processedHtml = html;
|
|
4100
|
-
const htmlPlugins = [...allPlugins.filter((p) => p.transformIndexHtml), htmlPlugin(config)];
|
|
4101
|
-
for (const p of htmlPlugins) {
|
|
4102
|
-
const result = await p.transformIndexHtml(processedHtml);
|
|
4103
|
-
if (typeof result === "string") {
|
|
4104
|
-
processedHtml = result;
|
|
4105
|
-
} else if (result && "html" in result) {
|
|
4106
|
-
processedHtml = processHtml(result.html, result.tags);
|
|
4107
|
-
} else if (Array.isArray(result)) {
|
|
4108
|
-
processedHtml = processHtml(processedHtml, result);
|
|
4109
|
-
}
|
|
4110
|
-
}
|
|
4111
|
-
processedHtml = injectCssLinks(processedHtml, cssEngine, config);
|
|
4112
|
-
for (const chunk of output) {
|
|
4113
|
-
if (chunk.type === "chunk" && chunk.isEntry && chunk.facadeModuleId) {
|
|
4114
|
-
const originalEntry = import_node_path12.default.relative(config.root, chunk.facadeModuleId);
|
|
4115
|
-
processedHtml = processedHtml.replace(
|
|
4116
|
-
new RegExp(`(src=["'])/?(${escapeRegExp(originalEntry)})(["'])`, "g"),
|
|
4117
|
-
`$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()`
|
|
4118
4298
|
);
|
|
4119
4299
|
}
|
|
4300
|
+
const result = await clientEnv.driver.build(clientEnv.getDriverContext());
|
|
4301
|
+
return { environment: clientEnv, result };
|
|
4120
4302
|
}
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
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;
|
|
4125
4371
|
}
|
|
4126
|
-
warnLargeChunks(output, config, logger);
|
|
4127
|
-
return output;
|
|
4128
4372
|
}
|
|
4129
4373
|
async function buildServerEnvironment(config, name) {
|
|
4130
4374
|
const envOptions = config.environments[name];
|
|
4131
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
|
+
}
|
|
4132
4398
|
for (const entry of envOptions.entry) {
|
|
4133
4399
|
if (!import_node_fs9.default.existsSync(entry)) {
|
|
4400
|
+
await environment.close();
|
|
4134
4401
|
throw new Error(`[nasti] environment "${name}" entry not found: ${entry}`);
|
|
4135
4402
|
}
|
|
4136
4403
|
}
|
|
4137
|
-
const pluginList = resolvePluginList(config, config.plugins, { consumer: envOptions.consumer });
|
|
4138
|
-
const environment = new NastiEnvironment(name, { ...config, plugins: pluginList }, {
|
|
4139
|
-
mode: "build",
|
|
4140
|
-
plugins: pluginList
|
|
4141
|
-
});
|
|
4142
|
-
await environment.init();
|
|
4143
4404
|
const rolldownPlugins = [
|
|
4144
4405
|
createOxcTransformPlugin(config, environment),
|
|
4145
4406
|
...toRolldownPlugins(environment.plugins)
|
|
@@ -4159,7 +4420,7 @@ async function buildServerEnvironment(config, name) {
|
|
|
4159
4420
|
logger.info(
|
|
4160
4421
|
import_picocolors6.default.dim(` [${name}] `) + output.map((o) => import_node_path12.default.join(envOptions.build.outDir, o.fileName)).join(import_picocolors6.default.dim(", "))
|
|
4161
4422
|
);
|
|
4162
|
-
return output;
|
|
4423
|
+
return { environment, result: { output } };
|
|
4163
4424
|
}
|
|
4164
4425
|
function injectCssLinks(html, cssEngine, config) {
|
|
4165
4426
|
const cssLinkTags = [];
|
|
@@ -4185,6 +4446,25 @@ function injectCssLinks(html, cssEngine, config) {
|
|
|
4185
4446
|
function escapeRegExp(string) {
|
|
4186
4447
|
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4187
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
|
+
}
|
|
4188
4468
|
var import_node_path12, import_node_fs9, import_node_module5, import_rolldown, import_picocolors6, debug5, NODE_BUILTINS2;
|
|
4189
4469
|
var init_build = __esm({
|
|
4190
4470
|
"src/build/index.ts"() {
|
|
@@ -4202,6 +4482,7 @@ var init_build = __esm({
|
|
|
4202
4482
|
init_env();
|
|
4203
4483
|
init_reporter();
|
|
4204
4484
|
init_debug();
|
|
4485
|
+
init_plugin_api();
|
|
4205
4486
|
import_picocolors6 = __toESM(require("picocolors"), 1);
|
|
4206
4487
|
debug5 = createDebugger("nasti:build");
|
|
4207
4488
|
NODE_BUILTINS2 = /* @__PURE__ */ new Set([...import_node_module5.builtinModules, ...import_node_module5.builtinModules.map((m) => `node:${m}`)]);
|
|
@@ -4230,7 +4511,7 @@ async function createBundledDevServer(opts) {
|
|
|
4230
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.`
|
|
4231
4512
|
);
|
|
4232
4513
|
}
|
|
4233
|
-
const html = await readHtmlFile(config.root);
|
|
4514
|
+
const html = await readHtmlFile(config.root, config.environments.client?.html);
|
|
4234
4515
|
const entryPoints = resolveClientEntries(config, html);
|
|
4235
4516
|
if (entryPoints.length === 0) {
|
|
4236
4517
|
throw new Error("No entry point found. Add a <script> tag to index.html or create src/main.ts");
|
|
@@ -4285,7 +4566,10 @@ async function createBundledDevServer(opts) {
|
|
|
4285
4566
|
continue;
|
|
4286
4567
|
}
|
|
4287
4568
|
const patchPath = `__nasti_patch/${update.filename}`;
|
|
4288
|
-
patches.set(
|
|
4569
|
+
patches.set(
|
|
4570
|
+
patchPath,
|
|
4571
|
+
update.code + "\n;globalThis.location?.reload();\n;export {}"
|
|
4572
|
+
);
|
|
4289
4573
|
if (update.sourcemap && update.sourcemapFilename) {
|
|
4290
4574
|
patches.set(`__nasti_patch/${update.sourcemapFilename}`, update.sourcemap);
|
|
4291
4575
|
}
|
|
@@ -4433,7 +4717,7 @@ async function createBundledDevServer(opts) {
|
|
|
4433
4717
|
return;
|
|
4434
4718
|
}
|
|
4435
4719
|
if (pathname === "/" || pathname.endsWith(".html")) {
|
|
4436
|
-
const rawHtml = await readHtmlFile(config.root);
|
|
4720
|
+
const rawHtml = await readHtmlFile(config.root, config.environments.client?.html);
|
|
4437
4721
|
if (rawHtml) {
|
|
4438
4722
|
res.setHeader("Content-Type", "text/html");
|
|
4439
4723
|
res.setHeader("Cache-Control", "no-store");
|
|
@@ -4517,10 +4801,13 @@ async function renderBundledIndexHtml(html, config, entryFileNames) {
|
|
|
4517
4801
|
}
|
|
4518
4802
|
}
|
|
4519
4803
|
for (const [facadeModuleId, fileName] of entryFileNames) {
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4804
|
+
processed = replaceEntryScript(
|
|
4805
|
+
processed,
|
|
4806
|
+
facadeModuleId,
|
|
4807
|
+
fileName,
|
|
4808
|
+
config,
|
|
4809
|
+
config.environments.client?.html ?? "index.html",
|
|
4810
|
+
"/"
|
|
4524
4811
|
);
|
|
4525
4812
|
}
|
|
4526
4813
|
return processed;
|
|
@@ -4661,10 +4948,12 @@ async function createServer(inlineConfig = {}) {
|
|
|
4661
4948
|
const app = (0, import_connect.default)();
|
|
4662
4949
|
const httpServer = import_node_http.default.createServer(app);
|
|
4663
4950
|
const ws = createWebSocketServer(httpServer);
|
|
4664
|
-
const
|
|
4951
|
+
const pluginApi = getPluginApi(config);
|
|
4952
|
+
const clientEnv = new NastiEnvironment("client", config, {
|
|
4665
4953
|
hot: createWsHotChannel(ws),
|
|
4666
4954
|
mode: "dev",
|
|
4667
|
-
plugins: allPlugins
|
|
4955
|
+
plugins: allPlugins,
|
|
4956
|
+
pluginApi
|
|
4668
4957
|
});
|
|
4669
4958
|
await clientEnv.init();
|
|
4670
4959
|
const environments = { client: clientEnv };
|
|
@@ -4672,11 +4961,15 @@ async function createServer(inlineConfig = {}) {
|
|
|
4672
4961
|
if (name === "client") continue;
|
|
4673
4962
|
const consumer = config.environments[name].consumer;
|
|
4674
4963
|
const envPlugins = resolvePluginList(config, config.plugins, { consumer });
|
|
4675
|
-
environments[name] = new NastiEnvironment(name,
|
|
4964
|
+
environments[name] = new NastiEnvironment(name, config, {
|
|
4676
4965
|
mode: "dev",
|
|
4677
|
-
plugins: envPlugins
|
|
4966
|
+
plugins: envPlugins,
|
|
4967
|
+
pluginApi
|
|
4678
4968
|
});
|
|
4679
4969
|
}
|
|
4970
|
+
for (const [name, environment] of Object.entries(environments)) {
|
|
4971
|
+
if (name !== "client" && environment.options.driver) await environment.init();
|
|
4972
|
+
}
|
|
4680
4973
|
let ssrRunner = null;
|
|
4681
4974
|
async function getSsrRunner() {
|
|
4682
4975
|
if (ssrRunner) return ssrRunner;
|
|
@@ -4701,14 +4994,6 @@ async function createServer(inlineConfig = {}) {
|
|
|
4701
4994
|
});
|
|
4702
4995
|
app.use(bundledServer.middleware);
|
|
4703
4996
|
}
|
|
4704
|
-
app.use(transformMiddleware({
|
|
4705
|
-
config: configWithPlugins,
|
|
4706
|
-
pluginContainer,
|
|
4707
|
-
moduleGraph
|
|
4708
|
-
}));
|
|
4709
|
-
const publicDir = import_node_path14.default.resolve(config.root, "public");
|
|
4710
|
-
app.use((0, import_sirv.default)(publicDir, { dev: true, etag: true }));
|
|
4711
|
-
app.use((0, import_sirv.default)(config.root, { dev: true, etag: true }));
|
|
4712
4997
|
const ignoredSegments = /* @__PURE__ */ new Set(["node_modules", ".git", ".nasti"]);
|
|
4713
4998
|
const outDirAbs = import_node_path14.default.resolve(config.root, config.build.outDir);
|
|
4714
4999
|
const watcher = (0, import_chokidar.watch)(config.root, {
|
|
@@ -4725,13 +5010,72 @@ async function createServer(inlineConfig = {}) {
|
|
|
4725
5010
|
ignoreInitial: true
|
|
4726
5011
|
});
|
|
4727
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
|
+
};
|
|
4728
5066
|
watcher.on("change", (file) => {
|
|
4729
5067
|
ssrRunner?.invalidateFile(file);
|
|
4730
5068
|
handleFileChange(file, server);
|
|
5069
|
+
notifyEnvironmentDrivers(file, "change");
|
|
4731
5070
|
});
|
|
4732
5071
|
watcher.on("add", (file) => {
|
|
4733
5072
|
ssrRunner?.invalidateFile(file);
|
|
4734
5073
|
handleFileChange(file, server);
|
|
5074
|
+
notifyEnvironmentDrivers(file, "add");
|
|
5075
|
+
});
|
|
5076
|
+
watcher.on("unlink", (file) => {
|
|
5077
|
+
ssrRunner?.invalidateFile(file);
|
|
5078
|
+
notifyEnvironmentDrivers(file, "unlink");
|
|
4735
5079
|
});
|
|
4736
5080
|
server = {
|
|
4737
5081
|
config: configWithPlugins,
|
|
@@ -4740,10 +5084,12 @@ async function createServer(inlineConfig = {}) {
|
|
|
4740
5084
|
watcher,
|
|
4741
5085
|
ws,
|
|
4742
5086
|
environments,
|
|
5087
|
+
environmentServices,
|
|
4743
5088
|
async listen(port) {
|
|
4744
5089
|
const finalPort = port ?? config.server.port;
|
|
4745
5090
|
const host = config.server.host === true ? "0.0.0.0" : config.server.host;
|
|
4746
5091
|
await pluginContainer.buildStart();
|
|
5092
|
+
await startEnvironmentDrivers();
|
|
4747
5093
|
return new Promise((resolve, reject) => {
|
|
4748
5094
|
let currentPort = finalPort;
|
|
4749
5095
|
const onListening = () => {
|
|
@@ -4751,15 +5097,20 @@ async function createServer(inlineConfig = {}) {
|
|
|
4751
5097
|
config.server.port = actualPort;
|
|
4752
5098
|
const localUrl = `http://localhost:${actualPort}/`;
|
|
4753
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 ?? []);
|
|
4754
5102
|
logger.clearScreen("info");
|
|
4755
5103
|
const readyIn = Math.ceil(performance.now() - startTime);
|
|
4756
5104
|
logger.info(
|
|
4757
5105
|
`
|
|
4758
|
-
${import_picocolors8.default.cyan(import_picocolors8.default.bold("NASTI"))} ${import_picocolors8.default.cyan(`v${"2.1
|
|
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")}
|
|
4759
5107
|
`
|
|
4760
5108
|
);
|
|
4761
5109
|
printServerUrls(
|
|
4762
|
-
{
|
|
5110
|
+
{
|
|
5111
|
+
local: [localUrl, ...driverLocalUrls],
|
|
5112
|
+
network: [...networkUrl ? [networkUrl] : [], ...driverNetworkUrls]
|
|
5113
|
+
},
|
|
4763
5114
|
logger.info
|
|
4764
5115
|
);
|
|
4765
5116
|
logger.info("");
|
|
@@ -4789,11 +5140,62 @@ async function createServer(inlineConfig = {}) {
|
|
|
4789
5140
|
async close() {
|
|
4790
5141
|
await pluginContainer.buildEnd();
|
|
4791
5142
|
await bundledServer?.close();
|
|
4792
|
-
|
|
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();
|
|
4793
5157
|
ws.close();
|
|
4794
5158
|
httpServer.close();
|
|
5159
|
+
if (environmentCloseFailed) {
|
|
5160
|
+
throw firstEnvironmentCloseError;
|
|
5161
|
+
}
|
|
4795
5162
|
}
|
|
4796
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 }));
|
|
4797
5199
|
const postMiddlewares = [];
|
|
4798
5200
|
for (const plugin of allPlugins) {
|
|
4799
5201
|
if (plugin.configureServer) {
|
|
@@ -4836,6 +5238,7 @@ var init_server = __esm({
|
|
|
4836
5238
|
init_middleware();
|
|
4837
5239
|
init_hmr();
|
|
4838
5240
|
init_builtins();
|
|
5241
|
+
init_plugin_api();
|
|
4839
5242
|
}
|
|
4840
5243
|
});
|
|
4841
5244
|
|
|
@@ -4882,6 +5285,7 @@ var init_electron = __esm({
|
|
|
4882
5285
|
var electron_exports = {};
|
|
4883
5286
|
__export(electron_exports, {
|
|
4884
5287
|
buildElectron: () => buildElectron,
|
|
5288
|
+
createElectronRendererConfig: () => createElectronRendererConfig,
|
|
4885
5289
|
detectInstalledElectron: () => detectInstalledElectron,
|
|
4886
5290
|
normalizePreload: () => normalizePreload
|
|
4887
5291
|
});
|
|
@@ -4889,7 +5293,7 @@ async function buildElectron(inlineConfig = {}) {
|
|
|
4889
5293
|
const config = await resolveConfig({ ...inlineConfig, target: "electron" }, "build");
|
|
4890
5294
|
const startTime = performance.now();
|
|
4891
5295
|
assertElectronVersion(config);
|
|
4892
|
-
console.log(import_picocolors9.default.cyan("\n\u26A1 nasti build (electron)") + import_picocolors9.default.dim(` v${"2.1
|
|
5296
|
+
console.log(import_picocolors9.default.cyan("\n\u26A1 nasti build (electron)") + import_picocolors9.default.dim(` v${"2.3.1"}`));
|
|
4893
5297
|
console.log(import_picocolors9.default.dim(` root: ${config.root}`));
|
|
4894
5298
|
console.log(import_picocolors9.default.dim(` mode: ${config.mode}`));
|
|
4895
5299
|
console.log(import_picocolors9.default.dim(` target: electron (\u2265 ${config.electron.minVersion})`));
|
|
@@ -4900,15 +5304,13 @@ async function buildElectron(inlineConfig = {}) {
|
|
|
4900
5304
|
import_node_fs10.default.mkdirSync(outDir, { recursive: true });
|
|
4901
5305
|
const rendererOutDir = import_node_path15.default.join(outDir, "renderer");
|
|
4902
5306
|
const { build: build2 } = await Promise.resolve().then(() => (init_build(), build_exports));
|
|
4903
|
-
await build2({
|
|
4904
|
-
...inlineConfig,
|
|
4905
|
-
target: "web",
|
|
5307
|
+
await build2(createElectronRendererConfig(config, inlineConfig, {
|
|
4906
5308
|
build: {
|
|
4907
5309
|
...inlineConfig.build,
|
|
4908
5310
|
outDir: rendererOutDir,
|
|
4909
5311
|
emptyOutDir: false
|
|
4910
5312
|
}
|
|
4911
|
-
});
|
|
5313
|
+
}));
|
|
4912
5314
|
const mainEntry = import_node_path15.default.resolve(config.root, config.electron.main);
|
|
4913
5315
|
if (!import_node_fs10.default.existsSync(mainEntry)) {
|
|
4914
5316
|
throw new Error(
|
|
@@ -4962,7 +5364,8 @@ async function bundleNode(config, entry, opts) {
|
|
|
4962
5364
|
const result = transformCode(id, code, {
|
|
4963
5365
|
sourcemap: !!config.build.sourcemap,
|
|
4964
5366
|
jsxRuntime: "automatic",
|
|
4965
|
-
jsxImportSource: config.framework === "vue" ? "vue" : "react"
|
|
5367
|
+
jsxImportSource: config.framework === "vue" ? "vue" : "react",
|
|
5368
|
+
target: config.electron.nodeTarget
|
|
4966
5369
|
});
|
|
4967
5370
|
return { code: result.code, map: result.map ? JSON.parse(result.map) : void 0 };
|
|
4968
5371
|
}
|
|
@@ -4973,7 +5376,11 @@ async function bundleNode(config, entry, opts) {
|
|
|
4973
5376
|
...restInputOptions,
|
|
4974
5377
|
input: entry,
|
|
4975
5378
|
platform: "node",
|
|
4976
|
-
transform: {
|
|
5379
|
+
transform: {
|
|
5380
|
+
...userTransform,
|
|
5381
|
+
target: config.electron.nodeTarget,
|
|
5382
|
+
define: mergedDefine
|
|
5383
|
+
},
|
|
4977
5384
|
plugins: [oxcTransformPlugin, electronPlugin(config), resolvePlugin(config)]
|
|
4978
5385
|
});
|
|
4979
5386
|
import_node_fs10.default.mkdirSync(import_node_path15.default.dirname(opts.outFile), { recursive: true });
|
|
@@ -4990,6 +5397,25 @@ async function bundleNode(config, entry, opts) {
|
|
|
4990
5397
|
console.log(import_picocolors9.default.dim(` \u2713 ${opts.label} \u2192 ${import_node_path15.default.relative(config.root, opts.outFile)}`));
|
|
4991
5398
|
return opts.outFile;
|
|
4992
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
|
+
}
|
|
4993
5419
|
function outFileName(outDir, base, format) {
|
|
4994
5420
|
const ext = format === "cjs" ? ".cjs" : ".mjs";
|
|
4995
5421
|
return import_node_path15.default.join(outDir, base + ext);
|
|
@@ -5039,17 +5465,22 @@ var init_electron2 = __esm({
|
|
|
5039
5465
|
// src/server/electron-dev.ts
|
|
5040
5466
|
var electron_dev_exports = {};
|
|
5041
5467
|
__export(electron_dev_exports, {
|
|
5468
|
+
electronRendererDevPath: () => electronRendererDevPath,
|
|
5042
5469
|
startElectronDev: () => startElectronDev
|
|
5043
5470
|
});
|
|
5044
5471
|
async function startElectronDev(inlineConfig = {}) {
|
|
5045
5472
|
const { noSpawn, ...rest } = inlineConfig;
|
|
5046
5473
|
const config = await resolveConfig({ ...rest, target: "electron" }, "serve");
|
|
5047
5474
|
warnElectronVersion(config);
|
|
5048
|
-
console.log(import_picocolors10.default.cyan("\n\u26A1 nasti electron dev") + import_picocolors10.default.dim(` v${"2.1
|
|
5475
|
+
console.log(import_picocolors10.default.cyan("\n\u26A1 nasti electron dev") + import_picocolors10.default.dim(` v${"2.3.1"}`));
|
|
5049
5476
|
const { createServer: createServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
|
|
5050
|
-
const server = await createServer2({
|
|
5477
|
+
const server = await createServer2({
|
|
5478
|
+
...rest,
|
|
5479
|
+
target: "electron",
|
|
5480
|
+
framework: config.framework
|
|
5481
|
+
});
|
|
5051
5482
|
await server.listen();
|
|
5052
|
-
const devUrl = `http://localhost:${server.config.server.port}
|
|
5483
|
+
const devUrl = `http://localhost:${server.config.server.port}` + electronRendererDevPath(config.electron.renderer);
|
|
5053
5484
|
console.log(import_picocolors10.default.dim(` renderer: ${devUrl}`));
|
|
5054
5485
|
const stageDir = import_node_path16.default.resolve(config.root, ".nasti");
|
|
5055
5486
|
import_node_fs11.default.mkdirSync(stageDir, { recursive: true });
|
|
@@ -5171,14 +5602,18 @@ async function compileNode(config, entry, opts) {
|
|
|
5171
5602
|
const result = transformCode(id, code, {
|
|
5172
5603
|
sourcemap: true,
|
|
5173
5604
|
jsxRuntime: "automatic",
|
|
5174
|
-
jsxImportSource: config.framework === "vue" ? "vue" : "react"
|
|
5605
|
+
jsxImportSource: config.framework === "vue" ? "vue" : "react",
|
|
5606
|
+
target: config.electron.nodeTarget
|
|
5175
5607
|
});
|
|
5176
5608
|
return { code: result.code, map: result.map ? JSON.parse(result.map) : void 0 };
|
|
5177
5609
|
}
|
|
5178
5610
|
};
|
|
5179
5611
|
const bundle2 = await (0, import_rolldown3.rolldown)({
|
|
5180
5612
|
input: entry,
|
|
5181
|
-
transform: {
|
|
5613
|
+
transform: {
|
|
5614
|
+
target: config.electron.nodeTarget,
|
|
5615
|
+
define: envDefine
|
|
5616
|
+
},
|
|
5182
5617
|
platform: "node",
|
|
5183
5618
|
plugins: [oxcTransformPlugin, electronPlugin(config), resolvePlugin(config)]
|
|
5184
5619
|
});
|
|
@@ -5194,6 +5629,10 @@ async function compileNode(config, entry, opts) {
|
|
|
5194
5629
|
});
|
|
5195
5630
|
await bundle2.close();
|
|
5196
5631
|
}
|
|
5632
|
+
function electronRendererDevPath(renderer) {
|
|
5633
|
+
const normalized = renderer.split(import_node_path16.default.sep).join("/").replace(/^\.?\//, "");
|
|
5634
|
+
return normalized === "index.html" ? "/" : `/${normalized}`;
|
|
5635
|
+
}
|
|
5197
5636
|
function resolveElectronBinary(config) {
|
|
5198
5637
|
if (config.electron.electronPath && import_node_fs11.default.existsSync(config.electron.electronPath)) {
|
|
5199
5638
|
return config.electron.electronPath;
|
|
@@ -5398,7 +5837,7 @@ cli.command("preview [root]", "Preview production build").option("--port <port>"
|
|
|
5398
5837
|
const host = options.host === true ? "0.0.0.0" : options.host ?? "localhost";
|
|
5399
5838
|
http2.createServer(app).listen(port, host, () => {
|
|
5400
5839
|
logger.info(`
|
|
5401
|
-
${import_picocolors11.default.cyan(import_picocolors11.default.bold("NASTI"))} ${import_picocolors11.default.cyan(`v${"2.1
|
|
5840
|
+
${import_picocolors11.default.cyan(import_picocolors11.default.bold("NASTI"))} ${import_picocolors11.default.cyan(`v${"2.3.1"}`)} ${import_picocolors11.default.dim("preview")}
|
|
5402
5841
|
`);
|
|
5403
5842
|
printServerUrls2(
|
|
5404
5843
|
{
|
|
@@ -5415,6 +5854,6 @@ cli.command("preview [root]", "Preview production build").option("--port <port>"
|
|
|
5415
5854
|
}
|
|
5416
5855
|
});
|
|
5417
5856
|
cli.help();
|
|
5418
|
-
cli.version("2.1
|
|
5857
|
+
cli.version("2.3.1");
|
|
5419
5858
|
cli.parse();
|
|
5420
5859
|
//# sourceMappingURL=cli.cjs.map
|