@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.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) {
|
|
@@ -1867,7 +2032,7 @@ const hotModulesMap = new Map();
|
|
|
1867
2032
|
const disposeMap = new Map();
|
|
1868
2033
|
const pruneMap = new Map();
|
|
1869
2034
|
|
|
1870
|
-
socket.addEventListener('message', ({ data }) => {
|
|
2035
|
+
socket.addEventListener('message', async ({ data }) => {
|
|
1871
2036
|
const payload = JSON.parse(data);
|
|
1872
2037
|
switch (payload.type) {
|
|
1873
2038
|
case 'connected':
|
|
@@ -1875,14 +2040,21 @@ socket.addEventListener('message', ({ data }) => {
|
|
|
1875
2040
|
clearErrorOverlay();
|
|
1876
2041
|
break;
|
|
1877
2042
|
case 'update':
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
2043
|
+
try {
|
|
2044
|
+
await Promise.all(payload.updates.map((update) => {
|
|
2045
|
+
if (update.type === 'js-update') {
|
|
2046
|
+
return fetchUpdate(update);
|
|
2047
|
+
} else if (update.type === 'css-update') {
|
|
2048
|
+
return updateCss(update.path);
|
|
2049
|
+
}
|
|
2050
|
+
}));
|
|
2051
|
+
clearErrorOverlay();
|
|
2052
|
+
console.log('[nasti] HMR update complete, reloading page');
|
|
2053
|
+
location.reload();
|
|
2054
|
+
} catch (err) {
|
|
2055
|
+
console.error('[nasti] HMR update failed:', err);
|
|
2056
|
+
showErrorOverlay(err);
|
|
2057
|
+
}
|
|
1886
2058
|
break;
|
|
1887
2059
|
case 'full-reload':
|
|
1888
2060
|
console.log('[nasti] full reload');
|
|
@@ -1924,7 +2096,7 @@ async function fetchUpdate(update) {
|
|
|
1924
2096
|
function updateCss(path) {
|
|
1925
2097
|
const el = document.querySelector(\`style[data-nasti-css="\${path}"]\`);
|
|
1926
2098
|
if (el) {
|
|
1927
|
-
fetch(path + '?t=' + Date.now())
|
|
2099
|
+
return fetch(path + '?t=' + Date.now())
|
|
1928
2100
|
.then(r => r.text())
|
|
1929
2101
|
.then(css => { el.textContent = css; });
|
|
1930
2102
|
}
|
|
@@ -3915,6 +4087,7 @@ var build_exports = {};
|
|
|
3915
4087
|
__export(build_exports, {
|
|
3916
4088
|
build: () => build,
|
|
3917
4089
|
getRolldownOptions: () => getRolldownOptions,
|
|
4090
|
+
replaceEntryScript: () => replaceEntryScript,
|
|
3918
4091
|
resolveClientEntries: () => resolveClientEntries,
|
|
3919
4092
|
toRolldownPlugins: () => toRolldownPlugins
|
|
3920
4093
|
});
|
|
@@ -3998,13 +4171,20 @@ function toRolldownPlugins(plugins) {
|
|
|
3998
4171
|
}));
|
|
3999
4172
|
}
|
|
4000
4173
|
function resolveClientEntries(config, html) {
|
|
4174
|
+
const configuredEntries = config.environments.client?.entry ?? [];
|
|
4175
|
+
if (configuredEntries.length > 0) return configuredEntries;
|
|
4001
4176
|
const entryPoints = [];
|
|
4177
|
+
const htmlFile = config.environments.client?.html;
|
|
4178
|
+
const htmlDir = htmlFile ? path12.dirname(htmlFile) : config.root;
|
|
4002
4179
|
if (html) {
|
|
4003
4180
|
const scriptMatches = html.matchAll(/<script[^>]+src=["']([^"']+)["'][^>]*>/gi);
|
|
4004
4181
|
for (const match of scriptMatches) {
|
|
4005
4182
|
const src = match[1];
|
|
4006
4183
|
if (src && !src.startsWith("http")) {
|
|
4007
|
-
|
|
4184
|
+
const cleanSrc = src.split(/[?#]/, 1)[0];
|
|
4185
|
+
entryPoints.push(
|
|
4186
|
+
cleanSrc.startsWith("/") ? path12.resolve(config.root, cleanSrc.replace(/^\//, "")) : path12.resolve(htmlDir, cleanSrc)
|
|
4187
|
+
);
|
|
4008
4188
|
}
|
|
4009
4189
|
}
|
|
4010
4190
|
}
|
|
@@ -4040,21 +4220,55 @@ async function build(inlineConfig = {}) {
|
|
|
4040
4220
|
const startTime = performance.now();
|
|
4041
4221
|
logger.info(
|
|
4042
4222
|
pc6.cyan(`
|
|
4043
|
-
nasti v${"2.1
|
|
4223
|
+
nasti v${"2.3.1"} `) + pc6.green(`building for ${config.mode}...`)
|
|
4044
4224
|
);
|
|
4045
4225
|
debug5?.(`root: ${config.root}`);
|
|
4046
4226
|
const buildableNames = Object.keys(config.environments).filter(
|
|
4047
|
-
(name) => name === "client" || config.environments[name].entry.length > 0
|
|
4227
|
+
(name) => name === "client" || config.environments[name].entry.length > 0 || !!config.environments[name].driver
|
|
4048
4228
|
);
|
|
4049
4229
|
buildableNames.sort((a, b) => a === "client" ? -1 : b === "client" ? 1 : 0);
|
|
4050
4230
|
const environments = {};
|
|
4231
|
+
const environmentResults = {};
|
|
4232
|
+
const initializedEnvironments = [];
|
|
4051
4233
|
let clientOutput = [];
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
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;
|
|
4058
4272
|
}
|
|
4059
4273
|
}
|
|
4060
4274
|
const elapsed = ((performance.now() - startTime) / 1e3).toFixed(2);
|
|
@@ -4067,83 +4281,130 @@ nasti v${"2.1.0"} `) + pc6.green(`building for ${config.mode}...`)
|
|
|
4067
4281
|
const envSuffix = buildableNames.length > 1 ? ` (${buildableNames.join(" + ")})` : "";
|
|
4068
4282
|
logger.info(pc6.green(`\u2713 built in ${elapsed}s`) + pc6.dim(envSuffix));
|
|
4069
4283
|
logger.info(pc6.dim(` ${fileCount} files, ${displaySize(totalSize)} total \u2192 ${config.build.outDir}/`));
|
|
4070
|
-
return { output: clientOutput, environments };
|
|
4284
|
+
return { output: clientOutput, environments, environmentResults };
|
|
4071
4285
|
}
|
|
4072
4286
|
async function buildClientEnvironment(config) {
|
|
4073
4287
|
const logger = config.logger;
|
|
4074
4288
|
const outDir = path12.resolve(config.root, config.build.outDir);
|
|
4075
|
-
if (config.build.emptyOutDir && fs9.existsSync(outDir)) {
|
|
4076
|
-
fs9.rmSync(outDir, { recursive: true, force: true });
|
|
4077
|
-
}
|
|
4078
|
-
fs9.mkdirSync(outDir, { recursive: true });
|
|
4079
|
-
const html = await readHtmlFile(config.root);
|
|
4080
|
-
const entryPoints = resolveClientEntries(config, html);
|
|
4081
|
-
if (entryPoints.length === 0) {
|
|
4082
|
-
throw new Error("No entry point found. Add a <script> tag to index.html or create src/main.ts");
|
|
4083
|
-
}
|
|
4084
4289
|
const cssEngine = createCssEngine();
|
|
4085
4290
|
const pluginList = resolvePluginList(config, config.plugins, { cssEngine });
|
|
4086
|
-
const clientEnv = new NastiEnvironment("client",
|
|
4291
|
+
const clientEnv = new NastiEnvironment("client", config, {
|
|
4087
4292
|
mode: "build",
|
|
4088
|
-
plugins: pluginList
|
|
4293
|
+
plugins: pluginList,
|
|
4294
|
+
pluginApi: getPluginApi(config)
|
|
4089
4295
|
});
|
|
4090
4296
|
await clientEnv.init();
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
...nativeReporter ? [nativeReporter] : []
|
|
4097
|
-
];
|
|
4098
|
-
const { inputOptions, outputOptions } = getRolldownOptions(clientEnv, entryPoints, rolldownPlugins);
|
|
4099
|
-
const bundle2 = await rolldown(inputOptions);
|
|
4100
|
-
const { output } = await bundle2.write(outputOptions);
|
|
4101
|
-
await bundle2.close();
|
|
4102
|
-
if (html) {
|
|
4103
|
-
let processedHtml = html;
|
|
4104
|
-
const htmlPlugins = [...allPlugins.filter((p) => p.transformIndexHtml), htmlPlugin(config)];
|
|
4105
|
-
for (const p of htmlPlugins) {
|
|
4106
|
-
const result = await p.transformIndexHtml(processedHtml);
|
|
4107
|
-
if (typeof result === "string") {
|
|
4108
|
-
processedHtml = result;
|
|
4109
|
-
} else if (result && "html" in result) {
|
|
4110
|
-
processedHtml = processHtml(result.html, result.tags);
|
|
4111
|
-
} else if (Array.isArray(result)) {
|
|
4112
|
-
processedHtml = processHtml(processedHtml, result);
|
|
4113
|
-
}
|
|
4114
|
-
}
|
|
4115
|
-
processedHtml = injectCssLinks(processedHtml, cssEngine, config);
|
|
4116
|
-
for (const chunk of output) {
|
|
4117
|
-
if (chunk.type === "chunk" && chunk.isEntry && chunk.facadeModuleId) {
|
|
4118
|
-
const originalEntry = path12.relative(config.root, chunk.facadeModuleId);
|
|
4119
|
-
processedHtml = processedHtml.replace(
|
|
4120
|
-
new RegExp(`(src=["'])/?(${escapeRegExp(originalEntry)})(["'])`, "g"),
|
|
4121
|
-
`$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()`
|
|
4122
4302
|
);
|
|
4123
4303
|
}
|
|
4304
|
+
const result = await clientEnv.driver.build(clientEnv.getDriverContext());
|
|
4305
|
+
return { environment: clientEnv, result };
|
|
4124
4306
|
}
|
|
4125
|
-
fs9.
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
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;
|
|
4129
4375
|
}
|
|
4130
|
-
warnLargeChunks(output, config, logger);
|
|
4131
|
-
return output;
|
|
4132
4376
|
}
|
|
4133
4377
|
async function buildServerEnvironment(config, name) {
|
|
4134
4378
|
const envOptions = config.environments[name];
|
|
4135
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
|
+
}
|
|
4136
4402
|
for (const entry of envOptions.entry) {
|
|
4137
4403
|
if (!fs9.existsSync(entry)) {
|
|
4404
|
+
await environment.close();
|
|
4138
4405
|
throw new Error(`[nasti] environment "${name}" entry not found: ${entry}`);
|
|
4139
4406
|
}
|
|
4140
4407
|
}
|
|
4141
|
-
const pluginList = resolvePluginList(config, config.plugins, { consumer: envOptions.consumer });
|
|
4142
|
-
const environment = new NastiEnvironment(name, { ...config, plugins: pluginList }, {
|
|
4143
|
-
mode: "build",
|
|
4144
|
-
plugins: pluginList
|
|
4145
|
-
});
|
|
4146
|
-
await environment.init();
|
|
4147
4408
|
const rolldownPlugins = [
|
|
4148
4409
|
createOxcTransformPlugin(config, environment),
|
|
4149
4410
|
...toRolldownPlugins(environment.plugins)
|
|
@@ -4163,7 +4424,7 @@ async function buildServerEnvironment(config, name) {
|
|
|
4163
4424
|
logger.info(
|
|
4164
4425
|
pc6.dim(` [${name}] `) + output.map((o) => path12.join(envOptions.build.outDir, o.fileName)).join(pc6.dim(", "))
|
|
4165
4426
|
);
|
|
4166
|
-
return output;
|
|
4427
|
+
return { environment, result: { output } };
|
|
4167
4428
|
}
|
|
4168
4429
|
function injectCssLinks(html, cssEngine, config) {
|
|
4169
4430
|
const cssLinkTags = [];
|
|
@@ -4189,6 +4450,25 @@ function injectCssLinks(html, cssEngine, config) {
|
|
|
4189
4450
|
function escapeRegExp(string) {
|
|
4190
4451
|
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4191
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
|
+
}
|
|
4192
4472
|
var debug5, NODE_BUILTINS2;
|
|
4193
4473
|
var init_build = __esm({
|
|
4194
4474
|
"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
|
debug5 = createDebugger("nasti:build");
|
|
4206
4487
|
NODE_BUILTINS2 = /* @__PURE__ */ new Set([...builtinModules2, ...builtinModules2.map((m) => `node:${m}`)]);
|
|
4207
4488
|
}
|
|
@@ -4233,7 +4514,7 @@ async function createBundledDevServer(opts) {
|
|
|
4233
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.`
|
|
4234
4515
|
);
|
|
4235
4516
|
}
|
|
4236
|
-
const html = await readHtmlFile(config.root);
|
|
4517
|
+
const html = await readHtmlFile(config.root, config.environments.client?.html);
|
|
4237
4518
|
const entryPoints = resolveClientEntries(config, html);
|
|
4238
4519
|
if (entryPoints.length === 0) {
|
|
4239
4520
|
throw new Error("No entry point found. Add a <script> tag to index.html or create src/main.ts");
|
|
@@ -4288,7 +4569,10 @@ async function createBundledDevServer(opts) {
|
|
|
4288
4569
|
continue;
|
|
4289
4570
|
}
|
|
4290
4571
|
const patchPath = `__nasti_patch/${update.filename}`;
|
|
4291
|
-
patches.set(
|
|
4572
|
+
patches.set(
|
|
4573
|
+
patchPath,
|
|
4574
|
+
update.code + "\n;globalThis.location?.reload();\n;export {}"
|
|
4575
|
+
);
|
|
4292
4576
|
if (update.sourcemap && update.sourcemapFilename) {
|
|
4293
4577
|
patches.set(`__nasti_patch/${update.sourcemapFilename}`, update.sourcemap);
|
|
4294
4578
|
}
|
|
@@ -4436,7 +4720,7 @@ async function createBundledDevServer(opts) {
|
|
|
4436
4720
|
return;
|
|
4437
4721
|
}
|
|
4438
4722
|
if (pathname === "/" || pathname.endsWith(".html")) {
|
|
4439
|
-
const rawHtml = await readHtmlFile(config.root);
|
|
4723
|
+
const rawHtml = await readHtmlFile(config.root, config.environments.client?.html);
|
|
4440
4724
|
if (rawHtml) {
|
|
4441
4725
|
res.setHeader("Content-Type", "text/html");
|
|
4442
4726
|
res.setHeader("Cache-Control", "no-store");
|
|
@@ -4520,10 +4804,13 @@ async function renderBundledIndexHtml(html, config, entryFileNames) {
|
|
|
4520
4804
|
}
|
|
4521
4805
|
}
|
|
4522
4806
|
for (const [facadeModuleId, fileName] of entryFileNames) {
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4807
|
+
processed = replaceEntryScript(
|
|
4808
|
+
processed,
|
|
4809
|
+
facadeModuleId,
|
|
4810
|
+
fileName,
|
|
4811
|
+
config,
|
|
4812
|
+
config.environments.client?.html ?? "index.html",
|
|
4813
|
+
"/"
|
|
4527
4814
|
);
|
|
4528
4815
|
}
|
|
4529
4816
|
return processed;
|
|
@@ -4667,10 +4954,12 @@ async function createServer(inlineConfig = {}) {
|
|
|
4667
4954
|
const app = connect();
|
|
4668
4955
|
const httpServer = http.createServer(app);
|
|
4669
4956
|
const ws = createWebSocketServer(httpServer);
|
|
4670
|
-
const
|
|
4957
|
+
const pluginApi = getPluginApi(config);
|
|
4958
|
+
const clientEnv = new NastiEnvironment("client", config, {
|
|
4671
4959
|
hot: createWsHotChannel(ws),
|
|
4672
4960
|
mode: "dev",
|
|
4673
|
-
plugins: allPlugins
|
|
4961
|
+
plugins: allPlugins,
|
|
4962
|
+
pluginApi
|
|
4674
4963
|
});
|
|
4675
4964
|
await clientEnv.init();
|
|
4676
4965
|
const environments = { client: clientEnv };
|
|
@@ -4678,11 +4967,15 @@ async function createServer(inlineConfig = {}) {
|
|
|
4678
4967
|
if (name === "client") continue;
|
|
4679
4968
|
const consumer = config.environments[name].consumer;
|
|
4680
4969
|
const envPlugins = resolvePluginList(config, config.plugins, { consumer });
|
|
4681
|
-
environments[name] = new NastiEnvironment(name,
|
|
4970
|
+
environments[name] = new NastiEnvironment(name, config, {
|
|
4682
4971
|
mode: "dev",
|
|
4683
|
-
plugins: envPlugins
|
|
4972
|
+
plugins: envPlugins,
|
|
4973
|
+
pluginApi
|
|
4684
4974
|
});
|
|
4685
4975
|
}
|
|
4976
|
+
for (const [name, environment] of Object.entries(environments)) {
|
|
4977
|
+
if (name !== "client" && environment.options.driver) await environment.init();
|
|
4978
|
+
}
|
|
4686
4979
|
let ssrRunner = null;
|
|
4687
4980
|
async function getSsrRunner() {
|
|
4688
4981
|
if (ssrRunner) return ssrRunner;
|
|
@@ -4707,14 +5000,6 @@ async function createServer(inlineConfig = {}) {
|
|
|
4707
5000
|
});
|
|
4708
5001
|
app.use(bundledServer.middleware);
|
|
4709
5002
|
}
|
|
4710
|
-
app.use(transformMiddleware({
|
|
4711
|
-
config: configWithPlugins,
|
|
4712
|
-
pluginContainer,
|
|
4713
|
-
moduleGraph
|
|
4714
|
-
}));
|
|
4715
|
-
const publicDir = path14.resolve(config.root, "public");
|
|
4716
|
-
app.use(sirv(publicDir, { dev: true, etag: true }));
|
|
4717
|
-
app.use(sirv(config.root, { dev: true, etag: true }));
|
|
4718
5003
|
const ignoredSegments = /* @__PURE__ */ new Set(["node_modules", ".git", ".nasti"]);
|
|
4719
5004
|
const outDirAbs = path14.resolve(config.root, config.build.outDir);
|
|
4720
5005
|
const watcher = watch(config.root, {
|
|
@@ -4731,13 +5016,72 @@ async function createServer(inlineConfig = {}) {
|
|
|
4731
5016
|
ignoreInitial: true
|
|
4732
5017
|
});
|
|
4733
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
|
+
};
|
|
4734
5072
|
watcher.on("change", (file) => {
|
|
4735
5073
|
ssrRunner?.invalidateFile(file);
|
|
4736
5074
|
handleFileChange(file, server);
|
|
5075
|
+
notifyEnvironmentDrivers(file, "change");
|
|
4737
5076
|
});
|
|
4738
5077
|
watcher.on("add", (file) => {
|
|
4739
5078
|
ssrRunner?.invalidateFile(file);
|
|
4740
5079
|
handleFileChange(file, server);
|
|
5080
|
+
notifyEnvironmentDrivers(file, "add");
|
|
5081
|
+
});
|
|
5082
|
+
watcher.on("unlink", (file) => {
|
|
5083
|
+
ssrRunner?.invalidateFile(file);
|
|
5084
|
+
notifyEnvironmentDrivers(file, "unlink");
|
|
4741
5085
|
});
|
|
4742
5086
|
server = {
|
|
4743
5087
|
config: configWithPlugins,
|
|
@@ -4746,10 +5090,12 @@ async function createServer(inlineConfig = {}) {
|
|
|
4746
5090
|
watcher,
|
|
4747
5091
|
ws,
|
|
4748
5092
|
environments,
|
|
5093
|
+
environmentServices,
|
|
4749
5094
|
async listen(port) {
|
|
4750
5095
|
const finalPort = port ?? config.server.port;
|
|
4751
5096
|
const host = config.server.host === true ? "0.0.0.0" : config.server.host;
|
|
4752
5097
|
await pluginContainer.buildStart();
|
|
5098
|
+
await startEnvironmentDrivers();
|
|
4753
5099
|
return new Promise((resolve, reject) => {
|
|
4754
5100
|
let currentPort = finalPort;
|
|
4755
5101
|
const onListening = () => {
|
|
@@ -4757,15 +5103,20 @@ async function createServer(inlineConfig = {}) {
|
|
|
4757
5103
|
config.server.port = actualPort;
|
|
4758
5104
|
const localUrl = `http://localhost:${actualPort}/`;
|
|
4759
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 ?? []);
|
|
4760
5108
|
logger.clearScreen("info");
|
|
4761
5109
|
const readyIn = Math.ceil(performance.now() - startTime);
|
|
4762
5110
|
logger.info(
|
|
4763
5111
|
`
|
|
4764
|
-
${pc8.cyan(pc8.bold("NASTI"))} ${pc8.cyan(`v${"2.1
|
|
5112
|
+
${pc8.cyan(pc8.bold("NASTI"))} ${pc8.cyan(`v${"2.3.1"}`)} ${pc8.dim("ready in")} ${pc8.bold(readyIn)} ${pc8.dim("ms")}
|
|
4765
5113
|
`
|
|
4766
5114
|
);
|
|
4767
5115
|
printServerUrls(
|
|
4768
|
-
{
|
|
5116
|
+
{
|
|
5117
|
+
local: [localUrl, ...driverLocalUrls],
|
|
5118
|
+
network: [...networkUrl ? [networkUrl] : [], ...driverNetworkUrls]
|
|
5119
|
+
},
|
|
4769
5120
|
logger.info
|
|
4770
5121
|
);
|
|
4771
5122
|
logger.info("");
|
|
@@ -4795,11 +5146,62 @@ async function createServer(inlineConfig = {}) {
|
|
|
4795
5146
|
async close() {
|
|
4796
5147
|
await pluginContainer.buildEnd();
|
|
4797
5148
|
await bundledServer?.close();
|
|
4798
|
-
|
|
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();
|
|
4799
5163
|
ws.close();
|
|
4800
5164
|
httpServer.close();
|
|
5165
|
+
if (environmentCloseFailed) {
|
|
5166
|
+
throw firstEnvironmentCloseError;
|
|
5167
|
+
}
|
|
4801
5168
|
}
|
|
4802
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 }));
|
|
4803
5205
|
const postMiddlewares = [];
|
|
4804
5206
|
for (const plugin of allPlugins) {
|
|
4805
5207
|
if (plugin.configureServer) {
|
|
@@ -4834,6 +5236,7 @@ var init_server = __esm({
|
|
|
4834
5236
|
init_middleware();
|
|
4835
5237
|
init_hmr();
|
|
4836
5238
|
init_builtins();
|
|
5239
|
+
init_plugin_api();
|
|
4837
5240
|
}
|
|
4838
5241
|
});
|
|
4839
5242
|
|
|
@@ -4880,6 +5283,7 @@ var init_electron = __esm({
|
|
|
4880
5283
|
var electron_exports = {};
|
|
4881
5284
|
__export(electron_exports, {
|
|
4882
5285
|
buildElectron: () => buildElectron,
|
|
5286
|
+
createElectronRendererConfig: () => createElectronRendererConfig,
|
|
4883
5287
|
detectInstalledElectron: () => detectInstalledElectron,
|
|
4884
5288
|
normalizePreload: () => normalizePreload
|
|
4885
5289
|
});
|
|
@@ -4891,7 +5295,7 @@ async function buildElectron(inlineConfig = {}) {
|
|
|
4891
5295
|
const config = await resolveConfig({ ...inlineConfig, target: "electron" }, "build");
|
|
4892
5296
|
const startTime = performance.now();
|
|
4893
5297
|
assertElectronVersion(config);
|
|
4894
|
-
console.log(pc9.cyan("\n\u26A1 nasti build (electron)") + pc9.dim(` v${"2.1
|
|
5298
|
+
console.log(pc9.cyan("\n\u26A1 nasti build (electron)") + pc9.dim(` v${"2.3.1"}`));
|
|
4895
5299
|
console.log(pc9.dim(` root: ${config.root}`));
|
|
4896
5300
|
console.log(pc9.dim(` mode: ${config.mode}`));
|
|
4897
5301
|
console.log(pc9.dim(` target: electron (\u2265 ${config.electron.minVersion})`));
|
|
@@ -4902,15 +5306,13 @@ async function buildElectron(inlineConfig = {}) {
|
|
|
4902
5306
|
fs10.mkdirSync(outDir, { recursive: true });
|
|
4903
5307
|
const rendererOutDir = path15.join(outDir, "renderer");
|
|
4904
5308
|
const { build: build2 } = await Promise.resolve().then(() => (init_build(), build_exports));
|
|
4905
|
-
await build2({
|
|
4906
|
-
...inlineConfig,
|
|
4907
|
-
target: "web",
|
|
5309
|
+
await build2(createElectronRendererConfig(config, inlineConfig, {
|
|
4908
5310
|
build: {
|
|
4909
5311
|
...inlineConfig.build,
|
|
4910
5312
|
outDir: rendererOutDir,
|
|
4911
5313
|
emptyOutDir: false
|
|
4912
5314
|
}
|
|
4913
|
-
});
|
|
5315
|
+
}));
|
|
4914
5316
|
const mainEntry = path15.resolve(config.root, config.electron.main);
|
|
4915
5317
|
if (!fs10.existsSync(mainEntry)) {
|
|
4916
5318
|
throw new Error(
|
|
@@ -4964,7 +5366,8 @@ async function bundleNode(config, entry, opts) {
|
|
|
4964
5366
|
const result = transformCode(id, code, {
|
|
4965
5367
|
sourcemap: !!config.build.sourcemap,
|
|
4966
5368
|
jsxRuntime: "automatic",
|
|
4967
|
-
jsxImportSource: config.framework === "vue" ? "vue" : "react"
|
|
5369
|
+
jsxImportSource: config.framework === "vue" ? "vue" : "react",
|
|
5370
|
+
target: config.electron.nodeTarget
|
|
4968
5371
|
});
|
|
4969
5372
|
return { code: result.code, map: result.map ? JSON.parse(result.map) : void 0 };
|
|
4970
5373
|
}
|
|
@@ -4975,7 +5378,11 @@ async function bundleNode(config, entry, opts) {
|
|
|
4975
5378
|
...restInputOptions,
|
|
4976
5379
|
input: entry,
|
|
4977
5380
|
platform: "node",
|
|
4978
|
-
transform: {
|
|
5381
|
+
transform: {
|
|
5382
|
+
...userTransform,
|
|
5383
|
+
target: config.electron.nodeTarget,
|
|
5384
|
+
define: mergedDefine
|
|
5385
|
+
},
|
|
4979
5386
|
plugins: [oxcTransformPlugin, electronPlugin(config), resolvePlugin(config)]
|
|
4980
5387
|
});
|
|
4981
5388
|
fs10.mkdirSync(path15.dirname(opts.outFile), { recursive: true });
|
|
@@ -4992,6 +5399,25 @@ async function bundleNode(config, entry, opts) {
|
|
|
4992
5399
|
console.log(pc9.dim(` \u2713 ${opts.label} \u2192 ${path15.relative(config.root, opts.outFile)}`));
|
|
4993
5400
|
return opts.outFile;
|
|
4994
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
|
+
}
|
|
4995
5421
|
function outFileName(outDir, base, format) {
|
|
4996
5422
|
const ext = format === "cjs" ? ".cjs" : ".mjs";
|
|
4997
5423
|
return path15.join(outDir, base + ext);
|
|
@@ -5036,6 +5462,7 @@ var init_electron2 = __esm({
|
|
|
5036
5462
|
// src/server/electron-dev.ts
|
|
5037
5463
|
var electron_dev_exports = {};
|
|
5038
5464
|
__export(electron_dev_exports, {
|
|
5465
|
+
electronRendererDevPath: () => electronRendererDevPath,
|
|
5039
5466
|
startElectronDev: () => startElectronDev
|
|
5040
5467
|
});
|
|
5041
5468
|
import path16 from "path";
|
|
@@ -5049,11 +5476,15 @@ async function startElectronDev(inlineConfig = {}) {
|
|
|
5049
5476
|
const { noSpawn, ...rest } = inlineConfig;
|
|
5050
5477
|
const config = await resolveConfig({ ...rest, target: "electron" }, "serve");
|
|
5051
5478
|
warnElectronVersion(config);
|
|
5052
|
-
console.log(pc10.cyan("\n\u26A1 nasti electron dev") + pc10.dim(` v${"2.1
|
|
5479
|
+
console.log(pc10.cyan("\n\u26A1 nasti electron dev") + pc10.dim(` v${"2.3.1"}`));
|
|
5053
5480
|
const { createServer: createServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
|
|
5054
|
-
const server = await createServer2({
|
|
5481
|
+
const server = await createServer2({
|
|
5482
|
+
...rest,
|
|
5483
|
+
target: "electron",
|
|
5484
|
+
framework: config.framework
|
|
5485
|
+
});
|
|
5055
5486
|
await server.listen();
|
|
5056
|
-
const devUrl = `http://localhost:${server.config.server.port}
|
|
5487
|
+
const devUrl = `http://localhost:${server.config.server.port}` + electronRendererDevPath(config.electron.renderer);
|
|
5057
5488
|
console.log(pc10.dim(` renderer: ${devUrl}`));
|
|
5058
5489
|
const stageDir = path16.resolve(config.root, ".nasti");
|
|
5059
5490
|
fs11.mkdirSync(stageDir, { recursive: true });
|
|
@@ -5175,14 +5606,18 @@ async function compileNode(config, entry, opts) {
|
|
|
5175
5606
|
const result = transformCode(id, code, {
|
|
5176
5607
|
sourcemap: true,
|
|
5177
5608
|
jsxRuntime: "automatic",
|
|
5178
|
-
jsxImportSource: config.framework === "vue" ? "vue" : "react"
|
|
5609
|
+
jsxImportSource: config.framework === "vue" ? "vue" : "react",
|
|
5610
|
+
target: config.electron.nodeTarget
|
|
5179
5611
|
});
|
|
5180
5612
|
return { code: result.code, map: result.map ? JSON.parse(result.map) : void 0 };
|
|
5181
5613
|
}
|
|
5182
5614
|
};
|
|
5183
5615
|
const bundle2 = await rolldown3({
|
|
5184
5616
|
input: entry,
|
|
5185
|
-
transform: {
|
|
5617
|
+
transform: {
|
|
5618
|
+
target: config.electron.nodeTarget,
|
|
5619
|
+
define: envDefine
|
|
5620
|
+
},
|
|
5186
5621
|
platform: "node",
|
|
5187
5622
|
plugins: [oxcTransformPlugin, electronPlugin(config), resolvePlugin(config)]
|
|
5188
5623
|
});
|
|
@@ -5198,6 +5633,10 @@ async function compileNode(config, entry, opts) {
|
|
|
5198
5633
|
});
|
|
5199
5634
|
await bundle2.close();
|
|
5200
5635
|
}
|
|
5636
|
+
function electronRendererDevPath(renderer) {
|
|
5637
|
+
const normalized = renderer.split(path16.sep).join("/").replace(/^\.?\//, "");
|
|
5638
|
+
return normalized === "index.html" ? "/" : `/${normalized}`;
|
|
5639
|
+
}
|
|
5201
5640
|
function resolveElectronBinary(config) {
|
|
5202
5641
|
if (config.electron.electronPath && fs11.existsSync(config.electron.electronPath)) {
|
|
5203
5642
|
return config.electron.electronPath;
|
|
@@ -5394,7 +5833,7 @@ cli.command("preview [root]", "Preview production build").option("--port <port>"
|
|
|
5394
5833
|
const host = options.host === true ? "0.0.0.0" : options.host ?? "localhost";
|
|
5395
5834
|
http2.createServer(app).listen(port, host, () => {
|
|
5396
5835
|
logger.info(`
|
|
5397
|
-
${pc11.cyan(pc11.bold("NASTI"))} ${pc11.cyan(`v${"2.1
|
|
5836
|
+
${pc11.cyan(pc11.bold("NASTI"))} ${pc11.cyan(`v${"2.3.1"}`)} ${pc11.dim("preview")}
|
|
5398
5837
|
`);
|
|
5399
5838
|
printServerUrls2(
|
|
5400
5839
|
{
|
|
@@ -5411,6 +5850,6 @@ cli.command("preview [root]", "Preview production build").option("--port <port>"
|
|
|
5411
5850
|
}
|
|
5412
5851
|
});
|
|
5413
5852
|
cli.help();
|
|
5414
|
-
cli.version("2.1
|
|
5853
|
+
cli.version("2.3.1");
|
|
5415
5854
|
cli.parse();
|
|
5416
5855
|
//# sourceMappingURL=cli.js.map
|