@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.cjs
CHANGED
|
@@ -221,6 +221,92 @@ var init_logger = __esm({
|
|
|
221
221
|
}
|
|
222
222
|
});
|
|
223
223
|
|
|
224
|
+
// src/core/plugin-api.ts
|
|
225
|
+
function orderPlugins(plugins) {
|
|
226
|
+
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);
|
|
227
|
+
const indexesByName = /* @__PURE__ */ new Map();
|
|
228
|
+
baseline.forEach((plugin, index2) => {
|
|
229
|
+
const indexes = indexesByName.get(plugin.name) ?? [];
|
|
230
|
+
indexes.push(index2);
|
|
231
|
+
indexesByName.set(plugin.name, indexes);
|
|
232
|
+
});
|
|
233
|
+
const edges = baseline.map(() => /* @__PURE__ */ new Set());
|
|
234
|
+
const indegree = baseline.map(() => 0);
|
|
235
|
+
const addEdge = (from, to) => {
|
|
236
|
+
if (from === to || edges[from].has(to)) return;
|
|
237
|
+
edges[from].add(to);
|
|
238
|
+
indegree[to]++;
|
|
239
|
+
};
|
|
240
|
+
baseline.forEach((plugin, current) => {
|
|
241
|
+
for (const dependency of plugin.pre ?? []) {
|
|
242
|
+
for (const before of indexesByName.get(dependency) ?? []) addEdge(before, current);
|
|
243
|
+
}
|
|
244
|
+
for (const dependency of plugin.post ?? []) {
|
|
245
|
+
for (const after of indexesByName.get(dependency) ?? []) addEdge(current, after);
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
const ready = indegree.map((degree, index2) => ({ degree, index: index2 })).filter(({ degree }) => degree === 0).map(({ index: index2 }) => index2);
|
|
249
|
+
const ordered = [];
|
|
250
|
+
while (ready.length > 0) {
|
|
251
|
+
ready.sort((a, b) => a - b);
|
|
252
|
+
const current = ready.shift();
|
|
253
|
+
ordered.push(baseline[current]);
|
|
254
|
+
for (const next of edges[current]) {
|
|
255
|
+
indegree[next]--;
|
|
256
|
+
if (indegree[next] === 0) ready.push(next);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (ordered.length !== baseline.length) {
|
|
260
|
+
const cyclic = baseline.filter((_, index2) => indegree[index2] > 0).map((plugin) => plugin.name);
|
|
261
|
+
throw new Error(
|
|
262
|
+
`[nasti] circular plugin setup dependency: ${[...new Set(cyclic)].join(", ")}`
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
return ordered;
|
|
266
|
+
}
|
|
267
|
+
async function setupPluginApi(config, plugins) {
|
|
268
|
+
const exposed = /* @__PURE__ */ new Map();
|
|
269
|
+
const api = {
|
|
270
|
+
config,
|
|
271
|
+
logger: config.logger,
|
|
272
|
+
expose(key, value) {
|
|
273
|
+
if (exposed.has(key) && exposed.get(key) !== value) {
|
|
274
|
+
throw new Error(`[nasti] plugin API key already exposed: ${String(key)}`);
|
|
275
|
+
}
|
|
276
|
+
exposed.set(key, value);
|
|
277
|
+
},
|
|
278
|
+
useExposed(key) {
|
|
279
|
+
return exposed.get(key);
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
apiByConfig.set(config, api);
|
|
283
|
+
for (const plugin of plugins) {
|
|
284
|
+
await plugin.setup?.(api);
|
|
285
|
+
}
|
|
286
|
+
return api;
|
|
287
|
+
}
|
|
288
|
+
function getPluginApi(config) {
|
|
289
|
+
const api = apiByConfig.get(config);
|
|
290
|
+
if (!api) {
|
|
291
|
+
throw new Error(
|
|
292
|
+
"[nasti] internal: plugin API requested before setup; getPluginApi requires the original ResolvedConfig reference registered by setupPluginApi, not a shallow copy"
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
return api;
|
|
296
|
+
}
|
|
297
|
+
function enforceRank(plugin) {
|
|
298
|
+
if (plugin.enforce === "pre") return 0;
|
|
299
|
+
if (plugin.enforce === "post") return 2;
|
|
300
|
+
return 1;
|
|
301
|
+
}
|
|
302
|
+
var apiByConfig;
|
|
303
|
+
var init_plugin_api = __esm({
|
|
304
|
+
"src/core/plugin-api.ts"() {
|
|
305
|
+
"use strict";
|
|
306
|
+
apiByConfig = /* @__PURE__ */ new WeakMap();
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
|
|
224
310
|
// src/config/index.ts
|
|
225
311
|
function loadTsconfigPaths(root) {
|
|
226
312
|
const tsconfigPath = import_node_path.default.resolve(root, "tsconfig.json");
|
|
@@ -258,6 +344,43 @@ async function loadConfigFromFile(root) {
|
|
|
258
344
|
}
|
|
259
345
|
return {};
|
|
260
346
|
}
|
|
347
|
+
function detectFramework(root) {
|
|
348
|
+
const sourceRoot = import_node_path.default.resolve(root, "src");
|
|
349
|
+
if (containsVueFile(sourceRoot)) return "vue";
|
|
350
|
+
const packagePath = import_node_path.default.resolve(root, "package.json");
|
|
351
|
+
if (import_node_fs.default.existsSync(packagePath)) {
|
|
352
|
+
try {
|
|
353
|
+
const pkg = JSON.parse(import_node_fs.default.readFileSync(packagePath, "utf-8"));
|
|
354
|
+
const dependencies = {
|
|
355
|
+
...pkg.dependencies ?? {},
|
|
356
|
+
...pkg.devDependencies ?? {},
|
|
357
|
+
...pkg.peerDependencies ?? {},
|
|
358
|
+
...pkg.optionalDependencies ?? {}
|
|
359
|
+
};
|
|
360
|
+
const hasVue = "vue" in dependencies || "@vue/runtime-dom" in dependencies;
|
|
361
|
+
const hasReact = "react" in dependencies || "react-dom" in dependencies;
|
|
362
|
+
if (hasVue && !hasReact) return "vue";
|
|
363
|
+
if (hasReact) return "react";
|
|
364
|
+
if (hasVue) return "vue";
|
|
365
|
+
} catch {
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return "react";
|
|
369
|
+
}
|
|
370
|
+
function containsVueFile(dir, depth = 0) {
|
|
371
|
+
if (depth > 5 || !import_node_fs.default.existsSync(dir)) return false;
|
|
372
|
+
try {
|
|
373
|
+
for (const entry of import_node_fs.default.readdirSync(dir, { withFileTypes: true })) {
|
|
374
|
+
if (entry.isFile() && entry.name.endsWith(".vue")) return true;
|
|
375
|
+
if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== "dist" && containsVueFile(import_node_path.default.join(dir, entry.name), depth + 1)) {
|
|
376
|
+
return true;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
} catch {
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
return false;
|
|
383
|
+
}
|
|
261
384
|
async function loadTsConfig(filePath) {
|
|
262
385
|
const { transformSync: transformSync2 } = await import("oxc-transform");
|
|
263
386
|
const code = import_node_fs.default.readFileSync(filePath, "utf-8");
|
|
@@ -304,7 +427,7 @@ async function resolveConfig(inlineConfig = {}, command) {
|
|
|
304
427
|
base: merged.base ?? defaults.base,
|
|
305
428
|
mode,
|
|
306
429
|
target: merged.target ?? defaults.target,
|
|
307
|
-
framework: merged.framework ?? defaults.framework,
|
|
430
|
+
framework: (merged.framework ?? defaults.framework) === "auto" ? detectFramework(root) : merged.framework,
|
|
308
431
|
command,
|
|
309
432
|
resolve: {
|
|
310
433
|
// tsconfig paths 优先级最低:tsconfig < defaults < user config
|
|
@@ -351,7 +474,12 @@ async function resolveConfig(inlineConfig = {}, command) {
|
|
|
351
474
|
if (envOptions.build) Object.assign(resolved.build, envOptions.build);
|
|
352
475
|
resolved.environments.client = {
|
|
353
476
|
consumer,
|
|
354
|
-
entry:
|
|
477
|
+
entry: normalizeEnvironmentEntries(envOptions.entry, root),
|
|
478
|
+
html: import_node_path.default.resolve(
|
|
479
|
+
root,
|
|
480
|
+
envOptions.html ?? (resolved.target === "electron" ? resolved.electron.renderer : "index.html")
|
|
481
|
+
),
|
|
482
|
+
driver: envOptions.driver,
|
|
355
483
|
// 同引用 —— 精确镜像(assertClientEnvironmentMirror 校验)
|
|
356
484
|
resolve: resolved.resolve,
|
|
357
485
|
build: resolved.build
|
|
@@ -360,7 +488,9 @@ async function resolveConfig(inlineConfig = {}, command) {
|
|
|
360
488
|
}
|
|
361
489
|
resolved.environments[name] = {
|
|
362
490
|
consumer,
|
|
363
|
-
entry: (
|
|
491
|
+
entry: normalizeEnvironmentEntries(envOptions.entry, root),
|
|
492
|
+
html: envOptions.consumer === "client" && envOptions.html ? import_node_path.default.resolve(root, envOptions.html) : void 0,
|
|
493
|
+
driver: envOptions.driver,
|
|
364
494
|
resolve: {
|
|
365
495
|
alias: { ...resolved.resolve.alias, ...envOptions.resolve?.alias },
|
|
366
496
|
extensions: envOptions.resolve?.extensions ?? [...resolved.resolve.extensions],
|
|
@@ -379,12 +509,13 @@ async function resolveConfig(inlineConfig = {}, command) {
|
|
|
379
509
|
};
|
|
380
510
|
}
|
|
381
511
|
assertClientEnvironmentMirror(resolved);
|
|
382
|
-
const filteredPlugins = rawPlugins.filter((p) => {
|
|
512
|
+
const filteredPlugins = orderPlugins(rawPlugins.filter((p) => {
|
|
383
513
|
if (!p.apply) return true;
|
|
384
514
|
if (typeof p.apply === "function") return p.apply(resolved, env);
|
|
385
515
|
return p.apply === command;
|
|
386
|
-
});
|
|
516
|
+
}));
|
|
387
517
|
resolved.plugins = filteredPlugins;
|
|
518
|
+
await setupPluginApi(resolved, filteredPlugins);
|
|
388
519
|
if (resolved.target === "electron") {
|
|
389
520
|
const autoExternal = detectNativeDeps(root);
|
|
390
521
|
if (autoExternal.length > 0) {
|
|
@@ -400,6 +531,10 @@ async function resolveConfig(inlineConfig = {}, command) {
|
|
|
400
531
|
}
|
|
401
532
|
return resolved;
|
|
402
533
|
}
|
|
534
|
+
function normalizeEnvironmentEntries(entry, root) {
|
|
535
|
+
const entries = Array.isArray(entry) ? entry : entry ? [entry] : [];
|
|
536
|
+
return entries.map((item) => import_node_path.default.resolve(root, item));
|
|
537
|
+
}
|
|
403
538
|
function detectNativeDeps(root) {
|
|
404
539
|
const result = /* @__PURE__ */ new Set();
|
|
405
540
|
const pkgJsonPath = import_node_path.default.resolve(root, "package.json");
|
|
@@ -520,6 +655,7 @@ var init_config = __esm({
|
|
|
520
655
|
import_node_fs = __toESM(require("fs"), 1);
|
|
521
656
|
init_defaults();
|
|
522
657
|
init_logger();
|
|
658
|
+
init_plugin_api();
|
|
523
659
|
CONFIG_FILES = [
|
|
524
660
|
"nasti.config.ts",
|
|
525
661
|
"nasti.config.js",
|
|
@@ -1915,7 +2051,8 @@ function transformCode(filename, code, options = {}) {
|
|
|
1915
2051
|
importSource: options.jsxImportSource ?? "react",
|
|
1916
2052
|
refresh: options.reactRefresh ?? false
|
|
1917
2053
|
} : void 0,
|
|
1918
|
-
sourcemap: options.sourcemap ?? true
|
|
2054
|
+
sourcemap: options.sourcemap ?? true,
|
|
2055
|
+
target: options.target
|
|
1919
2056
|
});
|
|
1920
2057
|
if (result.errors && result.errors.length > 0) {
|
|
1921
2058
|
const msg = result.errors.map((e) => e.message ?? String(e)).join("\n");
|
|
@@ -2118,7 +2255,7 @@ function htmlPlugin(config) {
|
|
|
2118
2255
|
transformIndexHtml(html) {
|
|
2119
2256
|
const tags = [];
|
|
2120
2257
|
if (config.command === "serve") {
|
|
2121
|
-
const isReactLike = config.framework === "react"
|
|
2258
|
+
const isReactLike = config.framework === "react";
|
|
2122
2259
|
if (isReactLike) {
|
|
2123
2260
|
tags.push({
|
|
2124
2261
|
tag: "script",
|
|
@@ -2172,8 +2309,8 @@ function serializeTag(tag) {
|
|
|
2172
2309
|
}
|
|
2173
2310
|
return ` <${tag.tag}${attrs}>${children}</${tag.tag}>`;
|
|
2174
2311
|
}
|
|
2175
|
-
async function readHtmlFile(root) {
|
|
2176
|
-
const htmlPath = import_node_path6.default.resolve(root,
|
|
2312
|
+
async function readHtmlFile(root, htmlFile = "index.html") {
|
|
2313
|
+
const htmlPath = import_node_path6.default.isAbsolute(htmlFile) ? htmlFile : import_node_path6.default.resolve(root, htmlFile);
|
|
2177
2314
|
if (!import_node_fs4.default.existsSync(htmlPath)) return null;
|
|
2178
2315
|
return import_node_fs4.default.readFileSync(htmlPath, "utf-8");
|
|
2179
2316
|
}
|
|
@@ -2221,21 +2358,11 @@ var init_builtins = __esm({
|
|
|
2221
2358
|
});
|
|
2222
2359
|
|
|
2223
2360
|
// src/core/plugin-container.ts
|
|
2224
|
-
function sortPlugins(plugins) {
|
|
2225
|
-
const pre = [];
|
|
2226
|
-
const normal = [];
|
|
2227
|
-
const post = [];
|
|
2228
|
-
for (const plugin of plugins) {
|
|
2229
|
-
if (plugin.enforce === "pre") pre.push(plugin);
|
|
2230
|
-
else if (plugin.enforce === "post") post.push(plugin);
|
|
2231
|
-
else normal.push(plugin);
|
|
2232
|
-
}
|
|
2233
|
-
return [...pre, ...normal, ...post];
|
|
2234
|
-
}
|
|
2235
2361
|
var PluginContainer;
|
|
2236
2362
|
var init_plugin_container = __esm({
|
|
2237
2363
|
"src/core/plugin-container.ts"() {
|
|
2238
2364
|
"use strict";
|
|
2365
|
+
init_plugin_api();
|
|
2239
2366
|
PluginContainer = class {
|
|
2240
2367
|
plugins;
|
|
2241
2368
|
config;
|
|
@@ -2246,7 +2373,7 @@ var init_plugin_container = __esm({
|
|
|
2246
2373
|
constructor(config, environment) {
|
|
2247
2374
|
this.config = config;
|
|
2248
2375
|
this.environment = environment;
|
|
2249
|
-
this.plugins =
|
|
2376
|
+
this.plugins = orderPlugins(config.plugins);
|
|
2250
2377
|
this.ctx = this.createContext();
|
|
2251
2378
|
}
|
|
2252
2379
|
createContext() {
|
|
@@ -2542,6 +2669,7 @@ var init_environment = __esm({
|
|
|
2542
2669
|
init_module_graph();
|
|
2543
2670
|
init_hot_channel();
|
|
2544
2671
|
init_debug();
|
|
2672
|
+
init_plugin_api();
|
|
2545
2673
|
debug2 = createDebugger("nasti:environment");
|
|
2546
2674
|
NastiEnvironment = class {
|
|
2547
2675
|
name;
|
|
@@ -2550,6 +2678,7 @@ var init_environment = __esm({
|
|
|
2550
2678
|
config;
|
|
2551
2679
|
options;
|
|
2552
2680
|
hot;
|
|
2681
|
+
driver;
|
|
2553
2682
|
/** applyToEnvironment 过滤后的插件(init() 后可用) */
|
|
2554
2683
|
plugins = [];
|
|
2555
2684
|
/** per-env 插件容器(init() 后可用;dev 管线使用) */
|
|
@@ -2557,6 +2686,7 @@ var init_environment = __esm({
|
|
|
2557
2686
|
/** per-env 模块图(dev 管线使用) */
|
|
2558
2687
|
moduleGraph;
|
|
2559
2688
|
candidatePlugins;
|
|
2689
|
+
pluginApi;
|
|
2560
2690
|
initialized = false;
|
|
2561
2691
|
constructor(name, config, init = {}) {
|
|
2562
2692
|
const options = config.environments[name];
|
|
@@ -2573,6 +2703,7 @@ var init_environment = __esm({
|
|
|
2573
2703
|
this.hot = init.hot ?? createNoopHotChannel();
|
|
2574
2704
|
this.moduleGraph = new ModuleGraph();
|
|
2575
2705
|
this.candidatePlugins = init.plugins ?? config.plugins;
|
|
2706
|
+
this.pluginApi = init.pluginApi ?? getPluginApi(config);
|
|
2576
2707
|
}
|
|
2577
2708
|
/** 过滤插件并建 per-env PluginContainer */
|
|
2578
2709
|
async init() {
|
|
@@ -2583,10 +2714,41 @@ var init_environment = __esm({
|
|
|
2583
2714
|
{ ...this.config, plugins: this.plugins },
|
|
2584
2715
|
this
|
|
2585
2716
|
);
|
|
2717
|
+
if (this.options.driver) {
|
|
2718
|
+
const claimed = [];
|
|
2719
|
+
for (const plugin of this.plugins) {
|
|
2720
|
+
const driver = await plugin.createEnvironmentDriver?.(this, this.pluginApi);
|
|
2721
|
+
if (driver) claimed.push({ plugin, driver });
|
|
2722
|
+
}
|
|
2723
|
+
if (claimed.length === 0) {
|
|
2724
|
+
throw new Error(
|
|
2725
|
+
`[nasti] environment "${this.name}" requested driver "${this.options.driver}", but no plugin provided it`
|
|
2726
|
+
);
|
|
2727
|
+
}
|
|
2728
|
+
if (claimed.length > 1) {
|
|
2729
|
+
throw new Error(
|
|
2730
|
+
`[nasti] environment "${this.name}" was claimed by multiple drivers: ` + claimed.map(({ plugin, driver }) => `${plugin.name} (${driver.name})`).join(", ")
|
|
2731
|
+
);
|
|
2732
|
+
}
|
|
2733
|
+
this.driver = claimed[0].driver;
|
|
2734
|
+
debug2?.(`env "${this.name}" uses driver "${this.driver.name}"`);
|
|
2735
|
+
}
|
|
2586
2736
|
debug2?.(`env "${this.name}" initialized (${this.plugins.length} plugins)`);
|
|
2587
2737
|
}
|
|
2738
|
+
getDriverContext() {
|
|
2739
|
+
return {
|
|
2740
|
+
environment: this,
|
|
2741
|
+
config: this.config,
|
|
2742
|
+
api: this.pluginApi,
|
|
2743
|
+
logger: this.config.logger
|
|
2744
|
+
};
|
|
2745
|
+
}
|
|
2588
2746
|
async close() {
|
|
2589
|
-
|
|
2747
|
+
try {
|
|
2748
|
+
await this.driver?.close?.(this.getDriverContext());
|
|
2749
|
+
} finally {
|
|
2750
|
+
await this.hot.close?.();
|
|
2751
|
+
}
|
|
2590
2752
|
}
|
|
2591
2753
|
};
|
|
2592
2754
|
}
|
|
@@ -2751,6 +2913,7 @@ var build_exports = {};
|
|
|
2751
2913
|
__export(build_exports, {
|
|
2752
2914
|
build: () => build,
|
|
2753
2915
|
getRolldownOptions: () => getRolldownOptions,
|
|
2916
|
+
replaceEntryScript: () => replaceEntryScript,
|
|
2754
2917
|
resolveClientEntries: () => resolveClientEntries,
|
|
2755
2918
|
toRolldownPlugins: () => toRolldownPlugins
|
|
2756
2919
|
});
|
|
@@ -2829,13 +2992,20 @@ function toRolldownPlugins(plugins) {
|
|
|
2829
2992
|
}));
|
|
2830
2993
|
}
|
|
2831
2994
|
function resolveClientEntries(config, html) {
|
|
2995
|
+
const configuredEntries = config.environments.client?.entry ?? [];
|
|
2996
|
+
if (configuredEntries.length > 0) return configuredEntries;
|
|
2832
2997
|
const entryPoints = [];
|
|
2998
|
+
const htmlFile = config.environments.client?.html;
|
|
2999
|
+
const htmlDir = htmlFile ? import_node_path9.default.dirname(htmlFile) : config.root;
|
|
2833
3000
|
if (html) {
|
|
2834
3001
|
const scriptMatches = html.matchAll(/<script[^>]+src=["']([^"']+)["'][^>]*>/gi);
|
|
2835
3002
|
for (const match of scriptMatches) {
|
|
2836
3003
|
const src = match[1];
|
|
2837
3004
|
if (src && !src.startsWith("http")) {
|
|
2838
|
-
|
|
3005
|
+
const cleanSrc = src.split(/[?#]/, 1)[0];
|
|
3006
|
+
entryPoints.push(
|
|
3007
|
+
cleanSrc.startsWith("/") ? import_node_path9.default.resolve(config.root, cleanSrc.replace(/^\//, "")) : import_node_path9.default.resolve(htmlDir, cleanSrc)
|
|
3008
|
+
);
|
|
2839
3009
|
}
|
|
2840
3010
|
}
|
|
2841
3011
|
}
|
|
@@ -2871,21 +3041,55 @@ async function build(inlineConfig = {}) {
|
|
|
2871
3041
|
const startTime = performance.now();
|
|
2872
3042
|
logger.info(
|
|
2873
3043
|
import_picocolors4.default.cyan(`
|
|
2874
|
-
nasti v${"2.
|
|
3044
|
+
nasti v${"2.3.1"} `) + import_picocolors4.default.green(`building for ${config.mode}...`)
|
|
2875
3045
|
);
|
|
2876
3046
|
debug4?.(`root: ${config.root}`);
|
|
2877
3047
|
const buildableNames = Object.keys(config.environments).filter(
|
|
2878
|
-
(name) => name === "client" || config.environments[name].entry.length > 0
|
|
3048
|
+
(name) => name === "client" || config.environments[name].entry.length > 0 || !!config.environments[name].driver
|
|
2879
3049
|
);
|
|
2880
3050
|
buildableNames.sort((a, b) => a === "client" ? -1 : b === "client" ? 1 : 0);
|
|
2881
3051
|
const environments = {};
|
|
3052
|
+
const environmentResults = {};
|
|
3053
|
+
const initializedEnvironments = [];
|
|
2882
3054
|
let clientOutput = [];
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
3055
|
+
let buildFailed = false;
|
|
3056
|
+
try {
|
|
3057
|
+
for (const name of buildableNames) {
|
|
3058
|
+
const built = name === "client" ? await buildClientEnvironment(config) : await buildServerEnvironment(config, name);
|
|
3059
|
+
initializedEnvironments.push(built.environment);
|
|
3060
|
+
environments[name] = built.result.output;
|
|
3061
|
+
environmentResults[name] = built.result;
|
|
3062
|
+
if (name === "client") clientOutput = built.result.output;
|
|
3063
|
+
if (buildableNames.length > 1) {
|
|
3064
|
+
debug4?.(`environment "${name}" built (${built.result.output.length} files)`);
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
3067
|
+
const pluginApi = getPluginApi(config);
|
|
3068
|
+
for (const plugin of config.plugins) {
|
|
3069
|
+
await plugin.afterBuildApp?.(environmentResults, pluginApi);
|
|
3070
|
+
}
|
|
3071
|
+
} catch (error) {
|
|
3072
|
+
buildFailed = true;
|
|
3073
|
+
throw error;
|
|
3074
|
+
} finally {
|
|
3075
|
+
let closeFailed = false;
|
|
3076
|
+
let firstCloseError;
|
|
3077
|
+
for (const environment of [...initializedEnvironments].reverse()) {
|
|
3078
|
+
try {
|
|
3079
|
+
await environment.close();
|
|
3080
|
+
} catch (error) {
|
|
3081
|
+
if (!closeFailed) {
|
|
3082
|
+
closeFailed = true;
|
|
3083
|
+
firstCloseError = error;
|
|
3084
|
+
}
|
|
3085
|
+
const closeError = error instanceof Error ? error : new Error(String(error));
|
|
3086
|
+
logger.error(`[nasti] failed to close environment "${environment.name}"`, {
|
|
3087
|
+
error: closeError
|
|
3088
|
+
});
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
3091
|
+
if (closeFailed && !buildFailed) {
|
|
3092
|
+
throw firstCloseError;
|
|
2889
3093
|
}
|
|
2890
3094
|
}
|
|
2891
3095
|
const elapsed = ((performance.now() - startTime) / 1e3).toFixed(2);
|
|
@@ -2898,83 +3102,130 @@ nasti v${"2.2.0"} `) + import_picocolors4.default.green(`building for ${config.m
|
|
|
2898
3102
|
const envSuffix = buildableNames.length > 1 ? ` (${buildableNames.join(" + ")})` : "";
|
|
2899
3103
|
logger.info(import_picocolors4.default.green(`\u2713 built in ${elapsed}s`) + import_picocolors4.default.dim(envSuffix));
|
|
2900
3104
|
logger.info(import_picocolors4.default.dim(` ${fileCount} files, ${displaySize(totalSize)} total \u2192 ${config.build.outDir}/`));
|
|
2901
|
-
return { output: clientOutput, environments };
|
|
3105
|
+
return { output: clientOutput, environments, environmentResults };
|
|
2902
3106
|
}
|
|
2903
3107
|
async function buildClientEnvironment(config) {
|
|
2904
3108
|
const logger = config.logger;
|
|
2905
3109
|
const outDir = import_node_path9.default.resolve(config.root, config.build.outDir);
|
|
2906
|
-
if (config.build.emptyOutDir && import_node_fs6.default.existsSync(outDir)) {
|
|
2907
|
-
import_node_fs6.default.rmSync(outDir, { recursive: true, force: true });
|
|
2908
|
-
}
|
|
2909
|
-
import_node_fs6.default.mkdirSync(outDir, { recursive: true });
|
|
2910
|
-
const html = await readHtmlFile(config.root);
|
|
2911
|
-
const entryPoints = resolveClientEntries(config, html);
|
|
2912
|
-
if (entryPoints.length === 0) {
|
|
2913
|
-
throw new Error("No entry point found. Add a <script> tag to index.html or create src/main.ts");
|
|
2914
|
-
}
|
|
2915
3110
|
const cssEngine = createCssEngine();
|
|
2916
3111
|
const pluginList = resolvePluginList(config, config.plugins, { cssEngine });
|
|
2917
|
-
const clientEnv = new NastiEnvironment("client",
|
|
3112
|
+
const clientEnv = new NastiEnvironment("client", config, {
|
|
2918
3113
|
mode: "build",
|
|
2919
|
-
plugins: pluginList
|
|
3114
|
+
plugins: pluginList,
|
|
3115
|
+
pluginApi: getPluginApi(config)
|
|
2920
3116
|
});
|
|
2921
3117
|
await clientEnv.init();
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
...nativeReporter ? [nativeReporter] : []
|
|
2928
|
-
];
|
|
2929
|
-
const { inputOptions, outputOptions } = getRolldownOptions(clientEnv, entryPoints, rolldownPlugins);
|
|
2930
|
-
const bundle2 = await (0, import_rolldown.rolldown)(inputOptions);
|
|
2931
|
-
const { output } = await bundle2.write(outputOptions);
|
|
2932
|
-
await bundle2.close();
|
|
2933
|
-
if (html) {
|
|
2934
|
-
let processedHtml = html;
|
|
2935
|
-
const htmlPlugins = [...allPlugins.filter((p) => p.transformIndexHtml), htmlPlugin(config)];
|
|
2936
|
-
for (const p of htmlPlugins) {
|
|
2937
|
-
const result = await p.transformIndexHtml(processedHtml);
|
|
2938
|
-
if (typeof result === "string") {
|
|
2939
|
-
processedHtml = result;
|
|
2940
|
-
} else if (result && "html" in result) {
|
|
2941
|
-
processedHtml = processHtml(result.html, result.tags);
|
|
2942
|
-
} else if (Array.isArray(result)) {
|
|
2943
|
-
processedHtml = processHtml(processedHtml, result);
|
|
2944
|
-
}
|
|
2945
|
-
}
|
|
2946
|
-
processedHtml = injectCssLinks(processedHtml, cssEngine, config);
|
|
2947
|
-
for (const chunk of output) {
|
|
2948
|
-
if (chunk.type === "chunk" && chunk.isEntry && chunk.facadeModuleId) {
|
|
2949
|
-
const originalEntry = import_node_path9.default.relative(config.root, chunk.facadeModuleId);
|
|
2950
|
-
processedHtml = processedHtml.replace(
|
|
2951
|
-
new RegExp(`(src=["'])/?(${escapeRegExp(originalEntry)})(["'])`, "g"),
|
|
2952
|
-
`$1${config.base}${chunk.fileName}$3`
|
|
3118
|
+
try {
|
|
3119
|
+
if (clientEnv.driver) {
|
|
3120
|
+
if (!clientEnv.driver.build) {
|
|
3121
|
+
throw new Error(
|
|
3122
|
+
`[nasti] environment "client" driver "${clientEnv.driver.name}" does not implement build()`
|
|
2953
3123
|
);
|
|
2954
3124
|
}
|
|
3125
|
+
const result = await clientEnv.driver.build(clientEnv.getDriverContext());
|
|
3126
|
+
return { environment: clientEnv, result };
|
|
2955
3127
|
}
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
3128
|
+
if (config.build.emptyOutDir && import_node_fs6.default.existsSync(outDir)) {
|
|
3129
|
+
import_node_fs6.default.rmSync(outDir, { recursive: true, force: true });
|
|
3130
|
+
}
|
|
3131
|
+
import_node_fs6.default.mkdirSync(outDir, { recursive: true });
|
|
3132
|
+
const htmlFile = config.environments.client.html ?? import_node_path9.default.resolve(config.root, "index.html");
|
|
3133
|
+
const html = await readHtmlFile(config.root, htmlFile);
|
|
3134
|
+
const entryPoints = resolveClientEntries(config, html);
|
|
3135
|
+
if (entryPoints.length === 0) {
|
|
3136
|
+
throw new Error("No entry point found. Add a <script> tag to index.html or create src/main.ts");
|
|
3137
|
+
}
|
|
3138
|
+
const allPlugins = clientEnv.plugins;
|
|
3139
|
+
const nativeReporter = config.logLevel === "silent" ? null : await tryNativeReporterPlugin(config, logger);
|
|
3140
|
+
const rolldownPlugins = [
|
|
3141
|
+
createOxcTransformPlugin(config, clientEnv),
|
|
3142
|
+
...toRolldownPlugins(allPlugins),
|
|
3143
|
+
...nativeReporter ? [nativeReporter] : []
|
|
3144
|
+
];
|
|
3145
|
+
const { inputOptions, outputOptions } = getRolldownOptions(
|
|
3146
|
+
clientEnv,
|
|
3147
|
+
entryPoints,
|
|
3148
|
+
rolldownPlugins
|
|
3149
|
+
);
|
|
3150
|
+
const bundle2 = await (0, import_rolldown.rolldown)(inputOptions);
|
|
3151
|
+
const { output } = await bundle2.write(outputOptions);
|
|
3152
|
+
await bundle2.close();
|
|
3153
|
+
if (html) {
|
|
3154
|
+
let processedHtml = html;
|
|
3155
|
+
const htmlPlugins = [...allPlugins.filter((p) => p.transformIndexHtml), htmlPlugin(config)];
|
|
3156
|
+
for (const p of htmlPlugins) {
|
|
3157
|
+
const result = await p.transformIndexHtml(processedHtml);
|
|
3158
|
+
if (typeof result === "string") {
|
|
3159
|
+
processedHtml = result;
|
|
3160
|
+
} else if (result && "html" in result) {
|
|
3161
|
+
processedHtml = processHtml(result.html, result.tags);
|
|
3162
|
+
} else if (Array.isArray(result)) {
|
|
3163
|
+
processedHtml = processHtml(processedHtml, result);
|
|
3164
|
+
}
|
|
3165
|
+
}
|
|
3166
|
+
processedHtml = injectCssLinks(processedHtml, cssEngine, config);
|
|
3167
|
+
for (const chunk of output) {
|
|
3168
|
+
if (chunk.type === "chunk" && chunk.isEntry && chunk.facadeModuleId) {
|
|
3169
|
+
processedHtml = replaceEntryScript(
|
|
3170
|
+
processedHtml,
|
|
3171
|
+
chunk.facadeModuleId,
|
|
3172
|
+
chunk.fileName,
|
|
3173
|
+
config,
|
|
3174
|
+
htmlFile,
|
|
3175
|
+
config.base
|
|
3176
|
+
);
|
|
3177
|
+
}
|
|
3178
|
+
}
|
|
3179
|
+
import_node_fs6.default.writeFileSync(import_node_path9.default.resolve(outDir, "index.html"), processedHtml);
|
|
3180
|
+
}
|
|
3181
|
+
if (!nativeReporter && config.logLevel !== "silent") {
|
|
3182
|
+
reportBuildOutput(output, config, logger);
|
|
3183
|
+
}
|
|
3184
|
+
warnLargeChunks(output, config, logger);
|
|
3185
|
+
return { environment: clientEnv, result: { output } };
|
|
3186
|
+
} catch (error) {
|
|
3187
|
+
try {
|
|
3188
|
+
await clientEnv.close();
|
|
3189
|
+
} catch (closeError) {
|
|
3190
|
+
const normalized = closeError instanceof Error ? closeError : new Error(String(closeError));
|
|
3191
|
+
logger.error("[nasti] failed to close client environment after build failure", {
|
|
3192
|
+
error: normalized
|
|
3193
|
+
});
|
|
3194
|
+
}
|
|
3195
|
+
throw error;
|
|
2960
3196
|
}
|
|
2961
|
-
warnLargeChunks(output, config, logger);
|
|
2962
|
-
return output;
|
|
2963
3197
|
}
|
|
2964
3198
|
async function buildServerEnvironment(config, name) {
|
|
2965
3199
|
const envOptions = config.environments[name];
|
|
2966
3200
|
const logger = config.logger;
|
|
3201
|
+
const pluginList = resolvePluginList(config, config.plugins, { consumer: envOptions.consumer });
|
|
3202
|
+
const environment = new NastiEnvironment(name, config, {
|
|
3203
|
+
mode: "build",
|
|
3204
|
+
plugins: pluginList,
|
|
3205
|
+
pluginApi: getPluginApi(config)
|
|
3206
|
+
});
|
|
3207
|
+
await environment.init();
|
|
3208
|
+
if (environment.driver) {
|
|
3209
|
+
if (!environment.driver.build) {
|
|
3210
|
+
await environment.close();
|
|
3211
|
+
throw new Error(
|
|
3212
|
+
`[nasti] environment "${name}" driver "${environment.driver.name}" does not implement build()`
|
|
3213
|
+
);
|
|
3214
|
+
}
|
|
3215
|
+
try {
|
|
3216
|
+
const result = await environment.driver.build(environment.getDriverContext());
|
|
3217
|
+
return { environment, result };
|
|
3218
|
+
} catch (error) {
|
|
3219
|
+
await environment.close();
|
|
3220
|
+
throw error;
|
|
3221
|
+
}
|
|
3222
|
+
}
|
|
2967
3223
|
for (const entry of envOptions.entry) {
|
|
2968
3224
|
if (!import_node_fs6.default.existsSync(entry)) {
|
|
3225
|
+
await environment.close();
|
|
2969
3226
|
throw new Error(`[nasti] environment "${name}" entry not found: ${entry}`);
|
|
2970
3227
|
}
|
|
2971
3228
|
}
|
|
2972
|
-
const pluginList = resolvePluginList(config, config.plugins, { consumer: envOptions.consumer });
|
|
2973
|
-
const environment = new NastiEnvironment(name, { ...config, plugins: pluginList }, {
|
|
2974
|
-
mode: "build",
|
|
2975
|
-
plugins: pluginList
|
|
2976
|
-
});
|
|
2977
|
-
await environment.init();
|
|
2978
3229
|
const rolldownPlugins = [
|
|
2979
3230
|
createOxcTransformPlugin(config, environment),
|
|
2980
3231
|
...toRolldownPlugins(environment.plugins)
|
|
@@ -2994,7 +3245,7 @@ async function buildServerEnvironment(config, name) {
|
|
|
2994
3245
|
logger.info(
|
|
2995
3246
|
import_picocolors4.default.dim(` [${name}] `) + output.map((o) => import_node_path9.default.join(envOptions.build.outDir, o.fileName)).join(import_picocolors4.default.dim(", "))
|
|
2996
3247
|
);
|
|
2997
|
-
return output;
|
|
3248
|
+
return { environment, result: { output } };
|
|
2998
3249
|
}
|
|
2999
3250
|
function injectCssLinks(html, cssEngine, config) {
|
|
3000
3251
|
const cssLinkTags = [];
|
|
@@ -3020,6 +3271,25 @@ function injectCssLinks(html, cssEngine, config) {
|
|
|
3020
3271
|
function escapeRegExp(string) {
|
|
3021
3272
|
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3022
3273
|
}
|
|
3274
|
+
function replaceEntryScript(html, facadeModuleId, fileName, config, htmlFile, urlPrefix) {
|
|
3275
|
+
const rootRelative = import_node_path9.default.relative(config.root, facadeModuleId).split(import_node_path9.default.sep).join("/");
|
|
3276
|
+
const resolvedHtmlFile = import_node_path9.default.resolve(config.root, htmlFile);
|
|
3277
|
+
const htmlRelative = import_node_path9.default.relative(import_node_path9.default.dirname(resolvedHtmlFile), facadeModuleId).split(import_node_path9.default.sep).join("/");
|
|
3278
|
+
const candidates = /* @__PURE__ */ new Set([
|
|
3279
|
+
rootRelative,
|
|
3280
|
+
`/${rootRelative}`,
|
|
3281
|
+
htmlRelative,
|
|
3282
|
+
`./${htmlRelative}`
|
|
3283
|
+
]);
|
|
3284
|
+
let processed = html;
|
|
3285
|
+
for (const candidate of candidates) {
|
|
3286
|
+
processed = processed.replace(
|
|
3287
|
+
new RegExp(`(src=["'])${escapeRegExp(candidate)}([?#][^"']*)?(["'])`, "g"),
|
|
3288
|
+
`$1${urlPrefix}${fileName}$3`
|
|
3289
|
+
);
|
|
3290
|
+
}
|
|
3291
|
+
return processed;
|
|
3292
|
+
}
|
|
3023
3293
|
var import_node_path9, import_node_fs6, import_node_module3, import_rolldown, import_picocolors4, debug4, NODE_BUILTINS;
|
|
3024
3294
|
var init_build = __esm({
|
|
3025
3295
|
"src/build/index.ts"() {
|
|
@@ -3037,6 +3307,7 @@ var init_build = __esm({
|
|
|
3037
3307
|
init_env();
|
|
3038
3308
|
init_reporter();
|
|
3039
3309
|
init_debug();
|
|
3310
|
+
init_plugin_api();
|
|
3040
3311
|
import_picocolors4 = __toESM(require("picocolors"), 1);
|
|
3041
3312
|
debug4 = createDebugger("nasti:build");
|
|
3042
3313
|
NODE_BUILTINS = /* @__PURE__ */ new Set([...import_node_module3.builtinModules, ...import_node_module3.builtinModules.map((m) => `node:${m}`)]);
|
|
@@ -3200,7 +3471,10 @@ function transformMiddleware(ctx) {
|
|
|
3200
3471
|
return;
|
|
3201
3472
|
}
|
|
3202
3473
|
if (url === "/" || url.endsWith(".html")) {
|
|
3203
|
-
const html = await readHtmlFile(
|
|
3474
|
+
const html = await readHtmlFile(
|
|
3475
|
+
ctx.config.root,
|
|
3476
|
+
ctx.config.environments.client?.html
|
|
3477
|
+
);
|
|
3204
3478
|
if (html) {
|
|
3205
3479
|
let processedHtml = html;
|
|
3206
3480
|
for (const plugin of ctx.config.plugins) {
|
|
@@ -4235,7 +4509,7 @@ async function createBundledDevServer(opts) {
|
|
|
4235
4509
|
`[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.`
|
|
4236
4510
|
);
|
|
4237
4511
|
}
|
|
4238
|
-
const html = await readHtmlFile(config.root);
|
|
4512
|
+
const html = await readHtmlFile(config.root, config.environments.client?.html);
|
|
4239
4513
|
const entryPoints = resolveClientEntries(config, html);
|
|
4240
4514
|
if (entryPoints.length === 0) {
|
|
4241
4515
|
throw new Error("No entry point found. Add a <script> tag to index.html or create src/main.ts");
|
|
@@ -4441,7 +4715,7 @@ async function createBundledDevServer(opts) {
|
|
|
4441
4715
|
return;
|
|
4442
4716
|
}
|
|
4443
4717
|
if (pathname === "/" || pathname.endsWith(".html")) {
|
|
4444
|
-
const rawHtml = await readHtmlFile(config.root);
|
|
4718
|
+
const rawHtml = await readHtmlFile(config.root, config.environments.client?.html);
|
|
4445
4719
|
if (rawHtml) {
|
|
4446
4720
|
res.setHeader("Content-Type", "text/html");
|
|
4447
4721
|
res.setHeader("Cache-Control", "no-store");
|
|
@@ -4525,10 +4799,13 @@ async function renderBundledIndexHtml(html, config, entryFileNames) {
|
|
|
4525
4799
|
}
|
|
4526
4800
|
}
|
|
4527
4801
|
for (const [facadeModuleId, fileName] of entryFileNames) {
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4802
|
+
processed = replaceEntryScript(
|
|
4803
|
+
processed,
|
|
4804
|
+
facadeModuleId,
|
|
4805
|
+
fileName,
|
|
4806
|
+
config,
|
|
4807
|
+
config.environments.client?.html ?? "index.html",
|
|
4808
|
+
"/"
|
|
4532
4809
|
);
|
|
4533
4810
|
}
|
|
4534
4811
|
return processed;
|
|
@@ -4669,10 +4946,12 @@ async function createServer(inlineConfig = {}) {
|
|
|
4669
4946
|
const app = (0, import_connect.default)();
|
|
4670
4947
|
const httpServer = import_node_http.default.createServer(app);
|
|
4671
4948
|
const ws = createWebSocketServer(httpServer);
|
|
4672
|
-
const
|
|
4949
|
+
const pluginApi = getPluginApi(config);
|
|
4950
|
+
const clientEnv = new NastiEnvironment("client", config, {
|
|
4673
4951
|
hot: createWsHotChannel(ws),
|
|
4674
4952
|
mode: "dev",
|
|
4675
|
-
plugins: allPlugins
|
|
4953
|
+
plugins: allPlugins,
|
|
4954
|
+
pluginApi
|
|
4676
4955
|
});
|
|
4677
4956
|
await clientEnv.init();
|
|
4678
4957
|
const environments = { client: clientEnv };
|
|
@@ -4680,11 +4959,15 @@ async function createServer(inlineConfig = {}) {
|
|
|
4680
4959
|
if (name === "client") continue;
|
|
4681
4960
|
const consumer = config.environments[name].consumer;
|
|
4682
4961
|
const envPlugins = resolvePluginList(config, config.plugins, { consumer });
|
|
4683
|
-
environments[name] = new NastiEnvironment(name,
|
|
4962
|
+
environments[name] = new NastiEnvironment(name, config, {
|
|
4684
4963
|
mode: "dev",
|
|
4685
|
-
plugins: envPlugins
|
|
4964
|
+
plugins: envPlugins,
|
|
4965
|
+
pluginApi
|
|
4686
4966
|
});
|
|
4687
4967
|
}
|
|
4968
|
+
for (const [name, environment] of Object.entries(environments)) {
|
|
4969
|
+
if (name !== "client" && environment.options.driver) await environment.init();
|
|
4970
|
+
}
|
|
4688
4971
|
let ssrRunner = null;
|
|
4689
4972
|
async function getSsrRunner() {
|
|
4690
4973
|
if (ssrRunner) return ssrRunner;
|
|
@@ -4709,14 +4992,6 @@ async function createServer(inlineConfig = {}) {
|
|
|
4709
4992
|
});
|
|
4710
4993
|
app.use(bundledServer.middleware);
|
|
4711
4994
|
}
|
|
4712
|
-
app.use(transformMiddleware({
|
|
4713
|
-
config: configWithPlugins,
|
|
4714
|
-
pluginContainer,
|
|
4715
|
-
moduleGraph
|
|
4716
|
-
}));
|
|
4717
|
-
const publicDir = import_node_path15.default.resolve(config.root, "public");
|
|
4718
|
-
app.use((0, import_sirv.default)(publicDir, { dev: true, etag: true }));
|
|
4719
|
-
app.use((0, import_sirv.default)(config.root, { dev: true, etag: true }));
|
|
4720
4995
|
const ignoredSegments = /* @__PURE__ */ new Set(["node_modules", ".git", ".nasti"]);
|
|
4721
4996
|
const outDirAbs = import_node_path15.default.resolve(config.root, config.build.outDir);
|
|
4722
4997
|
const watcher = (0, import_chokidar.watch)(config.root, {
|
|
@@ -4733,13 +5008,72 @@ async function createServer(inlineConfig = {}) {
|
|
|
4733
5008
|
ignoreInitial: true
|
|
4734
5009
|
});
|
|
4735
5010
|
let server;
|
|
5011
|
+
const environmentServices = {};
|
|
5012
|
+
let environmentDriversStarted = false;
|
|
5013
|
+
const logCloseError = (target, error) => {
|
|
5014
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
5015
|
+
logger.error(`[nasti] failed to close ${target}`, { error: normalized });
|
|
5016
|
+
};
|
|
5017
|
+
const startEnvironmentDrivers = async () => {
|
|
5018
|
+
if (environmentDriversStarted) return;
|
|
5019
|
+
environmentDriversStarted = true;
|
|
5020
|
+
const started = [];
|
|
5021
|
+
const attempted = [];
|
|
5022
|
+
try {
|
|
5023
|
+
for (const [name, environment] of Object.entries(environments)) {
|
|
5024
|
+
if (!environment.driver?.serve) continue;
|
|
5025
|
+
attempted.push(environment);
|
|
5026
|
+
const result = await environment.driver.serve({
|
|
5027
|
+
...environment.getDriverContext(),
|
|
5028
|
+
server
|
|
5029
|
+
});
|
|
5030
|
+
started.push({ name, environment, service: result ?? {} });
|
|
5031
|
+
}
|
|
5032
|
+
for (const { name, service } of started) {
|
|
5033
|
+
environmentServices[name] = service;
|
|
5034
|
+
if (service.middleware) app.use(service.middleware);
|
|
5035
|
+
}
|
|
5036
|
+
} catch (error) {
|
|
5037
|
+
environmentDriversStarted = false;
|
|
5038
|
+
for (const { name } of started) {
|
|
5039
|
+
delete environmentServices[name];
|
|
5040
|
+
}
|
|
5041
|
+
for (const environment of attempted.reverse()) {
|
|
5042
|
+
try {
|
|
5043
|
+
await environment.driver?.close?.(environment.getDriverContext());
|
|
5044
|
+
} catch (closeError) {
|
|
5045
|
+
logCloseError(`environment driver "${environment.driver.name}"`, closeError);
|
|
5046
|
+
}
|
|
5047
|
+
}
|
|
5048
|
+
throw error;
|
|
5049
|
+
}
|
|
5050
|
+
};
|
|
5051
|
+
const notifyEnvironmentDrivers = (file, event) => {
|
|
5052
|
+
for (const environment of Object.values(environments)) {
|
|
5053
|
+
if (!environment.driver?.watchChange) continue;
|
|
5054
|
+
void Promise.resolve(
|
|
5055
|
+
environment.driver.watchChange(file, event, environment.getDriverContext())
|
|
5056
|
+
).catch((error) => {
|
|
5057
|
+
logger.error(
|
|
5058
|
+
`[nasti] environment driver "${environment.driver.name}" watchChange failed`,
|
|
5059
|
+
{ error }
|
|
5060
|
+
);
|
|
5061
|
+
});
|
|
5062
|
+
}
|
|
5063
|
+
};
|
|
4736
5064
|
watcher.on("change", (file) => {
|
|
4737
5065
|
ssrRunner?.invalidateFile(file);
|
|
4738
5066
|
handleFileChange(file, server);
|
|
5067
|
+
notifyEnvironmentDrivers(file, "change");
|
|
4739
5068
|
});
|
|
4740
5069
|
watcher.on("add", (file) => {
|
|
4741
5070
|
ssrRunner?.invalidateFile(file);
|
|
4742
5071
|
handleFileChange(file, server);
|
|
5072
|
+
notifyEnvironmentDrivers(file, "add");
|
|
5073
|
+
});
|
|
5074
|
+
watcher.on("unlink", (file) => {
|
|
5075
|
+
ssrRunner?.invalidateFile(file);
|
|
5076
|
+
notifyEnvironmentDrivers(file, "unlink");
|
|
4743
5077
|
});
|
|
4744
5078
|
server = {
|
|
4745
5079
|
config: configWithPlugins,
|
|
@@ -4748,10 +5082,12 @@ async function createServer(inlineConfig = {}) {
|
|
|
4748
5082
|
watcher,
|
|
4749
5083
|
ws,
|
|
4750
5084
|
environments,
|
|
5085
|
+
environmentServices,
|
|
4751
5086
|
async listen(port) {
|
|
4752
5087
|
const finalPort = port ?? config.server.port;
|
|
4753
5088
|
const host = config.server.host === true ? "0.0.0.0" : config.server.host;
|
|
4754
5089
|
await pluginContainer.buildStart();
|
|
5090
|
+
await startEnvironmentDrivers();
|
|
4755
5091
|
return new Promise((resolve, reject) => {
|
|
4756
5092
|
let currentPort = finalPort;
|
|
4757
5093
|
const onListening = () => {
|
|
@@ -4759,15 +5095,20 @@ async function createServer(inlineConfig = {}) {
|
|
|
4759
5095
|
config.server.port = actualPort;
|
|
4760
5096
|
const localUrl = `http://localhost:${actualPort}/`;
|
|
4761
5097
|
const networkUrl = host === "0.0.0.0" ? `http://${getNetworkAddress()}:${actualPort}/` : null;
|
|
5098
|
+
const driverLocalUrls = Object.values(environmentServices).flatMap((service) => service.localUrls ?? []);
|
|
5099
|
+
const driverNetworkUrls = Object.values(environmentServices).flatMap((service) => service.networkUrls ?? []);
|
|
4762
5100
|
logger.clearScreen("info");
|
|
4763
5101
|
const readyIn = Math.ceil(performance.now() - startTime);
|
|
4764
5102
|
logger.info(
|
|
4765
5103
|
`
|
|
4766
|
-
${import_picocolors9.default.cyan(import_picocolors9.default.bold("NASTI"))} ${import_picocolors9.default.cyan(`v${"2.
|
|
5104
|
+
${import_picocolors9.default.cyan(import_picocolors9.default.bold("NASTI"))} ${import_picocolors9.default.cyan(`v${"2.3.1"}`)} ${import_picocolors9.default.dim("ready in")} ${import_picocolors9.default.bold(readyIn)} ${import_picocolors9.default.dim("ms")}
|
|
4767
5105
|
`
|
|
4768
5106
|
);
|
|
4769
5107
|
printServerUrls(
|
|
4770
|
-
{
|
|
5108
|
+
{
|
|
5109
|
+
local: [localUrl, ...driverLocalUrls],
|
|
5110
|
+
network: [...networkUrl ? [networkUrl] : [], ...driverNetworkUrls]
|
|
5111
|
+
},
|
|
4771
5112
|
logger.info
|
|
4772
5113
|
);
|
|
4773
5114
|
logger.info("");
|
|
@@ -4797,11 +5138,62 @@ async function createServer(inlineConfig = {}) {
|
|
|
4797
5138
|
async close() {
|
|
4798
5139
|
await pluginContainer.buildEnd();
|
|
4799
5140
|
await bundledServer?.close();
|
|
4800
|
-
|
|
5141
|
+
let environmentCloseFailed = false;
|
|
5142
|
+
let firstEnvironmentCloseError;
|
|
5143
|
+
for (const environment of Object.values(environments).reverse()) {
|
|
5144
|
+
try {
|
|
5145
|
+
await environment.close();
|
|
5146
|
+
} catch (error) {
|
|
5147
|
+
if (!environmentCloseFailed) {
|
|
5148
|
+
environmentCloseFailed = true;
|
|
5149
|
+
firstEnvironmentCloseError = error;
|
|
5150
|
+
}
|
|
5151
|
+
logCloseError(`environment "${environment.name}"`, error);
|
|
5152
|
+
}
|
|
5153
|
+
}
|
|
5154
|
+
await watcher.close();
|
|
4801
5155
|
ws.close();
|
|
4802
5156
|
httpServer.close();
|
|
5157
|
+
if (environmentCloseFailed) {
|
|
5158
|
+
throw firstEnvironmentCloseError;
|
|
5159
|
+
}
|
|
4803
5160
|
}
|
|
4804
5161
|
};
|
|
5162
|
+
try {
|
|
5163
|
+
await startEnvironmentDrivers();
|
|
5164
|
+
} catch (error) {
|
|
5165
|
+
if (bundledServer) {
|
|
5166
|
+
try {
|
|
5167
|
+
await bundledServer.close();
|
|
5168
|
+
} catch (closeError) {
|
|
5169
|
+
logCloseError("bundled dev server after driver startup failure", closeError);
|
|
5170
|
+
}
|
|
5171
|
+
}
|
|
5172
|
+
try {
|
|
5173
|
+
await watcher.close();
|
|
5174
|
+
} catch (closeError) {
|
|
5175
|
+
logCloseError("file watcher after driver startup failure", closeError);
|
|
5176
|
+
}
|
|
5177
|
+
try {
|
|
5178
|
+
ws.close();
|
|
5179
|
+
} catch (closeError) {
|
|
5180
|
+
logCloseError("WebSocket server after driver startup failure", closeError);
|
|
5181
|
+
}
|
|
5182
|
+
try {
|
|
5183
|
+
httpServer.close();
|
|
5184
|
+
} catch (closeError) {
|
|
5185
|
+
logCloseError("HTTP server after driver startup failure", closeError);
|
|
5186
|
+
}
|
|
5187
|
+
throw error;
|
|
5188
|
+
}
|
|
5189
|
+
app.use(transformMiddleware({
|
|
5190
|
+
config: configWithPlugins,
|
|
5191
|
+
pluginContainer,
|
|
5192
|
+
moduleGraph
|
|
5193
|
+
}));
|
|
5194
|
+
const publicDir = import_node_path15.default.resolve(config.root, "public");
|
|
5195
|
+
app.use((0, import_sirv.default)(publicDir, { dev: true, etag: true }));
|
|
5196
|
+
app.use((0, import_sirv.default)(config.root, { dev: true, etag: true }));
|
|
4805
5197
|
const postMiddlewares = [];
|
|
4806
5198
|
for (const plugin of allPlugins) {
|
|
4807
5199
|
if (plugin.configureServer) {
|
|
@@ -4844,6 +5236,7 @@ var init_server = __esm({
|
|
|
4844
5236
|
init_middleware();
|
|
4845
5237
|
init_hmr();
|
|
4846
5238
|
init_builtins();
|
|
5239
|
+
init_plugin_api();
|
|
4847
5240
|
}
|
|
4848
5241
|
});
|
|
4849
5242
|
|
|
@@ -4856,12 +5249,15 @@ __export(src_exports, {
|
|
|
4856
5249
|
buildElectron: () => buildElectron,
|
|
4857
5250
|
buildEnvDefine: () => buildEnvDefine,
|
|
4858
5251
|
createDebugger: () => createDebugger,
|
|
5252
|
+
createElectronRendererConfig: () => createElectronRendererConfig,
|
|
4859
5253
|
createLogger: () => createLogger,
|
|
4860
5254
|
createNoopHotChannel: () => createNoopHotChannel,
|
|
4861
5255
|
createServer: () => createServer,
|
|
4862
5256
|
createWsHotChannel: () => createWsHotChannel,
|
|
4863
5257
|
defineConfig: () => defineConfig,
|
|
5258
|
+
detectFramework: () => detectFramework,
|
|
4864
5259
|
electronPlugin: () => electronPlugin,
|
|
5260
|
+
electronRendererDevPath: () => electronRendererDevPath,
|
|
4865
5261
|
loadEnv: () => loadEnv,
|
|
4866
5262
|
monacoEditorPlugin: () => monacoEditorPlugin,
|
|
4867
5263
|
printServerUrls: () => printServerUrls,
|
|
@@ -4922,7 +5318,7 @@ async function buildElectron(inlineConfig = {}) {
|
|
|
4922
5318
|
const config = await resolveConfig({ ...inlineConfig, target: "electron" }, "build");
|
|
4923
5319
|
const startTime = performance.now();
|
|
4924
5320
|
assertElectronVersion(config);
|
|
4925
|
-
console.log(import_picocolors5.default.cyan("\n\u26A1 nasti build (electron)") + import_picocolors5.default.dim(` v${"2.
|
|
5321
|
+
console.log(import_picocolors5.default.cyan("\n\u26A1 nasti build (electron)") + import_picocolors5.default.dim(` v${"2.3.1"}`));
|
|
4926
5322
|
console.log(import_picocolors5.default.dim(` root: ${config.root}`));
|
|
4927
5323
|
console.log(import_picocolors5.default.dim(` mode: ${config.mode}`));
|
|
4928
5324
|
console.log(import_picocolors5.default.dim(` target: electron (\u2265 ${config.electron.minVersion})`));
|
|
@@ -4933,15 +5329,13 @@ async function buildElectron(inlineConfig = {}) {
|
|
|
4933
5329
|
import_node_fs7.default.mkdirSync(outDir, { recursive: true });
|
|
4934
5330
|
const rendererOutDir = import_node_path10.default.join(outDir, "renderer");
|
|
4935
5331
|
const { build: build2 } = await Promise.resolve().then(() => (init_build(), build_exports));
|
|
4936
|
-
await build2({
|
|
4937
|
-
...inlineConfig,
|
|
4938
|
-
target: "web",
|
|
5332
|
+
await build2(createElectronRendererConfig(config, inlineConfig, {
|
|
4939
5333
|
build: {
|
|
4940
5334
|
...inlineConfig.build,
|
|
4941
5335
|
outDir: rendererOutDir,
|
|
4942
5336
|
emptyOutDir: false
|
|
4943
5337
|
}
|
|
4944
|
-
});
|
|
5338
|
+
}));
|
|
4945
5339
|
const mainEntry = import_node_path10.default.resolve(config.root, config.electron.main);
|
|
4946
5340
|
if (!import_node_fs7.default.existsSync(mainEntry)) {
|
|
4947
5341
|
throw new Error(
|
|
@@ -4995,7 +5389,8 @@ async function bundleNode(config, entry, opts) {
|
|
|
4995
5389
|
const result = transformCode(id, code, {
|
|
4996
5390
|
sourcemap: !!config.build.sourcemap,
|
|
4997
5391
|
jsxRuntime: "automatic",
|
|
4998
|
-
jsxImportSource: config.framework === "vue" ? "vue" : "react"
|
|
5392
|
+
jsxImportSource: config.framework === "vue" ? "vue" : "react",
|
|
5393
|
+
target: config.electron.nodeTarget
|
|
4999
5394
|
});
|
|
5000
5395
|
return { code: result.code, map: result.map ? JSON.parse(result.map) : void 0 };
|
|
5001
5396
|
}
|
|
@@ -5006,7 +5401,11 @@ async function bundleNode(config, entry, opts) {
|
|
|
5006
5401
|
...restInputOptions,
|
|
5007
5402
|
input: entry,
|
|
5008
5403
|
platform: "node",
|
|
5009
|
-
transform: {
|
|
5404
|
+
transform: {
|
|
5405
|
+
...userTransform,
|
|
5406
|
+
target: config.electron.nodeTarget,
|
|
5407
|
+
define: mergedDefine
|
|
5408
|
+
},
|
|
5010
5409
|
plugins: [oxcTransformPlugin, electronPlugin(config), resolvePlugin(config)]
|
|
5011
5410
|
});
|
|
5012
5411
|
import_node_fs7.default.mkdirSync(import_node_path10.default.dirname(opts.outFile), { recursive: true });
|
|
@@ -5023,6 +5422,25 @@ async function bundleNode(config, entry, opts) {
|
|
|
5023
5422
|
console.log(import_picocolors5.default.dim(` \u2713 ${opts.label} \u2192 ${import_node_path10.default.relative(config.root, opts.outFile)}`));
|
|
5024
5423
|
return opts.outFile;
|
|
5025
5424
|
}
|
|
5425
|
+
function createElectronRendererConfig(config, inlineConfig = {}, overrides = {}) {
|
|
5426
|
+
const inlineClient = inlineConfig.environments?.client ?? {};
|
|
5427
|
+
return {
|
|
5428
|
+
...inlineConfig,
|
|
5429
|
+
...overrides,
|
|
5430
|
+
root: config.root,
|
|
5431
|
+
mode: config.mode,
|
|
5432
|
+
target: "web",
|
|
5433
|
+
framework: config.framework,
|
|
5434
|
+
base: config.base === "/" ? "./" : config.base,
|
|
5435
|
+
environments: {
|
|
5436
|
+
...inlineConfig.environments ?? {},
|
|
5437
|
+
client: {
|
|
5438
|
+
...inlineClient,
|
|
5439
|
+
html: config.electron.renderer
|
|
5440
|
+
}
|
|
5441
|
+
}
|
|
5442
|
+
};
|
|
5443
|
+
}
|
|
5026
5444
|
function outFileName(outDir, base, format) {
|
|
5027
5445
|
const ext = format === "cjs" ? ".cjs" : ".mjs";
|
|
5028
5446
|
return import_node_path10.default.join(outDir, base + ext);
|
|
@@ -5073,11 +5491,15 @@ async function startElectronDev(inlineConfig = {}) {
|
|
|
5073
5491
|
const { noSpawn, ...rest } = inlineConfig;
|
|
5074
5492
|
const config = await resolveConfig({ ...rest, target: "electron" }, "serve");
|
|
5075
5493
|
warnElectronVersion(config);
|
|
5076
|
-
console.log(import_picocolors10.default.cyan("\n\u26A1 nasti electron dev") + import_picocolors10.default.dim(` v${"2.
|
|
5494
|
+
console.log(import_picocolors10.default.cyan("\n\u26A1 nasti electron dev") + import_picocolors10.default.dim(` v${"2.3.1"}`));
|
|
5077
5495
|
const { createServer: createServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
|
|
5078
|
-
const server = await createServer2({
|
|
5496
|
+
const server = await createServer2({
|
|
5497
|
+
...rest,
|
|
5498
|
+
target: "electron",
|
|
5499
|
+
framework: config.framework
|
|
5500
|
+
});
|
|
5079
5501
|
await server.listen();
|
|
5080
|
-
const devUrl = `http://localhost:${server.config.server.port}
|
|
5502
|
+
const devUrl = `http://localhost:${server.config.server.port}` + electronRendererDevPath(config.electron.renderer);
|
|
5081
5503
|
console.log(import_picocolors10.default.dim(` renderer: ${devUrl}`));
|
|
5082
5504
|
const stageDir = import_node_path16.default.resolve(config.root, ".nasti");
|
|
5083
5505
|
import_node_fs11.default.mkdirSync(stageDir, { recursive: true });
|
|
@@ -5199,14 +5621,18 @@ async function compileNode(config, entry, opts) {
|
|
|
5199
5621
|
const result = transformCode(id, code, {
|
|
5200
5622
|
sourcemap: true,
|
|
5201
5623
|
jsxRuntime: "automatic",
|
|
5202
|
-
jsxImportSource: config.framework === "vue" ? "vue" : "react"
|
|
5624
|
+
jsxImportSource: config.framework === "vue" ? "vue" : "react",
|
|
5625
|
+
target: config.electron.nodeTarget
|
|
5203
5626
|
});
|
|
5204
5627
|
return { code: result.code, map: result.map ? JSON.parse(result.map) : void 0 };
|
|
5205
5628
|
}
|
|
5206
5629
|
};
|
|
5207
5630
|
const bundle2 = await (0, import_rolldown3.rolldown)({
|
|
5208
5631
|
input: entry,
|
|
5209
|
-
transform: {
|
|
5632
|
+
transform: {
|
|
5633
|
+
target: config.electron.nodeTarget,
|
|
5634
|
+
define: envDefine
|
|
5635
|
+
},
|
|
5210
5636
|
platform: "node",
|
|
5211
5637
|
plugins: [oxcTransformPlugin, electronPlugin(config), resolvePlugin(config)]
|
|
5212
5638
|
});
|
|
@@ -5222,6 +5648,10 @@ async function compileNode(config, entry, opts) {
|
|
|
5222
5648
|
});
|
|
5223
5649
|
await bundle2.close();
|
|
5224
5650
|
}
|
|
5651
|
+
function electronRendererDevPath(renderer) {
|
|
5652
|
+
const normalized = renderer.split(import_node_path16.default.sep).join("/").replace(/^\.?\//, "");
|
|
5653
|
+
return normalized === "index.html" ? "/" : `/${normalized}`;
|
|
5654
|
+
}
|
|
5225
5655
|
function resolveElectronBinary(config) {
|
|
5226
5656
|
if (config.electron.electronPath && import_node_fs11.default.existsSync(config.electron.electronPath)) {
|
|
5227
5657
|
return config.electron.electronPath;
|
|
@@ -5479,12 +5909,15 @@ init_env();
|
|
|
5479
5909
|
buildElectron,
|
|
5480
5910
|
buildEnvDefine,
|
|
5481
5911
|
createDebugger,
|
|
5912
|
+
createElectronRendererConfig,
|
|
5482
5913
|
createLogger,
|
|
5483
5914
|
createNoopHotChannel,
|
|
5484
5915
|
createServer,
|
|
5485
5916
|
createWsHotChannel,
|
|
5486
5917
|
defineConfig,
|
|
5918
|
+
detectFramework,
|
|
5487
5919
|
electronPlugin,
|
|
5920
|
+
electronRendererDevPath,
|
|
5488
5921
|
loadEnv,
|
|
5489
5922
|
monacoEditorPlugin,
|
|
5490
5923
|
printServerUrls,
|