@nasti-toolchain/nasti 1.1.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/dist/cli.js ADDED
@@ -0,0 +1,1346 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
4
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
5
+ }) : x)(function(x) {
6
+ if (typeof require !== "undefined") return require.apply(this, arguments);
7
+ throw Error('Dynamic require of "' + x + '" is not supported');
8
+ });
9
+ var __esm = (fn, res) => function __init() {
10
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
+ };
12
+ var __export = (target, all) => {
13
+ for (var name in all)
14
+ __defProp(target, name, { get: all[name], enumerable: true });
15
+ };
16
+
17
+ // src/config/defaults.ts
18
+ var defaultResolve, defaultServer, defaultBuild, defaults;
19
+ var init_defaults = __esm({
20
+ "src/config/defaults.ts"() {
21
+ "use strict";
22
+ defaultResolve = {
23
+ alias: {},
24
+ extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".json", ".vue"],
25
+ conditions: ["import", "module", "browser", "default"],
26
+ mainFields: ["module", "jsnext:main", "jsnext", "main"]
27
+ };
28
+ defaultServer = {
29
+ port: 3e3,
30
+ host: "localhost",
31
+ https: false,
32
+ open: false,
33
+ proxy: {},
34
+ cors: true,
35
+ hmr: true
36
+ };
37
+ defaultBuild = {
38
+ outDir: "dist",
39
+ assetsDir: "assets",
40
+ minify: true,
41
+ sourcemap: false,
42
+ target: "es2022",
43
+ rolldownOptions: {},
44
+ emptyOutDir: true
45
+ };
46
+ defaults = {
47
+ root: ".",
48
+ base: "/",
49
+ mode: "development",
50
+ framework: "auto",
51
+ resolve: defaultResolve,
52
+ server: defaultServer,
53
+ build: defaultBuild,
54
+ plugins: [],
55
+ envPrefix: ["NASTI_", "VITE_"],
56
+ logLevel: "info"
57
+ };
58
+ }
59
+ });
60
+
61
+ // src/config/index.ts
62
+ import { pathToFileURL } from "url";
63
+ import path from "path";
64
+ import fs from "fs";
65
+ async function loadConfigFromFile(root) {
66
+ for (const file of CONFIG_FILES) {
67
+ const filePath = path.resolve(root, file);
68
+ if (!fs.existsSync(filePath)) continue;
69
+ if (file.endsWith(".ts") || file.endsWith(".mts")) {
70
+ return await loadTsConfig(filePath);
71
+ }
72
+ const mod = await import(pathToFileURL(filePath).href);
73
+ return mod.default ?? mod;
74
+ }
75
+ return {};
76
+ }
77
+ async function loadTsConfig(filePath) {
78
+ const { transformSync: transformSync2 } = await import("oxc-transform");
79
+ const code = fs.readFileSync(filePath, "utf-8");
80
+ const result = transformSync2(filePath, code, {
81
+ typescript: {}
82
+ });
83
+ const tmpFile = filePath + ".timestamp-" + Date.now() + ".mjs";
84
+ try {
85
+ fs.writeFileSync(tmpFile, result.code);
86
+ const mod = await import(pathToFileURL(tmpFile).href);
87
+ return mod.default ?? mod;
88
+ } finally {
89
+ fs.unlinkSync(tmpFile);
90
+ }
91
+ }
92
+ async function resolveConfig(inlineConfig = {}, command) {
93
+ const root = path.resolve(inlineConfig.root ?? defaults.root);
94
+ const fileConfig = await loadConfigFromFile(root);
95
+ const merged = deepMerge(deepMerge({}, fileConfig), inlineConfig);
96
+ const rawPlugins = [
97
+ ...fileConfig.plugins ?? [],
98
+ ...inlineConfig.plugins ?? []
99
+ ];
100
+ const env = { mode: merged.mode ?? defaults.mode, command };
101
+ for (const plugin of rawPlugins) {
102
+ if (plugin.config) {
103
+ const result = await plugin.config(merged, env);
104
+ if (result) Object.assign(merged, result);
105
+ }
106
+ }
107
+ const filteredPlugins = rawPlugins.filter((p) => {
108
+ if (!p.apply) return true;
109
+ if (typeof p.apply === "function") return p.apply(resolved, env);
110
+ return p.apply === command;
111
+ });
112
+ const resolved = {
113
+ root,
114
+ base: merged.base ?? defaults.base,
115
+ mode: command === "build" ? "production" : "development",
116
+ framework: merged.framework ?? defaults.framework,
117
+ command,
118
+ resolve: {
119
+ alias: { ...defaults.resolve.alias, ...merged.resolve?.alias },
120
+ extensions: merged.resolve?.extensions ?? defaults.resolve.extensions,
121
+ conditions: merged.resolve?.conditions ?? defaults.resolve.conditions,
122
+ mainFields: merged.resolve?.mainFields ?? defaults.resolve.mainFields
123
+ },
124
+ plugins: filteredPlugins,
125
+ server: { ...defaults.server, ...merged.server },
126
+ build: { ...defaults.build, ...merged.build },
127
+ envPrefix: Array.isArray(merged.envPrefix) ? merged.envPrefix : merged.envPrefix ? [merged.envPrefix] : [...defaults.envPrefix],
128
+ logLevel: merged.logLevel ?? defaults.logLevel
129
+ };
130
+ for (const plugin of resolved.plugins) {
131
+ if (plugin.configResolved) {
132
+ await plugin.configResolved(resolved);
133
+ }
134
+ }
135
+ return resolved;
136
+ }
137
+ function deepMerge(target, source) {
138
+ const result = { ...target };
139
+ for (const key of Object.keys(source)) {
140
+ const val = source[key];
141
+ if (val && typeof val === "object" && !Array.isArray(val)) {
142
+ result[key] = deepMerge(
143
+ result[key] ?? {},
144
+ val
145
+ );
146
+ } else if (val !== void 0) {
147
+ result[key] = val;
148
+ }
149
+ }
150
+ return result;
151
+ }
152
+ var CONFIG_FILES;
153
+ var init_config = __esm({
154
+ "src/config/index.ts"() {
155
+ "use strict";
156
+ init_defaults();
157
+ CONFIG_FILES = [
158
+ "nasti.config.ts",
159
+ "nasti.config.js",
160
+ "nasti.config.mjs",
161
+ "nasti.config.mts"
162
+ ];
163
+ }
164
+ });
165
+
166
+ // src/core/plugin-container.ts
167
+ function sortPlugins(plugins) {
168
+ const pre = [];
169
+ const normal = [];
170
+ const post = [];
171
+ for (const plugin of plugins) {
172
+ if (plugin.enforce === "pre") pre.push(plugin);
173
+ else if (plugin.enforce === "post") post.push(plugin);
174
+ else normal.push(plugin);
175
+ }
176
+ return [...pre, ...normal, ...post];
177
+ }
178
+ var PluginContainer;
179
+ var init_plugin_container = __esm({
180
+ "src/core/plugin-container.ts"() {
181
+ "use strict";
182
+ PluginContainer = class {
183
+ plugins;
184
+ config;
185
+ ctx;
186
+ constructor(config) {
187
+ this.config = config;
188
+ this.plugins = sortPlugins(config.plugins);
189
+ this.ctx = this.createContext();
190
+ }
191
+ createContext() {
192
+ const container = this;
193
+ return {
194
+ async resolve(source, importer) {
195
+ return container.resolveId(source, importer);
196
+ },
197
+ emitFile(_file) {
198
+ return "";
199
+ },
200
+ getModuleInfo(_id) {
201
+ return null;
202
+ }
203
+ };
204
+ }
205
+ async buildStart() {
206
+ for (const plugin of this.plugins) {
207
+ if (plugin.buildStart) {
208
+ await plugin.buildStart.call(this.ctx);
209
+ }
210
+ }
211
+ }
212
+ async buildEnd(error) {
213
+ for (const plugin of this.plugins) {
214
+ if (plugin.buildEnd) {
215
+ await plugin.buildEnd.call(this.ctx, error);
216
+ }
217
+ }
218
+ }
219
+ async resolveId(source, importer, options = {}) {
220
+ for (const plugin of this.plugins) {
221
+ if (!plugin.resolveId) continue;
222
+ const result = await plugin.resolveId.call(
223
+ this.ctx,
224
+ source,
225
+ importer ?? void 0,
226
+ { isEntry: options.isEntry ?? false, ssr: false }
227
+ );
228
+ if (result != null) return result;
229
+ }
230
+ return null;
231
+ }
232
+ async load(id) {
233
+ for (const plugin of this.plugins) {
234
+ if (!plugin.load) continue;
235
+ const result = await plugin.load.call(this.ctx, id);
236
+ if (result != null) return result;
237
+ }
238
+ return null;
239
+ }
240
+ async transform(code, id) {
241
+ let currentCode = code;
242
+ for (const plugin of this.plugins) {
243
+ if (!plugin.transform) continue;
244
+ const result = await plugin.transform.call(this.ctx, currentCode, id);
245
+ if (result == null) continue;
246
+ if (typeof result === "string") {
247
+ currentCode = result;
248
+ } else {
249
+ currentCode = result.code;
250
+ }
251
+ }
252
+ return currentCode === code ? null : { code: currentCode };
253
+ }
254
+ /** 完整的模块处理管道: resolveId → load → transform */
255
+ async processModule(source, importer) {
256
+ const resolveResult = await this.resolveId(source, importer, {
257
+ isEntry: !importer
258
+ });
259
+ if (resolveResult == null) return null;
260
+ const id = typeof resolveResult === "string" ? resolveResult : resolveResult.id;
261
+ const loadResult = await this.load(id);
262
+ if (loadResult == null) return null;
263
+ const loadedCode = typeof loadResult === "string" ? loadResult : loadResult.code;
264
+ const transformResult = await this.transform(loadedCode, id);
265
+ const finalCode = transformResult == null ? loadedCode : typeof transformResult === "string" ? transformResult : transformResult.code;
266
+ return { id, code: finalCode };
267
+ }
268
+ getPlugins() {
269
+ return this.plugins;
270
+ }
271
+ };
272
+ }
273
+ });
274
+
275
+ // src/core/module-graph.ts
276
+ var ModuleGraph;
277
+ var init_module_graph = __esm({
278
+ "src/core/module-graph.ts"() {
279
+ "use strict";
280
+ ModuleGraph = class {
281
+ urlToModuleMap = /* @__PURE__ */ new Map();
282
+ idToModuleMap = /* @__PURE__ */ new Map();
283
+ fileToModulesMap = /* @__PURE__ */ new Map();
284
+ getModuleByUrl(url) {
285
+ return this.urlToModuleMap.get(url);
286
+ }
287
+ getModuleById(id) {
288
+ return this.idToModuleMap.get(id);
289
+ }
290
+ getModulesByFile(file) {
291
+ return this.fileToModulesMap.get(file);
292
+ }
293
+ async ensureEntryFromUrl(url) {
294
+ let mod = this.urlToModuleMap.get(url);
295
+ if (mod) return mod;
296
+ mod = this.createModule(url);
297
+ this.urlToModuleMap.set(url, mod);
298
+ return mod;
299
+ }
300
+ createModule(url, id) {
301
+ const mod = {
302
+ id: id ?? url,
303
+ file: null,
304
+ url,
305
+ type: url.endsWith(".css") ? "css" : "js",
306
+ importers: /* @__PURE__ */ new Set(),
307
+ importedModules: /* @__PURE__ */ new Set(),
308
+ acceptedHmrDeps: /* @__PURE__ */ new Set(),
309
+ transformResult: null,
310
+ lastHMRTimestamp: 0,
311
+ isSelfAccepting: false
312
+ };
313
+ this.idToModuleMap.set(mod.id, mod);
314
+ return mod;
315
+ }
316
+ /** 注册文件路径到模块的映射 */
317
+ registerModule(mod, file) {
318
+ mod.file = file;
319
+ let mods = this.fileToModulesMap.get(file);
320
+ if (!mods) {
321
+ mods = /* @__PURE__ */ new Set();
322
+ this.fileToModulesMap.set(file, mods);
323
+ }
324
+ mods.add(mod);
325
+ }
326
+ /** 更新模块依赖关系 */
327
+ updateModuleImports(mod, importedIds) {
328
+ for (const imported of mod.importedModules) {
329
+ imported.importers.delete(mod);
330
+ }
331
+ mod.importedModules.clear();
332
+ for (const id of importedIds) {
333
+ const importedMod = this.idToModuleMap.get(id);
334
+ if (importedMod) {
335
+ mod.importedModules.add(importedMod);
336
+ importedMod.importers.add(mod);
337
+ }
338
+ }
339
+ }
340
+ /** 使模块的转换缓存失效 */
341
+ invalidateModule(mod) {
342
+ mod.transformResult = null;
343
+ mod.lastHMRTimestamp = Date.now();
344
+ }
345
+ /** 使所有模块缓存失效 */
346
+ invalidateAll() {
347
+ for (const mod of this.idToModuleMap.values()) {
348
+ this.invalidateModule(mod);
349
+ }
350
+ }
351
+ /** 获取 HMR 传播边界 - 从变更模块向上遍历找到接受更新的边界 */
352
+ getHmrBoundaries(mod) {
353
+ const boundaries = [];
354
+ const visited = /* @__PURE__ */ new Set();
355
+ const propagate = (node, via) => {
356
+ if (visited.has(node)) return true;
357
+ visited.add(node);
358
+ if (node.isSelfAccepting) {
359
+ boundaries.push({ boundary: node, acceptedVia: via });
360
+ return true;
361
+ }
362
+ if (node.acceptedHmrDeps.has(via)) {
363
+ boundaries.push({ boundary: node, acceptedVia: via });
364
+ return true;
365
+ }
366
+ if (node.importers.size === 0) return false;
367
+ for (const importer of node.importers) {
368
+ if (!propagate(importer, node)) return false;
369
+ }
370
+ return true;
371
+ };
372
+ if (mod.isSelfAccepting) {
373
+ boundaries.push({ boundary: mod, acceptedVia: mod });
374
+ return boundaries;
375
+ }
376
+ for (const importer of mod.importers) {
377
+ if (!propagate(importer, mod)) {
378
+ return [];
379
+ }
380
+ }
381
+ return boundaries;
382
+ }
383
+ };
384
+ }
385
+ });
386
+
387
+ // src/server/ws.ts
388
+ import { WebSocketServer as WsServer } from "ws";
389
+ function createWebSocketServer(server) {
390
+ const wss = new WsServer({ noServer: true });
391
+ const clients = /* @__PURE__ */ new Set();
392
+ server.on("upgrade", (req, socket, head) => {
393
+ if (req.headers["sec-websocket-protocol"] === "nasti-hmr") {
394
+ wss.handleUpgrade(req, socket, head, (ws) => {
395
+ wss.emit("connection", ws, req);
396
+ });
397
+ }
398
+ });
399
+ wss.on("connection", (ws) => {
400
+ clients.add(ws);
401
+ ws.send(JSON.stringify({ type: "connected" }));
402
+ ws.on("close", () => {
403
+ clients.delete(ws);
404
+ });
405
+ ws.on("error", (err) => {
406
+ console.error("[nasti] WebSocket error:", err);
407
+ clients.delete(ws);
408
+ });
409
+ });
410
+ return {
411
+ send(payload) {
412
+ const data = JSON.stringify(payload);
413
+ for (const client of clients) {
414
+ if (client.readyState === 1) {
415
+ client.send(data);
416
+ }
417
+ }
418
+ },
419
+ close() {
420
+ clients.clear();
421
+ wss.close();
422
+ }
423
+ };
424
+ }
425
+ var init_ws = __esm({
426
+ "src/server/ws.ts"() {
427
+ "use strict";
428
+ }
429
+ });
430
+
431
+ // src/core/transformer.ts
432
+ import { transformSync } from "oxc-transform";
433
+ function shouldTransform(id) {
434
+ return TS_EXTENSIONS.test(id) || JSX_EXTENSIONS.test(id) || JS_EXTENSIONS.test(id) && false;
435
+ }
436
+ function transformCode(filename, code, options = {}) {
437
+ const isTS = TS_EXTENSIONS.test(filename) || /\.tsx$/.test(filename);
438
+ const isJSX = JSX_EXTENSIONS.test(filename);
439
+ const result = transformSync(filename, code, {
440
+ typescript: isTS ? {} : void 0,
441
+ jsx: isJSX || /\.tsx$/.test(filename) ? {
442
+ runtime: options.jsxRuntime ?? "automatic",
443
+ importSource: options.jsxImportSource ?? "react",
444
+ refresh: options.reactRefresh ?? false
445
+ } : void 0,
446
+ sourcemap: options.sourcemap ?? true
447
+ });
448
+ return {
449
+ code: result.code,
450
+ map: result.map ? JSON.stringify(result.map) : null
451
+ };
452
+ }
453
+ var JS_EXTENSIONS, TS_EXTENSIONS, JSX_EXTENSIONS;
454
+ var init_transformer = __esm({
455
+ "src/core/transformer.ts"() {
456
+ "use strict";
457
+ JS_EXTENSIONS = /\.(js|mjs|cjs)$/;
458
+ TS_EXTENSIONS = /\.(ts|mts|cts)$/;
459
+ JSX_EXTENSIONS = /\.(jsx|tsx)$/;
460
+ }
461
+ });
462
+
463
+ // src/plugins/html.ts
464
+ import path2 from "path";
465
+ import fs2 from "fs";
466
+ function htmlPlugin(config) {
467
+ return {
468
+ name: "nasti:html",
469
+ enforce: "post",
470
+ transformIndexHtml(html) {
471
+ const tags = [];
472
+ if (config.command === "serve") {
473
+ tags.push({
474
+ tag: "script",
475
+ attrs: { type: "module", src: "/@nasti/client" },
476
+ injectTo: "head-prepend"
477
+ });
478
+ }
479
+ return { html, tags };
480
+ }
481
+ };
482
+ }
483
+ function processHtml(html, tags) {
484
+ const headPrepend = tags.filter((t) => t.injectTo === "head-prepend");
485
+ const head = tags.filter((t) => t.injectTo === "head" || !t.injectTo);
486
+ const bodyPrepend = tags.filter((t) => t.injectTo === "body-prepend");
487
+ const body = tags.filter((t) => t.injectTo === "body");
488
+ if (headPrepend.length) {
489
+ html = html.replace(/<head([^>]*)>/i, `<head$1>
490
+ ${serializeTags(headPrepend)}`);
491
+ }
492
+ if (head.length) {
493
+ html = html.replace(/<\/head>/i, `${serializeTags(head)}
494
+ </head>`);
495
+ }
496
+ if (bodyPrepend.length) {
497
+ html = html.replace(/<body([^>]*)>/i, `<body$1>
498
+ ${serializeTags(bodyPrepend)}`);
499
+ }
500
+ if (body.length) {
501
+ html = html.replace(/<\/body>/i, `${serializeTags(body)}
502
+ </body>`);
503
+ }
504
+ return html;
505
+ }
506
+ function serializeTags(tags) {
507
+ return tags.map(serializeTag).join("\n");
508
+ }
509
+ function serializeTag(tag) {
510
+ const attrs = tag.attrs ? " " + Object.entries(tag.attrs).map(([k, v]) => v === true ? k : `${k}="${v}"`).join(" ") : "";
511
+ const children = typeof tag.children === "string" ? tag.children : tag.children ? serializeTags(tag.children) : "";
512
+ const selfClosing = ["link", "meta", "br", "hr", "img", "input"].includes(tag.tag);
513
+ if (selfClosing && !children) {
514
+ return ` <${tag.tag}${attrs} />`;
515
+ }
516
+ return ` <${tag.tag}${attrs}>${children}</${tag.tag}>`;
517
+ }
518
+ async function readHtmlFile(root) {
519
+ const htmlPath = path2.resolve(root, "index.html");
520
+ if (!fs2.existsSync(htmlPath)) return null;
521
+ return fs2.readFileSync(htmlPath, "utf-8");
522
+ }
523
+ var init_html = __esm({
524
+ "src/plugins/html.ts"() {
525
+ "use strict";
526
+ }
527
+ });
528
+
529
+ // src/server/middleware.ts
530
+ var middleware_exports = {};
531
+ __export(middleware_exports, {
532
+ transformMiddleware: () => transformMiddleware,
533
+ transformRequest: () => transformRequest
534
+ });
535
+ import path3 from "path";
536
+ import fs3 from "fs";
537
+ function transformMiddleware(ctx) {
538
+ return async (req, res, next) => {
539
+ const url = req.url ?? "/";
540
+ if (req.method !== "GET") return next();
541
+ if (url === "/@nasti/client") {
542
+ res.setHeader("Content-Type", "application/javascript");
543
+ res.end(getHmrClientCode());
544
+ return;
545
+ }
546
+ if (url === "/" || url.endsWith(".html")) {
547
+ const html = await readHtmlFile(ctx.config.root);
548
+ if (html) {
549
+ let processedHtml = html;
550
+ for (const plugin of ctx.config.plugins) {
551
+ if (plugin.transformIndexHtml) {
552
+ const result = await plugin.transformIndexHtml(processedHtml);
553
+ if (typeof result === "string") {
554
+ processedHtml = result;
555
+ } else if (result && "html" in result) {
556
+ processedHtml = processHtml(result.html, result.tags);
557
+ } else if (Array.isArray(result)) {
558
+ processedHtml = processHtml(processedHtml, result);
559
+ }
560
+ }
561
+ }
562
+ processedHtml = processedHtml.replace(
563
+ "<head>",
564
+ '<head>\n <script type="module" src="/@nasti/client"></script>'
565
+ );
566
+ res.setHeader("Content-Type", "text/html");
567
+ res.end(processedHtml);
568
+ return;
569
+ }
570
+ }
571
+ if (isModuleRequest(url)) {
572
+ try {
573
+ const result = await transformRequest(url, ctx);
574
+ if (result) {
575
+ const contentType = url.endsWith(".css") ? "application/javascript" : "application/javascript";
576
+ res.setHeader("Content-Type", contentType);
577
+ res.setHeader("Cache-Control", "no-cache");
578
+ res.end(typeof result === "string" ? result : result.code);
579
+ return;
580
+ }
581
+ } catch (err) {
582
+ console.error(`[nasti] Transform error: ${url}`, err.message);
583
+ res.statusCode = 500;
584
+ res.end(`Transform error: ${err.message}`);
585
+ return;
586
+ }
587
+ }
588
+ next();
589
+ };
590
+ }
591
+ async function transformRequest(url, ctx) {
592
+ const { config, pluginContainer, moduleGraph } = ctx;
593
+ const cached = moduleGraph.getModuleByUrl(url);
594
+ if (cached?.transformResult) {
595
+ return cached.transformResult;
596
+ }
597
+ const filePath = resolveUrlToFile(url, config.root);
598
+ if (!filePath || !fs3.existsSync(filePath)) return null;
599
+ const mod = await moduleGraph.ensureEntryFromUrl(url);
600
+ moduleGraph.registerModule(mod, filePath);
601
+ let code = fs3.readFileSync(filePath, "utf-8");
602
+ const pluginResult = await pluginContainer.transform(code, filePath);
603
+ if (pluginResult) {
604
+ code = typeof pluginResult === "string" ? pluginResult : pluginResult.code;
605
+ }
606
+ if (shouldTransform(filePath)) {
607
+ const result = transformCode(filePath, code, {
608
+ sourcemap: true,
609
+ jsxRuntime: "automatic",
610
+ jsxImportSource: config.framework === "vue" ? "vue" : "react",
611
+ reactRefresh: config.framework !== "vue"
612
+ });
613
+ code = result.code;
614
+ }
615
+ code = rewriteImports(code, config);
616
+ const transformResult = { code };
617
+ mod.transformResult = transformResult;
618
+ return transformResult;
619
+ }
620
+ function rewriteImports(code, config) {
621
+ return code.replace(
622
+ /from\s+['"]([^'"./][^'"]*)['"]/g,
623
+ (match, specifier) => {
624
+ if (specifier.startsWith("/") || specifier.startsWith(".")) return match;
625
+ return `from "/@modules/${specifier}"`;
626
+ }
627
+ ).replace(
628
+ /import\s+['"]([^'"./][^'"]*)['"]/g,
629
+ (match, specifier) => {
630
+ if (specifier.startsWith("/") || specifier.startsWith(".")) return match;
631
+ return `import "/@modules/${specifier}"`;
632
+ }
633
+ );
634
+ }
635
+ function resolveUrlToFile(url, root) {
636
+ const cleanUrl = url.split("?")[0];
637
+ if (cleanUrl.startsWith("/@modules/")) {
638
+ const moduleName = cleanUrl.slice("/@modules/".length);
639
+ try {
640
+ const { createRequire: createRequire2 } = __require("module");
641
+ const req = createRequire2(path3.resolve(root, "package.json"));
642
+ return req.resolve(moduleName);
643
+ } catch {
644
+ return null;
645
+ }
646
+ }
647
+ return path3.resolve(root, cleanUrl.replace(/^\//, ""));
648
+ }
649
+ function isModuleRequest(url) {
650
+ const cleanUrl = url.split("?")[0];
651
+ return /\.(ts|tsx|jsx|js|mjs|vue|css|json)$/.test(cleanUrl) || cleanUrl.startsWith("/@modules/");
652
+ }
653
+ function getHmrClientCode() {
654
+ return `
655
+ // Nasti HMR Client
656
+ const socket = new WebSocket(\`ws://\${location.host}\`, 'nasti-hmr');
657
+ const hotModulesMap = new Map();
658
+
659
+ socket.addEventListener('message', ({ data }) => {
660
+ const payload = JSON.parse(data);
661
+ switch (payload.type) {
662
+ case 'connected':
663
+ console.log('[nasti] connected.');
664
+ break;
665
+ case 'update':
666
+ payload.updates.forEach((update) => {
667
+ if (update.type === 'js-update') {
668
+ fetchUpdate(update);
669
+ } else if (update.type === 'css-update') {
670
+ updateCss(update.path);
671
+ }
672
+ });
673
+ break;
674
+ case 'full-reload':
675
+ console.log('[nasti] full reload');
676
+ location.reload();
677
+ break;
678
+ case 'error':
679
+ console.error('[nasti] error:', payload.err.message);
680
+ showErrorOverlay(payload.err);
681
+ break;
682
+ }
683
+ });
684
+
685
+ async function fetchUpdate(update) {
686
+ const mod = hotModulesMap.get(update.path);
687
+ if (mod) {
688
+ const newMod = await import(update.acceptedPath + '?t=' + update.timestamp);
689
+ mod.callbacks.forEach((cb) => cb(newMod));
690
+ } else {
691
+ // \u6CA1\u6709\u6CE8\u518C hot \u56DE\u8C03\uFF0C\u5C1D\u8BD5\u91CD\u65B0 import
692
+ await import(update.path + '?t=' + update.timestamp);
693
+ }
694
+ }
695
+
696
+ function updateCss(path) {
697
+ const el = document.querySelector(\`style[data-nasti-css="\${path}"]\`);
698
+ if (el) {
699
+ fetch(path + '?t=' + Date.now())
700
+ .then(r => r.text())
701
+ .then(css => { el.textContent = css; });
702
+ }
703
+ }
704
+
705
+ function showErrorOverlay(err) {
706
+ const overlay = document.createElement('div');
707
+ overlay.id = 'nasti-error-overlay';
708
+ 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;';
709
+ overlay.innerHTML = \`<h2 style="color:#ff5555">Build Error</h2><pre>\${err.message}\\n\${err.stack || ''}</pre><button onclick="this.parentElement.remove()" style="margin-top:1rem;padding:0.5rem 1rem;cursor:pointer">Close</button>\`;
710
+ document.body.appendChild(overlay);
711
+ }
712
+
713
+ // import.meta.hot API
714
+ const createHotContext = (ownerPath) => ({
715
+ accept(deps, callback) {
716
+ if (typeof deps === 'function' || !deps) {
717
+ // self-accepting
718
+ const callbacks = hotModulesMap.get(ownerPath)?.callbacks || [];
719
+ callbacks.push(deps || (() => {}));
720
+ hotModulesMap.set(ownerPath, { callbacks });
721
+ }
722
+ },
723
+ prune(callback) {
724
+ // \u6A21\u5757\u88AB\u79FB\u9664\u65F6\u6267\u884C
725
+ },
726
+ dispose(callback) {
727
+ // \u6A21\u5757\u66F4\u65B0\u524D\u6267\u884C\u6E05\u7406
728
+ },
729
+ invalidate() {
730
+ location.reload();
731
+ },
732
+ data: {},
733
+ });
734
+
735
+ // \u66B4\u9732\u7ED9\u6A21\u5757\u4F7F\u7528
736
+ if (!window.__nasti_hot_map) window.__nasti_hot_map = new Map();
737
+ window.__NASTI_HMR__ = { createHotContext };
738
+ `;
739
+ }
740
+ var init_middleware = __esm({
741
+ "src/server/middleware.ts"() {
742
+ "use strict";
743
+ init_transformer();
744
+ init_html();
745
+ }
746
+ });
747
+
748
+ // src/server/hmr.ts
749
+ import path4 from "path";
750
+ import fs4 from "fs";
751
+ async function handleFileChange(file, server) {
752
+ const { moduleGraph, ws, config } = server;
753
+ const relativePath = "/" + path4.relative(config.root, file);
754
+ const mods = moduleGraph.getModulesByFile(file);
755
+ if (!mods || mods.size === 0) {
756
+ return;
757
+ }
758
+ const updates = [];
759
+ const timestamp = Date.now();
760
+ for (const mod of mods) {
761
+ moduleGraph.invalidateModule(mod);
762
+ const ctx = {
763
+ file,
764
+ timestamp,
765
+ modules: [mod],
766
+ read: () => fs4.readFileSync(file, "utf-8"),
767
+ server
768
+ };
769
+ let affectedModules = [mod];
770
+ for (const plugin of config.plugins) {
771
+ if (plugin.handleHotUpdate) {
772
+ const result = await plugin.handleHotUpdate(ctx);
773
+ if (result) {
774
+ affectedModules = result;
775
+ }
776
+ }
777
+ }
778
+ for (const affected of affectedModules) {
779
+ const boundaries = moduleGraph.getHmrBoundaries(affected);
780
+ if (boundaries.length === 0) {
781
+ ws.send({ type: "full-reload", path: relativePath });
782
+ return;
783
+ }
784
+ for (const { boundary } of boundaries) {
785
+ updates.push({
786
+ type: boundary.type === "css" ? "css-update" : "js-update",
787
+ path: boundary.url,
788
+ acceptedPath: affected.url,
789
+ timestamp
790
+ });
791
+ }
792
+ }
793
+ }
794
+ if (updates.length > 0) {
795
+ ws.send({ type: "update", updates });
796
+ }
797
+ }
798
+ var init_hmr = __esm({
799
+ "src/server/hmr.ts"() {
800
+ "use strict";
801
+ }
802
+ });
803
+
804
+ // src/plugins/resolve.ts
805
+ import path5 from "path";
806
+ import fs5 from "fs";
807
+ import { createRequire } from "module";
808
+ function resolvePlugin(config) {
809
+ const { alias, extensions } = config.resolve;
810
+ const require2 = createRequire(path5.resolve(config.root, "package.json"));
811
+ return {
812
+ name: "nasti:resolve",
813
+ enforce: "pre",
814
+ resolveId(source, importer) {
815
+ for (const [key, value] of Object.entries(alias)) {
816
+ if (source === key || source.startsWith(key + "/")) {
817
+ source = source.replace(key, value);
818
+ if (!path5.isAbsolute(source)) {
819
+ source = path5.resolve(config.root, source);
820
+ }
821
+ break;
822
+ }
823
+ }
824
+ if (path5.isAbsolute(source)) {
825
+ const resolved = tryResolveFile(source, extensions);
826
+ if (resolved) return resolved;
827
+ }
828
+ if (source.startsWith(".")) {
829
+ const dir = importer ? path5.dirname(importer) : config.root;
830
+ const absolute = path5.resolve(dir, source);
831
+ const resolved = tryResolveFile(absolute, extensions);
832
+ if (resolved) return resolved;
833
+ }
834
+ if (!source.startsWith("/") && !source.startsWith(".")) {
835
+ try {
836
+ const resolved = require2.resolve(source, {
837
+ paths: [importer ? path5.dirname(importer) : config.root]
838
+ });
839
+ return resolved;
840
+ } catch {
841
+ return null;
842
+ }
843
+ }
844
+ return null;
845
+ },
846
+ load(id) {
847
+ if (fs5.existsSync(id)) {
848
+ return fs5.readFileSync(id, "utf-8");
849
+ }
850
+ return null;
851
+ }
852
+ };
853
+ }
854
+ function tryResolveFile(file, extensions) {
855
+ if (fs5.existsSync(file) && fs5.statSync(file).isFile()) {
856
+ return file;
857
+ }
858
+ for (const ext of extensions) {
859
+ const withExt = file + ext;
860
+ if (fs5.existsSync(withExt) && fs5.statSync(withExt).isFile()) {
861
+ return withExt;
862
+ }
863
+ }
864
+ if (fs5.existsSync(file) && fs5.statSync(file).isDirectory()) {
865
+ for (const ext of extensions) {
866
+ const indexFile = path5.join(file, "index" + ext);
867
+ if (fs5.existsSync(indexFile)) {
868
+ return indexFile;
869
+ }
870
+ }
871
+ }
872
+ return null;
873
+ }
874
+ var init_resolve = __esm({
875
+ "src/plugins/resolve.ts"() {
876
+ "use strict";
877
+ }
878
+ });
879
+
880
+ // src/plugins/css.ts
881
+ import path6 from "path";
882
+ function cssPlugin(config) {
883
+ return {
884
+ name: "nasti:css",
885
+ resolveId(source) {
886
+ if (source.endsWith(".css")) return null;
887
+ return null;
888
+ },
889
+ transform(code, id) {
890
+ if (!id.endsWith(".css")) return null;
891
+ if (config.command === "serve") {
892
+ const escaped = JSON.stringify(code);
893
+ return {
894
+ code: `
895
+ const css = ${escaped};
896
+ const style = document.createElement('style');
897
+ style.setAttribute('data-nasti-css', ${JSON.stringify(id)});
898
+ style.textContent = css;
899
+ document.head.appendChild(style);
900
+
901
+ // HMR
902
+ if (import.meta.hot) {
903
+ import.meta.hot.accept();
904
+ import.meta.hot.prune(() => {
905
+ style.remove();
906
+ });
907
+ }
908
+
909
+ export default css;
910
+ `
911
+ };
912
+ }
913
+ return null;
914
+ }
915
+ };
916
+ }
917
+ var init_css = __esm({
918
+ "src/plugins/css.ts"() {
919
+ "use strict";
920
+ }
921
+ });
922
+
923
+ // src/plugins/assets.ts
924
+ import path7 from "path";
925
+ import fs6 from "fs";
926
+ import crypto from "crypto";
927
+ function assetsPlugin(config) {
928
+ return {
929
+ name: "nasti:assets",
930
+ resolveId(source) {
931
+ if (source.endsWith("?url") || source.endsWith("?raw")) {
932
+ return source;
933
+ }
934
+ return null;
935
+ },
936
+ load(id) {
937
+ const ext = path7.extname(id.replace(/\?.*$/, ""));
938
+ if (id.endsWith("?raw")) {
939
+ const file = id.slice(0, -4);
940
+ if (fs6.existsSync(file)) {
941
+ const content = fs6.readFileSync(file, "utf-8");
942
+ return `export default ${JSON.stringify(content)}`;
943
+ }
944
+ }
945
+ if (id.endsWith("?url") || ASSET_EXTENSIONS.has(ext)) {
946
+ const file = id.replace(/\?.*$/, "");
947
+ if (!fs6.existsSync(file)) return null;
948
+ if (config.command === "serve") {
949
+ const url = "/" + path7.relative(config.root, file);
950
+ return `export default ${JSON.stringify(url)}`;
951
+ }
952
+ const content = fs6.readFileSync(file);
953
+ const hash = crypto.createHash("sha256").update(content).digest("hex").slice(0, 8);
954
+ const basename = path7.basename(file, ext);
955
+ const hashedName = `${config.build.assetsDir}/${basename}.${hash}${ext}`;
956
+ return `export default ${JSON.stringify(config.base + hashedName)}`;
957
+ }
958
+ return null;
959
+ }
960
+ };
961
+ }
962
+ var ASSET_EXTENSIONS;
963
+ var init_assets = __esm({
964
+ "src/plugins/assets.ts"() {
965
+ "use strict";
966
+ ASSET_EXTENSIONS = /* @__PURE__ */ new Set([
967
+ ".png",
968
+ ".jpg",
969
+ ".jpeg",
970
+ ".gif",
971
+ ".svg",
972
+ ".ico",
973
+ ".webp",
974
+ ".avif",
975
+ ".mp4",
976
+ ".webm",
977
+ ".ogg",
978
+ ".mp3",
979
+ ".wav",
980
+ ".flac",
981
+ ".aac",
982
+ ".woff",
983
+ ".woff2",
984
+ ".eot",
985
+ ".ttf",
986
+ ".otf",
987
+ ".pdf",
988
+ ".txt"
989
+ ]);
990
+ }
991
+ });
992
+
993
+ // src/server/index.ts
994
+ var server_exports = {};
995
+ __export(server_exports, {
996
+ createServer: () => createServer
997
+ });
998
+ import http from "http";
999
+ import path8 from "path";
1000
+ import connect from "connect";
1001
+ import sirv from "sirv";
1002
+ import { watch } from "chokidar";
1003
+ import pc from "picocolors";
1004
+ async function createServer(inlineConfig = {}) {
1005
+ const config = await resolveConfig(inlineConfig, "serve");
1006
+ const allPlugins = [
1007
+ resolvePlugin(config),
1008
+ cssPlugin(config),
1009
+ assetsPlugin(config),
1010
+ htmlPlugin(config),
1011
+ ...config.plugins
1012
+ ];
1013
+ const configWithPlugins = { ...config, plugins: allPlugins };
1014
+ const moduleGraph = new ModuleGraph();
1015
+ const pluginContainer = new PluginContainer(configWithPlugins);
1016
+ const app = connect();
1017
+ app.use(transformMiddleware({
1018
+ config: configWithPlugins,
1019
+ pluginContainer,
1020
+ moduleGraph
1021
+ }));
1022
+ const publicDir = path8.resolve(config.root, "public");
1023
+ app.use(sirv(publicDir, { dev: true, etag: true }));
1024
+ app.use(sirv(config.root, { dev: true, etag: true }));
1025
+ const httpServer = http.createServer(app);
1026
+ const ws = createWebSocketServer(httpServer);
1027
+ const watcher = watch(config.root, {
1028
+ ignored: [
1029
+ "**/node_modules/**",
1030
+ "**/.git/**",
1031
+ `**/${config.build.outDir}/**`
1032
+ ],
1033
+ ignoreInitial: true
1034
+ });
1035
+ let server;
1036
+ watcher.on("change", (file) => {
1037
+ handleFileChange(file, server);
1038
+ });
1039
+ watcher.on("add", (file) => {
1040
+ handleFileChange(file, server);
1041
+ });
1042
+ const postMiddlewares = [];
1043
+ for (const plugin of allPlugins) {
1044
+ if (plugin.configureServer) {
1045
+ const result = await plugin.configureServer(server);
1046
+ if (typeof result === "function") {
1047
+ postMiddlewares.push(result);
1048
+ }
1049
+ }
1050
+ }
1051
+ server = {
1052
+ config: configWithPlugins,
1053
+ middlewares: app,
1054
+ moduleGraph,
1055
+ watcher,
1056
+ ws,
1057
+ async listen(port) {
1058
+ const finalPort = port ?? config.server.port;
1059
+ const host = config.server.host === true ? "0.0.0.0" : config.server.host;
1060
+ await pluginContainer.buildStart();
1061
+ return new Promise((resolve, reject) => {
1062
+ httpServer.listen(finalPort, host, () => {
1063
+ const localUrl = `http://localhost:${finalPort}`;
1064
+ const networkUrl = host === "0.0.0.0" ? `http://${getNetworkAddress()}:${finalPort}` : null;
1065
+ console.log();
1066
+ console.log(pc.cyan(" nasti dev server") + pc.dim(` v0.0.1`));
1067
+ console.log();
1068
+ console.log(` ${pc.green(">")} Local: ${pc.cyan(localUrl)}`);
1069
+ if (networkUrl) {
1070
+ console.log(` ${pc.green(">")} Network: ${pc.cyan(networkUrl)}`);
1071
+ }
1072
+ console.log();
1073
+ resolve(server);
1074
+ });
1075
+ httpServer.on("error", (err) => {
1076
+ if (err.code === "EADDRINUSE") {
1077
+ console.log(pc.yellow(`Port ${finalPort} is in use, trying ${finalPort + 1}...`));
1078
+ httpServer.listen(finalPort + 1, host);
1079
+ } else {
1080
+ reject(err);
1081
+ }
1082
+ });
1083
+ });
1084
+ },
1085
+ async transformRequest(url) {
1086
+ const { transformRequest: transformRequest2 } = await Promise.resolve().then(() => (init_middleware(), middleware_exports));
1087
+ return transformRequest2(url, { config: configWithPlugins, pluginContainer, moduleGraph });
1088
+ },
1089
+ async close() {
1090
+ await pluginContainer.buildEnd();
1091
+ watcher.close();
1092
+ ws.close();
1093
+ httpServer.close();
1094
+ }
1095
+ };
1096
+ return server;
1097
+ }
1098
+ function getNetworkAddress() {
1099
+ const os = __require("os");
1100
+ const interfaces = os.networkInterfaces();
1101
+ for (const name of Object.keys(interfaces)) {
1102
+ for (const iface of interfaces[name] ?? []) {
1103
+ if (iface.family === "IPv4" && !iface.internal) {
1104
+ return iface.address;
1105
+ }
1106
+ }
1107
+ }
1108
+ return "localhost";
1109
+ }
1110
+ var init_server = __esm({
1111
+ "src/server/index.ts"() {
1112
+ "use strict";
1113
+ init_config();
1114
+ init_plugin_container();
1115
+ init_module_graph();
1116
+ init_ws();
1117
+ init_middleware();
1118
+ init_hmr();
1119
+ init_resolve();
1120
+ init_css();
1121
+ init_assets();
1122
+ init_html();
1123
+ }
1124
+ });
1125
+
1126
+ // src/build/index.ts
1127
+ var build_exports = {};
1128
+ __export(build_exports, {
1129
+ build: () => build
1130
+ });
1131
+ import path9 from "path";
1132
+ import fs7 from "fs";
1133
+ import { rolldown } from "rolldown";
1134
+ import pc2 from "picocolors";
1135
+ async function build(inlineConfig = {}) {
1136
+ const config = await resolveConfig(inlineConfig, "build");
1137
+ const startTime = performance.now();
1138
+ console.log(pc2.cyan("\n\u{1F528} nasti build") + pc2.dim(` v${process.env.npm_package_version ?? "0.0.1"}`));
1139
+ console.log(pc2.dim(` root: ${config.root}`));
1140
+ console.log(pc2.dim(` mode: ${config.mode}`));
1141
+ const outDir = path9.resolve(config.root, config.build.outDir);
1142
+ if (config.build.emptyOutDir && fs7.existsSync(outDir)) {
1143
+ fs7.rmSync(outDir, { recursive: true, force: true });
1144
+ }
1145
+ fs7.mkdirSync(outDir, { recursive: true });
1146
+ const html = await readHtmlFile(config.root);
1147
+ let entryPoints = [];
1148
+ if (html) {
1149
+ const scriptMatches = html.matchAll(/<script[^>]+src=["']([^"']+)["'][^>]*>/gi);
1150
+ for (const match of scriptMatches) {
1151
+ const src = match[1];
1152
+ if (src && !src.startsWith("http")) {
1153
+ entryPoints.push(path9.resolve(config.root, src.replace(/^\//, "")));
1154
+ }
1155
+ }
1156
+ }
1157
+ if (entryPoints.length === 0) {
1158
+ const fallbackEntries = ["src/main.ts", "src/main.tsx", "src/main.js", "src/index.ts", "src/index.tsx", "src/index.js"];
1159
+ for (const entry of fallbackEntries) {
1160
+ const fullPath = path9.resolve(config.root, entry);
1161
+ if (fs7.existsSync(fullPath)) {
1162
+ entryPoints.push(fullPath);
1163
+ break;
1164
+ }
1165
+ }
1166
+ }
1167
+ if (entryPoints.length === 0) {
1168
+ throw new Error("No entry point found. Add a <script> tag to index.html or create src/main.ts");
1169
+ }
1170
+ const builtinPlugins = [
1171
+ resolvePlugin(config),
1172
+ cssPlugin(config),
1173
+ assetsPlugin(config)
1174
+ ];
1175
+ const allPlugins = [...builtinPlugins, ...config.plugins];
1176
+ const oxcTransformPlugin = {
1177
+ name: "nasti:oxc-transform",
1178
+ transform(code, id) {
1179
+ if (!shouldTransform(id)) return null;
1180
+ const result = transformCode(id, code, {
1181
+ sourcemap: !!config.build.sourcemap,
1182
+ jsxRuntime: "automatic",
1183
+ jsxImportSource: config.framework === "vue" ? "vue" : "react"
1184
+ });
1185
+ return { code: result.code, map: result.map ? JSON.parse(result.map) : void 0 };
1186
+ }
1187
+ };
1188
+ const bundle = await rolldown({
1189
+ input: entryPoints,
1190
+ plugins: [
1191
+ oxcTransformPlugin,
1192
+ // 转换 Nasti 插件为 Rolldown 插件格式
1193
+ ...allPlugins.map((p) => ({
1194
+ name: p.name,
1195
+ resolveId: p.resolveId,
1196
+ load: p.load,
1197
+ transform: p.transform,
1198
+ buildStart: p.buildStart,
1199
+ buildEnd: p.buildEnd
1200
+ }))
1201
+ ],
1202
+ ...config.build.rolldownOptions
1203
+ });
1204
+ const { output } = await bundle.write({
1205
+ dir: outDir,
1206
+ format: "esm",
1207
+ sourcemap: !!config.build.sourcemap,
1208
+ entryFileNames: "assets/[name].[hash].js",
1209
+ chunkFileNames: "assets/[name].[hash].js",
1210
+ assetFileNames: "assets/[name].[hash][extname]"
1211
+ });
1212
+ await bundle.close();
1213
+ if (html) {
1214
+ let processedHtml = html;
1215
+ const htmlPlugin_ = htmlPlugin(config);
1216
+ if (htmlPlugin_.transformIndexHtml) {
1217
+ const result = await htmlPlugin_.transformIndexHtml(processedHtml);
1218
+ if (typeof result === "string") {
1219
+ processedHtml = result;
1220
+ } else if (result && "html" in result) {
1221
+ processedHtml = processHtml(result.html, result.tags);
1222
+ } else if (Array.isArray(result)) {
1223
+ processedHtml = processHtml(processedHtml, result);
1224
+ }
1225
+ }
1226
+ for (const chunk of output) {
1227
+ if (chunk.type === "chunk" && chunk.isEntry) {
1228
+ const originalEntry = path9.relative(config.root, entryPoints[0]);
1229
+ processedHtml = processedHtml.replace(
1230
+ new RegExp(`(src=["'])/?(${escapeRegExp(originalEntry)})(["'])`, "g"),
1231
+ `$1${config.base}${chunk.fileName}$3`
1232
+ );
1233
+ }
1234
+ }
1235
+ fs7.writeFileSync(path9.resolve(outDir, "index.html"), processedHtml);
1236
+ }
1237
+ const elapsed = ((performance.now() - startTime) / 1e3).toFixed(2);
1238
+ const totalSize = output.reduce((sum, chunk) => {
1239
+ if (chunk.type === "chunk" && chunk.code) return sum + chunk.code.length;
1240
+ return sum;
1241
+ }, 0);
1242
+ console.log(pc2.green(`
1243
+ \u2713 Built in ${elapsed}s`));
1244
+ console.log(pc2.dim(` ${output.length} files, ${formatSize(totalSize)} total`));
1245
+ console.log(pc2.dim(` output: ${config.build.outDir}/
1246
+ `));
1247
+ return { output };
1248
+ }
1249
+ function formatSize(bytes) {
1250
+ if (bytes < 1024) return `${bytes} B`;
1251
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} kB`;
1252
+ return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
1253
+ }
1254
+ function escapeRegExp(string) {
1255
+ return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1256
+ }
1257
+ var init_build = __esm({
1258
+ "src/build/index.ts"() {
1259
+ "use strict";
1260
+ init_config();
1261
+ init_resolve();
1262
+ init_css();
1263
+ init_assets();
1264
+ init_html();
1265
+ init_transformer();
1266
+ }
1267
+ });
1268
+
1269
+ // src/cli.ts
1270
+ import { cac } from "cac";
1271
+ import pc3 from "picocolors";
1272
+ var cli = cac("nasti");
1273
+ cli.command("[root]", "Start dev server").alias("dev").option("--port <port>", "Port number", { default: 3e3 }).option("--host [host]", "Hostname").option("--open [path]", "Open browser on startup").option("--mode <mode>", "Set env mode").action(async (root, options) => {
1274
+ try {
1275
+ const { createServer: createServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
1276
+ const server = await createServer2({
1277
+ root: root ?? ".",
1278
+ mode: options.mode ?? "development",
1279
+ server: {
1280
+ port: options.port,
1281
+ host: options.host,
1282
+ open: options.open
1283
+ }
1284
+ });
1285
+ await server.listen();
1286
+ } catch (err) {
1287
+ console.error(pc3.red(`
1288
+ Error starting dev server:
1289
+ ${err.message}
1290
+ `));
1291
+ if (err.stack) console.error(pc3.dim(err.stack));
1292
+ process.exit(1);
1293
+ }
1294
+ });
1295
+ cli.command("build [root]", "Build for production").option("--outDir <dir>", "Output directory", { default: "dist" }).option("--sourcemap", "Generate source map").option("--minify", "Minify output", { default: true }).option("--mode <mode>", "Set env mode").action(async (root, options) => {
1296
+ try {
1297
+ const { build: build2 } = await Promise.resolve().then(() => (init_build(), build_exports));
1298
+ await build2({
1299
+ root: root ?? ".",
1300
+ mode: options.mode ?? "production",
1301
+ build: {
1302
+ outDir: options.outDir,
1303
+ sourcemap: options.sourcemap,
1304
+ minify: options.minify
1305
+ }
1306
+ });
1307
+ } catch (err) {
1308
+ console.error(pc3.red(`
1309
+ Build failed:
1310
+ ${err.message}
1311
+ `));
1312
+ if (err.stack) console.error(pc3.dim(err.stack));
1313
+ process.exit(1);
1314
+ }
1315
+ });
1316
+ cli.command("preview [root]", "Preview production build").option("--port <port>", "Port number", { default: 4173 }).option("--host [host]", "Hostname").option("--outDir <dir>", "Output directory to serve", { default: "dist" }).action(async (root, options) => {
1317
+ try {
1318
+ const http2 = await import("http");
1319
+ const path10 = await import("path");
1320
+ const sirv2 = (await import("sirv")).default;
1321
+ const connect2 = (await import("connect")).default;
1322
+ const resolvedRoot = path10.resolve(root ?? ".");
1323
+ const outDir = path10.resolve(resolvedRoot, options.outDir);
1324
+ const app = connect2();
1325
+ app.use(sirv2(outDir, { single: true, etag: true, gzip: true, brotli: true }));
1326
+ const port = options.port;
1327
+ const host = options.host === true ? "0.0.0.0" : options.host ?? "localhost";
1328
+ http2.createServer(app).listen(port, host, () => {
1329
+ console.log();
1330
+ console.log(pc3.cyan(" \u{1F50D} nasti preview"));
1331
+ console.log();
1332
+ console.log(` ${pc3.green("\u279C")} Local: ${pc3.cyan(`http://localhost:${port}`)}`);
1333
+ console.log();
1334
+ });
1335
+ } catch (err) {
1336
+ console.error(pc3.red(`
1337
+ Preview failed:
1338
+ ${err.message}
1339
+ `));
1340
+ process.exit(1);
1341
+ }
1342
+ });
1343
+ cli.help();
1344
+ cli.version("0.0.1");
1345
+ cli.parse();
1346
+ //# sourceMappingURL=cli.js.map