@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/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.1
|
|
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.1.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) {
|
|
@@ -3776,7 +4050,7 @@ const hotModulesMap = new Map();
|
|
|
3776
4050
|
const disposeMap = new Map();
|
|
3777
4051
|
const pruneMap = new Map();
|
|
3778
4052
|
|
|
3779
|
-
socket.addEventListener('message', ({ data }) => {
|
|
4053
|
+
socket.addEventListener('message', async ({ data }) => {
|
|
3780
4054
|
const payload = JSON.parse(data);
|
|
3781
4055
|
switch (payload.type) {
|
|
3782
4056
|
case 'connected':
|
|
@@ -3784,14 +4058,21 @@ socket.addEventListener('message', ({ data }) => {
|
|
|
3784
4058
|
clearErrorOverlay();
|
|
3785
4059
|
break;
|
|
3786
4060
|
case 'update':
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
4061
|
+
try {
|
|
4062
|
+
await Promise.all(payload.updates.map((update) => {
|
|
4063
|
+
if (update.type === 'js-update') {
|
|
4064
|
+
return fetchUpdate(update);
|
|
4065
|
+
} else if (update.type === 'css-update') {
|
|
4066
|
+
return updateCss(update.path);
|
|
4067
|
+
}
|
|
4068
|
+
}));
|
|
4069
|
+
clearErrorOverlay();
|
|
4070
|
+
console.log('[nasti] HMR update complete, reloading page');
|
|
4071
|
+
location.reload();
|
|
4072
|
+
} catch (err) {
|
|
4073
|
+
console.error('[nasti] HMR update failed:', err);
|
|
4074
|
+
showErrorOverlay(err);
|
|
4075
|
+
}
|
|
3795
4076
|
break;
|
|
3796
4077
|
case 'full-reload':
|
|
3797
4078
|
console.log('[nasti] full reload');
|
|
@@ -3833,7 +4114,7 @@ async function fetchUpdate(update) {
|
|
|
3833
4114
|
function updateCss(path) {
|
|
3834
4115
|
const el = document.querySelector(\`style[data-nasti-css="\${path}"]\`);
|
|
3835
4116
|
if (el) {
|
|
3836
|
-
fetch(path + '?t=' + Date.now())
|
|
4117
|
+
return fetch(path + '?t=' + Date.now())
|
|
3837
4118
|
.then(r => r.text())
|
|
3838
4119
|
.then(css => { el.textContent = css; });
|
|
3839
4120
|
}
|
|
@@ -4230,7 +4511,7 @@ async function createBundledDevServer(opts) {
|
|
|
4230
4511
|
`[nasti] experimental.bundledDev requires rolldown's experimental dev() API (locked to the installed rc; got: ${err.message}). Remove --bundle / experimental.bundledDev to use the default unbundled dev server.`
|
|
4231
4512
|
);
|
|
4232
4513
|
}
|
|
4233
|
-
const html = await readHtmlFile(config.root);
|
|
4514
|
+
const html = await readHtmlFile(config.root, config.environments.client?.html);
|
|
4234
4515
|
const entryPoints = resolveClientEntries(config, html);
|
|
4235
4516
|
if (entryPoints.length === 0) {
|
|
4236
4517
|
throw new Error("No entry point found. Add a <script> tag to index.html or create src/main.ts");
|
|
@@ -4285,7 +4566,10 @@ async function createBundledDevServer(opts) {
|
|
|
4285
4566
|
continue;
|
|
4286
4567
|
}
|
|
4287
4568
|
const patchPath = `__nasti_patch/${update.filename}`;
|
|
4288
|
-
patches.set(
|
|
4569
|
+
patches.set(
|
|
4570
|
+
patchPath,
|
|
4571
|
+
update.code + "\n;globalThis.location?.reload();\n;export {}"
|
|
4572
|
+
);
|
|
4289
4573
|
if (update.sourcemap && update.sourcemapFilename) {
|
|
4290
4574
|
patches.set(`__nasti_patch/${update.sourcemapFilename}`, update.sourcemap);
|
|
4291
4575
|
}
|
|
@@ -4433,7 +4717,7 @@ async function createBundledDevServer(opts) {
|
|
|
4433
4717
|
return;
|
|
4434
4718
|
}
|
|
4435
4719
|
if (pathname === "/" || pathname.endsWith(".html")) {
|
|
4436
|
-
const rawHtml = await readHtmlFile(config.root);
|
|
4720
|
+
const rawHtml = await readHtmlFile(config.root, config.environments.client?.html);
|
|
4437
4721
|
if (rawHtml) {
|
|
4438
4722
|
res.setHeader("Content-Type", "text/html");
|
|
4439
4723
|
res.setHeader("Cache-Control", "no-store");
|
|
@@ -4517,10 +4801,13 @@ async function renderBundledIndexHtml(html, config, entryFileNames) {
|
|
|
4517
4801
|
}
|
|
4518
4802
|
}
|
|
4519
4803
|
for (const [facadeModuleId, fileName] of entryFileNames) {
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4804
|
+
processed = replaceEntryScript(
|
|
4805
|
+
processed,
|
|
4806
|
+
facadeModuleId,
|
|
4807
|
+
fileName,
|
|
4808
|
+
config,
|
|
4809
|
+
config.environments.client?.html ?? "index.html",
|
|
4810
|
+
"/"
|
|
4524
4811
|
);
|
|
4525
4812
|
}
|
|
4526
4813
|
return processed;
|
|
@@ -4664,10 +4951,12 @@ async function createServer(inlineConfig = {}) {
|
|
|
4664
4951
|
const app = connect();
|
|
4665
4952
|
const httpServer = http.createServer(app);
|
|
4666
4953
|
const ws = createWebSocketServer(httpServer);
|
|
4667
|
-
const
|
|
4954
|
+
const pluginApi = getPluginApi(config);
|
|
4955
|
+
const clientEnv = new NastiEnvironment("client", config, {
|
|
4668
4956
|
hot: createWsHotChannel(ws),
|
|
4669
4957
|
mode: "dev",
|
|
4670
|
-
plugins: allPlugins
|
|
4958
|
+
plugins: allPlugins,
|
|
4959
|
+
pluginApi
|
|
4671
4960
|
});
|
|
4672
4961
|
await clientEnv.init();
|
|
4673
4962
|
const environments = { client: clientEnv };
|
|
@@ -4675,11 +4964,15 @@ async function createServer(inlineConfig = {}) {
|
|
|
4675
4964
|
if (name === "client") continue;
|
|
4676
4965
|
const consumer = config.environments[name].consumer;
|
|
4677
4966
|
const envPlugins = resolvePluginList(config, config.plugins, { consumer });
|
|
4678
|
-
environments[name] = new NastiEnvironment(name,
|
|
4967
|
+
environments[name] = new NastiEnvironment(name, config, {
|
|
4679
4968
|
mode: "dev",
|
|
4680
|
-
plugins: envPlugins
|
|
4969
|
+
plugins: envPlugins,
|
|
4970
|
+
pluginApi
|
|
4681
4971
|
});
|
|
4682
4972
|
}
|
|
4973
|
+
for (const [name, environment] of Object.entries(environments)) {
|
|
4974
|
+
if (name !== "client" && environment.options.driver) await environment.init();
|
|
4975
|
+
}
|
|
4683
4976
|
let ssrRunner = null;
|
|
4684
4977
|
async function getSsrRunner() {
|
|
4685
4978
|
if (ssrRunner) return ssrRunner;
|
|
@@ -4704,14 +4997,6 @@ async function createServer(inlineConfig = {}) {
|
|
|
4704
4997
|
});
|
|
4705
4998
|
app.use(bundledServer.middleware);
|
|
4706
4999
|
}
|
|
4707
|
-
app.use(transformMiddleware({
|
|
4708
|
-
config: configWithPlugins,
|
|
4709
|
-
pluginContainer,
|
|
4710
|
-
moduleGraph
|
|
4711
|
-
}));
|
|
4712
|
-
const publicDir = path15.resolve(config.root, "public");
|
|
4713
|
-
app.use(sirv(publicDir, { dev: true, etag: true }));
|
|
4714
|
-
app.use(sirv(config.root, { dev: true, etag: true }));
|
|
4715
5000
|
const ignoredSegments = /* @__PURE__ */ new Set(["node_modules", ".git", ".nasti"]);
|
|
4716
5001
|
const outDirAbs = path15.resolve(config.root, config.build.outDir);
|
|
4717
5002
|
const watcher = watch(config.root, {
|
|
@@ -4728,13 +5013,72 @@ async function createServer(inlineConfig = {}) {
|
|
|
4728
5013
|
ignoreInitial: true
|
|
4729
5014
|
});
|
|
4730
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
|
+
};
|
|
4731
5069
|
watcher.on("change", (file) => {
|
|
4732
5070
|
ssrRunner?.invalidateFile(file);
|
|
4733
5071
|
handleFileChange(file, server);
|
|
5072
|
+
notifyEnvironmentDrivers(file, "change");
|
|
4734
5073
|
});
|
|
4735
5074
|
watcher.on("add", (file) => {
|
|
4736
5075
|
ssrRunner?.invalidateFile(file);
|
|
4737
5076
|
handleFileChange(file, server);
|
|
5077
|
+
notifyEnvironmentDrivers(file, "add");
|
|
5078
|
+
});
|
|
5079
|
+
watcher.on("unlink", (file) => {
|
|
5080
|
+
ssrRunner?.invalidateFile(file);
|
|
5081
|
+
notifyEnvironmentDrivers(file, "unlink");
|
|
4738
5082
|
});
|
|
4739
5083
|
server = {
|
|
4740
5084
|
config: configWithPlugins,
|
|
@@ -4743,10 +5087,12 @@ async function createServer(inlineConfig = {}) {
|
|
|
4743
5087
|
watcher,
|
|
4744
5088
|
ws,
|
|
4745
5089
|
environments,
|
|
5090
|
+
environmentServices,
|
|
4746
5091
|
async listen(port) {
|
|
4747
5092
|
const finalPort = port ?? config.server.port;
|
|
4748
5093
|
const host = config.server.host === true ? "0.0.0.0" : config.server.host;
|
|
4749
5094
|
await pluginContainer.buildStart();
|
|
5095
|
+
await startEnvironmentDrivers();
|
|
4750
5096
|
return new Promise((resolve, reject) => {
|
|
4751
5097
|
let currentPort = finalPort;
|
|
4752
5098
|
const onListening = () => {
|
|
@@ -4754,15 +5100,20 @@ async function createServer(inlineConfig = {}) {
|
|
|
4754
5100
|
config.server.port = actualPort;
|
|
4755
5101
|
const localUrl = `http://localhost:${actualPort}/`;
|
|
4756
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 ?? []);
|
|
4757
5105
|
logger.clearScreen("info");
|
|
4758
5106
|
const readyIn = Math.ceil(performance.now() - startTime);
|
|
4759
5107
|
logger.info(
|
|
4760
5108
|
`
|
|
4761
|
-
${pc9.cyan(pc9.bold("NASTI"))} ${pc9.cyan(`v${"2.1
|
|
5109
|
+
${pc9.cyan(pc9.bold("NASTI"))} ${pc9.cyan(`v${"2.3.1"}`)} ${pc9.dim("ready in")} ${pc9.bold(readyIn)} ${pc9.dim("ms")}
|
|
4762
5110
|
`
|
|
4763
5111
|
);
|
|
4764
5112
|
printServerUrls(
|
|
4765
|
-
{
|
|
5113
|
+
{
|
|
5114
|
+
local: [localUrl, ...driverLocalUrls],
|
|
5115
|
+
network: [...networkUrl ? [networkUrl] : [], ...driverNetworkUrls]
|
|
5116
|
+
},
|
|
4766
5117
|
logger.info
|
|
4767
5118
|
);
|
|
4768
5119
|
logger.info("");
|
|
@@ -4792,11 +5143,62 @@ async function createServer(inlineConfig = {}) {
|
|
|
4792
5143
|
async close() {
|
|
4793
5144
|
await pluginContainer.buildEnd();
|
|
4794
5145
|
await bundledServer?.close();
|
|
4795
|
-
|
|
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();
|
|
4796
5160
|
ws.close();
|
|
4797
5161
|
httpServer.close();
|
|
5162
|
+
if (environmentCloseFailed) {
|
|
5163
|
+
throw firstEnvironmentCloseError;
|
|
5164
|
+
}
|
|
4798
5165
|
}
|
|
4799
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 }));
|
|
4800
5202
|
const postMiddlewares = [];
|
|
4801
5203
|
for (const plugin of allPlugins) {
|
|
4802
5204
|
if (plugin.configureServer) {
|
|
@@ -4831,6 +5233,7 @@ var init_server = __esm({
|
|
|
4831
5233
|
init_middleware();
|
|
4832
5234
|
init_hmr();
|
|
4833
5235
|
init_builtins();
|
|
5236
|
+
init_plugin_api();
|
|
4834
5237
|
}
|
|
4835
5238
|
});
|
|
4836
5239
|
|
|
@@ -4886,7 +5289,7 @@ async function buildElectron(inlineConfig = {}) {
|
|
|
4886
5289
|
const config = await resolveConfig({ ...inlineConfig, target: "electron" }, "build");
|
|
4887
5290
|
const startTime = performance.now();
|
|
4888
5291
|
assertElectronVersion(config);
|
|
4889
|
-
console.log(pc5.cyan("\n\u26A1 nasti build (electron)") + pc5.dim(` v${"2.1
|
|
5292
|
+
console.log(pc5.cyan("\n\u26A1 nasti build (electron)") + pc5.dim(` v${"2.3.1"}`));
|
|
4890
5293
|
console.log(pc5.dim(` root: ${config.root}`));
|
|
4891
5294
|
console.log(pc5.dim(` mode: ${config.mode}`));
|
|
4892
5295
|
console.log(pc5.dim(` target: electron (\u2265 ${config.electron.minVersion})`));
|
|
@@ -4897,15 +5300,13 @@ async function buildElectron(inlineConfig = {}) {
|
|
|
4897
5300
|
fs7.mkdirSync(outDir, { recursive: true });
|
|
4898
5301
|
const rendererOutDir = path10.join(outDir, "renderer");
|
|
4899
5302
|
const { build: build2 } = await Promise.resolve().then(() => (init_build(), build_exports));
|
|
4900
|
-
await build2({
|
|
4901
|
-
...inlineConfig,
|
|
4902
|
-
target: "web",
|
|
5303
|
+
await build2(createElectronRendererConfig(config, inlineConfig, {
|
|
4903
5304
|
build: {
|
|
4904
5305
|
...inlineConfig.build,
|
|
4905
5306
|
outDir: rendererOutDir,
|
|
4906
5307
|
emptyOutDir: false
|
|
4907
5308
|
}
|
|
4908
|
-
});
|
|
5309
|
+
}));
|
|
4909
5310
|
const mainEntry = path10.resolve(config.root, config.electron.main);
|
|
4910
5311
|
if (!fs7.existsSync(mainEntry)) {
|
|
4911
5312
|
throw new Error(
|
|
@@ -4959,7 +5360,8 @@ async function bundleNode(config, entry, opts) {
|
|
|
4959
5360
|
const result = transformCode(id, code, {
|
|
4960
5361
|
sourcemap: !!config.build.sourcemap,
|
|
4961
5362
|
jsxRuntime: "automatic",
|
|
4962
|
-
jsxImportSource: config.framework === "vue" ? "vue" : "react"
|
|
5363
|
+
jsxImportSource: config.framework === "vue" ? "vue" : "react",
|
|
5364
|
+
target: config.electron.nodeTarget
|
|
4963
5365
|
});
|
|
4964
5366
|
return { code: result.code, map: result.map ? JSON.parse(result.map) : void 0 };
|
|
4965
5367
|
}
|
|
@@ -4970,7 +5372,11 @@ async function bundleNode(config, entry, opts) {
|
|
|
4970
5372
|
...restInputOptions,
|
|
4971
5373
|
input: entry,
|
|
4972
5374
|
platform: "node",
|
|
4973
|
-
transform: {
|
|
5375
|
+
transform: {
|
|
5376
|
+
...userTransform,
|
|
5377
|
+
target: config.electron.nodeTarget,
|
|
5378
|
+
define: mergedDefine
|
|
5379
|
+
},
|
|
4974
5380
|
plugins: [oxcTransformPlugin, electronPlugin(config), resolvePlugin(config)]
|
|
4975
5381
|
});
|
|
4976
5382
|
fs7.mkdirSync(path10.dirname(opts.outFile), { recursive: true });
|
|
@@ -4987,6 +5393,25 @@ async function bundleNode(config, entry, opts) {
|
|
|
4987
5393
|
console.log(pc5.dim(` \u2713 ${opts.label} \u2192 ${path10.relative(config.root, opts.outFile)}`));
|
|
4988
5394
|
return opts.outFile;
|
|
4989
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
|
+
}
|
|
4990
5415
|
function outFileName(outDir, base, format) {
|
|
4991
5416
|
const ext = format === "cjs" ? ".cjs" : ".mjs";
|
|
4992
5417
|
return path10.join(outDir, base + ext);
|
|
@@ -5037,11 +5462,15 @@ async function startElectronDev(inlineConfig = {}) {
|
|
|
5037
5462
|
const { noSpawn, ...rest } = inlineConfig;
|
|
5038
5463
|
const config = await resolveConfig({ ...rest, target: "electron" }, "serve");
|
|
5039
5464
|
warnElectronVersion(config);
|
|
5040
|
-
console.log(pc10.cyan("\n\u26A1 nasti electron dev") + pc10.dim(` v${"2.1
|
|
5465
|
+
console.log(pc10.cyan("\n\u26A1 nasti electron dev") + pc10.dim(` v${"2.3.1"}`));
|
|
5041
5466
|
const { createServer: createServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
|
|
5042
|
-
const server = await createServer2({
|
|
5467
|
+
const server = await createServer2({
|
|
5468
|
+
...rest,
|
|
5469
|
+
target: "electron",
|
|
5470
|
+
framework: config.framework
|
|
5471
|
+
});
|
|
5043
5472
|
await server.listen();
|
|
5044
|
-
const devUrl = `http://localhost:${server.config.server.port}
|
|
5473
|
+
const devUrl = `http://localhost:${server.config.server.port}` + electronRendererDevPath(config.electron.renderer);
|
|
5045
5474
|
console.log(pc10.dim(` renderer: ${devUrl}`));
|
|
5046
5475
|
const stageDir = path16.resolve(config.root, ".nasti");
|
|
5047
5476
|
fs11.mkdirSync(stageDir, { recursive: true });
|
|
@@ -5163,14 +5592,18 @@ async function compileNode(config, entry, opts) {
|
|
|
5163
5592
|
const result = transformCode(id, code, {
|
|
5164
5593
|
sourcemap: true,
|
|
5165
5594
|
jsxRuntime: "automatic",
|
|
5166
|
-
jsxImportSource: config.framework === "vue" ? "vue" : "react"
|
|
5595
|
+
jsxImportSource: config.framework === "vue" ? "vue" : "react",
|
|
5596
|
+
target: config.electron.nodeTarget
|
|
5167
5597
|
});
|
|
5168
5598
|
return { code: result.code, map: result.map ? JSON.parse(result.map) : void 0 };
|
|
5169
5599
|
}
|
|
5170
5600
|
};
|
|
5171
5601
|
const bundle2 = await rolldown3({
|
|
5172
5602
|
input: entry,
|
|
5173
|
-
transform: {
|
|
5603
|
+
transform: {
|
|
5604
|
+
target: config.electron.nodeTarget,
|
|
5605
|
+
define: envDefine
|
|
5606
|
+
},
|
|
5174
5607
|
platform: "node",
|
|
5175
5608
|
plugins: [oxcTransformPlugin, electronPlugin(config), resolvePlugin(config)]
|
|
5176
5609
|
});
|
|
@@ -5186,6 +5619,10 @@ async function compileNode(config, entry, opts) {
|
|
|
5186
5619
|
});
|
|
5187
5620
|
await bundle2.close();
|
|
5188
5621
|
}
|
|
5622
|
+
function electronRendererDevPath(renderer) {
|
|
5623
|
+
const normalized = renderer.split(path16.sep).join("/").replace(/^\.?\//, "");
|
|
5624
|
+
return normalized === "index.html" ? "/" : `/${normalized}`;
|
|
5625
|
+
}
|
|
5189
5626
|
function resolveElectronBinary(config) {
|
|
5190
5627
|
if (config.electron.electronPath && fs11.existsSync(config.electron.electronPath)) {
|
|
5191
5628
|
return config.electron.electronPath;
|
|
@@ -5442,12 +5879,15 @@ export {
|
|
|
5442
5879
|
buildElectron,
|
|
5443
5880
|
buildEnvDefine,
|
|
5444
5881
|
createDebugger,
|
|
5882
|
+
createElectronRendererConfig,
|
|
5445
5883
|
createLogger,
|
|
5446
5884
|
createNoopHotChannel,
|
|
5447
5885
|
createServer,
|
|
5448
5886
|
createWsHotChannel,
|
|
5449
5887
|
defineConfig,
|
|
5888
|
+
detectFramework,
|
|
5450
5889
|
electronPlugin,
|
|
5890
|
+
electronRendererDevPath,
|
|
5451
5891
|
loadEnv,
|
|
5452
5892
|
monacoEditorPlugin,
|
|
5453
5893
|
printServerUrls,
|