@nasti-toolchain/nasti 1.3.9 → 1.4.0
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 +83 -2
- package/dist/cli.cjs +486 -24
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +484 -24
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1752 -1288
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +82 -1
- package/dist/index.d.ts +82 -1
- package/dist/index.js +1738 -1281
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8,1415 +8,1475 @@ var __export = (target, all) => {
|
|
|
8
8
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
// src/
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
11
|
+
// src/config/defaults.ts
|
|
12
|
+
var defaultResolve, defaultServer, defaultBuild, defaultElectron, defaults;
|
|
13
|
+
var init_defaults = __esm({
|
|
14
|
+
"src/config/defaults.ts"() {
|
|
15
|
+
"use strict";
|
|
16
|
+
defaultResolve = {
|
|
17
|
+
alias: {},
|
|
18
|
+
extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".json", ".vue"],
|
|
19
|
+
conditions: ["import", "module", "browser", "default"],
|
|
20
|
+
mainFields: ["module", "jsnext:main", "jsnext", "main"]
|
|
21
|
+
};
|
|
22
|
+
defaultServer = {
|
|
23
|
+
port: 3e3,
|
|
24
|
+
host: "localhost",
|
|
25
|
+
https: false,
|
|
26
|
+
open: false,
|
|
27
|
+
proxy: {},
|
|
28
|
+
cors: true,
|
|
29
|
+
hmr: true
|
|
30
|
+
};
|
|
31
|
+
defaultBuild = {
|
|
32
|
+
outDir: "dist",
|
|
33
|
+
assetsDir: "assets",
|
|
34
|
+
minify: true,
|
|
35
|
+
sourcemap: false,
|
|
36
|
+
target: "es2022",
|
|
37
|
+
rolldownOptions: {},
|
|
38
|
+
emptyOutDir: true
|
|
39
|
+
};
|
|
40
|
+
defaultElectron = {
|
|
41
|
+
main: "src/electron/main.ts",
|
|
42
|
+
preload: "src/electron/preload.ts",
|
|
43
|
+
renderer: "index.html",
|
|
44
|
+
nodeTarget: "node22",
|
|
45
|
+
mainFormat: "cjs",
|
|
46
|
+
preloadFormat: "cjs",
|
|
47
|
+
electronPath: "",
|
|
48
|
+
electronArgs: [],
|
|
49
|
+
autoRestart: true,
|
|
50
|
+
minVersion: 41,
|
|
51
|
+
external: ["electron"]
|
|
52
|
+
};
|
|
53
|
+
defaults = {
|
|
54
|
+
root: ".",
|
|
55
|
+
base: "/",
|
|
56
|
+
mode: "development",
|
|
57
|
+
target: "web",
|
|
58
|
+
framework: "auto",
|
|
59
|
+
resolve: defaultResolve,
|
|
60
|
+
server: defaultServer,
|
|
61
|
+
build: defaultBuild,
|
|
62
|
+
electron: defaultElectron,
|
|
63
|
+
plugins: [],
|
|
64
|
+
envPrefix: ["NASTI_", "VITE_"],
|
|
65
|
+
logLevel: "info"
|
|
66
|
+
};
|
|
51
67
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// src/config/index.ts
|
|
71
|
+
import { pathToFileURL } from "url";
|
|
72
|
+
import path from "path";
|
|
73
|
+
import fs from "fs";
|
|
74
|
+
function loadTsconfigPaths(root) {
|
|
75
|
+
const tsconfigPath = path.resolve(root, "tsconfig.json");
|
|
76
|
+
if (!fs.existsSync(tsconfigPath)) return {};
|
|
77
|
+
try {
|
|
78
|
+
const content = fs.readFileSync(tsconfigPath, "utf-8");
|
|
79
|
+
const stripped = content.replace(/\/\/[^\n]*/g, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
80
|
+
const tsconfig = JSON.parse(stripped);
|
|
81
|
+
const paths = tsconfig?.compilerOptions?.paths ?? {};
|
|
82
|
+
const baseUrl = tsconfig?.compilerOptions?.baseUrl ?? ".";
|
|
83
|
+
const alias = {};
|
|
84
|
+
for (const [pattern, targets] of Object.entries(paths)) {
|
|
85
|
+
if (!targets.length) continue;
|
|
86
|
+
const cleanKey = pattern.replace(/\/\*$/, "");
|
|
87
|
+
const cleanTarget = targets[0].replace(/\/\*$/, "");
|
|
88
|
+
alias[cleanKey] = path.resolve(root, baseUrl, cleanTarget);
|
|
89
|
+
}
|
|
90
|
+
return alias;
|
|
91
|
+
} catch {
|
|
92
|
+
return {};
|
|
63
93
|
}
|
|
64
|
-
return ` <${tag.tag}${attrs}>${children}</${tag.tag}>`;
|
|
65
94
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (!fs4.existsSync(htmlPath)) return null;
|
|
69
|
-
return fs4.readFileSync(htmlPath, "utf-8");
|
|
95
|
+
function defineConfig(config) {
|
|
96
|
+
return config;
|
|
70
97
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
98
|
+
async function loadConfigFromFile(root) {
|
|
99
|
+
for (const file of CONFIG_FILES) {
|
|
100
|
+
const filePath = path.resolve(root, file);
|
|
101
|
+
if (!fs.existsSync(filePath)) continue;
|
|
102
|
+
if (file.endsWith(".ts") || file.endsWith(".mts")) {
|
|
103
|
+
return await loadTsConfig(filePath);
|
|
104
|
+
}
|
|
105
|
+
const mod = await import(pathToFileURL(filePath).href);
|
|
106
|
+
return mod.default ?? mod;
|
|
74
107
|
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// src/core/transformer.ts
|
|
78
|
-
import { transformSync } from "oxc-transform";
|
|
79
|
-
function shouldTransform(id) {
|
|
80
|
-
return TS_EXTENSIONS.test(id) || JSX_EXTENSIONS.test(id) || JS_EXTENSIONS.test(id) && false;
|
|
108
|
+
return {};
|
|
81
109
|
}
|
|
82
|
-
function
|
|
83
|
-
const
|
|
84
|
-
const
|
|
85
|
-
const result =
|
|
86
|
-
typescript:
|
|
87
|
-
jsx: isJSX || /\.tsx$/.test(filename) ? {
|
|
88
|
-
runtime: options.jsxRuntime ?? "automatic",
|
|
89
|
-
importSource: options.jsxImportSource ?? "react",
|
|
90
|
-
refresh: options.reactRefresh ?? false
|
|
91
|
-
} : void 0,
|
|
92
|
-
sourcemap: options.sourcemap ?? true
|
|
110
|
+
async function loadTsConfig(filePath) {
|
|
111
|
+
const { transformSync: transformSync2 } = await import("oxc-transform");
|
|
112
|
+
const code = fs.readFileSync(filePath, "utf-8");
|
|
113
|
+
const result = transformSync2(filePath, code, {
|
|
114
|
+
typescript: {}
|
|
93
115
|
});
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
116
|
+
const tmpFile = filePath + ".timestamp-" + Date.now() + ".mjs";
|
|
117
|
+
try {
|
|
118
|
+
fs.writeFileSync(tmpFile, result.code);
|
|
119
|
+
const mod = await import(pathToFileURL(tmpFile).href);
|
|
120
|
+
return mod.default ?? mod;
|
|
121
|
+
} finally {
|
|
122
|
+
fs.unlinkSync(tmpFile);
|
|
98
123
|
}
|
|
99
|
-
return {
|
|
100
|
-
code: result.code,
|
|
101
|
-
map: result.map ? JSON.stringify(result.map) : null
|
|
102
|
-
};
|
|
103
124
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
// src/core/env.ts
|
|
115
|
-
import path6 from "path";
|
|
116
|
-
import fs5 from "fs";
|
|
117
|
-
function loadEnv(mode, root, prefixes) {
|
|
118
|
-
const envFiles = [
|
|
119
|
-
".env",
|
|
120
|
-
`.env.${mode}`,
|
|
121
|
-
".env.local",
|
|
122
|
-
`.env.${mode}.local`
|
|
125
|
+
async function resolveConfig(inlineConfig = {}, command) {
|
|
126
|
+
const root = path.resolve(inlineConfig.root ?? defaults.root);
|
|
127
|
+
const fileConfig = await loadConfigFromFile(root);
|
|
128
|
+
const merged = deepMerge(deepMerge({}, fileConfig), inlineConfig);
|
|
129
|
+
const rawPlugins = [
|
|
130
|
+
...fileConfig.plugins ?? [],
|
|
131
|
+
...inlineConfig.plugins ?? []
|
|
123
132
|
];
|
|
124
|
-
const
|
|
125
|
-
for (const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
for (const line of content.split("\n")) {
|
|
130
|
-
const trimmed = line.trim();
|
|
131
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
132
|
-
const eqIdx = trimmed.indexOf("=");
|
|
133
|
-
if (eqIdx === -1) continue;
|
|
134
|
-
const key = trimmed.slice(0, eqIdx).trim();
|
|
135
|
-
let value = trimmed.slice(eqIdx + 1).trim();
|
|
136
|
-
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
137
|
-
value = value.slice(1, -1);
|
|
138
|
-
}
|
|
139
|
-
raw[key] = value;
|
|
133
|
+
const env = { mode: merged.mode ?? defaults.mode, command };
|
|
134
|
+
for (const plugin of rawPlugins) {
|
|
135
|
+
if (plugin.config) {
|
|
136
|
+
const result = await plugin.config(merged, env);
|
|
137
|
+
if (result) Object.assign(merged, result);
|
|
140
138
|
}
|
|
141
139
|
}
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
140
|
+
const resolved = {
|
|
141
|
+
root,
|
|
142
|
+
base: merged.base ?? defaults.base,
|
|
143
|
+
mode: command === "build" ? "production" : "development",
|
|
144
|
+
target: merged.target ?? defaults.target,
|
|
145
|
+
framework: merged.framework ?? defaults.framework,
|
|
146
|
+
command,
|
|
147
|
+
resolve: {
|
|
148
|
+
// tsconfig paths 优先级最低:tsconfig < defaults < user config
|
|
149
|
+
alias: { ...loadTsconfigPaths(root), ...defaults.resolve.alias, ...merged.resolve?.alias },
|
|
150
|
+
extensions: merged.resolve?.extensions ?? defaults.resolve.extensions,
|
|
151
|
+
conditions: merged.resolve?.conditions ?? defaults.resolve.conditions,
|
|
152
|
+
mainFields: merged.resolve?.mainFields ?? defaults.resolve.mainFields
|
|
153
|
+
},
|
|
154
|
+
plugins: [],
|
|
155
|
+
server: { ...defaults.server, ...merged.server },
|
|
156
|
+
build: { ...defaults.build, ...merged.build },
|
|
157
|
+
electron: { ...defaults.electron, ...merged.electron },
|
|
158
|
+
envPrefix: Array.isArray(merged.envPrefix) ? merged.envPrefix : merged.envPrefix ? [merged.envPrefix] : [...defaults.envPrefix],
|
|
159
|
+
logLevel: merged.logLevel ?? defaults.logLevel
|
|
160
|
+
};
|
|
161
|
+
const filteredPlugins = rawPlugins.filter((p) => {
|
|
162
|
+
if (!p.apply) return true;
|
|
163
|
+
if (typeof p.apply === "function") return p.apply(resolved, env);
|
|
164
|
+
return p.apply === command;
|
|
165
|
+
});
|
|
166
|
+
resolved.plugins = filteredPlugins;
|
|
167
|
+
for (const plugin of resolved.plugins) {
|
|
168
|
+
if (plugin.configResolved) {
|
|
169
|
+
await plugin.configResolved(resolved);
|
|
146
170
|
}
|
|
147
171
|
}
|
|
148
|
-
return
|
|
149
|
-
}
|
|
150
|
-
function buildEnvDefine(env, mode) {
|
|
151
|
-
const define = {};
|
|
152
|
-
for (const [key, value] of Object.entries(env)) {
|
|
153
|
-
define[`import.meta.env.${key}`] = JSON.stringify(value);
|
|
154
|
-
}
|
|
155
|
-
define["import.meta.env.MODE"] = JSON.stringify(mode);
|
|
156
|
-
define["import.meta.env.DEV"] = mode !== "production" ? "true" : "false";
|
|
157
|
-
define["import.meta.env.PROD"] = mode === "production" ? "true" : "false";
|
|
158
|
-
define["import.meta.env.SSR"] = "false";
|
|
159
|
-
return define;
|
|
172
|
+
return resolved;
|
|
160
173
|
}
|
|
161
|
-
function
|
|
162
|
-
|
|
163
|
-
for (const
|
|
164
|
-
const
|
|
165
|
-
|
|
174
|
+
function deepMerge(target, source) {
|
|
175
|
+
const result = { ...target };
|
|
176
|
+
for (const key of Object.keys(source)) {
|
|
177
|
+
const val = source[key];
|
|
178
|
+
if (val && typeof val === "object" && !Array.isArray(val)) {
|
|
179
|
+
result[key] = deepMerge(
|
|
180
|
+
result[key] ?? {},
|
|
181
|
+
val
|
|
182
|
+
);
|
|
183
|
+
} else if (val !== void 0) {
|
|
184
|
+
result[key] = val;
|
|
185
|
+
}
|
|
166
186
|
}
|
|
167
187
|
return result;
|
|
168
188
|
}
|
|
169
|
-
var
|
|
170
|
-
|
|
189
|
+
var CONFIG_FILES;
|
|
190
|
+
var init_config = __esm({
|
|
191
|
+
"src/config/index.ts"() {
|
|
171
192
|
"use strict";
|
|
193
|
+
init_defaults();
|
|
194
|
+
CONFIG_FILES = [
|
|
195
|
+
"nasti.config.ts",
|
|
196
|
+
"nasti.config.js",
|
|
197
|
+
"nasti.config.mjs",
|
|
198
|
+
"nasti.config.mts"
|
|
199
|
+
];
|
|
172
200
|
}
|
|
173
201
|
});
|
|
174
202
|
|
|
175
|
-
// src/
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const origin = req.headers.origin ?? "*";
|
|
192
|
-
res.setHeader("Access-Control-Allow-Origin", origin);
|
|
193
|
-
res.setHeader("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS");
|
|
194
|
-
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
195
|
-
if (req.method === "OPTIONS") {
|
|
196
|
-
res.statusCode = 204;
|
|
197
|
-
res.end();
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
if (req.method !== "GET") return next();
|
|
202
|
-
if (url === "/@nasti/client") {
|
|
203
|
-
res.setHeader("Content-Type", "application/javascript");
|
|
204
|
-
res.end(getHmrClientCode());
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
if (url === "/" || url.endsWith(".html")) {
|
|
208
|
-
const html = await readHtmlFile(ctx.config.root);
|
|
209
|
-
if (html) {
|
|
210
|
-
let processedHtml = html;
|
|
211
|
-
for (const plugin of ctx.config.plugins) {
|
|
212
|
-
if (plugin.transformIndexHtml) {
|
|
213
|
-
const result = await plugin.transformIndexHtml(processedHtml);
|
|
214
|
-
if (typeof result === "string") {
|
|
215
|
-
processedHtml = result;
|
|
216
|
-
} else if (result && "html" in result) {
|
|
217
|
-
processedHtml = processHtml(result.html, result.tags);
|
|
218
|
-
} else if (Array.isArray(result)) {
|
|
219
|
-
processedHtml = processHtml(processedHtml, result);
|
|
220
|
-
}
|
|
203
|
+
// src/plugins/resolve.ts
|
|
204
|
+
import path2 from "path";
|
|
205
|
+
import fs2 from "fs";
|
|
206
|
+
import { createRequire } from "module";
|
|
207
|
+
function resolvePlugin(config) {
|
|
208
|
+
const { alias, extensions } = config.resolve;
|
|
209
|
+
const require2 = createRequire(path2.resolve(config.root, "package.json"));
|
|
210
|
+
return {
|
|
211
|
+
name: "nasti:resolve",
|
|
212
|
+
enforce: "pre",
|
|
213
|
+
resolveId(source, importer) {
|
|
214
|
+
for (const [key, value] of Object.entries(alias)) {
|
|
215
|
+
if (source === key || source.startsWith(key + "/")) {
|
|
216
|
+
source = source.replace(key, value);
|
|
217
|
+
if (!path2.isAbsolute(source)) {
|
|
218
|
+
source = path2.resolve(config.root, source);
|
|
221
219
|
}
|
|
220
|
+
break;
|
|
222
221
|
}
|
|
223
|
-
res.setHeader("Content-Type", "text/html");
|
|
224
|
-
res.end(processedHtml);
|
|
225
|
-
return;
|
|
226
222
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
223
|
+
if (path2.isAbsolute(source)) {
|
|
224
|
+
const resolved = tryResolveFile(source, extensions);
|
|
225
|
+
if (resolved) return resolved;
|
|
226
|
+
}
|
|
227
|
+
if (source.startsWith(".")) {
|
|
228
|
+
const dir = importer ? path2.dirname(importer) : config.root;
|
|
229
|
+
const absolute = path2.resolve(dir, source);
|
|
230
|
+
const resolved = tryResolveFile(absolute, extensions);
|
|
231
|
+
if (resolved) return resolved;
|
|
232
|
+
}
|
|
233
|
+
if (!source.startsWith("/") && !source.startsWith(".")) {
|
|
234
|
+
try {
|
|
235
|
+
const resolved = require2.resolve(source, {
|
|
236
|
+
paths: [importer ? path2.dirname(importer) : config.root]
|
|
237
|
+
});
|
|
238
|
+
return resolved;
|
|
239
|
+
} catch {
|
|
240
|
+
return null;
|
|
237
241
|
}
|
|
238
|
-
} catch (err) {
|
|
239
|
-
console.error(`[nasti] Transform error: ${url}`, err.message);
|
|
240
|
-
res.statusCode = 500;
|
|
241
|
-
res.end(`Transform error: ${err.message}`);
|
|
242
|
-
return;
|
|
243
242
|
}
|
|
243
|
+
return null;
|
|
244
|
+
},
|
|
245
|
+
load(id) {
|
|
246
|
+
if (!fs2.existsSync(id)) return null;
|
|
247
|
+
if (id.endsWith(".json")) {
|
|
248
|
+
const content = fs2.readFileSync(id, "utf-8");
|
|
249
|
+
return `export default ${content}`;
|
|
250
|
+
}
|
|
251
|
+
return fs2.readFileSync(id, "utf-8");
|
|
244
252
|
}
|
|
245
|
-
next();
|
|
246
253
|
};
|
|
247
254
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
const cached = moduleGraph.getModuleByUrl(url);
|
|
252
|
-
if (cached?.transformResult) {
|
|
253
|
-
return cached.transformResult;
|
|
254
|
-
}
|
|
255
|
-
if (cleanReqUrl === "/@react-refresh") {
|
|
256
|
-
return { code: REACT_REFRESH_RUNTIME };
|
|
257
|
-
}
|
|
258
|
-
const filePath = resolveUrlToFile(url, config.root);
|
|
259
|
-
if (!filePath || !fs7.existsSync(filePath)) return null;
|
|
260
|
-
const mod = await moduleGraph.ensureEntryFromUrl(url);
|
|
261
|
-
moduleGraph.registerModule(mod, filePath);
|
|
262
|
-
if (cleanReqUrl.startsWith("/@modules/")) {
|
|
263
|
-
const code2 = await bundlePackageAsEsm(filePath);
|
|
264
|
-
const transformResult2 = { code: code2 };
|
|
265
|
-
mod.transformResult = transformResult2;
|
|
266
|
-
return transformResult2;
|
|
255
|
+
function tryResolveFile(file, extensions) {
|
|
256
|
+
if (fs2.existsSync(file) && fs2.statSync(file).isFile()) {
|
|
257
|
+
return file;
|
|
267
258
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
259
|
+
for (const ext of extensions) {
|
|
260
|
+
const withExt = file + ext;
|
|
261
|
+
if (fs2.existsSync(withExt) && fs2.statSync(withExt).isFile()) {
|
|
262
|
+
return withExt;
|
|
263
|
+
}
|
|
272
264
|
}
|
|
273
|
-
if (
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
jsxImportSource: config.framework === "vue" ? "vue" : "react",
|
|
280
|
-
reactRefresh: useRefresh
|
|
281
|
-
});
|
|
282
|
-
code = result.code;
|
|
283
|
-
if (useRefresh) {
|
|
284
|
-
code = REACT_REFRESH_PREAMBLE + code + REACT_REFRESH_FOOTER;
|
|
265
|
+
if (fs2.existsSync(file) && fs2.statSync(file).isDirectory()) {
|
|
266
|
+
for (const ext of extensions) {
|
|
267
|
+
const indexFile = path2.join(file, "index" + ext);
|
|
268
|
+
if (fs2.existsSync(indexFile)) {
|
|
269
|
+
return indexFile;
|
|
270
|
+
}
|
|
285
271
|
}
|
|
286
272
|
}
|
|
287
|
-
|
|
288
|
-
loadEnv(config.mode, config.root, config.envPrefix),
|
|
289
|
-
config.mode
|
|
290
|
-
);
|
|
291
|
-
code = replaceEnvInCode(code, envDefine);
|
|
292
|
-
code = rewriteImports(code, config);
|
|
293
|
-
const transformResult = { code };
|
|
294
|
-
mod.transformResult = transformResult;
|
|
295
|
-
return transformResult;
|
|
273
|
+
return null;
|
|
296
274
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
275
|
+
var init_resolve = __esm({
|
|
276
|
+
"src/plugins/resolve.ts"() {
|
|
277
|
+
"use strict";
|
|
300
278
|
}
|
|
301
|
-
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// src/plugins/css.ts
|
|
282
|
+
import path3 from "path";
|
|
283
|
+
function cssPlugin(config) {
|
|
284
|
+
return {
|
|
285
|
+
name: "nasti:css",
|
|
286
|
+
resolveId(source) {
|
|
287
|
+
if (source.endsWith(".css")) return null;
|
|
288
|
+
return null;
|
|
289
|
+
},
|
|
290
|
+
transform(code, id) {
|
|
291
|
+
if (!id.endsWith(".css")) return null;
|
|
292
|
+
const rewritten = rewriteCssUrls(code, id, config.root);
|
|
293
|
+
if (config.command === "serve") {
|
|
294
|
+
const escaped = JSON.stringify(rewritten);
|
|
295
|
+
return {
|
|
296
|
+
code: `
|
|
297
|
+
const css = ${escaped};
|
|
298
|
+
const __nasti_css_id__ = ${JSON.stringify(id)};
|
|
299
|
+
const __nasti_existing__ = document.querySelector('style[data-nasti-css=' + JSON.stringify(__nasti_css_id__) + ']');
|
|
300
|
+
if (__nasti_existing__) __nasti_existing__.remove();
|
|
301
|
+
const style = document.createElement('style');
|
|
302
|
+
style.setAttribute('data-nasti-css', __nasti_css_id__);
|
|
303
|
+
style.textContent = css;
|
|
304
|
+
document.head.appendChild(style);
|
|
305
|
+
|
|
306
|
+
// HMR
|
|
307
|
+
if (import.meta.hot) {
|
|
308
|
+
import.meta.hot.accept();
|
|
309
|
+
import.meta.hot.prune(() => {
|
|
310
|
+
style.remove();
|
|
311
|
+
});
|
|
302
312
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
313
|
+
|
|
314
|
+
export default css;
|
|
315
|
+
`
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
return rewritten !== code ? { code: rewritten } : null;
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
function rewriteCssUrls(css, from, root) {
|
|
323
|
+
return css.replace(/url\(\s*['"]?([^'")\s]+)['"]?\s*\)/g, (match, url) => {
|
|
324
|
+
if (url.startsWith("/") || url.startsWith("data:") || url.startsWith("http")) {
|
|
325
|
+
return match;
|
|
311
326
|
}
|
|
327
|
+
const resolved = path3.resolve(path3.dirname(from), url);
|
|
328
|
+
const relative = "/" + path3.relative(root, resolved);
|
|
329
|
+
return `url(${relative})`;
|
|
312
330
|
});
|
|
313
|
-
const result = await bundle.generate({ format: "esm", exports: "named" });
|
|
314
|
-
await bundle.close();
|
|
315
|
-
let code = result.output[0].code;
|
|
316
|
-
code = code.replace(/process\.env\.NODE_ENV/g, '"development"');
|
|
317
|
-
code = code.replace(
|
|
318
|
-
/^(import\b[^;'"]*?\bfrom\s+)(['"])([^'"./][^'"]*)(\2)/gm,
|
|
319
|
-
(_, prefix, q, spec) => `${prefix}${q}/@modules/${spec}${q}`
|
|
320
|
-
).replace(
|
|
321
|
-
/^(export\b[^;'"]*?\bfrom\s+)(['"])([^'"./][^'"]*)(\2)/gm,
|
|
322
|
-
(_, prefix, q, spec) => `${prefix}${q}/@modules/${spec}${q}`
|
|
323
|
-
).replace(
|
|
324
|
-
/^(import\s+)(['"])([^'"./][^'"]*)(\2)/gm,
|
|
325
|
-
(_, prefix, q, spec) => `${prefix}${q}/@modules/${spec}${q}`
|
|
326
|
-
);
|
|
327
|
-
code = rewriteExternalRequires(code);
|
|
328
|
-
if (code.includes("__commonJSMin")) {
|
|
329
|
-
code = await injectCjsNamedExports(code, entryFile);
|
|
330
|
-
}
|
|
331
|
-
return code;
|
|
332
331
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
let m;
|
|
337
|
-
while ((m = re.exec(code)) !== null) {
|
|
338
|
-
pkgs.add(m[1]);
|
|
339
|
-
}
|
|
340
|
-
if (pkgs.size === 0) return code;
|
|
341
|
-
let result = code;
|
|
342
|
-
const imports = [];
|
|
343
|
-
for (const pkg of pkgs) {
|
|
344
|
-
const safe = pkg.replace(/[^a-zA-Z0-9_$]/g, "_");
|
|
345
|
-
imports.push(`import __req_${safe} from "/@modules/${pkg}";`);
|
|
346
|
-
result = result.replaceAll(`__require("${pkg}")`, `__req_${safe}`);
|
|
347
|
-
result = result.replaceAll(`__require('${pkg}')`, `__req_${safe}`);
|
|
332
|
+
var init_css = __esm({
|
|
333
|
+
"src/plugins/css.ts"() {
|
|
334
|
+
"use strict";
|
|
348
335
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
return code.replace(
|
|
362
|
-
/^export default (\w+\(\));?\s*$/m,
|
|
363
|
-
(_, call) => [
|
|
364
|
-
`const __cjsMod = ${call};`,
|
|
365
|
-
`export default __cjsMod;`,
|
|
366
|
-
...namedKeys.map((k) => `export const ${k} = __cjsMod[${JSON.stringify(k)}];`)
|
|
367
|
-
].join("\n")
|
|
368
|
-
);
|
|
369
|
-
} catch {
|
|
370
|
-
return code;
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
function rewriteImports(code, _config) {
|
|
374
|
-
return code.replace(
|
|
375
|
-
/\bfrom\s+(['"])([^'"./][^'"]*)\1/g,
|
|
376
|
-
(match, quote, specifier) => {
|
|
377
|
-
return `from ${quote}/@modules/${specifier}${quote}`;
|
|
378
|
-
}
|
|
379
|
-
).replace(
|
|
380
|
-
// 处理纯副作用导入: import 'bare-specifier'
|
|
381
|
-
/\bimport\s+(['"])([^'"./][^'"]*)\1/g,
|
|
382
|
-
(match, quote, specifier) => {
|
|
383
|
-
return `import ${quote}/@modules/${specifier}${quote}`;
|
|
384
|
-
}
|
|
385
|
-
).replace(
|
|
386
|
-
// 处理动态导入: import('bare-specifier')
|
|
387
|
-
/\bimport\s*\(\s*(['"])([^'"./][^'"]*)\1\s*\)/g,
|
|
388
|
-
(match, quote, specifier) => {
|
|
389
|
-
return `import(${quote}/@modules/${specifier}${quote})`;
|
|
390
|
-
}
|
|
391
|
-
);
|
|
392
|
-
}
|
|
393
|
-
function resolveNodeModule(root, moduleName) {
|
|
394
|
-
let pkgName;
|
|
395
|
-
let subpath;
|
|
396
|
-
if (moduleName.startsWith("@")) {
|
|
397
|
-
const parts = moduleName.split("/");
|
|
398
|
-
pkgName = parts.slice(0, 2).join("/");
|
|
399
|
-
subpath = parts.slice(2).join("/");
|
|
400
|
-
} else {
|
|
401
|
-
const slash = moduleName.indexOf("/");
|
|
402
|
-
pkgName = slash === -1 ? moduleName : moduleName.slice(0, slash);
|
|
403
|
-
subpath = slash === -1 ? "" : moduleName.slice(slash + 1);
|
|
404
|
-
}
|
|
405
|
-
let pkgDir = null;
|
|
406
|
-
let dir = root;
|
|
407
|
-
for (; ; ) {
|
|
408
|
-
const candidate = path8.join(dir, "node_modules", pkgName);
|
|
409
|
-
if (fs7.existsSync(candidate)) {
|
|
410
|
-
pkgDir = candidate;
|
|
411
|
-
break;
|
|
412
|
-
}
|
|
413
|
-
const parent = path8.dirname(dir);
|
|
414
|
-
if (parent === dir) break;
|
|
415
|
-
dir = parent;
|
|
416
|
-
}
|
|
417
|
-
if (!pkgDir) return null;
|
|
418
|
-
const pkgJsonPath = path8.join(pkgDir, "package.json");
|
|
419
|
-
if (!fs7.existsSync(pkgJsonPath)) return null;
|
|
420
|
-
let pkg;
|
|
421
|
-
try {
|
|
422
|
-
pkg = JSON.parse(fs7.readFileSync(pkgJsonPath, "utf-8"));
|
|
423
|
-
} catch {
|
|
424
|
-
return null;
|
|
425
|
-
}
|
|
426
|
-
if (pkg.exports) {
|
|
427
|
-
const exportKey = subpath ? `./${subpath}` : ".";
|
|
428
|
-
const resolved = resolvePackageExports(pkg.exports, exportKey, pkgDir);
|
|
429
|
-
if (resolved) return resolved;
|
|
430
|
-
}
|
|
431
|
-
if (subpath) {
|
|
432
|
-
const subDirs = [""];
|
|
433
|
-
for (const field of ["module", "main"]) {
|
|
434
|
-
if (typeof pkg[field] === "string") {
|
|
435
|
-
const dir2 = path8.dirname(pkg[field]);
|
|
436
|
-
if (dir2 && dir2 !== "." && !subDirs.includes(dir2)) subDirs.push(dir2);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// src/plugins/assets.ts
|
|
339
|
+
import path4 from "path";
|
|
340
|
+
import fs3 from "fs";
|
|
341
|
+
import crypto from "crypto";
|
|
342
|
+
function assetsPlugin(config) {
|
|
343
|
+
return {
|
|
344
|
+
name: "nasti:assets",
|
|
345
|
+
resolveId(source) {
|
|
346
|
+
if (source.endsWith("?url") || source.endsWith("?raw")) {
|
|
347
|
+
return source;
|
|
437
348
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
349
|
+
return null;
|
|
350
|
+
},
|
|
351
|
+
load(id) {
|
|
352
|
+
const ext = path4.extname(id.replace(/\?.*$/, ""));
|
|
353
|
+
if (id.endsWith("?raw")) {
|
|
354
|
+
const file = id.slice(0, -4);
|
|
355
|
+
if (fs3.existsSync(file)) {
|
|
356
|
+
const content = fs3.readFileSync(file, "utf-8");
|
|
357
|
+
return `export default ${JSON.stringify(content)}`;
|
|
358
|
+
}
|
|
444
359
|
}
|
|
360
|
+
if (id.endsWith("?url") || ASSET_EXTENSIONS.has(ext)) {
|
|
361
|
+
const file = id.replace(/\?.*$/, "");
|
|
362
|
+
if (!fs3.existsSync(file)) return null;
|
|
363
|
+
if (config.command === "serve") {
|
|
364
|
+
const url = "/" + path4.relative(config.root, file);
|
|
365
|
+
return `export default ${JSON.stringify(url)}`;
|
|
366
|
+
}
|
|
367
|
+
const content = fs3.readFileSync(file);
|
|
368
|
+
const hash = crypto.createHash("sha256").update(content).digest("hex").slice(0, 8);
|
|
369
|
+
const basename = path4.basename(file, ext);
|
|
370
|
+
const hashedName = `${config.build.assetsDir}/${basename}.${hash}${ext}`;
|
|
371
|
+
return `export default ${JSON.stringify(config.base + hashedName)}`;
|
|
372
|
+
}
|
|
373
|
+
return null;
|
|
445
374
|
}
|
|
446
|
-
|
|
447
|
-
}
|
|
448
|
-
for (const field of ["module", "jsnext:main", "jsnext", "main"]) {
|
|
449
|
-
if (typeof pkg[field] === "string") {
|
|
450
|
-
const entry = path8.join(pkgDir, pkg[field]);
|
|
451
|
-
if (fs7.existsSync(entry)) return entry;
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
const indexFallback = path8.join(pkgDir, "index.js");
|
|
455
|
-
if (fs7.existsSync(indexFallback)) return indexFallback;
|
|
456
|
-
return null;
|
|
457
|
-
}
|
|
458
|
-
function resolvePackageExports(exports, key, pkgDir) {
|
|
459
|
-
if (typeof exports === "string") {
|
|
460
|
-
return key === "." ? path8.join(pkgDir, exports) : null;
|
|
461
|
-
}
|
|
462
|
-
const entry = exports[key];
|
|
463
|
-
if (entry === void 0) return null;
|
|
464
|
-
return resolveExportValue(entry, pkgDir);
|
|
375
|
+
};
|
|
465
376
|
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
377
|
+
var ASSET_EXTENSIONS;
|
|
378
|
+
var init_assets = __esm({
|
|
379
|
+
"src/plugins/assets.ts"() {
|
|
380
|
+
"use strict";
|
|
381
|
+
ASSET_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
382
|
+
".png",
|
|
383
|
+
".jpg",
|
|
384
|
+
".jpeg",
|
|
385
|
+
".gif",
|
|
386
|
+
".svg",
|
|
387
|
+
".ico",
|
|
388
|
+
".webp",
|
|
389
|
+
".avif",
|
|
390
|
+
".mp4",
|
|
391
|
+
".webm",
|
|
392
|
+
".ogg",
|
|
393
|
+
".mp3",
|
|
394
|
+
".wav",
|
|
395
|
+
".flac",
|
|
396
|
+
".aac",
|
|
397
|
+
".woff",
|
|
398
|
+
".woff2",
|
|
399
|
+
".eot",
|
|
400
|
+
".ttf",
|
|
401
|
+
".otf",
|
|
402
|
+
".pdf",
|
|
403
|
+
".txt"
|
|
404
|
+
]);
|
|
474
405
|
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
// src/plugins/html.ts
|
|
409
|
+
import path5 from "path";
|
|
410
|
+
import fs4 from "fs";
|
|
411
|
+
function htmlPlugin(config) {
|
|
412
|
+
return {
|
|
413
|
+
name: "nasti:html",
|
|
414
|
+
enforce: "post",
|
|
415
|
+
transformIndexHtml(html) {
|
|
416
|
+
const tags = [];
|
|
417
|
+
if (config.command === "serve") {
|
|
418
|
+
tags.push({
|
|
419
|
+
tag: "script",
|
|
420
|
+
attrs: { type: "module", src: "/@nasti/client" },
|
|
421
|
+
injectTo: "head-prepend"
|
|
422
|
+
});
|
|
480
423
|
}
|
|
424
|
+
return { html, tags };
|
|
481
425
|
}
|
|
482
|
-
}
|
|
483
|
-
return null;
|
|
426
|
+
};
|
|
484
427
|
}
|
|
485
|
-
function
|
|
486
|
-
const
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
428
|
+
function processHtml(html, tags) {
|
|
429
|
+
const headPrepend = tags.filter((t) => t.injectTo === "head-prepend");
|
|
430
|
+
const head = tags.filter((t) => t.injectTo === "head" || !t.injectTo);
|
|
431
|
+
const bodyPrepend = tags.filter((t) => t.injectTo === "body-prepend");
|
|
432
|
+
const body = tags.filter((t) => t.injectTo === "body");
|
|
433
|
+
if (headPrepend.length) {
|
|
434
|
+
html = html.replace(/<head([^>]*)>/i, `<head$1>
|
|
435
|
+
${serializeTags(headPrepend)}`);
|
|
490
436
|
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
437
|
+
if (head.length) {
|
|
438
|
+
html = html.replace(/<\/head>/i, `${serializeTags(head)}
|
|
439
|
+
</head>`);
|
|
494
440
|
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
441
|
+
if (bodyPrepend.length) {
|
|
442
|
+
html = html.replace(/<body([^>]*)>/i, `<body$1>
|
|
443
|
+
${serializeTags(bodyPrepend)}`);
|
|
498
444
|
}
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
445
|
+
if (body.length) {
|
|
446
|
+
html = html.replace(/<\/body>/i, `${serializeTags(body)}
|
|
447
|
+
</body>`);
|
|
502
448
|
}
|
|
503
|
-
return
|
|
449
|
+
return html;
|
|
504
450
|
}
|
|
505
|
-
function
|
|
506
|
-
|
|
507
|
-
if (/\.(ts|tsx|jsx|js|mjs|vue|css|json)$/.test(cleanUrl)) return true;
|
|
508
|
-
if (cleanUrl.startsWith("/@modules/")) return true;
|
|
509
|
-
if (!path8.extname(cleanUrl)) return true;
|
|
510
|
-
return false;
|
|
451
|
+
function serializeTags(tags) {
|
|
452
|
+
return tags.map(serializeTag).join("\n");
|
|
511
453
|
}
|
|
512
|
-
function
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
const
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
updateCss(update.path);
|
|
530
|
-
}
|
|
531
|
-
});
|
|
532
|
-
break;
|
|
533
|
-
case 'full-reload':
|
|
534
|
-
console.log('[nasti] full reload');
|
|
535
|
-
location.reload();
|
|
536
|
-
break;
|
|
537
|
-
case 'error':
|
|
538
|
-
console.error('[nasti] error:', payload.err.message);
|
|
539
|
-
showErrorOverlay(payload.err);
|
|
540
|
-
break;
|
|
454
|
+
function serializeTag(tag) {
|
|
455
|
+
const attrs = tag.attrs ? " " + Object.entries(tag.attrs).map(([k, v]) => v === true ? k : `${k}="${v}"`).join(" ") : "";
|
|
456
|
+
const children = typeof tag.children === "string" ? tag.children : tag.children ? serializeTags(tag.children) : "";
|
|
457
|
+
const selfClosing = ["link", "meta", "br", "hr", "img", "input"].includes(tag.tag);
|
|
458
|
+
if (selfClosing && !children) {
|
|
459
|
+
return ` <${tag.tag}${attrs} />`;
|
|
460
|
+
}
|
|
461
|
+
return ` <${tag.tag}${attrs}>${children}</${tag.tag}>`;
|
|
462
|
+
}
|
|
463
|
+
async function readHtmlFile(root) {
|
|
464
|
+
const htmlPath = path5.resolve(root, "index.html");
|
|
465
|
+
if (!fs4.existsSync(htmlPath)) return null;
|
|
466
|
+
return fs4.readFileSync(htmlPath, "utf-8");
|
|
467
|
+
}
|
|
468
|
+
var init_html = __esm({
|
|
469
|
+
"src/plugins/html.ts"() {
|
|
470
|
+
"use strict";
|
|
541
471
|
}
|
|
542
472
|
});
|
|
543
473
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
mod.callbacks.forEach((cb) => cb(newMod));
|
|
549
|
-
} else {
|
|
550
|
-
// \u6CA1\u6709\u6CE8\u518C hot \u56DE\u8C03\uFF0C\u5C1D\u8BD5\u91CD\u65B0 import
|
|
551
|
-
await import(update.path + '?t=' + update.timestamp);
|
|
552
|
-
}
|
|
474
|
+
// src/core/transformer.ts
|
|
475
|
+
import { transformSync } from "oxc-transform";
|
|
476
|
+
function shouldTransform(id) {
|
|
477
|
+
return TS_EXTENSIONS.test(id) || JSX_EXTENSIONS.test(id) || JS_EXTENSIONS.test(id) && false;
|
|
553
478
|
}
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
const
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
479
|
+
function transformCode(filename, code, options = {}) {
|
|
480
|
+
const isTS = TS_EXTENSIONS.test(filename) || /\.tsx$/.test(filename);
|
|
481
|
+
const isJSX = JSX_EXTENSIONS.test(filename);
|
|
482
|
+
const result = transformSync(filename, code, {
|
|
483
|
+
typescript: isTS ? {} : void 0,
|
|
484
|
+
jsx: isJSX || /\.tsx$/.test(filename) ? {
|
|
485
|
+
runtime: options.jsxRuntime ?? "automatic",
|
|
486
|
+
importSource: options.jsxImportSource ?? "react",
|
|
487
|
+
refresh: options.reactRefresh ?? false
|
|
488
|
+
} : void 0,
|
|
489
|
+
sourcemap: options.sourcemap ?? true
|
|
490
|
+
});
|
|
491
|
+
if (result.errors && result.errors.length > 0) {
|
|
492
|
+
const msg = result.errors.map((e) => e.message ?? String(e)).join("\n");
|
|
493
|
+
throw new Error(`OXC transform failed for ${filename}:
|
|
494
|
+
${msg}`);
|
|
561
495
|
}
|
|
496
|
+
return {
|
|
497
|
+
code: result.code,
|
|
498
|
+
map: result.map ? JSON.stringify(result.map) : null
|
|
499
|
+
};
|
|
562
500
|
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
overlay.id = 'nasti-error-overlay';
|
|
567
|
-
overlay.style.cssText = 'position:fixed;inset:0;z-index:99999;background:rgba(0,0,0,0.85);color:#fff;font-family:monospace;padding:2rem;overflow:auto;';
|
|
568
|
-
const title = document.createElement('h2');
|
|
569
|
-
title.style.color = '#ff5555';
|
|
570
|
-
title.textContent = 'Build Error';
|
|
571
|
-
const pre = document.createElement('pre');
|
|
572
|
-
pre.textContent = err.message + '\\n' + (err.stack || '');
|
|
573
|
-
const btn = document.createElement('button');
|
|
574
|
-
btn.style.cssText = 'margin-top:1rem;padding:0.5rem 1rem;cursor:pointer';
|
|
575
|
-
btn.textContent = 'Close';
|
|
576
|
-
btn.onclick = () => overlay.remove();
|
|
577
|
-
overlay.appendChild(title);
|
|
578
|
-
overlay.appendChild(pre);
|
|
579
|
-
overlay.appendChild(btn);
|
|
580
|
-
document.body.appendChild(overlay);
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
// import.meta.hot API
|
|
584
|
-
const createHotContext = (ownerPath) => ({
|
|
585
|
-
accept(deps, callback) {
|
|
586
|
-
if (typeof deps === 'function' || !deps) {
|
|
587
|
-
// self-accepting
|
|
588
|
-
const callbacks = hotModulesMap.get(ownerPath)?.callbacks || [];
|
|
589
|
-
callbacks.push(deps || (() => {}));
|
|
590
|
-
hotModulesMap.set(ownerPath, { callbacks });
|
|
591
|
-
}
|
|
592
|
-
},
|
|
593
|
-
prune(callback) {
|
|
594
|
-
// \u6A21\u5757\u88AB\u79FB\u9664\u65F6\u6267\u884C
|
|
595
|
-
},
|
|
596
|
-
dispose(callback) {
|
|
597
|
-
// \u6A21\u5757\u66F4\u65B0\u524D\u6267\u884C\u6E05\u7406
|
|
598
|
-
},
|
|
599
|
-
invalidate() {
|
|
600
|
-
location.reload();
|
|
601
|
-
},
|
|
602
|
-
data: {},
|
|
603
|
-
});
|
|
604
|
-
|
|
605
|
-
// \u66B4\u9732\u7ED9\u6A21\u5757\u4F7F\u7528
|
|
606
|
-
if (!window.__nasti_hot_map) window.__nasti_hot_map = new Map();
|
|
607
|
-
window.__NASTI_HMR__ = { createHotContext };
|
|
608
|
-
`;
|
|
609
|
-
}
|
|
610
|
-
var REACT_REFRESH_RUNTIME, REACT_REFRESH_PREAMBLE, REACT_REFRESH_FOOTER, esmBundleCache, VALID_IDENT, RESOLVE_EXTENSIONS, ESM_CONDITIONS;
|
|
611
|
-
var init_middleware = __esm({
|
|
612
|
-
"src/server/middleware.ts"() {
|
|
501
|
+
var JS_EXTENSIONS, TS_EXTENSIONS, JSX_EXTENSIONS;
|
|
502
|
+
var init_transformer = __esm({
|
|
503
|
+
"src/core/transformer.ts"() {
|
|
613
504
|
"use strict";
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
REACT_REFRESH_RUNTIME = `
|
|
618
|
-
export function createSignatureFunctionForTransform() {
|
|
619
|
-
return function(type, key, forceReset, getCustomHooks) { return type; };
|
|
620
|
-
}
|
|
621
|
-
export function register(type, id) {}
|
|
622
|
-
export default { createSignatureFunctionForTransform, register };
|
|
623
|
-
`;
|
|
624
|
-
REACT_REFRESH_PREAMBLE = `
|
|
625
|
-
import RefreshRuntime from '/@react-refresh';
|
|
626
|
-
if (!window.$RefreshReg$) {
|
|
627
|
-
window.$RefreshReg$ = (type, id) => RefreshRuntime.register(type, import.meta.url + ' ' + id);
|
|
628
|
-
window.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform;
|
|
629
|
-
}
|
|
630
|
-
`;
|
|
631
|
-
REACT_REFRESH_FOOTER = `
|
|
632
|
-
if (import.meta.hot) {
|
|
633
|
-
import.meta.hot.accept();
|
|
634
|
-
}
|
|
635
|
-
`;
|
|
636
|
-
esmBundleCache = /* @__PURE__ */ new Map();
|
|
637
|
-
VALID_IDENT = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
638
|
-
RESOLVE_EXTENSIONS = [".tsx", ".ts", ".jsx", ".js", ".mjs", ".json", ".vue"];
|
|
639
|
-
ESM_CONDITIONS = ["import", "browser", "module", "default"];
|
|
505
|
+
JS_EXTENSIONS = /\.(js|mjs|cjs)$/;
|
|
506
|
+
TS_EXTENSIONS = /\.(ts|mts|cts)$/;
|
|
507
|
+
JSX_EXTENSIONS = /\.(jsx|tsx)$/;
|
|
640
508
|
}
|
|
641
509
|
});
|
|
642
510
|
|
|
643
|
-
// src/
|
|
644
|
-
import
|
|
645
|
-
import
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
target: "es2022",
|
|
670
|
-
rolldownOptions: {},
|
|
671
|
-
emptyOutDir: true
|
|
672
|
-
};
|
|
673
|
-
var defaults = {
|
|
674
|
-
root: ".",
|
|
675
|
-
base: "/",
|
|
676
|
-
mode: "development",
|
|
677
|
-
framework: "auto",
|
|
678
|
-
resolve: defaultResolve,
|
|
679
|
-
server: defaultServer,
|
|
680
|
-
build: defaultBuild,
|
|
681
|
-
plugins: [],
|
|
682
|
-
envPrefix: ["NASTI_", "VITE_"],
|
|
683
|
-
logLevel: "info"
|
|
684
|
-
};
|
|
685
|
-
|
|
686
|
-
// src/config/index.ts
|
|
687
|
-
function loadTsconfigPaths(root) {
|
|
688
|
-
const tsconfigPath = path.resolve(root, "tsconfig.json");
|
|
689
|
-
if (!fs.existsSync(tsconfigPath)) return {};
|
|
690
|
-
try {
|
|
691
|
-
const content = fs.readFileSync(tsconfigPath, "utf-8");
|
|
692
|
-
const stripped = content.replace(/\/\/[^\n]*/g, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
693
|
-
const tsconfig = JSON.parse(stripped);
|
|
694
|
-
const paths = tsconfig?.compilerOptions?.paths ?? {};
|
|
695
|
-
const baseUrl = tsconfig?.compilerOptions?.baseUrl ?? ".";
|
|
696
|
-
const alias = {};
|
|
697
|
-
for (const [pattern, targets] of Object.entries(paths)) {
|
|
698
|
-
if (!targets.length) continue;
|
|
699
|
-
const cleanKey = pattern.replace(/\/\*$/, "");
|
|
700
|
-
const cleanTarget = targets[0].replace(/\/\*$/, "");
|
|
701
|
-
alias[cleanKey] = path.resolve(root, baseUrl, cleanTarget);
|
|
511
|
+
// src/core/env.ts
|
|
512
|
+
import path6 from "path";
|
|
513
|
+
import fs5 from "fs";
|
|
514
|
+
function loadEnv(mode, root, prefixes) {
|
|
515
|
+
const envFiles = [
|
|
516
|
+
".env",
|
|
517
|
+
`.env.${mode}`,
|
|
518
|
+
".env.local",
|
|
519
|
+
`.env.${mode}.local`
|
|
520
|
+
];
|
|
521
|
+
const raw = {};
|
|
522
|
+
for (const file of envFiles) {
|
|
523
|
+
const filePath = path6.resolve(root, file);
|
|
524
|
+
if (!fs5.existsSync(filePath)) continue;
|
|
525
|
+
const content = fs5.readFileSync(filePath, "utf-8");
|
|
526
|
+
for (const line of content.split("\n")) {
|
|
527
|
+
const trimmed = line.trim();
|
|
528
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
529
|
+
const eqIdx = trimmed.indexOf("=");
|
|
530
|
+
if (eqIdx === -1) continue;
|
|
531
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
532
|
+
let value = trimmed.slice(eqIdx + 1).trim();
|
|
533
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
534
|
+
value = value.slice(1, -1);
|
|
535
|
+
}
|
|
536
|
+
raw[key] = value;
|
|
702
537
|
}
|
|
703
|
-
return alias;
|
|
704
|
-
} catch {
|
|
705
|
-
return {};
|
|
706
538
|
}
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
var CONFIG_FILES = [
|
|
712
|
-
"nasti.config.ts",
|
|
713
|
-
"nasti.config.js",
|
|
714
|
-
"nasti.config.mjs",
|
|
715
|
-
"nasti.config.mts"
|
|
716
|
-
];
|
|
717
|
-
async function loadConfigFromFile(root) {
|
|
718
|
-
for (const file of CONFIG_FILES) {
|
|
719
|
-
const filePath = path.resolve(root, file);
|
|
720
|
-
if (!fs.existsSync(filePath)) continue;
|
|
721
|
-
if (file.endsWith(".ts") || file.endsWith(".mts")) {
|
|
722
|
-
return await loadTsConfig(filePath);
|
|
539
|
+
const filtered = {};
|
|
540
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
541
|
+
if (prefixes.some((prefix) => key.startsWith(prefix))) {
|
|
542
|
+
filtered[key] = value;
|
|
723
543
|
}
|
|
724
|
-
const mod = await import(pathToFileURL(filePath).href);
|
|
725
|
-
return mod.default ?? mod;
|
|
726
544
|
}
|
|
727
|
-
return
|
|
545
|
+
return filtered;
|
|
728
546
|
}
|
|
729
|
-
|
|
730
|
-
const
|
|
731
|
-
const
|
|
732
|
-
|
|
733
|
-
typescript: {}
|
|
734
|
-
});
|
|
735
|
-
const tmpFile = filePath + ".timestamp-" + Date.now() + ".mjs";
|
|
736
|
-
try {
|
|
737
|
-
fs.writeFileSync(tmpFile, result.code);
|
|
738
|
-
const mod = await import(pathToFileURL(tmpFile).href);
|
|
739
|
-
return mod.default ?? mod;
|
|
740
|
-
} finally {
|
|
741
|
-
fs.unlinkSync(tmpFile);
|
|
547
|
+
function buildEnvDefine(env, mode) {
|
|
548
|
+
const define = {};
|
|
549
|
+
for (const [key, value] of Object.entries(env)) {
|
|
550
|
+
define[`import.meta.env.${key}`] = JSON.stringify(value);
|
|
742
551
|
}
|
|
552
|
+
define["import.meta.env.MODE"] = JSON.stringify(mode);
|
|
553
|
+
define["import.meta.env.DEV"] = mode !== "production" ? "true" : "false";
|
|
554
|
+
define["import.meta.env.PROD"] = mode === "production" ? "true" : "false";
|
|
555
|
+
define["import.meta.env.SSR"] = "false";
|
|
556
|
+
return define;
|
|
743
557
|
}
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
const
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
...fileConfig.plugins ?? [],
|
|
750
|
-
...inlineConfig.plugins ?? []
|
|
751
|
-
];
|
|
752
|
-
const env = { mode: merged.mode ?? defaults.mode, command };
|
|
753
|
-
for (const plugin of rawPlugins) {
|
|
754
|
-
if (plugin.config) {
|
|
755
|
-
const result = await plugin.config(merged, env);
|
|
756
|
-
if (result) Object.assign(merged, result);
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
const resolved = {
|
|
760
|
-
root,
|
|
761
|
-
base: merged.base ?? defaults.base,
|
|
762
|
-
mode: command === "build" ? "production" : "development",
|
|
763
|
-
framework: merged.framework ?? defaults.framework,
|
|
764
|
-
command,
|
|
765
|
-
resolve: {
|
|
766
|
-
// tsconfig paths 优先级最低:tsconfig < defaults < user config
|
|
767
|
-
alias: { ...loadTsconfigPaths(root), ...defaults.resolve.alias, ...merged.resolve?.alias },
|
|
768
|
-
extensions: merged.resolve?.extensions ?? defaults.resolve.extensions,
|
|
769
|
-
conditions: merged.resolve?.conditions ?? defaults.resolve.conditions,
|
|
770
|
-
mainFields: merged.resolve?.mainFields ?? defaults.resolve.mainFields
|
|
771
|
-
},
|
|
772
|
-
plugins: [],
|
|
773
|
-
server: { ...defaults.server, ...merged.server },
|
|
774
|
-
build: { ...defaults.build, ...merged.build },
|
|
775
|
-
envPrefix: Array.isArray(merged.envPrefix) ? merged.envPrefix : merged.envPrefix ? [merged.envPrefix] : [...defaults.envPrefix],
|
|
776
|
-
logLevel: merged.logLevel ?? defaults.logLevel
|
|
777
|
-
};
|
|
778
|
-
const filteredPlugins = rawPlugins.filter((p) => {
|
|
779
|
-
if (!p.apply) return true;
|
|
780
|
-
if (typeof p.apply === "function") return p.apply(resolved, env);
|
|
781
|
-
return p.apply === command;
|
|
782
|
-
});
|
|
783
|
-
resolved.plugins = filteredPlugins;
|
|
784
|
-
for (const plugin of resolved.plugins) {
|
|
785
|
-
if (plugin.configResolved) {
|
|
786
|
-
await plugin.configResolved(resolved);
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
return resolved;
|
|
790
|
-
}
|
|
791
|
-
function deepMerge(target, source) {
|
|
792
|
-
const result = { ...target };
|
|
793
|
-
for (const key of Object.keys(source)) {
|
|
794
|
-
const val = source[key];
|
|
795
|
-
if (val && typeof val === "object" && !Array.isArray(val)) {
|
|
796
|
-
result[key] = deepMerge(
|
|
797
|
-
result[key] ?? {},
|
|
798
|
-
val
|
|
799
|
-
);
|
|
800
|
-
} else if (val !== void 0) {
|
|
801
|
-
result[key] = val;
|
|
802
|
-
}
|
|
558
|
+
function replaceEnvInCode(code, define) {
|
|
559
|
+
let result = code;
|
|
560
|
+
for (const [key, value] of Object.entries(define)) {
|
|
561
|
+
const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
562
|
+
result = result.replace(new RegExp(escaped, "g"), value);
|
|
803
563
|
}
|
|
804
564
|
return result;
|
|
805
565
|
}
|
|
566
|
+
var init_env = __esm({
|
|
567
|
+
"src/core/env.ts"() {
|
|
568
|
+
"use strict";
|
|
569
|
+
}
|
|
570
|
+
});
|
|
806
571
|
|
|
807
|
-
// src/
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
572
|
+
// src/core/plugin-container.ts
|
|
573
|
+
function sortPlugins(plugins) {
|
|
574
|
+
const pre = [];
|
|
575
|
+
const normal = [];
|
|
576
|
+
const post = [];
|
|
577
|
+
for (const plugin of plugins) {
|
|
578
|
+
if (plugin.enforce === "pre") pre.push(plugin);
|
|
579
|
+
else if (plugin.enforce === "post") post.push(plugin);
|
|
580
|
+
else normal.push(plugin);
|
|
581
|
+
}
|
|
582
|
+
return [...pre, ...normal, ...post];
|
|
583
|
+
}
|
|
584
|
+
var PluginContainer;
|
|
585
|
+
var init_plugin_container = __esm({
|
|
586
|
+
"src/core/plugin-container.ts"() {
|
|
587
|
+
"use strict";
|
|
588
|
+
PluginContainer = class {
|
|
589
|
+
plugins;
|
|
590
|
+
config;
|
|
591
|
+
ctx;
|
|
592
|
+
emittedFiles = /* @__PURE__ */ new Map();
|
|
593
|
+
constructor(config) {
|
|
594
|
+
this.config = config;
|
|
595
|
+
this.plugins = sortPlugins(config.plugins);
|
|
596
|
+
this.ctx = this.createContext();
|
|
597
|
+
}
|
|
598
|
+
createContext() {
|
|
599
|
+
const container = this;
|
|
600
|
+
return {
|
|
601
|
+
async resolve(source, importer) {
|
|
602
|
+
return container.resolveId(source, importer);
|
|
603
|
+
},
|
|
604
|
+
emitFile(file) {
|
|
605
|
+
const fileName = file.fileName ?? file.name ?? `asset-${container.emittedFiles.size}`;
|
|
606
|
+
const id = `emitted:${fileName}`;
|
|
607
|
+
container.emittedFiles.set(id, {
|
|
608
|
+
fileName,
|
|
609
|
+
source: file.source ?? ""
|
|
610
|
+
});
|
|
611
|
+
return id;
|
|
612
|
+
},
|
|
613
|
+
getModuleInfo(_id) {
|
|
614
|
+
return null;
|
|
615
|
+
}
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
/** 返回所有通过 emitFile() 输出的文件 */
|
|
619
|
+
getEmittedFiles() {
|
|
620
|
+
return Array.from(this.emittedFiles.values());
|
|
621
|
+
}
|
|
622
|
+
async buildStart() {
|
|
623
|
+
for (const plugin of this.plugins) {
|
|
624
|
+
if (plugin.buildStart) {
|
|
625
|
+
await plugin.buildStart.call(this.ctx);
|
|
828
626
|
}
|
|
829
|
-
break;
|
|
830
627
|
}
|
|
831
628
|
}
|
|
832
|
-
|
|
833
|
-
const
|
|
834
|
-
|
|
629
|
+
async buildEnd(error) {
|
|
630
|
+
for (const plugin of this.plugins) {
|
|
631
|
+
if (plugin.buildEnd) {
|
|
632
|
+
await plugin.buildEnd.call(this.ctx, error);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
835
635
|
}
|
|
836
|
-
|
|
837
|
-
const
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
636
|
+
async resolveId(source, importer, options = {}) {
|
|
637
|
+
for (const plugin of this.plugins) {
|
|
638
|
+
if (!plugin.resolveId) continue;
|
|
639
|
+
const result = await plugin.resolveId.call(
|
|
640
|
+
this.ctx,
|
|
641
|
+
source,
|
|
642
|
+
importer ?? void 0,
|
|
643
|
+
{ isEntry: options.isEntry ?? false, ssr: false }
|
|
644
|
+
);
|
|
645
|
+
if (result != null) return result;
|
|
646
|
+
}
|
|
647
|
+
return null;
|
|
841
648
|
}
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
return resolved;
|
|
848
|
-
} catch {
|
|
849
|
-
return null;
|
|
649
|
+
async load(id) {
|
|
650
|
+
for (const plugin of this.plugins) {
|
|
651
|
+
if (!plugin.load) continue;
|
|
652
|
+
const result = await plugin.load.call(this.ctx, id);
|
|
653
|
+
if (result != null) return result;
|
|
850
654
|
}
|
|
655
|
+
return null;
|
|
851
656
|
}
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
657
|
+
async transform(code, id) {
|
|
658
|
+
let currentCode = code;
|
|
659
|
+
for (const plugin of this.plugins) {
|
|
660
|
+
if (!plugin.transform) continue;
|
|
661
|
+
const result = await plugin.transform.call(this.ctx, currentCode, id);
|
|
662
|
+
if (result == null) continue;
|
|
663
|
+
if (typeof result === "string") {
|
|
664
|
+
currentCode = result;
|
|
665
|
+
} else {
|
|
666
|
+
currentCode = result.code;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
return currentCode === code ? null : { code: currentCode };
|
|
859
670
|
}
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
671
|
+
/** 完整的模块处理管道: resolveId → load → transform */
|
|
672
|
+
async processModule(source, importer) {
|
|
673
|
+
const resolveResult = await this.resolveId(source, importer, {
|
|
674
|
+
isEntry: !importer
|
|
675
|
+
});
|
|
676
|
+
if (resolveResult == null) return null;
|
|
677
|
+
const id = typeof resolveResult === "string" ? resolveResult : resolveResult.id;
|
|
678
|
+
const loadResult = await this.load(id);
|
|
679
|
+
if (loadResult == null) return null;
|
|
680
|
+
const loadedCode = typeof loadResult === "string" ? loadResult : loadResult.code;
|
|
681
|
+
const transformResult = await this.transform(loadedCode, id);
|
|
682
|
+
const finalCode = transformResult == null ? loadedCode : typeof transformResult === "string" ? transformResult : transformResult.code;
|
|
683
|
+
return { id, code: finalCode };
|
|
684
|
+
}
|
|
685
|
+
getPlugins() {
|
|
686
|
+
return this.plugins;
|
|
687
|
+
}
|
|
688
|
+
};
|
|
867
689
|
}
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
// src/build/index.ts
|
|
693
|
+
var build_exports = {};
|
|
694
|
+
__export(build_exports, {
|
|
695
|
+
build: () => build
|
|
696
|
+
});
|
|
697
|
+
import path7 from "path";
|
|
698
|
+
import fs6 from "fs";
|
|
699
|
+
import { rolldown } from "rolldown";
|
|
700
|
+
import pc from "picocolors";
|
|
701
|
+
async function build(inlineConfig = {}) {
|
|
702
|
+
const config = await resolveConfig(inlineConfig, "build");
|
|
703
|
+
const startTime = performance.now();
|
|
704
|
+
console.log(pc.cyan("\n\u{1F528} nasti build") + pc.dim(` v${"1.4.0"}`));
|
|
705
|
+
console.log(pc.dim(` root: ${config.root}`));
|
|
706
|
+
console.log(pc.dim(` mode: ${config.mode}`));
|
|
707
|
+
const outDir = path7.resolve(config.root, config.build.outDir);
|
|
708
|
+
if (config.build.emptyOutDir && fs6.existsSync(outDir)) {
|
|
709
|
+
fs6.rmSync(outDir, { recursive: true, force: true });
|
|
710
|
+
}
|
|
711
|
+
fs6.mkdirSync(outDir, { recursive: true });
|
|
712
|
+
const html = await readHtmlFile(config.root);
|
|
713
|
+
let entryPoints = [];
|
|
714
|
+
if (html) {
|
|
715
|
+
const scriptMatches = html.matchAll(/<script[^>]+src=["']([^"']+)["'][^>]*>/gi);
|
|
716
|
+
for (const match of scriptMatches) {
|
|
717
|
+
const src = match[1];
|
|
718
|
+
if (src && !src.startsWith("http")) {
|
|
719
|
+
entryPoints.push(path7.resolve(config.root, src.replace(/^\//, "")));
|
|
720
|
+
}
|
|
872
721
|
}
|
|
873
722
|
}
|
|
874
|
-
if (
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
723
|
+
if (entryPoints.length === 0) {
|
|
724
|
+
const fallbackEntries = ["src/main.ts", "src/main.tsx", "src/main.js", "src/index.ts", "src/index.tsx", "src/index.js"];
|
|
725
|
+
for (const entry of fallbackEntries) {
|
|
726
|
+
const fullPath = path7.resolve(config.root, entry);
|
|
727
|
+
if (fs6.existsSync(fullPath)) {
|
|
728
|
+
entryPoints.push(fullPath);
|
|
729
|
+
break;
|
|
879
730
|
}
|
|
880
731
|
}
|
|
881
732
|
}
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
733
|
+
if (entryPoints.length === 0) {
|
|
734
|
+
throw new Error("No entry point found. Add a <script> tag to index.html or create src/main.ts");
|
|
735
|
+
}
|
|
736
|
+
const builtinPlugins = [
|
|
737
|
+
resolvePlugin(config),
|
|
738
|
+
cssPlugin(config),
|
|
739
|
+
assetsPlugin(config)
|
|
740
|
+
];
|
|
741
|
+
const allPlugins = [...builtinPlugins, ...config.plugins];
|
|
742
|
+
const pluginContainer = new PluginContainer(config);
|
|
743
|
+
await pluginContainer.buildStart();
|
|
744
|
+
const oxcTransformPlugin = {
|
|
745
|
+
name: "nasti:oxc-transform",
|
|
894
746
|
transform(code, id) {
|
|
895
|
-
if (!id
|
|
896
|
-
const
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
const __nasti_css_id__ = ${JSON.stringify(id)};
|
|
903
|
-
const __nasti_existing__ = document.querySelector('style[data-nasti-css=' + JSON.stringify(__nasti_css_id__) + ']');
|
|
904
|
-
if (__nasti_existing__) __nasti_existing__.remove();
|
|
905
|
-
const style = document.createElement('style');
|
|
906
|
-
style.setAttribute('data-nasti-css', __nasti_css_id__);
|
|
907
|
-
style.textContent = css;
|
|
908
|
-
document.head.appendChild(style);
|
|
909
|
-
|
|
910
|
-
// HMR
|
|
911
|
-
if (import.meta.hot) {
|
|
912
|
-
import.meta.hot.accept();
|
|
913
|
-
import.meta.hot.prune(() => {
|
|
914
|
-
style.remove();
|
|
915
|
-
});
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
export default css;
|
|
919
|
-
`
|
|
920
|
-
};
|
|
921
|
-
}
|
|
922
|
-
return rewritten !== code ? { code: rewritten } : null;
|
|
747
|
+
if (!shouldTransform(id)) return null;
|
|
748
|
+
const result = transformCode(id, code, {
|
|
749
|
+
sourcemap: !!config.build.sourcemap,
|
|
750
|
+
jsxRuntime: "automatic",
|
|
751
|
+
jsxImportSource: config.framework === "vue" ? "vue" : "react"
|
|
752
|
+
});
|
|
753
|
+
return { code: result.code, map: result.map ? JSON.parse(result.map) : void 0 };
|
|
923
754
|
}
|
|
924
755
|
};
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
756
|
+
const env = loadEnv(config.mode, config.root, config.envPrefix);
|
|
757
|
+
const envDefine = buildEnvDefine(env, config.mode);
|
|
758
|
+
const bundle = await rolldown({
|
|
759
|
+
input: entryPoints,
|
|
760
|
+
define: envDefine,
|
|
761
|
+
plugins: [
|
|
762
|
+
oxcTransformPlugin,
|
|
763
|
+
// 转换 Nasti 插件为 Rolldown 插件格式
|
|
764
|
+
...allPlugins.map((p) => ({
|
|
765
|
+
name: p.name,
|
|
766
|
+
resolveId: p.resolveId,
|
|
767
|
+
load: p.load,
|
|
768
|
+
transform: p.transform,
|
|
769
|
+
buildStart: p.buildStart,
|
|
770
|
+
buildEnd: p.buildEnd
|
|
771
|
+
}))
|
|
772
|
+
],
|
|
773
|
+
...config.build.rolldownOptions
|
|
934
774
|
});
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
775
|
+
const { output } = await bundle.write({
|
|
776
|
+
dir: outDir,
|
|
777
|
+
format: "esm",
|
|
778
|
+
sourcemap: !!config.build.sourcemap,
|
|
779
|
+
minify: !!config.build.minify,
|
|
780
|
+
entryFileNames: "assets/[name].[hash].js",
|
|
781
|
+
chunkFileNames: "assets/[name].[hash].js",
|
|
782
|
+
assetFileNames: "assets/[name].[hash][extname]"
|
|
783
|
+
});
|
|
784
|
+
await bundle.close();
|
|
785
|
+
await pluginContainer.buildEnd();
|
|
786
|
+
for (const ef of pluginContainer.getEmittedFiles()) {
|
|
787
|
+
const dest = path7.resolve(outDir, ef.fileName);
|
|
788
|
+
fs6.mkdirSync(path7.dirname(dest), { recursive: true });
|
|
789
|
+
fs6.writeFileSync(dest, ef.source);
|
|
790
|
+
}
|
|
791
|
+
if (html) {
|
|
792
|
+
let processedHtml = html;
|
|
793
|
+
const htmlPlugin_ = htmlPlugin(config);
|
|
794
|
+
if (htmlPlugin_.transformIndexHtml) {
|
|
795
|
+
const result = await htmlPlugin_.transformIndexHtml(processedHtml);
|
|
796
|
+
if (typeof result === "string") {
|
|
797
|
+
processedHtml = result;
|
|
798
|
+
} else if (result && "html" in result) {
|
|
799
|
+
processedHtml = processHtml(result.html, result.tags);
|
|
800
|
+
} else if (Array.isArray(result)) {
|
|
801
|
+
processedHtml = processHtml(processedHtml, result);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
for (const chunk of output) {
|
|
805
|
+
if (chunk.type === "chunk" && chunk.isEntry && chunk.facadeModuleId) {
|
|
806
|
+
const originalEntry = path7.relative(config.root, chunk.facadeModuleId);
|
|
807
|
+
processedHtml = processedHtml.replace(
|
|
808
|
+
new RegExp(`(src=["'])/?(${escapeRegExp(originalEntry)})(["'])`, "g"),
|
|
809
|
+
`$1${config.base}${chunk.fileName}$3`
|
|
810
|
+
);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
fs6.writeFileSync(path7.resolve(outDir, "index.html"), processedHtml);
|
|
814
|
+
}
|
|
815
|
+
const elapsed = ((performance.now() - startTime) / 1e3).toFixed(2);
|
|
816
|
+
const totalSize = output.reduce((sum, chunk) => {
|
|
817
|
+
if (chunk.type === "chunk" && chunk.code) return sum + chunk.code.length;
|
|
818
|
+
return sum;
|
|
819
|
+
}, 0);
|
|
820
|
+
console.log(pc.green(`
|
|
821
|
+
\u2713 Built in ${elapsed}s`));
|
|
822
|
+
console.log(pc.dim(` ${output.length} files, ${formatSize(totalSize)} total`));
|
|
823
|
+
console.log(pc.dim(` output: ${config.build.outDir}/
|
|
824
|
+
`));
|
|
825
|
+
return { output };
|
|
826
|
+
}
|
|
827
|
+
function formatSize(bytes) {
|
|
828
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
829
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} kB`;
|
|
830
|
+
return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
|
|
831
|
+
}
|
|
832
|
+
function escapeRegExp(string) {
|
|
833
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
834
|
+
}
|
|
835
|
+
var init_build = __esm({
|
|
836
|
+
"src/build/index.ts"() {
|
|
837
|
+
"use strict";
|
|
838
|
+
init_config();
|
|
839
|
+
init_resolve();
|
|
840
|
+
init_css();
|
|
841
|
+
init_assets();
|
|
842
|
+
init_html();
|
|
843
|
+
init_transformer();
|
|
844
|
+
init_env();
|
|
845
|
+
init_plugin_container();
|
|
846
|
+
}
|
|
847
|
+
});
|
|
848
|
+
|
|
849
|
+
// src/core/module-graph.ts
|
|
850
|
+
var ModuleGraph;
|
|
851
|
+
var init_module_graph = __esm({
|
|
852
|
+
"src/core/module-graph.ts"() {
|
|
853
|
+
"use strict";
|
|
854
|
+
ModuleGraph = class {
|
|
855
|
+
urlToModuleMap = /* @__PURE__ */ new Map();
|
|
856
|
+
idToModuleMap = /* @__PURE__ */ new Map();
|
|
857
|
+
fileToModulesMap = /* @__PURE__ */ new Map();
|
|
858
|
+
getModuleByUrl(url) {
|
|
859
|
+
return this.urlToModuleMap.get(url);
|
|
860
|
+
}
|
|
861
|
+
getModuleById(id) {
|
|
862
|
+
return this.idToModuleMap.get(id);
|
|
863
|
+
}
|
|
864
|
+
getModulesByFile(file) {
|
|
865
|
+
return this.fileToModulesMap.get(file);
|
|
866
|
+
}
|
|
867
|
+
async ensureEntryFromUrl(url) {
|
|
868
|
+
let mod = this.urlToModuleMap.get(url);
|
|
869
|
+
if (mod) return mod;
|
|
870
|
+
mod = this.createModule(url);
|
|
871
|
+
this.urlToModuleMap.set(url, mod);
|
|
872
|
+
return mod;
|
|
873
|
+
}
|
|
874
|
+
createModule(url, id) {
|
|
875
|
+
const mod = {
|
|
876
|
+
id: id ?? url,
|
|
877
|
+
file: null,
|
|
878
|
+
url,
|
|
879
|
+
type: url.endsWith(".css") ? "css" : "js",
|
|
880
|
+
importers: /* @__PURE__ */ new Set(),
|
|
881
|
+
importedModules: /* @__PURE__ */ new Set(),
|
|
882
|
+
acceptedHmrDeps: /* @__PURE__ */ new Set(),
|
|
883
|
+
transformResult: null,
|
|
884
|
+
lastHMRTimestamp: 0,
|
|
885
|
+
isSelfAccepting: false
|
|
886
|
+
};
|
|
887
|
+
this.idToModuleMap.set(mod.id, mod);
|
|
888
|
+
return mod;
|
|
889
|
+
}
|
|
890
|
+
/** 注册文件路径到模块的映射 */
|
|
891
|
+
registerModule(mod, file) {
|
|
892
|
+
mod.file = file;
|
|
893
|
+
let mods = this.fileToModulesMap.get(file);
|
|
894
|
+
if (!mods) {
|
|
895
|
+
mods = /* @__PURE__ */ new Set();
|
|
896
|
+
this.fileToModulesMap.set(file, mods);
|
|
981
897
|
}
|
|
898
|
+
mods.add(mod);
|
|
982
899
|
}
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
900
|
+
/** 更新模块依赖关系 */
|
|
901
|
+
updateModuleImports(mod, importedIds) {
|
|
902
|
+
for (const imported of mod.importedModules) {
|
|
903
|
+
imported.importers.delete(mod);
|
|
904
|
+
}
|
|
905
|
+
mod.importedModules.clear();
|
|
906
|
+
for (const id of importedIds) {
|
|
907
|
+
const importedMod = this.idToModuleMap.get(id);
|
|
908
|
+
if (importedMod) {
|
|
909
|
+
mod.importedModules.add(importedMod);
|
|
910
|
+
importedMod.importers.add(mod);
|
|
911
|
+
}
|
|
989
912
|
}
|
|
990
|
-
const content = fs3.readFileSync(file);
|
|
991
|
-
const hash = crypto.createHash("sha256").update(content).digest("hex").slice(0, 8);
|
|
992
|
-
const basename = path4.basename(file, ext);
|
|
993
|
-
const hashedName = `${config.build.assetsDir}/${basename}.${hash}${ext}`;
|
|
994
|
-
return `export default ${JSON.stringify(config.base + hashedName)}`;
|
|
995
913
|
}
|
|
996
|
-
|
|
914
|
+
/** 使模块的转换缓存失效 */
|
|
915
|
+
invalidateModule(mod) {
|
|
916
|
+
mod.transformResult = null;
|
|
917
|
+
mod.lastHMRTimestamp = Date.now();
|
|
918
|
+
}
|
|
919
|
+
/** 使所有模块缓存失效 */
|
|
920
|
+
invalidateAll() {
|
|
921
|
+
for (const mod of this.idToModuleMap.values()) {
|
|
922
|
+
this.invalidateModule(mod);
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
/** 获取 HMR 传播边界 - 从变更模块向上遍历找到接受更新的边界 */
|
|
926
|
+
getHmrBoundaries(mod) {
|
|
927
|
+
const boundaries = [];
|
|
928
|
+
const visited = /* @__PURE__ */ new Set();
|
|
929
|
+
const propagate = (node, via) => {
|
|
930
|
+
if (visited.has(node)) return true;
|
|
931
|
+
visited.add(node);
|
|
932
|
+
if (node.isSelfAccepting) {
|
|
933
|
+
boundaries.push({ boundary: node, acceptedVia: via });
|
|
934
|
+
return true;
|
|
935
|
+
}
|
|
936
|
+
if (node.acceptedHmrDeps.has(via)) {
|
|
937
|
+
boundaries.push({ boundary: node, acceptedVia: via });
|
|
938
|
+
return true;
|
|
939
|
+
}
|
|
940
|
+
if (node.importers.size === 0) return false;
|
|
941
|
+
for (const importer of node.importers) {
|
|
942
|
+
if (!propagate(importer, node)) return false;
|
|
943
|
+
}
|
|
944
|
+
return true;
|
|
945
|
+
};
|
|
946
|
+
if (mod.isSelfAccepting) {
|
|
947
|
+
boundaries.push({ boundary: mod, acceptedVia: mod });
|
|
948
|
+
return boundaries;
|
|
949
|
+
}
|
|
950
|
+
for (const importer of mod.importers) {
|
|
951
|
+
if (!propagate(importer, mod)) {
|
|
952
|
+
return [];
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
return boundaries;
|
|
956
|
+
}
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
});
|
|
960
|
+
|
|
961
|
+
// src/server/ws.ts
|
|
962
|
+
import { WebSocketServer as WsServer } from "ws";
|
|
963
|
+
function createWebSocketServer(server) {
|
|
964
|
+
const wss = new WsServer({ noServer: true });
|
|
965
|
+
const clients = /* @__PURE__ */ new Set();
|
|
966
|
+
server.on("upgrade", (req, socket, head) => {
|
|
967
|
+
if (req.headers["sec-websocket-protocol"] === "nasti-hmr") {
|
|
968
|
+
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
969
|
+
wss.emit("connection", ws, req);
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
});
|
|
973
|
+
wss.on("connection", (ws) => {
|
|
974
|
+
clients.add(ws);
|
|
975
|
+
ws.send(JSON.stringify({ type: "connected" }));
|
|
976
|
+
ws.on("close", () => {
|
|
977
|
+
clients.delete(ws);
|
|
978
|
+
});
|
|
979
|
+
ws.on("error", (err) => {
|
|
980
|
+
console.error("[nasti] WebSocket error:", err);
|
|
981
|
+
clients.delete(ws);
|
|
982
|
+
});
|
|
983
|
+
});
|
|
984
|
+
return {
|
|
985
|
+
send(payload) {
|
|
986
|
+
const data = JSON.stringify(payload);
|
|
987
|
+
for (const client of clients) {
|
|
988
|
+
if (client.readyState === 1) {
|
|
989
|
+
client.send(data);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
},
|
|
993
|
+
close() {
|
|
994
|
+
clients.clear();
|
|
995
|
+
wss.close();
|
|
997
996
|
}
|
|
998
997
|
};
|
|
999
998
|
}
|
|
999
|
+
var init_ws = __esm({
|
|
1000
|
+
"src/server/ws.ts"() {
|
|
1001
|
+
"use strict";
|
|
1002
|
+
}
|
|
1003
|
+
});
|
|
1000
1004
|
|
|
1001
|
-
// src/
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
ctx
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
const id = `emitted:${fileName}`;
|
|
1026
|
-
container.emittedFiles.set(id, {
|
|
1027
|
-
fileName,
|
|
1028
|
-
source: file.source ?? ""
|
|
1029
|
-
});
|
|
1030
|
-
return id;
|
|
1031
|
-
},
|
|
1032
|
-
getModuleInfo(_id) {
|
|
1033
|
-
return null;
|
|
1005
|
+
// src/server/middleware.ts
|
|
1006
|
+
var middleware_exports = {};
|
|
1007
|
+
__export(middleware_exports, {
|
|
1008
|
+
transformMiddleware: () => transformMiddleware,
|
|
1009
|
+
transformRequest: () => transformRequest
|
|
1010
|
+
});
|
|
1011
|
+
import path9 from "path";
|
|
1012
|
+
import fs8 from "fs";
|
|
1013
|
+
function transformMiddleware(ctx) {
|
|
1014
|
+
ctx.envDefine = buildEnvDefine(
|
|
1015
|
+
loadEnv(ctx.config.mode, ctx.config.root, ctx.config.envPrefix),
|
|
1016
|
+
ctx.config.mode
|
|
1017
|
+
);
|
|
1018
|
+
return async (req, res, next) => {
|
|
1019
|
+
const url = req.url ?? "/";
|
|
1020
|
+
if (ctx.config.server.cors) {
|
|
1021
|
+
const origin = req.headers.origin ?? "*";
|
|
1022
|
+
res.setHeader("Access-Control-Allow-Origin", origin);
|
|
1023
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS");
|
|
1024
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
1025
|
+
if (req.method === "OPTIONS") {
|
|
1026
|
+
res.statusCode = 204;
|
|
1027
|
+
res.end();
|
|
1028
|
+
return;
|
|
1034
1029
|
}
|
|
1035
|
-
}
|
|
1030
|
+
}
|
|
1031
|
+
if (req.method !== "GET") return next();
|
|
1032
|
+
if (url === "/@nasti/client") {
|
|
1033
|
+
res.setHeader("Content-Type", "application/javascript");
|
|
1034
|
+
res.end(getHmrClientCode());
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
if (url === "/" || url.endsWith(".html")) {
|
|
1038
|
+
const html = await readHtmlFile(ctx.config.root);
|
|
1039
|
+
if (html) {
|
|
1040
|
+
let processedHtml = html;
|
|
1041
|
+
for (const plugin of ctx.config.plugins) {
|
|
1042
|
+
if (plugin.transformIndexHtml) {
|
|
1043
|
+
const result = await plugin.transformIndexHtml(processedHtml);
|
|
1044
|
+
if (typeof result === "string") {
|
|
1045
|
+
processedHtml = result;
|
|
1046
|
+
} else if (result && "html" in result) {
|
|
1047
|
+
processedHtml = processHtml(result.html, result.tags);
|
|
1048
|
+
} else if (Array.isArray(result)) {
|
|
1049
|
+
processedHtml = processHtml(processedHtml, result);
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
res.setHeader("Content-Type", "text/html");
|
|
1054
|
+
res.end(processedHtml);
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
if (isModuleRequest(url)) {
|
|
1059
|
+
try {
|
|
1060
|
+
const result = await transformRequest(url, ctx);
|
|
1061
|
+
if (result) {
|
|
1062
|
+
const contentType = url.endsWith(".css") ? "application/javascript" : "application/javascript";
|
|
1063
|
+
res.setHeader("Content-Type", contentType);
|
|
1064
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
1065
|
+
res.end(typeof result === "string" ? result : result.code);
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
1068
|
+
} catch (err) {
|
|
1069
|
+
console.error(`[nasti] Transform error: ${url}`, err.message);
|
|
1070
|
+
res.statusCode = 500;
|
|
1071
|
+
res.end(`Transform error: ${err.message}`);
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
next();
|
|
1076
|
+
};
|
|
1077
|
+
}
|
|
1078
|
+
async function transformRequest(url, ctx) {
|
|
1079
|
+
const { config, pluginContainer, moduleGraph } = ctx;
|
|
1080
|
+
const cleanReqUrl = url.split("?")[0];
|
|
1081
|
+
const cached = moduleGraph.getModuleByUrl(url);
|
|
1082
|
+
if (cached?.transformResult) {
|
|
1083
|
+
return cached.transformResult;
|
|
1084
|
+
}
|
|
1085
|
+
if (cleanReqUrl === "/@react-refresh") {
|
|
1086
|
+
return { code: REACT_REFRESH_RUNTIME };
|
|
1087
|
+
}
|
|
1088
|
+
const filePath = resolveUrlToFile(url, config.root);
|
|
1089
|
+
if (!filePath || !fs8.existsSync(filePath)) return null;
|
|
1090
|
+
const mod = await moduleGraph.ensureEntryFromUrl(url);
|
|
1091
|
+
moduleGraph.registerModule(mod, filePath);
|
|
1092
|
+
if (cleanReqUrl.startsWith("/@modules/")) {
|
|
1093
|
+
const code2 = await bundlePackageAsEsm(filePath);
|
|
1094
|
+
const transformResult2 = { code: code2 };
|
|
1095
|
+
mod.transformResult = transformResult2;
|
|
1096
|
+
return transformResult2;
|
|
1097
|
+
}
|
|
1098
|
+
let code = fs8.readFileSync(filePath, "utf-8");
|
|
1099
|
+
const pluginResult = await pluginContainer.transform(code, filePath);
|
|
1100
|
+
if (pluginResult) {
|
|
1101
|
+
code = typeof pluginResult === "string" ? pluginResult : pluginResult.code;
|
|
1102
|
+
}
|
|
1103
|
+
if (shouldTransform(filePath)) {
|
|
1104
|
+
const isJsx = /\.[jt]sx$/.test(filePath);
|
|
1105
|
+
const useRefresh = isJsx && config.framework !== "vue";
|
|
1106
|
+
const result = transformCode(filePath, code, {
|
|
1107
|
+
sourcemap: true,
|
|
1108
|
+
jsxRuntime: "automatic",
|
|
1109
|
+
jsxImportSource: config.framework === "vue" ? "vue" : "react",
|
|
1110
|
+
reactRefresh: useRefresh
|
|
1111
|
+
});
|
|
1112
|
+
code = result.code;
|
|
1113
|
+
if (useRefresh) {
|
|
1114
|
+
code = REACT_REFRESH_PREAMBLE + code + REACT_REFRESH_FOOTER;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
const envDefine = ctx.envDefine ?? buildEnvDefine(
|
|
1118
|
+
loadEnv(config.mode, config.root, config.envPrefix),
|
|
1119
|
+
config.mode
|
|
1120
|
+
);
|
|
1121
|
+
code = replaceEnvInCode(code, envDefine);
|
|
1122
|
+
code = rewriteImports(code, config);
|
|
1123
|
+
const transformResult = { code };
|
|
1124
|
+
mod.transformResult = transformResult;
|
|
1125
|
+
return transformResult;
|
|
1126
|
+
}
|
|
1127
|
+
async function bundlePackageAsEsm(entryFile) {
|
|
1128
|
+
if (!esmBundleCache.has(entryFile)) {
|
|
1129
|
+
esmBundleCache.set(entryFile, doBundlePackage(entryFile));
|
|
1130
|
+
}
|
|
1131
|
+
return esmBundleCache.get(entryFile);
|
|
1132
|
+
}
|
|
1133
|
+
async function doBundlePackage(entryFile) {
|
|
1134
|
+
const { rolldown: rolldown4 } = await import("rolldown");
|
|
1135
|
+
const bundle = await rolldown4({
|
|
1136
|
+
input: entryFile,
|
|
1137
|
+
// 仅将其他 npm 包外部化;相对路径(包内部文件)全部内联打包
|
|
1138
|
+
external: (id) => {
|
|
1139
|
+
if (id.startsWith(".") || id.startsWith("/") || /^[A-Za-z]:\\/.test(id)) return false;
|
|
1140
|
+
return true;
|
|
1141
|
+
}
|
|
1142
|
+
});
|
|
1143
|
+
const result = await bundle.generate({ format: "esm", exports: "named" });
|
|
1144
|
+
await bundle.close();
|
|
1145
|
+
let code = result.output[0].code;
|
|
1146
|
+
code = code.replace(/process\.env\.NODE_ENV/g, '"development"');
|
|
1147
|
+
code = code.replace(
|
|
1148
|
+
/^(import\b[^;'"]*?\bfrom\s+)(['"])([^'"./][^'"]*)(\2)/gm,
|
|
1149
|
+
(_, prefix, q, spec) => `${prefix}${q}/@modules/${spec}${q}`
|
|
1150
|
+
).replace(
|
|
1151
|
+
/^(export\b[^;'"]*?\bfrom\s+)(['"])([^'"./][^'"]*)(\2)/gm,
|
|
1152
|
+
(_, prefix, q, spec) => `${prefix}${q}/@modules/${spec}${q}`
|
|
1153
|
+
).replace(
|
|
1154
|
+
/^(import\s+)(['"])([^'"./][^'"]*)(\2)/gm,
|
|
1155
|
+
(_, prefix, q, spec) => `${prefix}${q}/@modules/${spec}${q}`
|
|
1156
|
+
);
|
|
1157
|
+
code = rewriteExternalRequires(code);
|
|
1158
|
+
if (code.includes("__commonJSMin")) {
|
|
1159
|
+
code = await injectCjsNamedExports(code, entryFile);
|
|
1036
1160
|
}
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1161
|
+
return code;
|
|
1162
|
+
}
|
|
1163
|
+
function rewriteExternalRequires(code) {
|
|
1164
|
+
const pkgs = /* @__PURE__ */ new Set();
|
|
1165
|
+
const re = /__require\(["']([^"']+)["']\)/g;
|
|
1166
|
+
let m;
|
|
1167
|
+
while ((m = re.exec(code)) !== null) {
|
|
1168
|
+
pkgs.add(m[1]);
|
|
1169
|
+
}
|
|
1170
|
+
if (pkgs.size === 0) return code;
|
|
1171
|
+
let result = code;
|
|
1172
|
+
const imports = [];
|
|
1173
|
+
for (const pkg of pkgs) {
|
|
1174
|
+
const safe = pkg.replace(/[^a-zA-Z0-9_$]/g, "_");
|
|
1175
|
+
imports.push(`import * as __ns_${safe} from "/@modules/${pkg}";`);
|
|
1176
|
+
imports.push(`var __req_${safe} = "default" in __ns_${safe} ? __ns_${safe}["default"] : __ns_${safe};`);
|
|
1177
|
+
result = result.replaceAll(`__require("${pkg}")`, `__req_${safe}`);
|
|
1178
|
+
result = result.replaceAll(`__require('${pkg}')`, `__req_${safe}`);
|
|
1179
|
+
}
|
|
1180
|
+
return imports.join("\n") + "\n" + result;
|
|
1181
|
+
}
|
|
1182
|
+
async function injectCjsNamedExports(code, entryFile) {
|
|
1183
|
+
try {
|
|
1184
|
+
const { createRequire: createRequire3 } = await import("module");
|
|
1185
|
+
const req = createRequire3(entryFile);
|
|
1186
|
+
const cjsExports = req(entryFile);
|
|
1187
|
+
if (!cjsExports || typeof cjsExports !== "object" && typeof cjsExports !== "function" || Array.isArray(cjsExports)) return code;
|
|
1188
|
+
const namedKeys = Object.keys(cjsExports).filter(
|
|
1189
|
+
(k) => k !== "__esModule" && k !== "default" && VALID_IDENT.test(k)
|
|
1190
|
+
);
|
|
1191
|
+
if (namedKeys.length === 0) return code;
|
|
1192
|
+
return code.replace(
|
|
1193
|
+
/^export default (\w+\(\));?\s*$/m,
|
|
1194
|
+
(_, call) => [
|
|
1195
|
+
`const __cjsMod = ${call};`,
|
|
1196
|
+
`export default __cjsMod;`,
|
|
1197
|
+
...namedKeys.map((k) => `export const ${k} = __cjsMod[${JSON.stringify(k)}];`)
|
|
1198
|
+
].join("\n")
|
|
1199
|
+
);
|
|
1200
|
+
} catch {
|
|
1201
|
+
return code;
|
|
1040
1202
|
}
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1203
|
+
}
|
|
1204
|
+
function rewriteImports(code, _config) {
|
|
1205
|
+
return code.replace(
|
|
1206
|
+
/\bfrom\s+(['"])([^'"./][^'"]*)\1/g,
|
|
1207
|
+
(match, quote, specifier) => {
|
|
1208
|
+
return `from ${quote}/@modules/${specifier}${quote}`;
|
|
1209
|
+
}
|
|
1210
|
+
).replace(
|
|
1211
|
+
// 处理纯副作用导入: import 'bare-specifier'
|
|
1212
|
+
/\bimport\s+(['"])([^'"./][^'"]*)\1/g,
|
|
1213
|
+
(match, quote, specifier) => {
|
|
1214
|
+
return `import ${quote}/@modules/${specifier}${quote}`;
|
|
1215
|
+
}
|
|
1216
|
+
).replace(
|
|
1217
|
+
// 处理动态导入: import('bare-specifier')
|
|
1218
|
+
/\bimport\s*\(\s*(['"])([^'"./][^'"]*)\1\s*\)/g,
|
|
1219
|
+
(match, quote, specifier) => {
|
|
1220
|
+
return `import(${quote}/@modules/${specifier}${quote})`;
|
|
1221
|
+
}
|
|
1222
|
+
);
|
|
1223
|
+
}
|
|
1224
|
+
function resolveNodeModule(root, moduleName) {
|
|
1225
|
+
let pkgName;
|
|
1226
|
+
let subpath;
|
|
1227
|
+
if (moduleName.startsWith("@")) {
|
|
1228
|
+
const parts = moduleName.split("/");
|
|
1229
|
+
pkgName = parts.slice(0, 2).join("/");
|
|
1230
|
+
subpath = parts.slice(2).join("/");
|
|
1231
|
+
} else {
|
|
1232
|
+
const slash = moduleName.indexOf("/");
|
|
1233
|
+
pkgName = slash === -1 ? moduleName : moduleName.slice(0, slash);
|
|
1234
|
+
subpath = slash === -1 ? "" : moduleName.slice(slash + 1);
|
|
1235
|
+
}
|
|
1236
|
+
let pkgDir = null;
|
|
1237
|
+
let dir = root;
|
|
1238
|
+
for (; ; ) {
|
|
1239
|
+
const candidate = path9.join(dir, "node_modules", pkgName);
|
|
1240
|
+
if (fs8.existsSync(candidate)) {
|
|
1241
|
+
pkgDir = candidate;
|
|
1242
|
+
break;
|
|
1243
|
+
}
|
|
1244
|
+
const parent = path9.dirname(dir);
|
|
1245
|
+
if (parent === dir) break;
|
|
1246
|
+
dir = parent;
|
|
1247
|
+
}
|
|
1248
|
+
if (!pkgDir) return null;
|
|
1249
|
+
const pkgJsonPath = path9.join(pkgDir, "package.json");
|
|
1250
|
+
if (!fs8.existsSync(pkgJsonPath)) return null;
|
|
1251
|
+
let pkg;
|
|
1252
|
+
try {
|
|
1253
|
+
pkg = JSON.parse(fs8.readFileSync(pkgJsonPath, "utf-8"));
|
|
1254
|
+
} catch {
|
|
1255
|
+
return null;
|
|
1256
|
+
}
|
|
1257
|
+
if (pkg.exports) {
|
|
1258
|
+
const exportKey = subpath ? `./${subpath}` : ".";
|
|
1259
|
+
const resolved = resolvePackageExports(pkg.exports, exportKey, pkgDir);
|
|
1260
|
+
if (resolved) return resolved;
|
|
1261
|
+
}
|
|
1262
|
+
if (subpath) {
|
|
1263
|
+
const subDirs = [""];
|
|
1264
|
+
for (const field of ["module", "main"]) {
|
|
1265
|
+
if (typeof pkg[field] === "string") {
|
|
1266
|
+
const dir2 = path9.dirname(pkg[field]);
|
|
1267
|
+
if (dir2 && dir2 !== "." && !subDirs.includes(dir2)) subDirs.push(dir2);
|
|
1045
1268
|
}
|
|
1046
1269
|
}
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1270
|
+
for (const dir2 of subDirs) {
|
|
1271
|
+
const direct = path9.join(pkgDir, dir2, subpath);
|
|
1272
|
+
if (fs8.existsSync(direct) && fs8.statSync(direct).isFile()) return direct;
|
|
1273
|
+
for (const ext of RESOLVE_EXTENSIONS) {
|
|
1274
|
+
if (fs8.existsSync(direct + ext)) return direct + ext;
|
|
1052
1275
|
}
|
|
1053
1276
|
}
|
|
1054
|
-
}
|
|
1055
|
-
async resolveId(source, importer, options = {}) {
|
|
1056
|
-
for (const plugin of this.plugins) {
|
|
1057
|
-
if (!plugin.resolveId) continue;
|
|
1058
|
-
const result = await plugin.resolveId.call(
|
|
1059
|
-
this.ctx,
|
|
1060
|
-
source,
|
|
1061
|
-
importer ?? void 0,
|
|
1062
|
-
{ isEntry: options.isEntry ?? false, ssr: false }
|
|
1063
|
-
);
|
|
1064
|
-
if (result != null) return result;
|
|
1065
|
-
}
|
|
1066
1277
|
return null;
|
|
1067
1278
|
}
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
if (result != null) return result;
|
|
1073
|
-
}
|
|
1074
|
-
return null;
|
|
1075
|
-
}
|
|
1076
|
-
async transform(code, id) {
|
|
1077
|
-
let currentCode = code;
|
|
1078
|
-
for (const plugin of this.plugins) {
|
|
1079
|
-
if (!plugin.transform) continue;
|
|
1080
|
-
const result = await plugin.transform.call(this.ctx, currentCode, id);
|
|
1081
|
-
if (result == null) continue;
|
|
1082
|
-
if (typeof result === "string") {
|
|
1083
|
-
currentCode = result;
|
|
1084
|
-
} else {
|
|
1085
|
-
currentCode = result.code;
|
|
1086
|
-
}
|
|
1279
|
+
for (const field of ["module", "jsnext:main", "jsnext", "main"]) {
|
|
1280
|
+
if (typeof pkg[field] === "string") {
|
|
1281
|
+
const entry = path9.join(pkgDir, pkg[field]);
|
|
1282
|
+
if (fs8.existsSync(entry)) return entry;
|
|
1087
1283
|
}
|
|
1088
|
-
return currentCode === code ? null : { code: currentCode };
|
|
1089
|
-
}
|
|
1090
|
-
/** 完整的模块处理管道: resolveId → load → transform */
|
|
1091
|
-
async processModule(source, importer) {
|
|
1092
|
-
const resolveResult = await this.resolveId(source, importer, {
|
|
1093
|
-
isEntry: !importer
|
|
1094
|
-
});
|
|
1095
|
-
if (resolveResult == null) return null;
|
|
1096
|
-
const id = typeof resolveResult === "string" ? resolveResult : resolveResult.id;
|
|
1097
|
-
const loadResult = await this.load(id);
|
|
1098
|
-
if (loadResult == null) return null;
|
|
1099
|
-
const loadedCode = typeof loadResult === "string" ? loadResult : loadResult.code;
|
|
1100
|
-
const transformResult = await this.transform(loadedCode, id);
|
|
1101
|
-
const finalCode = transformResult == null ? loadedCode : typeof transformResult === "string" ? transformResult : transformResult.code;
|
|
1102
|
-
return { id, code: finalCode };
|
|
1103
|
-
}
|
|
1104
|
-
getPlugins() {
|
|
1105
|
-
return this.plugins;
|
|
1106
|
-
}
|
|
1107
|
-
};
|
|
1108
|
-
function sortPlugins(plugins) {
|
|
1109
|
-
const pre = [];
|
|
1110
|
-
const normal = [];
|
|
1111
|
-
const post = [];
|
|
1112
|
-
for (const plugin of plugins) {
|
|
1113
|
-
if (plugin.enforce === "pre") pre.push(plugin);
|
|
1114
|
-
else if (plugin.enforce === "post") post.push(plugin);
|
|
1115
|
-
else normal.push(plugin);
|
|
1116
1284
|
}
|
|
1117
|
-
|
|
1285
|
+
const indexFallback = path9.join(pkgDir, "index.js");
|
|
1286
|
+
if (fs8.existsSync(indexFallback)) return indexFallback;
|
|
1287
|
+
return null;
|
|
1118
1288
|
}
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
async function build(inlineConfig = {}) {
|
|
1123
|
-
const config = await resolveConfig(inlineConfig, "build");
|
|
1124
|
-
const startTime = performance.now();
|
|
1125
|
-
console.log(pc.cyan("\n\u{1F528} nasti build") + pc.dim(` v${"1.3.9"}`));
|
|
1126
|
-
console.log(pc.dim(` root: ${config.root}`));
|
|
1127
|
-
console.log(pc.dim(` mode: ${config.mode}`));
|
|
1128
|
-
const outDir = path7.resolve(config.root, config.build.outDir);
|
|
1129
|
-
if (config.build.emptyOutDir && fs6.existsSync(outDir)) {
|
|
1130
|
-
fs6.rmSync(outDir, { recursive: true, force: true });
|
|
1289
|
+
function resolvePackageExports(exports, key, pkgDir) {
|
|
1290
|
+
if (typeof exports === "string") {
|
|
1291
|
+
return key === "." ? path9.join(pkgDir, exports) : null;
|
|
1131
1292
|
}
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1293
|
+
const entry = exports[key];
|
|
1294
|
+
if (entry === void 0) return null;
|
|
1295
|
+
return resolveExportValue(entry, pkgDir);
|
|
1296
|
+
}
|
|
1297
|
+
function resolveExportValue(value, pkgDir) {
|
|
1298
|
+
if (typeof value === "string") return path9.join(pkgDir, value);
|
|
1299
|
+
if (Array.isArray(value)) {
|
|
1300
|
+
for (const item of value) {
|
|
1301
|
+
const r = resolveExportValue(item, pkgDir);
|
|
1302
|
+
if (r) return r;
|
|
1142
1303
|
}
|
|
1304
|
+
return null;
|
|
1143
1305
|
}
|
|
1144
|
-
if (
|
|
1145
|
-
const
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
entryPoints.push(fullPath);
|
|
1150
|
-
break;
|
|
1306
|
+
if (value && typeof value === "object") {
|
|
1307
|
+
for (const cond of ESM_CONDITIONS) {
|
|
1308
|
+
if (cond in value) {
|
|
1309
|
+
const r = resolveExportValue(value[cond], pkgDir);
|
|
1310
|
+
if (r) return r;
|
|
1151
1311
|
}
|
|
1152
1312
|
}
|
|
1153
1313
|
}
|
|
1154
|
-
|
|
1155
|
-
|
|
1314
|
+
return null;
|
|
1315
|
+
}
|
|
1316
|
+
function resolveUrlToFile(url, root) {
|
|
1317
|
+
const cleanUrl = url.split("?")[0];
|
|
1318
|
+
if (cleanUrl.startsWith("/@modules/")) {
|
|
1319
|
+
const moduleName = cleanUrl.slice("/@modules/".length);
|
|
1320
|
+
return resolveNodeModule(root, moduleName);
|
|
1156
1321
|
}
|
|
1157
|
-
const
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
assetsPlugin(config)
|
|
1161
|
-
];
|
|
1162
|
-
const allPlugins = [...builtinPlugins, ...config.plugins];
|
|
1163
|
-
const pluginContainer = new PluginContainer(config);
|
|
1164
|
-
await pluginContainer.buildStart();
|
|
1165
|
-
const oxcTransformPlugin = {
|
|
1166
|
-
name: "nasti:oxc-transform",
|
|
1167
|
-
transform(code, id) {
|
|
1168
|
-
if (!shouldTransform(id)) return null;
|
|
1169
|
-
const result = transformCode(id, code, {
|
|
1170
|
-
sourcemap: !!config.build.sourcemap,
|
|
1171
|
-
jsxRuntime: "automatic",
|
|
1172
|
-
jsxImportSource: config.framework === "vue" ? "vue" : "react"
|
|
1173
|
-
});
|
|
1174
|
-
return { code: result.code, map: result.map ? JSON.parse(result.map) : void 0 };
|
|
1175
|
-
}
|
|
1176
|
-
};
|
|
1177
|
-
const env = loadEnv(config.mode, config.root, config.envPrefix);
|
|
1178
|
-
const envDefine = buildEnvDefine(env, config.mode);
|
|
1179
|
-
const bundle = await rolldown({
|
|
1180
|
-
input: entryPoints,
|
|
1181
|
-
define: envDefine,
|
|
1182
|
-
plugins: [
|
|
1183
|
-
oxcTransformPlugin,
|
|
1184
|
-
// 转换 Nasti 插件为 Rolldown 插件格式
|
|
1185
|
-
...allPlugins.map((p) => ({
|
|
1186
|
-
name: p.name,
|
|
1187
|
-
resolveId: p.resolveId,
|
|
1188
|
-
load: p.load,
|
|
1189
|
-
transform: p.transform,
|
|
1190
|
-
buildStart: p.buildStart,
|
|
1191
|
-
buildEnd: p.buildEnd
|
|
1192
|
-
}))
|
|
1193
|
-
],
|
|
1194
|
-
...config.build.rolldownOptions
|
|
1195
|
-
});
|
|
1196
|
-
const { output } = await bundle.write({
|
|
1197
|
-
dir: outDir,
|
|
1198
|
-
format: "esm",
|
|
1199
|
-
sourcemap: !!config.build.sourcemap,
|
|
1200
|
-
minify: !!config.build.minify,
|
|
1201
|
-
entryFileNames: "assets/[name].[hash].js",
|
|
1202
|
-
chunkFileNames: "assets/[name].[hash].js",
|
|
1203
|
-
assetFileNames: "assets/[name].[hash][extname]"
|
|
1204
|
-
});
|
|
1205
|
-
await bundle.close();
|
|
1206
|
-
await pluginContainer.buildEnd();
|
|
1207
|
-
for (const ef of pluginContainer.getEmittedFiles()) {
|
|
1208
|
-
const dest = path7.resolve(outDir, ef.fileName);
|
|
1209
|
-
fs6.mkdirSync(path7.dirname(dest), { recursive: true });
|
|
1210
|
-
fs6.writeFileSync(dest, ef.source);
|
|
1322
|
+
const filePath = path9.resolve(root, cleanUrl.replace(/^\//, ""));
|
|
1323
|
+
if (fs8.existsSync(filePath) && fs8.statSync(filePath).isFile()) {
|
|
1324
|
+
return filePath;
|
|
1211
1325
|
}
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
if (htmlPlugin_.transformIndexHtml) {
|
|
1216
|
-
const result = await htmlPlugin_.transformIndexHtml(processedHtml);
|
|
1217
|
-
if (typeof result === "string") {
|
|
1218
|
-
processedHtml = result;
|
|
1219
|
-
} else if (result && "html" in result) {
|
|
1220
|
-
processedHtml = processHtml(result.html, result.tags);
|
|
1221
|
-
} else if (Array.isArray(result)) {
|
|
1222
|
-
processedHtml = processHtml(processedHtml, result);
|
|
1223
|
-
}
|
|
1224
|
-
}
|
|
1225
|
-
for (const chunk of output) {
|
|
1226
|
-
if (chunk.type === "chunk" && chunk.isEntry && chunk.facadeModuleId) {
|
|
1227
|
-
const originalEntry = path7.relative(config.root, chunk.facadeModuleId);
|
|
1228
|
-
processedHtml = processedHtml.replace(
|
|
1229
|
-
new RegExp(`(src=["'])/?(${escapeRegExp(originalEntry)})(["'])`, "g"),
|
|
1230
|
-
`$1${config.base}${chunk.fileName}$3`
|
|
1231
|
-
);
|
|
1232
|
-
}
|
|
1233
|
-
}
|
|
1234
|
-
fs6.writeFileSync(path7.resolve(outDir, "index.html"), processedHtml);
|
|
1326
|
+
for (const ext of RESOLVE_EXTENSIONS) {
|
|
1327
|
+
const withExt = filePath + ext;
|
|
1328
|
+
if (fs8.existsSync(withExt)) return withExt;
|
|
1235
1329
|
}
|
|
1236
|
-
const
|
|
1237
|
-
|
|
1238
|
-
if (
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
console.log(pc.green(`
|
|
1242
|
-
\u2713 Built in ${elapsed}s`));
|
|
1243
|
-
console.log(pc.dim(` ${output.length} files, ${formatSize(totalSize)} total`));
|
|
1244
|
-
console.log(pc.dim(` output: ${config.build.outDir}/
|
|
1245
|
-
`));
|
|
1246
|
-
return { output };
|
|
1247
|
-
}
|
|
1248
|
-
function formatSize(bytes) {
|
|
1249
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
1250
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} kB`;
|
|
1251
|
-
return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
|
|
1330
|
+
for (const ext of RESOLVE_EXTENSIONS) {
|
|
1331
|
+
const indexFile = path9.join(filePath, "index" + ext);
|
|
1332
|
+
if (fs8.existsSync(indexFile)) return indexFile;
|
|
1333
|
+
}
|
|
1334
|
+
return null;
|
|
1252
1335
|
}
|
|
1253
|
-
function
|
|
1254
|
-
|
|
1336
|
+
function isModuleRequest(url) {
|
|
1337
|
+
const cleanUrl = url.split("?")[0];
|
|
1338
|
+
if (/\.(ts|tsx|jsx|js|mjs|vue|css|json)$/.test(cleanUrl)) return true;
|
|
1339
|
+
if (cleanUrl.startsWith("/@modules/")) return true;
|
|
1340
|
+
if (!path9.extname(cleanUrl)) return true;
|
|
1341
|
+
return false;
|
|
1255
1342
|
}
|
|
1343
|
+
function getHmrClientCode() {
|
|
1344
|
+
return `
|
|
1345
|
+
// Nasti HMR Client
|
|
1346
|
+
const socket = new WebSocket(\`ws://\${location.host}\`, 'nasti-hmr');
|
|
1347
|
+
const hotModulesMap = new Map();
|
|
1256
1348
|
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
async ensureEntryFromUrl(url) {
|
|
1281
|
-
let mod = this.urlToModuleMap.get(url);
|
|
1282
|
-
if (mod) return mod;
|
|
1283
|
-
mod = this.createModule(url);
|
|
1284
|
-
this.urlToModuleMap.set(url, mod);
|
|
1285
|
-
return mod;
|
|
1286
|
-
}
|
|
1287
|
-
createModule(url, id) {
|
|
1288
|
-
const mod = {
|
|
1289
|
-
id: id ?? url,
|
|
1290
|
-
file: null,
|
|
1291
|
-
url,
|
|
1292
|
-
type: url.endsWith(".css") ? "css" : "js",
|
|
1293
|
-
importers: /* @__PURE__ */ new Set(),
|
|
1294
|
-
importedModules: /* @__PURE__ */ new Set(),
|
|
1295
|
-
acceptedHmrDeps: /* @__PURE__ */ new Set(),
|
|
1296
|
-
transformResult: null,
|
|
1297
|
-
lastHMRTimestamp: 0,
|
|
1298
|
-
isSelfAccepting: false
|
|
1299
|
-
};
|
|
1300
|
-
this.idToModuleMap.set(mod.id, mod);
|
|
1301
|
-
return mod;
|
|
1302
|
-
}
|
|
1303
|
-
/** 注册文件路径到模块的映射 */
|
|
1304
|
-
registerModule(mod, file) {
|
|
1305
|
-
mod.file = file;
|
|
1306
|
-
let mods = this.fileToModulesMap.get(file);
|
|
1307
|
-
if (!mods) {
|
|
1308
|
-
mods = /* @__PURE__ */ new Set();
|
|
1309
|
-
this.fileToModulesMap.set(file, mods);
|
|
1310
|
-
}
|
|
1311
|
-
mods.add(mod);
|
|
1312
|
-
}
|
|
1313
|
-
/** 更新模块依赖关系 */
|
|
1314
|
-
updateModuleImports(mod, importedIds) {
|
|
1315
|
-
for (const imported of mod.importedModules) {
|
|
1316
|
-
imported.importers.delete(mod);
|
|
1317
|
-
}
|
|
1318
|
-
mod.importedModules.clear();
|
|
1319
|
-
for (const id of importedIds) {
|
|
1320
|
-
const importedMod = this.idToModuleMap.get(id);
|
|
1321
|
-
if (importedMod) {
|
|
1322
|
-
mod.importedModules.add(importedMod);
|
|
1323
|
-
importedMod.importers.add(mod);
|
|
1324
|
-
}
|
|
1325
|
-
}
|
|
1326
|
-
}
|
|
1327
|
-
/** 使模块的转换缓存失效 */
|
|
1328
|
-
invalidateModule(mod) {
|
|
1329
|
-
mod.transformResult = null;
|
|
1330
|
-
mod.lastHMRTimestamp = Date.now();
|
|
1349
|
+
socket.addEventListener('message', ({ data }) => {
|
|
1350
|
+
const payload = JSON.parse(data);
|
|
1351
|
+
switch (payload.type) {
|
|
1352
|
+
case 'connected':
|
|
1353
|
+
console.log('[nasti] connected.');
|
|
1354
|
+
break;
|
|
1355
|
+
case 'update':
|
|
1356
|
+
payload.updates.forEach((update) => {
|
|
1357
|
+
if (update.type === 'js-update') {
|
|
1358
|
+
fetchUpdate(update);
|
|
1359
|
+
} else if (update.type === 'css-update') {
|
|
1360
|
+
updateCss(update.path);
|
|
1361
|
+
}
|
|
1362
|
+
});
|
|
1363
|
+
break;
|
|
1364
|
+
case 'full-reload':
|
|
1365
|
+
console.log('[nasti] full reload');
|
|
1366
|
+
location.reload();
|
|
1367
|
+
break;
|
|
1368
|
+
case 'error':
|
|
1369
|
+
console.error('[nasti] error:', payload.err.message);
|
|
1370
|
+
showErrorOverlay(payload.err);
|
|
1371
|
+
break;
|
|
1331
1372
|
}
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1373
|
+
});
|
|
1374
|
+
|
|
1375
|
+
async function fetchUpdate(update) {
|
|
1376
|
+
const mod = hotModulesMap.get(update.path);
|
|
1377
|
+
if (mod) {
|
|
1378
|
+
const newMod = await import(update.acceptedPath + '?t=' + update.timestamp);
|
|
1379
|
+
mod.callbacks.forEach((cb) => cb(newMod));
|
|
1380
|
+
} else {
|
|
1381
|
+
// \u6CA1\u6709\u6CE8\u518C hot \u56DE\u8C03\uFF0C\u5C1D\u8BD5\u91CD\u65B0 import
|
|
1382
|
+
await import(update.path + '?t=' + update.timestamp);
|
|
1337
1383
|
}
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
boundaries.push({ boundary: node, acceptedVia: via });
|
|
1347
|
-
return true;
|
|
1348
|
-
}
|
|
1349
|
-
if (node.acceptedHmrDeps.has(via)) {
|
|
1350
|
-
boundaries.push({ boundary: node, acceptedVia: via });
|
|
1351
|
-
return true;
|
|
1352
|
-
}
|
|
1353
|
-
if (node.importers.size === 0) return false;
|
|
1354
|
-
for (const importer of node.importers) {
|
|
1355
|
-
if (!propagate(importer, node)) return false;
|
|
1356
|
-
}
|
|
1357
|
-
return true;
|
|
1358
|
-
};
|
|
1359
|
-
if (mod.isSelfAccepting) {
|
|
1360
|
-
boundaries.push({ boundary: mod, acceptedVia: mod });
|
|
1361
|
-
return boundaries;
|
|
1362
|
-
}
|
|
1363
|
-
for (const importer of mod.importers) {
|
|
1364
|
-
if (!propagate(importer, mod)) {
|
|
1365
|
-
return [];
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
return boundaries;
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
function updateCss(path) {
|
|
1387
|
+
const el = document.querySelector(\`style[data-nasti-css="\${path}"]\`);
|
|
1388
|
+
if (el) {
|
|
1389
|
+
fetch(path + '?t=' + Date.now())
|
|
1390
|
+
.then(r => r.text())
|
|
1391
|
+
.then(css => { el.textContent = css; });
|
|
1369
1392
|
}
|
|
1370
|
-
}
|
|
1393
|
+
}
|
|
1371
1394
|
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
const
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
});
|
|
1390
|
-
ws.on("error", (err) => {
|
|
1391
|
-
console.error("[nasti] WebSocket error:", err);
|
|
1392
|
-
clients.delete(ws);
|
|
1393
|
-
});
|
|
1394
|
-
});
|
|
1395
|
-
return {
|
|
1396
|
-
send(payload) {
|
|
1397
|
-
const data = JSON.stringify(payload);
|
|
1398
|
-
for (const client of clients) {
|
|
1399
|
-
if (client.readyState === 1) {
|
|
1400
|
-
client.send(data);
|
|
1401
|
-
}
|
|
1402
|
-
}
|
|
1403
|
-
},
|
|
1404
|
-
close() {
|
|
1405
|
-
clients.clear();
|
|
1406
|
-
wss.close();
|
|
1407
|
-
}
|
|
1408
|
-
};
|
|
1395
|
+
function showErrorOverlay(err) {
|
|
1396
|
+
const overlay = document.createElement('div');
|
|
1397
|
+
overlay.id = 'nasti-error-overlay';
|
|
1398
|
+
overlay.style.cssText = 'position:fixed;inset:0;z-index:99999;background:rgba(0,0,0,0.85);color:#fff;font-family:monospace;padding:2rem;overflow:auto;';
|
|
1399
|
+
const title = document.createElement('h2');
|
|
1400
|
+
title.style.color = '#ff5555';
|
|
1401
|
+
title.textContent = 'Build Error';
|
|
1402
|
+
const pre = document.createElement('pre');
|
|
1403
|
+
pre.textContent = err.message + '\\n' + (err.stack || '');
|
|
1404
|
+
const btn = document.createElement('button');
|
|
1405
|
+
btn.style.cssText = 'margin-top:1rem;padding:0.5rem 1rem;cursor:pointer';
|
|
1406
|
+
btn.textContent = 'Close';
|
|
1407
|
+
btn.onclick = () => overlay.remove();
|
|
1408
|
+
overlay.appendChild(title);
|
|
1409
|
+
overlay.appendChild(pre);
|
|
1410
|
+
overlay.appendChild(btn);
|
|
1411
|
+
document.body.appendChild(overlay);
|
|
1409
1412
|
}
|
|
1410
1413
|
|
|
1411
|
-
//
|
|
1412
|
-
|
|
1414
|
+
// import.meta.hot API
|
|
1415
|
+
const createHotContext = (ownerPath) => ({
|
|
1416
|
+
accept(deps, callback) {
|
|
1417
|
+
if (typeof deps === 'function' || !deps) {
|
|
1418
|
+
// self-accepting
|
|
1419
|
+
const callbacks = hotModulesMap.get(ownerPath)?.callbacks || [];
|
|
1420
|
+
callbacks.push(deps || (() => {}));
|
|
1421
|
+
hotModulesMap.set(ownerPath, { callbacks });
|
|
1422
|
+
}
|
|
1423
|
+
},
|
|
1424
|
+
prune(callback) {
|
|
1425
|
+
// \u6A21\u5757\u88AB\u79FB\u9664\u65F6\u6267\u884C
|
|
1426
|
+
},
|
|
1427
|
+
dispose(callback) {
|
|
1428
|
+
// \u6A21\u5757\u66F4\u65B0\u524D\u6267\u884C\u6E05\u7406
|
|
1429
|
+
},
|
|
1430
|
+
invalidate() {
|
|
1431
|
+
location.reload();
|
|
1432
|
+
},
|
|
1433
|
+
data: {},
|
|
1434
|
+
});
|
|
1435
|
+
|
|
1436
|
+
// \u66B4\u9732\u7ED9\u6A21\u5757\u4F7F\u7528
|
|
1437
|
+
if (!window.__nasti_hot_map) window.__nasti_hot_map = new Map();
|
|
1438
|
+
window.__NASTI_HMR__ = { createHotContext };
|
|
1439
|
+
`;
|
|
1440
|
+
}
|
|
1441
|
+
var REACT_REFRESH_RUNTIME, REACT_REFRESH_PREAMBLE, REACT_REFRESH_FOOTER, esmBundleCache, VALID_IDENT, RESOLVE_EXTENSIONS, ESM_CONDITIONS;
|
|
1442
|
+
var init_middleware = __esm({
|
|
1443
|
+
"src/server/middleware.ts"() {
|
|
1444
|
+
"use strict";
|
|
1445
|
+
init_transformer();
|
|
1446
|
+
init_html();
|
|
1447
|
+
init_env();
|
|
1448
|
+
REACT_REFRESH_RUNTIME = `
|
|
1449
|
+
export function createSignatureFunctionForTransform() {
|
|
1450
|
+
return function(type, key, forceReset, getCustomHooks) { return type; };
|
|
1451
|
+
}
|
|
1452
|
+
export function register(type, id) {}
|
|
1453
|
+
export default { createSignatureFunctionForTransform, register };
|
|
1454
|
+
`;
|
|
1455
|
+
REACT_REFRESH_PREAMBLE = `
|
|
1456
|
+
import RefreshRuntime from '/@react-refresh';
|
|
1457
|
+
if (!window.$RefreshReg$) {
|
|
1458
|
+
window.$RefreshReg$ = (type, id) => RefreshRuntime.register(type, import.meta.url + ' ' + id);
|
|
1459
|
+
window.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform;
|
|
1460
|
+
}
|
|
1461
|
+
`;
|
|
1462
|
+
REACT_REFRESH_FOOTER = `
|
|
1463
|
+
if (import.meta.hot) {
|
|
1464
|
+
import.meta.hot.accept();
|
|
1465
|
+
}
|
|
1466
|
+
`;
|
|
1467
|
+
esmBundleCache = /* @__PURE__ */ new Map();
|
|
1468
|
+
VALID_IDENT = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
1469
|
+
RESOLVE_EXTENSIONS = [".tsx", ".ts", ".jsx", ".js", ".mjs", ".json", ".vue"];
|
|
1470
|
+
ESM_CONDITIONS = ["import", "browser", "module", "default"];
|
|
1471
|
+
}
|
|
1472
|
+
});
|
|
1413
1473
|
|
|
1414
1474
|
// src/server/hmr.ts
|
|
1415
|
-
import
|
|
1416
|
-
import
|
|
1475
|
+
import path10 from "path";
|
|
1476
|
+
import fs9 from "fs";
|
|
1417
1477
|
async function handleFileChange(file, server) {
|
|
1418
1478
|
const { moduleGraph, ws, config } = server;
|
|
1419
|
-
const relativePath = "/" +
|
|
1479
|
+
const relativePath = "/" + path10.relative(config.root, file);
|
|
1420
1480
|
const mods = moduleGraph.getModulesByFile(file);
|
|
1421
1481
|
if (!mods || mods.size === 0) {
|
|
1422
1482
|
return;
|
|
@@ -1429,7 +1489,7 @@ async function handleFileChange(file, server) {
|
|
|
1429
1489
|
file,
|
|
1430
1490
|
timestamp,
|
|
1431
1491
|
modules: [mod],
|
|
1432
|
-
read: () =>
|
|
1492
|
+
read: () => fs9.readFileSync(file, "utf-8"),
|
|
1433
1493
|
server
|
|
1434
1494
|
};
|
|
1435
1495
|
let affectedModules = [mod];
|
|
@@ -1461,9 +1521,24 @@ async function handleFileChange(file, server) {
|
|
|
1461
1521
|
ws.send({ type: "update", updates });
|
|
1462
1522
|
}
|
|
1463
1523
|
}
|
|
1524
|
+
var init_hmr = __esm({
|
|
1525
|
+
"src/server/hmr.ts"() {
|
|
1526
|
+
"use strict";
|
|
1527
|
+
}
|
|
1528
|
+
});
|
|
1464
1529
|
|
|
1465
1530
|
// src/server/index.ts
|
|
1466
|
-
|
|
1531
|
+
var server_exports = {};
|
|
1532
|
+
__export(server_exports, {
|
|
1533
|
+
createServer: () => createServer
|
|
1534
|
+
});
|
|
1535
|
+
import http from "http";
|
|
1536
|
+
import path11 from "path";
|
|
1537
|
+
import os from "os";
|
|
1538
|
+
import connect from "connect";
|
|
1539
|
+
import sirv from "sirv";
|
|
1540
|
+
import { watch } from "chokidar";
|
|
1541
|
+
import pc3 from "picocolors";
|
|
1467
1542
|
async function createServer(inlineConfig = {}) {
|
|
1468
1543
|
const config = await resolveConfig(inlineConfig, "serve");
|
|
1469
1544
|
const allPlugins = [
|
|
@@ -1482,7 +1557,7 @@ async function createServer(inlineConfig = {}) {
|
|
|
1482
1557
|
pluginContainer,
|
|
1483
1558
|
moduleGraph
|
|
1484
1559
|
}));
|
|
1485
|
-
const publicDir =
|
|
1560
|
+
const publicDir = path11.resolve(config.root, "public");
|
|
1486
1561
|
app.use(sirv(publicDir, { dev: true, etag: true }));
|
|
1487
1562
|
app.use(sirv(config.root, { dev: true, etag: true }));
|
|
1488
1563
|
const httpServer = http.createServer(app);
|
|
@@ -1525,14 +1600,15 @@ async function createServer(inlineConfig = {}) {
|
|
|
1525
1600
|
let currentPort = finalPort;
|
|
1526
1601
|
const onListening = () => {
|
|
1527
1602
|
const actualPort = httpServer.address()?.port ?? currentPort;
|
|
1603
|
+
config.server.port = actualPort;
|
|
1528
1604
|
const localUrl = `http://localhost:${actualPort}`;
|
|
1529
1605
|
const networkUrl = host === "0.0.0.0" ? `http://${getNetworkAddress()}:${actualPort}` : null;
|
|
1530
1606
|
console.log();
|
|
1531
|
-
console.log(
|
|
1607
|
+
console.log(pc3.cyan(" nasti dev server") + pc3.dim(` v${"1.4.0"}`));
|
|
1532
1608
|
console.log();
|
|
1533
|
-
console.log(` ${
|
|
1609
|
+
console.log(` ${pc3.green(">")} Local: ${pc3.cyan(localUrl)}`);
|
|
1534
1610
|
if (networkUrl) {
|
|
1535
|
-
console.log(` ${
|
|
1611
|
+
console.log(` ${pc3.green(">")} Network: ${pc3.cyan(networkUrl)}`);
|
|
1536
1612
|
}
|
|
1537
1613
|
console.log();
|
|
1538
1614
|
resolve(server);
|
|
@@ -1541,7 +1617,7 @@ async function createServer(inlineConfig = {}) {
|
|
|
1541
1617
|
httpServer.on("error", (err) => {
|
|
1542
1618
|
if (err.code === "EADDRINUSE") {
|
|
1543
1619
|
currentPort++;
|
|
1544
|
-
console.log(
|
|
1620
|
+
console.log(pc3.yellow(`Port ${currentPort - 1} is in use, trying ${currentPort}...`));
|
|
1545
1621
|
httpServer.listen(currentPort, host);
|
|
1546
1622
|
} else {
|
|
1547
1623
|
reject(err);
|
|
@@ -1574,10 +1650,391 @@ function getNetworkAddress() {
|
|
|
1574
1650
|
}
|
|
1575
1651
|
return "localhost";
|
|
1576
1652
|
}
|
|
1653
|
+
var init_server = __esm({
|
|
1654
|
+
"src/server/index.ts"() {
|
|
1655
|
+
"use strict";
|
|
1656
|
+
init_config();
|
|
1657
|
+
init_plugin_container();
|
|
1658
|
+
init_module_graph();
|
|
1659
|
+
init_ws();
|
|
1660
|
+
init_middleware();
|
|
1661
|
+
init_hmr();
|
|
1662
|
+
init_resolve();
|
|
1663
|
+
init_css();
|
|
1664
|
+
init_assets();
|
|
1665
|
+
init_html();
|
|
1666
|
+
}
|
|
1667
|
+
});
|
|
1668
|
+
|
|
1669
|
+
// src/index.ts
|
|
1670
|
+
init_config();
|
|
1671
|
+
init_build();
|
|
1672
|
+
|
|
1673
|
+
// src/build/electron.ts
|
|
1674
|
+
init_config();
|
|
1675
|
+
init_resolve();
|
|
1676
|
+
import path8 from "path";
|
|
1677
|
+
import fs7 from "fs";
|
|
1678
|
+
import { rolldown as rolldown2 } from "rolldown";
|
|
1679
|
+
import pc2 from "picocolors";
|
|
1680
|
+
|
|
1681
|
+
// src/plugins/electron.ts
|
|
1682
|
+
import { builtinModules } from "module";
|
|
1683
|
+
var NODE_BUILTINS = /* @__PURE__ */ new Set([
|
|
1684
|
+
...builtinModules,
|
|
1685
|
+
...builtinModules.map((m) => `node:${m}`)
|
|
1686
|
+
]);
|
|
1687
|
+
var ELECTRON_MODULES = /* @__PURE__ */ new Set([
|
|
1688
|
+
"electron",
|
|
1689
|
+
"electron/main",
|
|
1690
|
+
"electron/common",
|
|
1691
|
+
"electron/renderer"
|
|
1692
|
+
]);
|
|
1693
|
+
function electronPlugin(config) {
|
|
1694
|
+
const external = /* @__PURE__ */ new Set([
|
|
1695
|
+
...ELECTRON_MODULES,
|
|
1696
|
+
...NODE_BUILTINS,
|
|
1697
|
+
...config.electron.external ?? []
|
|
1698
|
+
]);
|
|
1699
|
+
return {
|
|
1700
|
+
name: "nasti:electron",
|
|
1701
|
+
enforce: "pre",
|
|
1702
|
+
resolveId(source) {
|
|
1703
|
+
if (external.has(source)) {
|
|
1704
|
+
return { id: source, external: true };
|
|
1705
|
+
}
|
|
1706
|
+
if (source.startsWith("electron/")) {
|
|
1707
|
+
return { id: source, external: true };
|
|
1708
|
+
}
|
|
1709
|
+
return null;
|
|
1710
|
+
}
|
|
1711
|
+
};
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
// src/build/electron.ts
|
|
1715
|
+
init_transformer();
|
|
1716
|
+
init_env();
|
|
1717
|
+
async function buildElectron(inlineConfig = {}) {
|
|
1718
|
+
const config = await resolveConfig({ ...inlineConfig, target: "electron" }, "build");
|
|
1719
|
+
const startTime = performance.now();
|
|
1720
|
+
assertElectronVersion(config);
|
|
1721
|
+
console.log(pc2.cyan("\n\u26A1 nasti build (electron)") + pc2.dim(` v${"1.4.0"}`));
|
|
1722
|
+
console.log(pc2.dim(` root: ${config.root}`));
|
|
1723
|
+
console.log(pc2.dim(` mode: ${config.mode}`));
|
|
1724
|
+
console.log(pc2.dim(` target: electron (\u2265 ${config.electron.minVersion})`));
|
|
1725
|
+
const outDir = path8.resolve(config.root, config.build.outDir);
|
|
1726
|
+
if (config.build.emptyOutDir && fs7.existsSync(outDir)) {
|
|
1727
|
+
fs7.rmSync(outDir, { recursive: true, force: true });
|
|
1728
|
+
}
|
|
1729
|
+
fs7.mkdirSync(outDir, { recursive: true });
|
|
1730
|
+
const rendererOutDir = path8.join(outDir, "renderer");
|
|
1731
|
+
const { build: build2 } = await Promise.resolve().then(() => (init_build(), build_exports));
|
|
1732
|
+
await build2({
|
|
1733
|
+
...inlineConfig,
|
|
1734
|
+
target: "web",
|
|
1735
|
+
build: {
|
|
1736
|
+
...inlineConfig.build,
|
|
1737
|
+
outDir: rendererOutDir,
|
|
1738
|
+
emptyOutDir: false
|
|
1739
|
+
}
|
|
1740
|
+
});
|
|
1741
|
+
const mainEntry = path8.resolve(config.root, config.electron.main);
|
|
1742
|
+
if (!fs7.existsSync(mainEntry)) {
|
|
1743
|
+
throw new Error(
|
|
1744
|
+
`Electron main entry not found: ${config.electron.main}
|
|
1745
|
+
\u5728 nasti.config.ts \u7684 electron.main \u6307\u5B9A\u4E3B\u8FDB\u7A0B\u5165\u53E3\u6587\u4EF6\u3002`
|
|
1746
|
+
);
|
|
1747
|
+
}
|
|
1748
|
+
const mainFile = await bundleNode(config, mainEntry, {
|
|
1749
|
+
outFile: outFileName(outDir, "main", config.electron.mainFormat),
|
|
1750
|
+
format: config.electron.mainFormat,
|
|
1751
|
+
label: "main"
|
|
1752
|
+
});
|
|
1753
|
+
const preloadEntries = normalizePreload(config.electron.preload, config.root);
|
|
1754
|
+
const preloadFiles = [];
|
|
1755
|
+
for (const entry of preloadEntries) {
|
|
1756
|
+
if (!fs7.existsSync(entry)) {
|
|
1757
|
+
console.warn(pc2.yellow(` \u26A0 preload entry not found, skipped: ${entry}`));
|
|
1758
|
+
continue;
|
|
1759
|
+
}
|
|
1760
|
+
const base = path8.basename(entry).replace(/\.[^.]+$/, "");
|
|
1761
|
+
const out = outFileName(outDir, base, config.electron.preloadFormat);
|
|
1762
|
+
await bundleNode(config, entry, {
|
|
1763
|
+
outFile: out,
|
|
1764
|
+
format: config.electron.preloadFormat,
|
|
1765
|
+
label: `preload (${base})`
|
|
1766
|
+
});
|
|
1767
|
+
preloadFiles.push(out);
|
|
1768
|
+
}
|
|
1769
|
+
const elapsed = ((performance.now() - startTime) / 1e3).toFixed(2);
|
|
1770
|
+
console.log(pc2.green(`
|
|
1771
|
+
\u2713 Electron build complete in ${elapsed}s`));
|
|
1772
|
+
console.log(pc2.dim(` renderer: ${path8.relative(config.root, rendererOutDir)}/`));
|
|
1773
|
+
console.log(pc2.dim(` main: ${path8.relative(config.root, mainFile)}`));
|
|
1774
|
+
for (const pf of preloadFiles) {
|
|
1775
|
+
console.log(pc2.dim(` preload: ${path8.relative(config.root, pf)}`));
|
|
1776
|
+
}
|
|
1777
|
+
console.log();
|
|
1778
|
+
return { rendererOutDir, mainFile, preloadFiles };
|
|
1779
|
+
}
|
|
1780
|
+
async function bundleNode(config, entry, opts) {
|
|
1781
|
+
const env = loadEnv(config.mode, config.root, config.envPrefix);
|
|
1782
|
+
const envDefine = {
|
|
1783
|
+
...buildEnvDefine(env, config.mode),
|
|
1784
|
+
__ELECTRON__: "true",
|
|
1785
|
+
__NASTI_TARGET__: JSON.stringify("electron")
|
|
1786
|
+
};
|
|
1787
|
+
const oxcTransformPlugin = {
|
|
1788
|
+
name: "nasti:oxc-transform",
|
|
1789
|
+
transform(code, id) {
|
|
1790
|
+
if (!shouldTransform(id)) return null;
|
|
1791
|
+
const result = transformCode(id, code, {
|
|
1792
|
+
sourcemap: !!config.build.sourcemap,
|
|
1793
|
+
jsxRuntime: "automatic",
|
|
1794
|
+
jsxImportSource: config.framework === "vue" ? "vue" : "react"
|
|
1795
|
+
});
|
|
1796
|
+
return { code: result.code, map: result.map ? JSON.parse(result.map) : void 0 };
|
|
1797
|
+
}
|
|
1798
|
+
};
|
|
1799
|
+
const bundle = await rolldown2({
|
|
1800
|
+
input: entry,
|
|
1801
|
+
define: envDefine,
|
|
1802
|
+
platform: "node",
|
|
1803
|
+
plugins: [oxcTransformPlugin, electronPlugin(config), resolvePlugin(config)],
|
|
1804
|
+
...config.build.rolldownOptions
|
|
1805
|
+
});
|
|
1806
|
+
fs7.mkdirSync(path8.dirname(opts.outFile), { recursive: true });
|
|
1807
|
+
await bundle.write({
|
|
1808
|
+
file: opts.outFile,
|
|
1809
|
+
format: opts.format === "cjs" ? "cjs" : "esm",
|
|
1810
|
+
sourcemap: !!config.build.sourcemap,
|
|
1811
|
+
minify: !!config.build.minify,
|
|
1812
|
+
inlineDynamicImports: true
|
|
1813
|
+
});
|
|
1814
|
+
await bundle.close();
|
|
1815
|
+
console.log(pc2.dim(` \u2713 ${opts.label} \u2192 ${path8.relative(config.root, opts.outFile)}`));
|
|
1816
|
+
return opts.outFile;
|
|
1817
|
+
}
|
|
1818
|
+
function outFileName(outDir, base, format) {
|
|
1819
|
+
const ext = format === "cjs" ? ".cjs" : ".mjs";
|
|
1820
|
+
return path8.join(outDir, base + ext);
|
|
1821
|
+
}
|
|
1822
|
+
function normalizePreload(preload, root) {
|
|
1823
|
+
const list = Array.isArray(preload) ? preload : preload ? [preload] : [];
|
|
1824
|
+
return list.map((p) => path8.resolve(root, p));
|
|
1825
|
+
}
|
|
1826
|
+
function assertElectronVersion(config) {
|
|
1827
|
+
const min = config.electron.minVersion;
|
|
1828
|
+
const installed = detectInstalledElectron(config.root);
|
|
1829
|
+
if (installed && installed < min) {
|
|
1830
|
+
console.warn(
|
|
1831
|
+
pc2.yellow(
|
|
1832
|
+
` \u26A0 \u68C0\u6D4B\u5230 Electron ${installed}\uFF0CNasti \u8981\u6C42 \u2265 ${min}\u3002\u65E7\u7248\u672C\u53EF\u80FD\u7F3A\u5C11 ESM \u4E3B\u8FDB\u7A0B\u652F\u6301\u3002`
|
|
1833
|
+
)
|
|
1834
|
+
);
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
function detectInstalledElectron(root) {
|
|
1838
|
+
try {
|
|
1839
|
+
const pkgPath = path8.resolve(root, "node_modules/electron/package.json");
|
|
1840
|
+
if (!fs7.existsSync(pkgPath)) return null;
|
|
1841
|
+
const pkg = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
|
|
1842
|
+
const major = parseInt(String(pkg.version).split(".")[0], 10);
|
|
1843
|
+
return Number.isFinite(major) ? major : null;
|
|
1844
|
+
} catch {
|
|
1845
|
+
return null;
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
// src/index.ts
|
|
1850
|
+
init_server();
|
|
1851
|
+
|
|
1852
|
+
// src/server/electron-dev.ts
|
|
1853
|
+
init_config();
|
|
1854
|
+
import path12 from "path";
|
|
1855
|
+
import fs10 from "fs";
|
|
1856
|
+
import { createRequire as createRequire2 } from "module";
|
|
1857
|
+
import { spawn } from "child_process";
|
|
1858
|
+
import chokidar from "chokidar";
|
|
1859
|
+
import pc4 from "picocolors";
|
|
1860
|
+
import { rolldown as rolldown3 } from "rolldown";
|
|
1861
|
+
init_resolve();
|
|
1862
|
+
init_transformer();
|
|
1863
|
+
init_env();
|
|
1864
|
+
async function startElectronDev(inlineConfig = {}) {
|
|
1865
|
+
const { noSpawn, ...rest } = inlineConfig;
|
|
1866
|
+
const config = await resolveConfig({ ...rest, target: "electron" }, "serve");
|
|
1867
|
+
warnElectronVersion(config);
|
|
1868
|
+
console.log(pc4.cyan("\n\u26A1 nasti electron dev") + pc4.dim(` v${"1.4.0"}`));
|
|
1869
|
+
const { createServer: createServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
|
|
1870
|
+
const server = await createServer2({ ...rest, target: "electron" });
|
|
1871
|
+
await server.listen();
|
|
1872
|
+
const devUrl = `http://localhost:${server.config.server.port}/`;
|
|
1873
|
+
console.log(pc4.dim(` renderer: ${devUrl}`));
|
|
1874
|
+
const stageDir = path12.resolve(config.root, ".nasti");
|
|
1875
|
+
fs10.mkdirSync(stageDir, { recursive: true });
|
|
1876
|
+
const mainEntry = path12.resolve(config.root, config.electron.main);
|
|
1877
|
+
const preloadEntries = normalizePreload(config.electron.preload, config.root);
|
|
1878
|
+
const builtMainFile = path12.join(stageDir, "main" + extFor(config.electron.mainFormat));
|
|
1879
|
+
const builtPreloadFiles = [];
|
|
1880
|
+
const compileAll = async () => {
|
|
1881
|
+
await compileNode(config, mainEntry, {
|
|
1882
|
+
outFile: builtMainFile,
|
|
1883
|
+
format: config.electron.mainFormat,
|
|
1884
|
+
devUrl
|
|
1885
|
+
});
|
|
1886
|
+
builtPreloadFiles.length = 0;
|
|
1887
|
+
for (const entry of preloadEntries) {
|
|
1888
|
+
if (!fs10.existsSync(entry)) continue;
|
|
1889
|
+
const base = path12.basename(entry).replace(/\.[^.]+$/, "");
|
|
1890
|
+
const out = path12.join(stageDir, base + extFor(config.electron.preloadFormat));
|
|
1891
|
+
await compileNode(config, entry, {
|
|
1892
|
+
outFile: out,
|
|
1893
|
+
format: config.electron.preloadFormat,
|
|
1894
|
+
devUrl
|
|
1895
|
+
});
|
|
1896
|
+
builtPreloadFiles.push(out);
|
|
1897
|
+
}
|
|
1898
|
+
};
|
|
1899
|
+
await compileAll();
|
|
1900
|
+
if (noSpawn) {
|
|
1901
|
+
console.log(pc4.dim(" (noSpawn) \u5DF2\u7F16\u8BD1\u4E3B/preload\uFF0C\u8DF3\u8FC7\u542F\u52A8 Electron\u3002"));
|
|
1902
|
+
return;
|
|
1903
|
+
}
|
|
1904
|
+
const electronBin = resolveElectronBinary(config);
|
|
1905
|
+
if (!electronBin) {
|
|
1906
|
+
console.warn(
|
|
1907
|
+
pc4.yellow(
|
|
1908
|
+
" \u26A0 \u672A\u627E\u5230 Electron \u53EF\u6267\u884C\u6587\u4EF6\uFF0C\u8BF7\u5148\u5B89\u88C5\uFF1Anpm install -D electron\n \u5DF2\u7F16\u8BD1\u4E3B/preload \u81F3 .nasti/\uFF0C\u53EF\u624B\u52A8\u8FD0\u884C\u3002"
|
|
1909
|
+
)
|
|
1910
|
+
);
|
|
1911
|
+
return;
|
|
1912
|
+
}
|
|
1913
|
+
let child = null;
|
|
1914
|
+
const spawnElectron = () => {
|
|
1915
|
+
const args = [builtMainFile, ...config.electron.electronArgs];
|
|
1916
|
+
child = spawn(electronBin, args, {
|
|
1917
|
+
stdio: "inherit",
|
|
1918
|
+
env: { ...process.env, NASTI_DEV_SERVER_URL: devUrl, NASTI_TARGET: "electron" }
|
|
1919
|
+
});
|
|
1920
|
+
child.on("exit", (code) => {
|
|
1921
|
+
if (code !== null && child && child.__nastiKilled !== true) {
|
|
1922
|
+
console.log(pc4.dim(` Electron exited (${code}).`));
|
|
1923
|
+
process.exit(code ?? 0);
|
|
1924
|
+
}
|
|
1925
|
+
});
|
|
1926
|
+
};
|
|
1927
|
+
spawnElectron();
|
|
1928
|
+
if (config.electron.autoRestart) {
|
|
1929
|
+
const watchTargets = [mainEntry, ...preloadEntries].filter(fs10.existsSync);
|
|
1930
|
+
const watcher = chokidar.watch(watchTargets, { ignoreInitial: true });
|
|
1931
|
+
let restarting = null;
|
|
1932
|
+
watcher.on("all", async () => {
|
|
1933
|
+
if (restarting) return;
|
|
1934
|
+
restarting = (async () => {
|
|
1935
|
+
console.log(pc4.cyan("\n \u267B \u4E3B/preload \u53D8\u66F4\uFF0C\u91CD\u542F Electron..."));
|
|
1936
|
+
try {
|
|
1937
|
+
if (child && !child.killed) {
|
|
1938
|
+
;
|
|
1939
|
+
child.__nastiKilled = true;
|
|
1940
|
+
const dying = child;
|
|
1941
|
+
await new Promise((resolve) => {
|
|
1942
|
+
const timer = setTimeout(() => resolve(), 3e3);
|
|
1943
|
+
dying.once("exit", () => {
|
|
1944
|
+
clearTimeout(timer);
|
|
1945
|
+
resolve();
|
|
1946
|
+
});
|
|
1947
|
+
dying.kill();
|
|
1948
|
+
});
|
|
1949
|
+
}
|
|
1950
|
+
await compileAll();
|
|
1951
|
+
spawnElectron();
|
|
1952
|
+
} finally {
|
|
1953
|
+
restarting = null;
|
|
1954
|
+
}
|
|
1955
|
+
})();
|
|
1956
|
+
});
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
function extFor(format) {
|
|
1960
|
+
return format === "cjs" ? ".cjs" : ".mjs";
|
|
1961
|
+
}
|
|
1962
|
+
async function compileNode(config, entry, opts) {
|
|
1963
|
+
const env = loadEnv(config.mode, config.root, config.envPrefix);
|
|
1964
|
+
const envDefine = {
|
|
1965
|
+
...buildEnvDefine(env, config.mode),
|
|
1966
|
+
__ELECTRON__: "true",
|
|
1967
|
+
__NASTI_TARGET__: JSON.stringify("electron"),
|
|
1968
|
+
__NASTI_DEV_SERVER_URL__: JSON.stringify(opts.devUrl)
|
|
1969
|
+
};
|
|
1970
|
+
const oxcTransformPlugin = {
|
|
1971
|
+
name: "nasti:oxc-transform",
|
|
1972
|
+
transform(code, id) {
|
|
1973
|
+
if (!shouldTransform(id)) return null;
|
|
1974
|
+
const result = transformCode(id, code, {
|
|
1975
|
+
sourcemap: !!config.build.sourcemap,
|
|
1976
|
+
jsxRuntime: "automatic",
|
|
1977
|
+
jsxImportSource: config.framework === "vue" ? "vue" : "react"
|
|
1978
|
+
});
|
|
1979
|
+
return { code: result.code, map: result.map ? JSON.parse(result.map) : void 0 };
|
|
1980
|
+
}
|
|
1981
|
+
};
|
|
1982
|
+
const bundle = await rolldown3({
|
|
1983
|
+
input: entry,
|
|
1984
|
+
transform: { define: envDefine },
|
|
1985
|
+
platform: "node",
|
|
1986
|
+
plugins: [oxcTransformPlugin, electronPlugin(config), resolvePlugin(config)]
|
|
1987
|
+
});
|
|
1988
|
+
fs10.mkdirSync(path12.dirname(opts.outFile), { recursive: true });
|
|
1989
|
+
await bundle.write({
|
|
1990
|
+
file: opts.outFile,
|
|
1991
|
+
format: opts.format === "cjs" ? "cjs" : "esm",
|
|
1992
|
+
sourcemap: false,
|
|
1993
|
+
minify: false,
|
|
1994
|
+
inlineDynamicImports: true
|
|
1995
|
+
});
|
|
1996
|
+
await bundle.close();
|
|
1997
|
+
}
|
|
1998
|
+
function resolveElectronBinary(config) {
|
|
1999
|
+
if (config.electron.electronPath && fs10.existsSync(config.electron.electronPath)) {
|
|
2000
|
+
return config.electron.electronPath;
|
|
2001
|
+
}
|
|
2002
|
+
try {
|
|
2003
|
+
const require2 = createRequire2(path12.resolve(config.root, "package.json"));
|
|
2004
|
+
const pathFile = require2.resolve("electron");
|
|
2005
|
+
const electronModule = require2(pathFile);
|
|
2006
|
+
if (typeof electronModule === "string" && fs10.existsSync(electronModule)) {
|
|
2007
|
+
return electronModule;
|
|
2008
|
+
}
|
|
2009
|
+
} catch {
|
|
2010
|
+
}
|
|
2011
|
+
return null;
|
|
2012
|
+
}
|
|
2013
|
+
function warnElectronVersion(config) {
|
|
2014
|
+
const installed = detectInstalledElectron(config.root);
|
|
2015
|
+
if (installed === null) {
|
|
2016
|
+
console.warn(
|
|
2017
|
+
pc4.yellow(
|
|
2018
|
+
` \u26A0 \u672A\u68C0\u6D4B\u5230 Electron\uFF0C\u8BF7\u5B89\u88C5\uFF1Anpm install -D electron@^${config.electron.minVersion}`
|
|
2019
|
+
)
|
|
2020
|
+
);
|
|
2021
|
+
return;
|
|
2022
|
+
}
|
|
2023
|
+
if (installed < config.electron.minVersion) {
|
|
2024
|
+
console.warn(
|
|
2025
|
+
pc4.yellow(
|
|
2026
|
+
` \u26A0 Electron ${installed} \u4F4E\u4E8E Nasti \u8981\u6C42\u7684 ${config.electron.minVersion}\uFF0C\u67D0\u4E9B\u7279\u6027\uFF08\u5982 ESM \u4E3B\u8FDB\u7A0B\uFF09\u4E0D\u53EF\u7528\u3002`
|
|
2027
|
+
)
|
|
2028
|
+
);
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
1577
2031
|
export {
|
|
1578
2032
|
build,
|
|
2033
|
+
buildElectron,
|
|
1579
2034
|
createServer,
|
|
1580
2035
|
defineConfig,
|
|
1581
|
-
|
|
2036
|
+
electronPlugin,
|
|
2037
|
+
resolveConfig,
|
|
2038
|
+
startElectronDev
|
|
1582
2039
|
};
|
|
1583
2040
|
//# sourceMappingURL=index.js.map
|