@nasti-toolchain/nasti 2.2.0 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -1
- package/dist/cli.cjs +552 -123
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +552 -123
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +554 -121
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +109 -11
- package/dist/index.d.ts +109 -11
- package/dist/index.js +551 -121
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -225,6 +225,92 @@ var init_logger = __esm({
|
|
|
225
225
|
}
|
|
226
226
|
});
|
|
227
227
|
|
|
228
|
+
// src/core/plugin-api.ts
|
|
229
|
+
function orderPlugins(plugins) {
|
|
230
|
+
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);
|
|
231
|
+
const indexesByName = /* @__PURE__ */ new Map();
|
|
232
|
+
baseline.forEach((plugin, index2) => {
|
|
233
|
+
const indexes = indexesByName.get(plugin.name) ?? [];
|
|
234
|
+
indexes.push(index2);
|
|
235
|
+
indexesByName.set(plugin.name, indexes);
|
|
236
|
+
});
|
|
237
|
+
const edges = baseline.map(() => /* @__PURE__ */ new Set());
|
|
238
|
+
const indegree = baseline.map(() => 0);
|
|
239
|
+
const addEdge = (from, to) => {
|
|
240
|
+
if (from === to || edges[from].has(to)) return;
|
|
241
|
+
edges[from].add(to);
|
|
242
|
+
indegree[to]++;
|
|
243
|
+
};
|
|
244
|
+
baseline.forEach((plugin, current) => {
|
|
245
|
+
for (const dependency of plugin.pre ?? []) {
|
|
246
|
+
for (const before of indexesByName.get(dependency) ?? []) addEdge(before, current);
|
|
247
|
+
}
|
|
248
|
+
for (const dependency of plugin.post ?? []) {
|
|
249
|
+
for (const after of indexesByName.get(dependency) ?? []) addEdge(current, after);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
const ready = indegree.map((degree, index2) => ({ degree, index: index2 })).filter(({ degree }) => degree === 0).map(({ index: index2 }) => index2);
|
|
253
|
+
const ordered = [];
|
|
254
|
+
while (ready.length > 0) {
|
|
255
|
+
ready.sort((a, b) => a - b);
|
|
256
|
+
const current = ready.shift();
|
|
257
|
+
ordered.push(baseline[current]);
|
|
258
|
+
for (const next of edges[current]) {
|
|
259
|
+
indegree[next]--;
|
|
260
|
+
if (indegree[next] === 0) ready.push(next);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
if (ordered.length !== baseline.length) {
|
|
264
|
+
const cyclic = baseline.filter((_, index2) => indegree[index2] > 0).map((plugin) => plugin.name);
|
|
265
|
+
throw new Error(
|
|
266
|
+
`[nasti] circular plugin setup dependency: ${[...new Set(cyclic)].join(", ")}`
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
return ordered;
|
|
270
|
+
}
|
|
271
|
+
async function setupPluginApi(config, plugins) {
|
|
272
|
+
const exposed = /* @__PURE__ */ new Map();
|
|
273
|
+
const api = {
|
|
274
|
+
config,
|
|
275
|
+
logger: config.logger,
|
|
276
|
+
expose(key, value) {
|
|
277
|
+
if (exposed.has(key) && exposed.get(key) !== value) {
|
|
278
|
+
throw new Error(`[nasti] plugin API key already exposed: ${String(key)}`);
|
|
279
|
+
}
|
|
280
|
+
exposed.set(key, value);
|
|
281
|
+
},
|
|
282
|
+
useExposed(key) {
|
|
283
|
+
return exposed.get(key);
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
apiByConfig.set(config, api);
|
|
287
|
+
for (const plugin of plugins) {
|
|
288
|
+
await plugin.setup?.(api);
|
|
289
|
+
}
|
|
290
|
+
return api;
|
|
291
|
+
}
|
|
292
|
+
function getPluginApi(config) {
|
|
293
|
+
const api = apiByConfig.get(config);
|
|
294
|
+
if (!api) {
|
|
295
|
+
throw new Error(
|
|
296
|
+
"[nasti] internal: plugin API requested before setup; getPluginApi requires the original ResolvedConfig reference registered by setupPluginApi, not a shallow copy"
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
return api;
|
|
300
|
+
}
|
|
301
|
+
function enforceRank(plugin) {
|
|
302
|
+
if (plugin.enforce === "pre") return 0;
|
|
303
|
+
if (plugin.enforce === "post") return 2;
|
|
304
|
+
return 1;
|
|
305
|
+
}
|
|
306
|
+
var apiByConfig;
|
|
307
|
+
var init_plugin_api = __esm({
|
|
308
|
+
"src/core/plugin-api.ts"() {
|
|
309
|
+
"use strict";
|
|
310
|
+
apiByConfig = /* @__PURE__ */ new WeakMap();
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
|
|
228
314
|
// src/config/index.ts
|
|
229
315
|
import { pathToFileURL } from "url";
|
|
230
316
|
import path from "path";
|
|
@@ -265,6 +351,43 @@ async function loadConfigFromFile(root) {
|
|
|
265
351
|
}
|
|
266
352
|
return {};
|
|
267
353
|
}
|
|
354
|
+
function detectFramework(root) {
|
|
355
|
+
const sourceRoot = path.resolve(root, "src");
|
|
356
|
+
if (containsVueFile(sourceRoot)) return "vue";
|
|
357
|
+
const packagePath = path.resolve(root, "package.json");
|
|
358
|
+
if (fs.existsSync(packagePath)) {
|
|
359
|
+
try {
|
|
360
|
+
const pkg = JSON.parse(fs.readFileSync(packagePath, "utf-8"));
|
|
361
|
+
const dependencies = {
|
|
362
|
+
...pkg.dependencies ?? {},
|
|
363
|
+
...pkg.devDependencies ?? {},
|
|
364
|
+
...pkg.peerDependencies ?? {},
|
|
365
|
+
...pkg.optionalDependencies ?? {}
|
|
366
|
+
};
|
|
367
|
+
const hasVue = "vue" in dependencies || "@vue/runtime-dom" in dependencies;
|
|
368
|
+
const hasReact = "react" in dependencies || "react-dom" in dependencies;
|
|
369
|
+
if (hasVue && !hasReact) return "vue";
|
|
370
|
+
if (hasReact) return "react";
|
|
371
|
+
if (hasVue) return "vue";
|
|
372
|
+
} catch {
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return "react";
|
|
376
|
+
}
|
|
377
|
+
function containsVueFile(dir, depth = 0) {
|
|
378
|
+
if (depth > 5 || !fs.existsSync(dir)) return false;
|
|
379
|
+
try {
|
|
380
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
381
|
+
if (entry.isFile() && entry.name.endsWith(".vue")) return true;
|
|
382
|
+
if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== "dist" && containsVueFile(path.join(dir, entry.name), depth + 1)) {
|
|
383
|
+
return true;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
} catch {
|
|
387
|
+
return false;
|
|
388
|
+
}
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
268
391
|
async function loadTsConfig(filePath) {
|
|
269
392
|
const { transformSync: transformSync2 } = await import("oxc-transform");
|
|
270
393
|
const code = fs.readFileSync(filePath, "utf-8");
|
|
@@ -311,7 +434,7 @@ async function resolveConfig(inlineConfig = {}, command) {
|
|
|
311
434
|
base: merged.base ?? defaults.base,
|
|
312
435
|
mode,
|
|
313
436
|
target: merged.target ?? defaults.target,
|
|
314
|
-
framework: merged.framework ?? defaults.framework,
|
|
437
|
+
framework: (merged.framework ?? defaults.framework) === "auto" ? detectFramework(root) : merged.framework,
|
|
315
438
|
command,
|
|
316
439
|
resolve: {
|
|
317
440
|
// tsconfig paths 优先级最低:tsconfig < defaults < user config
|
|
@@ -358,7 +481,12 @@ async function resolveConfig(inlineConfig = {}, command) {
|
|
|
358
481
|
if (envOptions.build) Object.assign(resolved.build, envOptions.build);
|
|
359
482
|
resolved.environments.client = {
|
|
360
483
|
consumer,
|
|
361
|
-
entry:
|
|
484
|
+
entry: normalizeEnvironmentEntries(envOptions.entry, root),
|
|
485
|
+
html: path.resolve(
|
|
486
|
+
root,
|
|
487
|
+
envOptions.html ?? (resolved.target === "electron" ? resolved.electron.renderer : "index.html")
|
|
488
|
+
),
|
|
489
|
+
driver: envOptions.driver,
|
|
362
490
|
// 同引用 —— 精确镜像(assertClientEnvironmentMirror 校验)
|
|
363
491
|
resolve: resolved.resolve,
|
|
364
492
|
build: resolved.build
|
|
@@ -367,7 +495,9 @@ async function resolveConfig(inlineConfig = {}, command) {
|
|
|
367
495
|
}
|
|
368
496
|
resolved.environments[name] = {
|
|
369
497
|
consumer,
|
|
370
|
-
entry: (
|
|
498
|
+
entry: normalizeEnvironmentEntries(envOptions.entry, root),
|
|
499
|
+
html: envOptions.consumer === "client" && envOptions.html ? path.resolve(root, envOptions.html) : void 0,
|
|
500
|
+
driver: envOptions.driver,
|
|
371
501
|
resolve: {
|
|
372
502
|
alias: { ...resolved.resolve.alias, ...envOptions.resolve?.alias },
|
|
373
503
|
extensions: envOptions.resolve?.extensions ?? [...resolved.resolve.extensions],
|
|
@@ -386,12 +516,13 @@ async function resolveConfig(inlineConfig = {}, command) {
|
|
|
386
516
|
};
|
|
387
517
|
}
|
|
388
518
|
assertClientEnvironmentMirror(resolved);
|
|
389
|
-
const filteredPlugins = rawPlugins.filter((p) => {
|
|
519
|
+
const filteredPlugins = orderPlugins(rawPlugins.filter((p) => {
|
|
390
520
|
if (!p.apply) return true;
|
|
391
521
|
if (typeof p.apply === "function") return p.apply(resolved, env);
|
|
392
522
|
return p.apply === command;
|
|
393
|
-
});
|
|
523
|
+
}));
|
|
394
524
|
resolved.plugins = filteredPlugins;
|
|
525
|
+
await setupPluginApi(resolved, filteredPlugins);
|
|
395
526
|
if (resolved.target === "electron") {
|
|
396
527
|
const autoExternal = detectNativeDeps(root);
|
|
397
528
|
if (autoExternal.length > 0) {
|
|
@@ -407,6 +538,10 @@ async function resolveConfig(inlineConfig = {}, command) {
|
|
|
407
538
|
}
|
|
408
539
|
return resolved;
|
|
409
540
|
}
|
|
541
|
+
function normalizeEnvironmentEntries(entry, root) {
|
|
542
|
+
const entries = Array.isArray(entry) ? entry : entry ? [entry] : [];
|
|
543
|
+
return entries.map((item) => path.resolve(root, item));
|
|
544
|
+
}
|
|
410
545
|
function detectNativeDeps(root) {
|
|
411
546
|
const result = /* @__PURE__ */ new Set();
|
|
412
547
|
const pkgJsonPath = path.resolve(root, "package.json");
|
|
@@ -524,6 +659,7 @@ var init_config = __esm({
|
|
|
524
659
|
"use strict";
|
|
525
660
|
init_defaults();
|
|
526
661
|
init_logger();
|
|
662
|
+
init_plugin_api();
|
|
527
663
|
CONFIG_FILES = [
|
|
528
664
|
"nasti.config.ts",
|
|
529
665
|
"nasti.config.js",
|
|
@@ -1918,7 +2054,8 @@ function transformCode(filename, code, options = {}) {
|
|
|
1918
2054
|
importSource: options.jsxImportSource ?? "react",
|
|
1919
2055
|
refresh: options.reactRefresh ?? false
|
|
1920
2056
|
} : void 0,
|
|
1921
|
-
sourcemap: options.sourcemap ?? true
|
|
2057
|
+
sourcemap: options.sourcemap ?? true,
|
|
2058
|
+
target: options.target
|
|
1922
2059
|
});
|
|
1923
2060
|
if (result.errors && result.errors.length > 0) {
|
|
1924
2061
|
const msg = result.errors.map((e) => e.message ?? String(e)).join("\n");
|
|
@@ -2122,7 +2259,7 @@ function htmlPlugin(config) {
|
|
|
2122
2259
|
transformIndexHtml(html) {
|
|
2123
2260
|
const tags = [];
|
|
2124
2261
|
if (config.command === "serve") {
|
|
2125
|
-
const isReactLike = config.framework === "react"
|
|
2262
|
+
const isReactLike = config.framework === "react";
|
|
2126
2263
|
if (isReactLike) {
|
|
2127
2264
|
tags.push({
|
|
2128
2265
|
tag: "script",
|
|
@@ -2176,8 +2313,8 @@ function serializeTag(tag) {
|
|
|
2176
2313
|
}
|
|
2177
2314
|
return ` <${tag.tag}${attrs}>${children}</${tag.tag}>`;
|
|
2178
2315
|
}
|
|
2179
|
-
async function readHtmlFile(root) {
|
|
2180
|
-
const htmlPath = path6.resolve(root,
|
|
2316
|
+
async function readHtmlFile(root, htmlFile = "index.html") {
|
|
2317
|
+
const htmlPath = path6.isAbsolute(htmlFile) ? htmlFile : path6.resolve(root, htmlFile);
|
|
2181
2318
|
if (!fs4.existsSync(htmlPath)) return null;
|
|
2182
2319
|
return fs4.readFileSync(htmlPath, "utf-8");
|
|
2183
2320
|
}
|
|
@@ -2223,21 +2360,11 @@ var init_builtins = __esm({
|
|
|
2223
2360
|
});
|
|
2224
2361
|
|
|
2225
2362
|
// src/core/plugin-container.ts
|
|
2226
|
-
function sortPlugins(plugins) {
|
|
2227
|
-
const pre = [];
|
|
2228
|
-
const normal = [];
|
|
2229
|
-
const post = [];
|
|
2230
|
-
for (const plugin of plugins) {
|
|
2231
|
-
if (plugin.enforce === "pre") pre.push(plugin);
|
|
2232
|
-
else if (plugin.enforce === "post") post.push(plugin);
|
|
2233
|
-
else normal.push(plugin);
|
|
2234
|
-
}
|
|
2235
|
-
return [...pre, ...normal, ...post];
|
|
2236
|
-
}
|
|
2237
2363
|
var PluginContainer;
|
|
2238
2364
|
var init_plugin_container = __esm({
|
|
2239
2365
|
"src/core/plugin-container.ts"() {
|
|
2240
2366
|
"use strict";
|
|
2367
|
+
init_plugin_api();
|
|
2241
2368
|
PluginContainer = class {
|
|
2242
2369
|
plugins;
|
|
2243
2370
|
config;
|
|
@@ -2248,7 +2375,7 @@ var init_plugin_container = __esm({
|
|
|
2248
2375
|
constructor(config, environment) {
|
|
2249
2376
|
this.config = config;
|
|
2250
2377
|
this.environment = environment;
|
|
2251
|
-
this.plugins =
|
|
2378
|
+
this.plugins = orderPlugins(config.plugins);
|
|
2252
2379
|
this.ctx = this.createContext();
|
|
2253
2380
|
}
|
|
2254
2381
|
createContext() {
|
|
@@ -2544,6 +2671,7 @@ var init_environment = __esm({
|
|
|
2544
2671
|
init_module_graph();
|
|
2545
2672
|
init_hot_channel();
|
|
2546
2673
|
init_debug();
|
|
2674
|
+
init_plugin_api();
|
|
2547
2675
|
debug2 = createDebugger("nasti:environment");
|
|
2548
2676
|
NastiEnvironment = class {
|
|
2549
2677
|
name;
|
|
@@ -2552,6 +2680,7 @@ var init_environment = __esm({
|
|
|
2552
2680
|
config;
|
|
2553
2681
|
options;
|
|
2554
2682
|
hot;
|
|
2683
|
+
driver;
|
|
2555
2684
|
/** applyToEnvironment 过滤后的插件(init() 后可用) */
|
|
2556
2685
|
plugins = [];
|
|
2557
2686
|
/** per-env 插件容器(init() 后可用;dev 管线使用) */
|
|
@@ -2559,6 +2688,7 @@ var init_environment = __esm({
|
|
|
2559
2688
|
/** per-env 模块图(dev 管线使用) */
|
|
2560
2689
|
moduleGraph;
|
|
2561
2690
|
candidatePlugins;
|
|
2691
|
+
pluginApi;
|
|
2562
2692
|
initialized = false;
|
|
2563
2693
|
constructor(name, config, init = {}) {
|
|
2564
2694
|
const options = config.environments[name];
|
|
@@ -2575,6 +2705,7 @@ var init_environment = __esm({
|
|
|
2575
2705
|
this.hot = init.hot ?? createNoopHotChannel();
|
|
2576
2706
|
this.moduleGraph = new ModuleGraph();
|
|
2577
2707
|
this.candidatePlugins = init.plugins ?? config.plugins;
|
|
2708
|
+
this.pluginApi = init.pluginApi ?? getPluginApi(config);
|
|
2578
2709
|
}
|
|
2579
2710
|
/** 过滤插件并建 per-env PluginContainer */
|
|
2580
2711
|
async init() {
|
|
@@ -2585,10 +2716,41 @@ var init_environment = __esm({
|
|
|
2585
2716
|
{ ...this.config, plugins: this.plugins },
|
|
2586
2717
|
this
|
|
2587
2718
|
);
|
|
2719
|
+
if (this.options.driver) {
|
|
2720
|
+
const claimed = [];
|
|
2721
|
+
for (const plugin of this.plugins) {
|
|
2722
|
+
const driver = await plugin.createEnvironmentDriver?.(this, this.pluginApi);
|
|
2723
|
+
if (driver) claimed.push({ plugin, driver });
|
|
2724
|
+
}
|
|
2725
|
+
if (claimed.length === 0) {
|
|
2726
|
+
throw new Error(
|
|
2727
|
+
`[nasti] environment "${this.name}" requested driver "${this.options.driver}", but no plugin provided it`
|
|
2728
|
+
);
|
|
2729
|
+
}
|
|
2730
|
+
if (claimed.length > 1) {
|
|
2731
|
+
throw new Error(
|
|
2732
|
+
`[nasti] environment "${this.name}" was claimed by multiple drivers: ` + claimed.map(({ plugin, driver }) => `${plugin.name} (${driver.name})`).join(", ")
|
|
2733
|
+
);
|
|
2734
|
+
}
|
|
2735
|
+
this.driver = claimed[0].driver;
|
|
2736
|
+
debug2?.(`env "${this.name}" uses driver "${this.driver.name}"`);
|
|
2737
|
+
}
|
|
2588
2738
|
debug2?.(`env "${this.name}" initialized (${this.plugins.length} plugins)`);
|
|
2589
2739
|
}
|
|
2740
|
+
getDriverContext() {
|
|
2741
|
+
return {
|
|
2742
|
+
environment: this,
|
|
2743
|
+
config: this.config,
|
|
2744
|
+
api: this.pluginApi,
|
|
2745
|
+
logger: this.config.logger
|
|
2746
|
+
};
|
|
2747
|
+
}
|
|
2590
2748
|
async close() {
|
|
2591
|
-
|
|
2749
|
+
try {
|
|
2750
|
+
await this.driver?.close?.(this.getDriverContext());
|
|
2751
|
+
} finally {
|
|
2752
|
+
await this.hot.close?.();
|
|
2753
|
+
}
|
|
2592
2754
|
}
|
|
2593
2755
|
};
|
|
2594
2756
|
}
|
|
@@ -2752,6 +2914,7 @@ var build_exports = {};
|
|
|
2752
2914
|
__export(build_exports, {
|
|
2753
2915
|
build: () => build,
|
|
2754
2916
|
getRolldownOptions: () => getRolldownOptions,
|
|
2917
|
+
replaceEntryScript: () => replaceEntryScript,
|
|
2755
2918
|
resolveClientEntries: () => resolveClientEntries,
|
|
2756
2919
|
toRolldownPlugins: () => toRolldownPlugins
|
|
2757
2920
|
});
|
|
@@ -2835,13 +2998,20 @@ function toRolldownPlugins(plugins) {
|
|
|
2835
2998
|
}));
|
|
2836
2999
|
}
|
|
2837
3000
|
function resolveClientEntries(config, html) {
|
|
3001
|
+
const configuredEntries = config.environments.client?.entry ?? [];
|
|
3002
|
+
if (configuredEntries.length > 0) return configuredEntries;
|
|
2838
3003
|
const entryPoints = [];
|
|
3004
|
+
const htmlFile = config.environments.client?.html;
|
|
3005
|
+
const htmlDir = htmlFile ? path9.dirname(htmlFile) : config.root;
|
|
2839
3006
|
if (html) {
|
|
2840
3007
|
const scriptMatches = html.matchAll(/<script[^>]+src=["']([^"']+)["'][^>]*>/gi);
|
|
2841
3008
|
for (const match of scriptMatches) {
|
|
2842
3009
|
const src = match[1];
|
|
2843
3010
|
if (src && !src.startsWith("http")) {
|
|
2844
|
-
|
|
3011
|
+
const cleanSrc = src.split(/[?#]/, 1)[0];
|
|
3012
|
+
entryPoints.push(
|
|
3013
|
+
cleanSrc.startsWith("/") ? path9.resolve(config.root, cleanSrc.replace(/^\//, "")) : path9.resolve(htmlDir, cleanSrc)
|
|
3014
|
+
);
|
|
2845
3015
|
}
|
|
2846
3016
|
}
|
|
2847
3017
|
}
|
|
@@ -2877,21 +3047,55 @@ async function build(inlineConfig = {}) {
|
|
|
2877
3047
|
const startTime = performance.now();
|
|
2878
3048
|
logger.info(
|
|
2879
3049
|
pc4.cyan(`
|
|
2880
|
-
nasti v${"2.
|
|
3050
|
+
nasti v${"2.3.1"} `) + pc4.green(`building for ${config.mode}...`)
|
|
2881
3051
|
);
|
|
2882
3052
|
debug4?.(`root: ${config.root}`);
|
|
2883
3053
|
const buildableNames = Object.keys(config.environments).filter(
|
|
2884
|
-
(name) => name === "client" || config.environments[name].entry.length > 0
|
|
3054
|
+
(name) => name === "client" || config.environments[name].entry.length > 0 || !!config.environments[name].driver
|
|
2885
3055
|
);
|
|
2886
3056
|
buildableNames.sort((a, b) => a === "client" ? -1 : b === "client" ? 1 : 0);
|
|
2887
3057
|
const environments = {};
|
|
3058
|
+
const environmentResults = {};
|
|
3059
|
+
const initializedEnvironments = [];
|
|
2888
3060
|
let clientOutput = [];
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
3061
|
+
let buildFailed = false;
|
|
3062
|
+
try {
|
|
3063
|
+
for (const name of buildableNames) {
|
|
3064
|
+
const built = name === "client" ? await buildClientEnvironment(config) : await buildServerEnvironment(config, name);
|
|
3065
|
+
initializedEnvironments.push(built.environment);
|
|
3066
|
+
environments[name] = built.result.output;
|
|
3067
|
+
environmentResults[name] = built.result;
|
|
3068
|
+
if (name === "client") clientOutput = built.result.output;
|
|
3069
|
+
if (buildableNames.length > 1) {
|
|
3070
|
+
debug4?.(`environment "${name}" built (${built.result.output.length} files)`);
|
|
3071
|
+
}
|
|
3072
|
+
}
|
|
3073
|
+
const pluginApi = getPluginApi(config);
|
|
3074
|
+
for (const plugin of config.plugins) {
|
|
3075
|
+
await plugin.afterBuildApp?.(environmentResults, pluginApi);
|
|
3076
|
+
}
|
|
3077
|
+
} catch (error) {
|
|
3078
|
+
buildFailed = true;
|
|
3079
|
+
throw error;
|
|
3080
|
+
} finally {
|
|
3081
|
+
let closeFailed = false;
|
|
3082
|
+
let firstCloseError;
|
|
3083
|
+
for (const environment of [...initializedEnvironments].reverse()) {
|
|
3084
|
+
try {
|
|
3085
|
+
await environment.close();
|
|
3086
|
+
} catch (error) {
|
|
3087
|
+
if (!closeFailed) {
|
|
3088
|
+
closeFailed = true;
|
|
3089
|
+
firstCloseError = error;
|
|
3090
|
+
}
|
|
3091
|
+
const closeError = error instanceof Error ? error : new Error(String(error));
|
|
3092
|
+
logger.error(`[nasti] failed to close environment "${environment.name}"`, {
|
|
3093
|
+
error: closeError
|
|
3094
|
+
});
|
|
3095
|
+
}
|
|
3096
|
+
}
|
|
3097
|
+
if (closeFailed && !buildFailed) {
|
|
3098
|
+
throw firstCloseError;
|
|
2895
3099
|
}
|
|
2896
3100
|
}
|
|
2897
3101
|
const elapsed = ((performance.now() - startTime) / 1e3).toFixed(2);
|
|
@@ -2904,83 +3108,130 @@ nasti v${"2.2.0"} `) + pc4.green(`building for ${config.mode}...`)
|
|
|
2904
3108
|
const envSuffix = buildableNames.length > 1 ? ` (${buildableNames.join(" + ")})` : "";
|
|
2905
3109
|
logger.info(pc4.green(`\u2713 built in ${elapsed}s`) + pc4.dim(envSuffix));
|
|
2906
3110
|
logger.info(pc4.dim(` ${fileCount} files, ${displaySize(totalSize)} total \u2192 ${config.build.outDir}/`));
|
|
2907
|
-
return { output: clientOutput, environments };
|
|
3111
|
+
return { output: clientOutput, environments, environmentResults };
|
|
2908
3112
|
}
|
|
2909
3113
|
async function buildClientEnvironment(config) {
|
|
2910
3114
|
const logger = config.logger;
|
|
2911
3115
|
const outDir = path9.resolve(config.root, config.build.outDir);
|
|
2912
|
-
if (config.build.emptyOutDir && fs6.existsSync(outDir)) {
|
|
2913
|
-
fs6.rmSync(outDir, { recursive: true, force: true });
|
|
2914
|
-
}
|
|
2915
|
-
fs6.mkdirSync(outDir, { recursive: true });
|
|
2916
|
-
const html = await readHtmlFile(config.root);
|
|
2917
|
-
const entryPoints = resolveClientEntries(config, html);
|
|
2918
|
-
if (entryPoints.length === 0) {
|
|
2919
|
-
throw new Error("No entry point found. Add a <script> tag to index.html or create src/main.ts");
|
|
2920
|
-
}
|
|
2921
3116
|
const cssEngine = createCssEngine();
|
|
2922
3117
|
const pluginList = resolvePluginList(config, config.plugins, { cssEngine });
|
|
2923
|
-
const clientEnv = new NastiEnvironment("client",
|
|
3118
|
+
const clientEnv = new NastiEnvironment("client", config, {
|
|
2924
3119
|
mode: "build",
|
|
2925
|
-
plugins: pluginList
|
|
3120
|
+
plugins: pluginList,
|
|
3121
|
+
pluginApi: getPluginApi(config)
|
|
2926
3122
|
});
|
|
2927
3123
|
await clientEnv.init();
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
...nativeReporter ? [nativeReporter] : []
|
|
2934
|
-
];
|
|
2935
|
-
const { inputOptions, outputOptions } = getRolldownOptions(clientEnv, entryPoints, rolldownPlugins);
|
|
2936
|
-
const bundle2 = await rolldown(inputOptions);
|
|
2937
|
-
const { output } = await bundle2.write(outputOptions);
|
|
2938
|
-
await bundle2.close();
|
|
2939
|
-
if (html) {
|
|
2940
|
-
let processedHtml = html;
|
|
2941
|
-
const htmlPlugins = [...allPlugins.filter((p) => p.transformIndexHtml), htmlPlugin(config)];
|
|
2942
|
-
for (const p of htmlPlugins) {
|
|
2943
|
-
const result = await p.transformIndexHtml(processedHtml);
|
|
2944
|
-
if (typeof result === "string") {
|
|
2945
|
-
processedHtml = result;
|
|
2946
|
-
} else if (result && "html" in result) {
|
|
2947
|
-
processedHtml = processHtml(result.html, result.tags);
|
|
2948
|
-
} else if (Array.isArray(result)) {
|
|
2949
|
-
processedHtml = processHtml(processedHtml, result);
|
|
2950
|
-
}
|
|
2951
|
-
}
|
|
2952
|
-
processedHtml = injectCssLinks(processedHtml, cssEngine, config);
|
|
2953
|
-
for (const chunk of output) {
|
|
2954
|
-
if (chunk.type === "chunk" && chunk.isEntry && chunk.facadeModuleId) {
|
|
2955
|
-
const originalEntry = path9.relative(config.root, chunk.facadeModuleId);
|
|
2956
|
-
processedHtml = processedHtml.replace(
|
|
2957
|
-
new RegExp(`(src=["'])/?(${escapeRegExp(originalEntry)})(["'])`, "g"),
|
|
2958
|
-
`$1${config.base}${chunk.fileName}$3`
|
|
3124
|
+
try {
|
|
3125
|
+
if (clientEnv.driver) {
|
|
3126
|
+
if (!clientEnv.driver.build) {
|
|
3127
|
+
throw new Error(
|
|
3128
|
+
`[nasti] environment "client" driver "${clientEnv.driver.name}" does not implement build()`
|
|
2959
3129
|
);
|
|
2960
3130
|
}
|
|
3131
|
+
const result = await clientEnv.driver.build(clientEnv.getDriverContext());
|
|
3132
|
+
return { environment: clientEnv, result };
|
|
2961
3133
|
}
|
|
2962
|
-
fs6.
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
3134
|
+
if (config.build.emptyOutDir && fs6.existsSync(outDir)) {
|
|
3135
|
+
fs6.rmSync(outDir, { recursive: true, force: true });
|
|
3136
|
+
}
|
|
3137
|
+
fs6.mkdirSync(outDir, { recursive: true });
|
|
3138
|
+
const htmlFile = config.environments.client.html ?? path9.resolve(config.root, "index.html");
|
|
3139
|
+
const html = await readHtmlFile(config.root, htmlFile);
|
|
3140
|
+
const entryPoints = resolveClientEntries(config, html);
|
|
3141
|
+
if (entryPoints.length === 0) {
|
|
3142
|
+
throw new Error("No entry point found. Add a <script> tag to index.html or create src/main.ts");
|
|
3143
|
+
}
|
|
3144
|
+
const allPlugins = clientEnv.plugins;
|
|
3145
|
+
const nativeReporter = config.logLevel === "silent" ? null : await tryNativeReporterPlugin(config, logger);
|
|
3146
|
+
const rolldownPlugins = [
|
|
3147
|
+
createOxcTransformPlugin(config, clientEnv),
|
|
3148
|
+
...toRolldownPlugins(allPlugins),
|
|
3149
|
+
...nativeReporter ? [nativeReporter] : []
|
|
3150
|
+
];
|
|
3151
|
+
const { inputOptions, outputOptions } = getRolldownOptions(
|
|
3152
|
+
clientEnv,
|
|
3153
|
+
entryPoints,
|
|
3154
|
+
rolldownPlugins
|
|
3155
|
+
);
|
|
3156
|
+
const bundle2 = await rolldown(inputOptions);
|
|
3157
|
+
const { output } = await bundle2.write(outputOptions);
|
|
3158
|
+
await bundle2.close();
|
|
3159
|
+
if (html) {
|
|
3160
|
+
let processedHtml = html;
|
|
3161
|
+
const htmlPlugins = [...allPlugins.filter((p) => p.transformIndexHtml), htmlPlugin(config)];
|
|
3162
|
+
for (const p of htmlPlugins) {
|
|
3163
|
+
const result = await p.transformIndexHtml(processedHtml);
|
|
3164
|
+
if (typeof result === "string") {
|
|
3165
|
+
processedHtml = result;
|
|
3166
|
+
} else if (result && "html" in result) {
|
|
3167
|
+
processedHtml = processHtml(result.html, result.tags);
|
|
3168
|
+
} else if (Array.isArray(result)) {
|
|
3169
|
+
processedHtml = processHtml(processedHtml, result);
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
3172
|
+
processedHtml = injectCssLinks(processedHtml, cssEngine, config);
|
|
3173
|
+
for (const chunk of output) {
|
|
3174
|
+
if (chunk.type === "chunk" && chunk.isEntry && chunk.facadeModuleId) {
|
|
3175
|
+
processedHtml = replaceEntryScript(
|
|
3176
|
+
processedHtml,
|
|
3177
|
+
chunk.facadeModuleId,
|
|
3178
|
+
chunk.fileName,
|
|
3179
|
+
config,
|
|
3180
|
+
htmlFile,
|
|
3181
|
+
config.base
|
|
3182
|
+
);
|
|
3183
|
+
}
|
|
3184
|
+
}
|
|
3185
|
+
fs6.writeFileSync(path9.resolve(outDir, "index.html"), processedHtml);
|
|
3186
|
+
}
|
|
3187
|
+
if (!nativeReporter && config.logLevel !== "silent") {
|
|
3188
|
+
reportBuildOutput(output, config, logger);
|
|
3189
|
+
}
|
|
3190
|
+
warnLargeChunks(output, config, logger);
|
|
3191
|
+
return { environment: clientEnv, result: { output } };
|
|
3192
|
+
} catch (error) {
|
|
3193
|
+
try {
|
|
3194
|
+
await clientEnv.close();
|
|
3195
|
+
} catch (closeError) {
|
|
3196
|
+
const normalized = closeError instanceof Error ? closeError : new Error(String(closeError));
|
|
3197
|
+
logger.error("[nasti] failed to close client environment after build failure", {
|
|
3198
|
+
error: normalized
|
|
3199
|
+
});
|
|
3200
|
+
}
|
|
3201
|
+
throw error;
|
|
2966
3202
|
}
|
|
2967
|
-
warnLargeChunks(output, config, logger);
|
|
2968
|
-
return output;
|
|
2969
3203
|
}
|
|
2970
3204
|
async function buildServerEnvironment(config, name) {
|
|
2971
3205
|
const envOptions = config.environments[name];
|
|
2972
3206
|
const logger = config.logger;
|
|
3207
|
+
const pluginList = resolvePluginList(config, config.plugins, { consumer: envOptions.consumer });
|
|
3208
|
+
const environment = new NastiEnvironment(name, config, {
|
|
3209
|
+
mode: "build",
|
|
3210
|
+
plugins: pluginList,
|
|
3211
|
+
pluginApi: getPluginApi(config)
|
|
3212
|
+
});
|
|
3213
|
+
await environment.init();
|
|
3214
|
+
if (environment.driver) {
|
|
3215
|
+
if (!environment.driver.build) {
|
|
3216
|
+
await environment.close();
|
|
3217
|
+
throw new Error(
|
|
3218
|
+
`[nasti] environment "${name}" driver "${environment.driver.name}" does not implement build()`
|
|
3219
|
+
);
|
|
3220
|
+
}
|
|
3221
|
+
try {
|
|
3222
|
+
const result = await environment.driver.build(environment.getDriverContext());
|
|
3223
|
+
return { environment, result };
|
|
3224
|
+
} catch (error) {
|
|
3225
|
+
await environment.close();
|
|
3226
|
+
throw error;
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
2973
3229
|
for (const entry of envOptions.entry) {
|
|
2974
3230
|
if (!fs6.existsSync(entry)) {
|
|
3231
|
+
await environment.close();
|
|
2975
3232
|
throw new Error(`[nasti] environment "${name}" entry not found: ${entry}`);
|
|
2976
3233
|
}
|
|
2977
3234
|
}
|
|
2978
|
-
const pluginList = resolvePluginList(config, config.plugins, { consumer: envOptions.consumer });
|
|
2979
|
-
const environment = new NastiEnvironment(name, { ...config, plugins: pluginList }, {
|
|
2980
|
-
mode: "build",
|
|
2981
|
-
plugins: pluginList
|
|
2982
|
-
});
|
|
2983
|
-
await environment.init();
|
|
2984
3235
|
const rolldownPlugins = [
|
|
2985
3236
|
createOxcTransformPlugin(config, environment),
|
|
2986
3237
|
...toRolldownPlugins(environment.plugins)
|
|
@@ -3000,7 +3251,7 @@ async function buildServerEnvironment(config, name) {
|
|
|
3000
3251
|
logger.info(
|
|
3001
3252
|
pc4.dim(` [${name}] `) + output.map((o) => path9.join(envOptions.build.outDir, o.fileName)).join(pc4.dim(", "))
|
|
3002
3253
|
);
|
|
3003
|
-
return output;
|
|
3254
|
+
return { environment, result: { output } };
|
|
3004
3255
|
}
|
|
3005
3256
|
function injectCssLinks(html, cssEngine, config) {
|
|
3006
3257
|
const cssLinkTags = [];
|
|
@@ -3026,6 +3277,25 @@ function injectCssLinks(html, cssEngine, config) {
|
|
|
3026
3277
|
function escapeRegExp(string) {
|
|
3027
3278
|
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3028
3279
|
}
|
|
3280
|
+
function replaceEntryScript(html, facadeModuleId, fileName, config, htmlFile, urlPrefix) {
|
|
3281
|
+
const rootRelative = path9.relative(config.root, facadeModuleId).split(path9.sep).join("/");
|
|
3282
|
+
const resolvedHtmlFile = path9.resolve(config.root, htmlFile);
|
|
3283
|
+
const htmlRelative = path9.relative(path9.dirname(resolvedHtmlFile), facadeModuleId).split(path9.sep).join("/");
|
|
3284
|
+
const candidates = /* @__PURE__ */ new Set([
|
|
3285
|
+
rootRelative,
|
|
3286
|
+
`/${rootRelative}`,
|
|
3287
|
+
htmlRelative,
|
|
3288
|
+
`./${htmlRelative}`
|
|
3289
|
+
]);
|
|
3290
|
+
let processed = html;
|
|
3291
|
+
for (const candidate of candidates) {
|
|
3292
|
+
processed = processed.replace(
|
|
3293
|
+
new RegExp(`(src=["'])${escapeRegExp(candidate)}([?#][^"']*)?(["'])`, "g"),
|
|
3294
|
+
`$1${urlPrefix}${fileName}$3`
|
|
3295
|
+
);
|
|
3296
|
+
}
|
|
3297
|
+
return processed;
|
|
3298
|
+
}
|
|
3029
3299
|
var debug4, NODE_BUILTINS;
|
|
3030
3300
|
var init_build = __esm({
|
|
3031
3301
|
"src/build/index.ts"() {
|
|
@@ -3039,6 +3309,7 @@ var init_build = __esm({
|
|
|
3039
3309
|
init_env();
|
|
3040
3310
|
init_reporter();
|
|
3041
3311
|
init_debug();
|
|
3312
|
+
init_plugin_api();
|
|
3042
3313
|
debug4 = createDebugger("nasti:build");
|
|
3043
3314
|
NODE_BUILTINS = /* @__PURE__ */ new Set([...builtinModules, ...builtinModules.map((m) => `node:${m}`)]);
|
|
3044
3315
|
}
|
|
@@ -3205,7 +3476,10 @@ function transformMiddleware(ctx) {
|
|
|
3205
3476
|
return;
|
|
3206
3477
|
}
|
|
3207
3478
|
if (url === "/" || url.endsWith(".html")) {
|
|
3208
|
-
const html = await readHtmlFile(
|
|
3479
|
+
const html = await readHtmlFile(
|
|
3480
|
+
ctx.config.root,
|
|
3481
|
+
ctx.config.environments.client?.html
|
|
3482
|
+
);
|
|
3209
3483
|
if (html) {
|
|
3210
3484
|
let processedHtml = html;
|
|
3211
3485
|
for (const plugin of ctx.config.plugins) {
|
|
@@ -4237,7 +4511,7 @@ async function createBundledDevServer(opts) {
|
|
|
4237
4511
|
`[nasti] experimental.bundledDev requires rolldown's experimental dev() API (locked to the installed rc; got: ${err.message}). Remove --bundle / experimental.bundledDev to use the default unbundled dev server.`
|
|
4238
4512
|
);
|
|
4239
4513
|
}
|
|
4240
|
-
const html = await readHtmlFile(config.root);
|
|
4514
|
+
const html = await readHtmlFile(config.root, config.environments.client?.html);
|
|
4241
4515
|
const entryPoints = resolveClientEntries(config, html);
|
|
4242
4516
|
if (entryPoints.length === 0) {
|
|
4243
4517
|
throw new Error("No entry point found. Add a <script> tag to index.html or create src/main.ts");
|
|
@@ -4443,7 +4717,7 @@ async function createBundledDevServer(opts) {
|
|
|
4443
4717
|
return;
|
|
4444
4718
|
}
|
|
4445
4719
|
if (pathname === "/" || pathname.endsWith(".html")) {
|
|
4446
|
-
const rawHtml = await readHtmlFile(config.root);
|
|
4720
|
+
const rawHtml = await readHtmlFile(config.root, config.environments.client?.html);
|
|
4447
4721
|
if (rawHtml) {
|
|
4448
4722
|
res.setHeader("Content-Type", "text/html");
|
|
4449
4723
|
res.setHeader("Cache-Control", "no-store");
|
|
@@ -4527,10 +4801,13 @@ async function renderBundledIndexHtml(html, config, entryFileNames) {
|
|
|
4527
4801
|
}
|
|
4528
4802
|
}
|
|
4529
4803
|
for (const [facadeModuleId, fileName] of entryFileNames) {
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4804
|
+
processed = replaceEntryScript(
|
|
4805
|
+
processed,
|
|
4806
|
+
facadeModuleId,
|
|
4807
|
+
fileName,
|
|
4808
|
+
config,
|
|
4809
|
+
config.environments.client?.html ?? "index.html",
|
|
4810
|
+
"/"
|
|
4534
4811
|
);
|
|
4535
4812
|
}
|
|
4536
4813
|
return processed;
|
|
@@ -4674,10 +4951,12 @@ async function createServer(inlineConfig = {}) {
|
|
|
4674
4951
|
const app = connect();
|
|
4675
4952
|
const httpServer = http.createServer(app);
|
|
4676
4953
|
const ws = createWebSocketServer(httpServer);
|
|
4677
|
-
const
|
|
4954
|
+
const pluginApi = getPluginApi(config);
|
|
4955
|
+
const clientEnv = new NastiEnvironment("client", config, {
|
|
4678
4956
|
hot: createWsHotChannel(ws),
|
|
4679
4957
|
mode: "dev",
|
|
4680
|
-
plugins: allPlugins
|
|
4958
|
+
plugins: allPlugins,
|
|
4959
|
+
pluginApi
|
|
4681
4960
|
});
|
|
4682
4961
|
await clientEnv.init();
|
|
4683
4962
|
const environments = { client: clientEnv };
|
|
@@ -4685,11 +4964,15 @@ async function createServer(inlineConfig = {}) {
|
|
|
4685
4964
|
if (name === "client") continue;
|
|
4686
4965
|
const consumer = config.environments[name].consumer;
|
|
4687
4966
|
const envPlugins = resolvePluginList(config, config.plugins, { consumer });
|
|
4688
|
-
environments[name] = new NastiEnvironment(name,
|
|
4967
|
+
environments[name] = new NastiEnvironment(name, config, {
|
|
4689
4968
|
mode: "dev",
|
|
4690
|
-
plugins: envPlugins
|
|
4969
|
+
plugins: envPlugins,
|
|
4970
|
+
pluginApi
|
|
4691
4971
|
});
|
|
4692
4972
|
}
|
|
4973
|
+
for (const [name, environment] of Object.entries(environments)) {
|
|
4974
|
+
if (name !== "client" && environment.options.driver) await environment.init();
|
|
4975
|
+
}
|
|
4693
4976
|
let ssrRunner = null;
|
|
4694
4977
|
async function getSsrRunner() {
|
|
4695
4978
|
if (ssrRunner) return ssrRunner;
|
|
@@ -4714,14 +4997,6 @@ async function createServer(inlineConfig = {}) {
|
|
|
4714
4997
|
});
|
|
4715
4998
|
app.use(bundledServer.middleware);
|
|
4716
4999
|
}
|
|
4717
|
-
app.use(transformMiddleware({
|
|
4718
|
-
config: configWithPlugins,
|
|
4719
|
-
pluginContainer,
|
|
4720
|
-
moduleGraph
|
|
4721
|
-
}));
|
|
4722
|
-
const publicDir = path15.resolve(config.root, "public");
|
|
4723
|
-
app.use(sirv(publicDir, { dev: true, etag: true }));
|
|
4724
|
-
app.use(sirv(config.root, { dev: true, etag: true }));
|
|
4725
5000
|
const ignoredSegments = /* @__PURE__ */ new Set(["node_modules", ".git", ".nasti"]);
|
|
4726
5001
|
const outDirAbs = path15.resolve(config.root, config.build.outDir);
|
|
4727
5002
|
const watcher = watch(config.root, {
|
|
@@ -4738,13 +5013,72 @@ async function createServer(inlineConfig = {}) {
|
|
|
4738
5013
|
ignoreInitial: true
|
|
4739
5014
|
});
|
|
4740
5015
|
let server;
|
|
5016
|
+
const environmentServices = {};
|
|
5017
|
+
let environmentDriversStarted = false;
|
|
5018
|
+
const logCloseError = (target, error) => {
|
|
5019
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
5020
|
+
logger.error(`[nasti] failed to close ${target}`, { error: normalized });
|
|
5021
|
+
};
|
|
5022
|
+
const startEnvironmentDrivers = async () => {
|
|
5023
|
+
if (environmentDriversStarted) return;
|
|
5024
|
+
environmentDriversStarted = true;
|
|
5025
|
+
const started = [];
|
|
5026
|
+
const attempted = [];
|
|
5027
|
+
try {
|
|
5028
|
+
for (const [name, environment] of Object.entries(environments)) {
|
|
5029
|
+
if (!environment.driver?.serve) continue;
|
|
5030
|
+
attempted.push(environment);
|
|
5031
|
+
const result = await environment.driver.serve({
|
|
5032
|
+
...environment.getDriverContext(),
|
|
5033
|
+
server
|
|
5034
|
+
});
|
|
5035
|
+
started.push({ name, environment, service: result ?? {} });
|
|
5036
|
+
}
|
|
5037
|
+
for (const { name, service } of started) {
|
|
5038
|
+
environmentServices[name] = service;
|
|
5039
|
+
if (service.middleware) app.use(service.middleware);
|
|
5040
|
+
}
|
|
5041
|
+
} catch (error) {
|
|
5042
|
+
environmentDriversStarted = false;
|
|
5043
|
+
for (const { name } of started) {
|
|
5044
|
+
delete environmentServices[name];
|
|
5045
|
+
}
|
|
5046
|
+
for (const environment of attempted.reverse()) {
|
|
5047
|
+
try {
|
|
5048
|
+
await environment.driver?.close?.(environment.getDriverContext());
|
|
5049
|
+
} catch (closeError) {
|
|
5050
|
+
logCloseError(`environment driver "${environment.driver.name}"`, closeError);
|
|
5051
|
+
}
|
|
5052
|
+
}
|
|
5053
|
+
throw error;
|
|
5054
|
+
}
|
|
5055
|
+
};
|
|
5056
|
+
const notifyEnvironmentDrivers = (file, event) => {
|
|
5057
|
+
for (const environment of Object.values(environments)) {
|
|
5058
|
+
if (!environment.driver?.watchChange) continue;
|
|
5059
|
+
void Promise.resolve(
|
|
5060
|
+
environment.driver.watchChange(file, event, environment.getDriverContext())
|
|
5061
|
+
).catch((error) => {
|
|
5062
|
+
logger.error(
|
|
5063
|
+
`[nasti] environment driver "${environment.driver.name}" watchChange failed`,
|
|
5064
|
+
{ error }
|
|
5065
|
+
);
|
|
5066
|
+
});
|
|
5067
|
+
}
|
|
5068
|
+
};
|
|
4741
5069
|
watcher.on("change", (file) => {
|
|
4742
5070
|
ssrRunner?.invalidateFile(file);
|
|
4743
5071
|
handleFileChange(file, server);
|
|
5072
|
+
notifyEnvironmentDrivers(file, "change");
|
|
4744
5073
|
});
|
|
4745
5074
|
watcher.on("add", (file) => {
|
|
4746
5075
|
ssrRunner?.invalidateFile(file);
|
|
4747
5076
|
handleFileChange(file, server);
|
|
5077
|
+
notifyEnvironmentDrivers(file, "add");
|
|
5078
|
+
});
|
|
5079
|
+
watcher.on("unlink", (file) => {
|
|
5080
|
+
ssrRunner?.invalidateFile(file);
|
|
5081
|
+
notifyEnvironmentDrivers(file, "unlink");
|
|
4748
5082
|
});
|
|
4749
5083
|
server = {
|
|
4750
5084
|
config: configWithPlugins,
|
|
@@ -4753,10 +5087,12 @@ async function createServer(inlineConfig = {}) {
|
|
|
4753
5087
|
watcher,
|
|
4754
5088
|
ws,
|
|
4755
5089
|
environments,
|
|
5090
|
+
environmentServices,
|
|
4756
5091
|
async listen(port) {
|
|
4757
5092
|
const finalPort = port ?? config.server.port;
|
|
4758
5093
|
const host = config.server.host === true ? "0.0.0.0" : config.server.host;
|
|
4759
5094
|
await pluginContainer.buildStart();
|
|
5095
|
+
await startEnvironmentDrivers();
|
|
4760
5096
|
return new Promise((resolve, reject) => {
|
|
4761
5097
|
let currentPort = finalPort;
|
|
4762
5098
|
const onListening = () => {
|
|
@@ -4764,15 +5100,20 @@ async function createServer(inlineConfig = {}) {
|
|
|
4764
5100
|
config.server.port = actualPort;
|
|
4765
5101
|
const localUrl = `http://localhost:${actualPort}/`;
|
|
4766
5102
|
const networkUrl = host === "0.0.0.0" ? `http://${getNetworkAddress()}:${actualPort}/` : null;
|
|
5103
|
+
const driverLocalUrls = Object.values(environmentServices).flatMap((service) => service.localUrls ?? []);
|
|
5104
|
+
const driverNetworkUrls = Object.values(environmentServices).flatMap((service) => service.networkUrls ?? []);
|
|
4767
5105
|
logger.clearScreen("info");
|
|
4768
5106
|
const readyIn = Math.ceil(performance.now() - startTime);
|
|
4769
5107
|
logger.info(
|
|
4770
5108
|
`
|
|
4771
|
-
${pc9.cyan(pc9.bold("NASTI"))} ${pc9.cyan(`v${"2.
|
|
5109
|
+
${pc9.cyan(pc9.bold("NASTI"))} ${pc9.cyan(`v${"2.3.1"}`)} ${pc9.dim("ready in")} ${pc9.bold(readyIn)} ${pc9.dim("ms")}
|
|
4772
5110
|
`
|
|
4773
5111
|
);
|
|
4774
5112
|
printServerUrls(
|
|
4775
|
-
{
|
|
5113
|
+
{
|
|
5114
|
+
local: [localUrl, ...driverLocalUrls],
|
|
5115
|
+
network: [...networkUrl ? [networkUrl] : [], ...driverNetworkUrls]
|
|
5116
|
+
},
|
|
4776
5117
|
logger.info
|
|
4777
5118
|
);
|
|
4778
5119
|
logger.info("");
|
|
@@ -4802,11 +5143,62 @@ async function createServer(inlineConfig = {}) {
|
|
|
4802
5143
|
async close() {
|
|
4803
5144
|
await pluginContainer.buildEnd();
|
|
4804
5145
|
await bundledServer?.close();
|
|
4805
|
-
|
|
5146
|
+
let environmentCloseFailed = false;
|
|
5147
|
+
let firstEnvironmentCloseError;
|
|
5148
|
+
for (const environment of Object.values(environments).reverse()) {
|
|
5149
|
+
try {
|
|
5150
|
+
await environment.close();
|
|
5151
|
+
} catch (error) {
|
|
5152
|
+
if (!environmentCloseFailed) {
|
|
5153
|
+
environmentCloseFailed = true;
|
|
5154
|
+
firstEnvironmentCloseError = error;
|
|
5155
|
+
}
|
|
5156
|
+
logCloseError(`environment "${environment.name}"`, error);
|
|
5157
|
+
}
|
|
5158
|
+
}
|
|
5159
|
+
await watcher.close();
|
|
4806
5160
|
ws.close();
|
|
4807
5161
|
httpServer.close();
|
|
5162
|
+
if (environmentCloseFailed) {
|
|
5163
|
+
throw firstEnvironmentCloseError;
|
|
5164
|
+
}
|
|
4808
5165
|
}
|
|
4809
5166
|
};
|
|
5167
|
+
try {
|
|
5168
|
+
await startEnvironmentDrivers();
|
|
5169
|
+
} catch (error) {
|
|
5170
|
+
if (bundledServer) {
|
|
5171
|
+
try {
|
|
5172
|
+
await bundledServer.close();
|
|
5173
|
+
} catch (closeError) {
|
|
5174
|
+
logCloseError("bundled dev server after driver startup failure", closeError);
|
|
5175
|
+
}
|
|
5176
|
+
}
|
|
5177
|
+
try {
|
|
5178
|
+
await watcher.close();
|
|
5179
|
+
} catch (closeError) {
|
|
5180
|
+
logCloseError("file watcher after driver startup failure", closeError);
|
|
5181
|
+
}
|
|
5182
|
+
try {
|
|
5183
|
+
ws.close();
|
|
5184
|
+
} catch (closeError) {
|
|
5185
|
+
logCloseError("WebSocket server after driver startup failure", closeError);
|
|
5186
|
+
}
|
|
5187
|
+
try {
|
|
5188
|
+
httpServer.close();
|
|
5189
|
+
} catch (closeError) {
|
|
5190
|
+
logCloseError("HTTP server after driver startup failure", closeError);
|
|
5191
|
+
}
|
|
5192
|
+
throw error;
|
|
5193
|
+
}
|
|
5194
|
+
app.use(transformMiddleware({
|
|
5195
|
+
config: configWithPlugins,
|
|
5196
|
+
pluginContainer,
|
|
5197
|
+
moduleGraph
|
|
5198
|
+
}));
|
|
5199
|
+
const publicDir = path15.resolve(config.root, "public");
|
|
5200
|
+
app.use(sirv(publicDir, { dev: true, etag: true }));
|
|
5201
|
+
app.use(sirv(config.root, { dev: true, etag: true }));
|
|
4810
5202
|
const postMiddlewares = [];
|
|
4811
5203
|
for (const plugin of allPlugins) {
|
|
4812
5204
|
if (plugin.configureServer) {
|
|
@@ -4841,6 +5233,7 @@ var init_server = __esm({
|
|
|
4841
5233
|
init_middleware();
|
|
4842
5234
|
init_hmr();
|
|
4843
5235
|
init_builtins();
|
|
5236
|
+
init_plugin_api();
|
|
4844
5237
|
}
|
|
4845
5238
|
});
|
|
4846
5239
|
|
|
@@ -4896,7 +5289,7 @@ async function buildElectron(inlineConfig = {}) {
|
|
|
4896
5289
|
const config = await resolveConfig({ ...inlineConfig, target: "electron" }, "build");
|
|
4897
5290
|
const startTime = performance.now();
|
|
4898
5291
|
assertElectronVersion(config);
|
|
4899
|
-
console.log(pc5.cyan("\n\u26A1 nasti build (electron)") + pc5.dim(` v${"2.
|
|
5292
|
+
console.log(pc5.cyan("\n\u26A1 nasti build (electron)") + pc5.dim(` v${"2.3.1"}`));
|
|
4900
5293
|
console.log(pc5.dim(` root: ${config.root}`));
|
|
4901
5294
|
console.log(pc5.dim(` mode: ${config.mode}`));
|
|
4902
5295
|
console.log(pc5.dim(` target: electron (\u2265 ${config.electron.minVersion})`));
|
|
@@ -4907,15 +5300,13 @@ async function buildElectron(inlineConfig = {}) {
|
|
|
4907
5300
|
fs7.mkdirSync(outDir, { recursive: true });
|
|
4908
5301
|
const rendererOutDir = path10.join(outDir, "renderer");
|
|
4909
5302
|
const { build: build2 } = await Promise.resolve().then(() => (init_build(), build_exports));
|
|
4910
|
-
await build2({
|
|
4911
|
-
...inlineConfig,
|
|
4912
|
-
target: "web",
|
|
5303
|
+
await build2(createElectronRendererConfig(config, inlineConfig, {
|
|
4913
5304
|
build: {
|
|
4914
5305
|
...inlineConfig.build,
|
|
4915
5306
|
outDir: rendererOutDir,
|
|
4916
5307
|
emptyOutDir: false
|
|
4917
5308
|
}
|
|
4918
|
-
});
|
|
5309
|
+
}));
|
|
4919
5310
|
const mainEntry = path10.resolve(config.root, config.electron.main);
|
|
4920
5311
|
if (!fs7.existsSync(mainEntry)) {
|
|
4921
5312
|
throw new Error(
|
|
@@ -4969,7 +5360,8 @@ async function bundleNode(config, entry, opts) {
|
|
|
4969
5360
|
const result = transformCode(id, code, {
|
|
4970
5361
|
sourcemap: !!config.build.sourcemap,
|
|
4971
5362
|
jsxRuntime: "automatic",
|
|
4972
|
-
jsxImportSource: config.framework === "vue" ? "vue" : "react"
|
|
5363
|
+
jsxImportSource: config.framework === "vue" ? "vue" : "react",
|
|
5364
|
+
target: config.electron.nodeTarget
|
|
4973
5365
|
});
|
|
4974
5366
|
return { code: result.code, map: result.map ? JSON.parse(result.map) : void 0 };
|
|
4975
5367
|
}
|
|
@@ -4980,7 +5372,11 @@ async function bundleNode(config, entry, opts) {
|
|
|
4980
5372
|
...restInputOptions,
|
|
4981
5373
|
input: entry,
|
|
4982
5374
|
platform: "node",
|
|
4983
|
-
transform: {
|
|
5375
|
+
transform: {
|
|
5376
|
+
...userTransform,
|
|
5377
|
+
target: config.electron.nodeTarget,
|
|
5378
|
+
define: mergedDefine
|
|
5379
|
+
},
|
|
4984
5380
|
plugins: [oxcTransformPlugin, electronPlugin(config), resolvePlugin(config)]
|
|
4985
5381
|
});
|
|
4986
5382
|
fs7.mkdirSync(path10.dirname(opts.outFile), { recursive: true });
|
|
@@ -4997,6 +5393,25 @@ async function bundleNode(config, entry, opts) {
|
|
|
4997
5393
|
console.log(pc5.dim(` \u2713 ${opts.label} \u2192 ${path10.relative(config.root, opts.outFile)}`));
|
|
4998
5394
|
return opts.outFile;
|
|
4999
5395
|
}
|
|
5396
|
+
function createElectronRendererConfig(config, inlineConfig = {}, overrides = {}) {
|
|
5397
|
+
const inlineClient = inlineConfig.environments?.client ?? {};
|
|
5398
|
+
return {
|
|
5399
|
+
...inlineConfig,
|
|
5400
|
+
...overrides,
|
|
5401
|
+
root: config.root,
|
|
5402
|
+
mode: config.mode,
|
|
5403
|
+
target: "web",
|
|
5404
|
+
framework: config.framework,
|
|
5405
|
+
base: config.base === "/" ? "./" : config.base,
|
|
5406
|
+
environments: {
|
|
5407
|
+
...inlineConfig.environments ?? {},
|
|
5408
|
+
client: {
|
|
5409
|
+
...inlineClient,
|
|
5410
|
+
html: config.electron.renderer
|
|
5411
|
+
}
|
|
5412
|
+
}
|
|
5413
|
+
};
|
|
5414
|
+
}
|
|
5000
5415
|
function outFileName(outDir, base, format) {
|
|
5001
5416
|
const ext = format === "cjs" ? ".cjs" : ".mjs";
|
|
5002
5417
|
return path10.join(outDir, base + ext);
|
|
@@ -5047,11 +5462,15 @@ async function startElectronDev(inlineConfig = {}) {
|
|
|
5047
5462
|
const { noSpawn, ...rest } = inlineConfig;
|
|
5048
5463
|
const config = await resolveConfig({ ...rest, target: "electron" }, "serve");
|
|
5049
5464
|
warnElectronVersion(config);
|
|
5050
|
-
console.log(pc10.cyan("\n\u26A1 nasti electron dev") + pc10.dim(` v${"2.
|
|
5465
|
+
console.log(pc10.cyan("\n\u26A1 nasti electron dev") + pc10.dim(` v${"2.3.1"}`));
|
|
5051
5466
|
const { createServer: createServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
|
|
5052
|
-
const server = await createServer2({
|
|
5467
|
+
const server = await createServer2({
|
|
5468
|
+
...rest,
|
|
5469
|
+
target: "electron",
|
|
5470
|
+
framework: config.framework
|
|
5471
|
+
});
|
|
5053
5472
|
await server.listen();
|
|
5054
|
-
const devUrl = `http://localhost:${server.config.server.port}
|
|
5473
|
+
const devUrl = `http://localhost:${server.config.server.port}` + electronRendererDevPath(config.electron.renderer);
|
|
5055
5474
|
console.log(pc10.dim(` renderer: ${devUrl}`));
|
|
5056
5475
|
const stageDir = path16.resolve(config.root, ".nasti");
|
|
5057
5476
|
fs11.mkdirSync(stageDir, { recursive: true });
|
|
@@ -5173,14 +5592,18 @@ async function compileNode(config, entry, opts) {
|
|
|
5173
5592
|
const result = transformCode(id, code, {
|
|
5174
5593
|
sourcemap: true,
|
|
5175
5594
|
jsxRuntime: "automatic",
|
|
5176
|
-
jsxImportSource: config.framework === "vue" ? "vue" : "react"
|
|
5595
|
+
jsxImportSource: config.framework === "vue" ? "vue" : "react",
|
|
5596
|
+
target: config.electron.nodeTarget
|
|
5177
5597
|
});
|
|
5178
5598
|
return { code: result.code, map: result.map ? JSON.parse(result.map) : void 0 };
|
|
5179
5599
|
}
|
|
5180
5600
|
};
|
|
5181
5601
|
const bundle2 = await rolldown3({
|
|
5182
5602
|
input: entry,
|
|
5183
|
-
transform: {
|
|
5603
|
+
transform: {
|
|
5604
|
+
target: config.electron.nodeTarget,
|
|
5605
|
+
define: envDefine
|
|
5606
|
+
},
|
|
5184
5607
|
platform: "node",
|
|
5185
5608
|
plugins: [oxcTransformPlugin, electronPlugin(config), resolvePlugin(config)]
|
|
5186
5609
|
});
|
|
@@ -5196,6 +5619,10 @@ async function compileNode(config, entry, opts) {
|
|
|
5196
5619
|
});
|
|
5197
5620
|
await bundle2.close();
|
|
5198
5621
|
}
|
|
5622
|
+
function electronRendererDevPath(renderer) {
|
|
5623
|
+
const normalized = renderer.split(path16.sep).join("/").replace(/^\.?\//, "");
|
|
5624
|
+
return normalized === "index.html" ? "/" : `/${normalized}`;
|
|
5625
|
+
}
|
|
5199
5626
|
function resolveElectronBinary(config) {
|
|
5200
5627
|
if (config.electron.electronPath && fs11.existsSync(config.electron.electronPath)) {
|
|
5201
5628
|
return config.electron.electronPath;
|
|
@@ -5452,12 +5879,15 @@ export {
|
|
|
5452
5879
|
buildElectron,
|
|
5453
5880
|
buildEnvDefine,
|
|
5454
5881
|
createDebugger,
|
|
5882
|
+
createElectronRendererConfig,
|
|
5455
5883
|
createLogger,
|
|
5456
5884
|
createNoopHotChannel,
|
|
5457
5885
|
createServer,
|
|
5458
5886
|
createWsHotChannel,
|
|
5459
5887
|
defineConfig,
|
|
5888
|
+
detectFramework,
|
|
5460
5889
|
electronPlugin,
|
|
5890
|
+
electronRendererDevPath,
|
|
5461
5891
|
loadEnv,
|
|
5462
5892
|
monacoEditorPlugin,
|
|
5463
5893
|
printServerUrls,
|