@nasti-toolchain/nasti 2.5.0 → 2.5.2

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.
Files changed (60) hide show
  1. package/README.md +34 -2
  2. package/dist/build-CENae4VM.js +748 -0
  3. package/dist/build-CENae4VM.js.map +1 -0
  4. package/dist/build-vjh6h7PB.cjs +786 -0
  5. package/dist/build-vjh6h7PB.cjs.map +1 -0
  6. package/dist/cli.cjs +166 -7608
  7. package/dist/cli.cjs.map +1 -1
  8. package/dist/cli.d.cts +1 -2
  9. package/dist/cli.d.ts +1 -2
  10. package/dist/cli.js +166 -7606
  11. package/dist/cli.js.map +1 -1
  12. package/dist/client/hmr.cjs +0 -19
  13. package/dist/client/hmr.d.cts +14 -12
  14. package/dist/client/hmr.d.cts.map +1 -0
  15. package/dist/client/hmr.d.ts +14 -12
  16. package/dist/client/hmr.d.ts.map +1 -0
  17. package/dist/client/hmr.js +1 -1
  18. package/dist/dev-engine-DJL9sfv5.cjs +473 -0
  19. package/dist/dev-engine-DJL9sfv5.cjs.map +1 -0
  20. package/dist/dev-engine-DapuTPIz.js +469 -0
  21. package/dist/dev-engine-DapuTPIz.js.map +1 -0
  22. package/dist/electron-Bj8H-BJ6.js +278 -0
  23. package/dist/electron-Bj8H-BJ6.js.map +1 -0
  24. package/dist/electron-D0PSXz-a.cjs +316 -0
  25. package/dist/electron-D0PSXz-a.cjs.map +1 -0
  26. package/dist/electron-dev-BzI2IFnR.cjs +272 -0
  27. package/dist/electron-dev-BzI2IFnR.cjs.map +1 -0
  28. package/dist/electron-dev-CM15gsFp.js +251 -0
  29. package/dist/electron-dev-CM15gsFp.js.map +1 -0
  30. package/dist/env-GY7K1zPK.js +833 -0
  31. package/dist/env-GY7K1zPK.js.map +1 -0
  32. package/dist/env-lOj3yw8v.cjs +924 -0
  33. package/dist/env-lOj3yw8v.cjs.map +1 -0
  34. package/dist/environment-B2jCrhY2.js +1361 -0
  35. package/dist/environment-B2jCrhY2.js.map +1 -0
  36. package/dist/environment-CjmvoHIG.cjs +1443 -0
  37. package/dist/environment-CjmvoHIG.cjs.map +1 -0
  38. package/dist/index.cjs +453 -7976
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.d.cts +872 -817
  41. package/dist/index.d.cts.map +1 -0
  42. package/dist/index.d.ts +872 -817
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +418 -7939
  45. package/dist/index.js.map +1 -1
  46. package/dist/logger-BVZwdqqT.js +116 -0
  47. package/dist/logger-BVZwdqqT.js.map +1 -0
  48. package/dist/logger-M7AuVVTV.cjs +171 -0
  49. package/dist/logger-M7AuVVTV.cjs.map +1 -0
  50. package/dist/runnable-environment-BjoOvdDa.js +1345 -0
  51. package/dist/runnable-environment-BjoOvdDa.js.map +1 -0
  52. package/dist/runnable-environment-ByA3DHQ6.cjs +1351 -0
  53. package/dist/runnable-environment-ByA3DHQ6.cjs.map +1 -0
  54. package/dist/server-Bqgl25z0.js +1871 -0
  55. package/dist/server-Bqgl25z0.js.map +1 -0
  56. package/dist/server-BynlMdFc.cjs +1895 -0
  57. package/dist/server-BynlMdFc.cjs.map +1 -0
  58. package/package.json +20 -10
  59. package/dist/client/hmr.cjs.map +0 -1
  60. package/dist/client/hmr.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"electron-D0PSXz-a.cjs","names":["builtinModules","resolveConfig","pc","path","fs","loadEnv","buildEnvDefine","transformReactCode","shouldTransform","transformCode","resolvePlugin","resolveMinifyOption","createRequire"],"sources":["../src/plugins/electron.ts","../src/build/electron.ts"],"sourcesContent":["// Electron 内置插件\n//\n// 负责:将 Electron 模块(electron、electron/*)与 Node 内建模块(fs、path、...)\n// 标记为 external,避免被打包进主进程/Preload 产物。\n//\n// 仅在 config.target === 'electron' 时启用\nimport { builtinModules } from 'node:module'\nimport type { NastiPlugin, ResolvedConfig } from '../types.js'\n\nconst NODE_BUILTINS = new Set([\n ...builtinModules,\n ...builtinModules.map((m) => `node:${m}`),\n])\n\nconst ELECTRON_MODULES = new Set([\n 'electron',\n 'electron/main',\n 'electron/common',\n 'electron/renderer',\n])\n\n/**\n * Create a Vite/Rollup plugin that externalizes Electron and Node built-in imports according to the resolved config.\n *\n * @param config - Resolved build config; any entries in `config.electron.external` are added to the externalized modules set.\n * @returns A plugin that marks `electron` and `electron/*` imports and Node built-in modules (including `node:`-prefixed names) as external.\n */\nexport function electronPlugin(config: ResolvedConfig): NastiPlugin {\n const external = new Set([\n ...ELECTRON_MODULES,\n ...NODE_BUILTINS,\n ...(config.electron.external ?? []),\n ])\n\n return {\n name: 'nasti:electron',\n enforce: 'pre',\n\n resolveId(source) {\n // 显式外部化 electron 与 Node 内建模块\n if (external.has(source)) {\n return { id: source, external: true }\n }\n // 形如 `electron/xxx` 的子路径一律标记外部\n if (source.startsWith('electron/')) {\n return { id: source, external: true }\n }\n return null\n },\n }\n}\n","// Electron 构建入口\n//\n// 流水线:\n// 1. 渲染进程(Web)构建 —— 复用现有 build() 流水线,输出到 outDir/renderer\n// 2. 主进程构建 —— 独立 Rolldown,target=node22,external=electron+内建模块\n// 3. Preload 构建 —— 独立 Rolldown,默认 cjs,contextIsolation 友好\n//\n// 产物目录结构(默认):\n// dist/\n// ├── renderer/ Web 渲染层,与 nasti build 一致\n// ├── main.{cjs,mjs}\n// └── preload.{cjs,mjs}\nimport path from 'node:path'\nimport fs from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { rolldown } from 'rolldown'\nimport pc from 'picocolors'\nimport type { NastiConfig, ResolvedConfig } from '../types.js'\nimport { resolveConfig } from '../config/index.js'\nimport { resolvePlugin } from '../plugins/resolve.js'\nimport { electronPlugin } from '../plugins/electron.js'\nimport { transformCode, transformReactCode, shouldTransform } from '../core/transformer.js'\nimport { loadEnv, buildEnvDefine } from '../core/env.js'\nimport { resolveMinifyOption } from '../core/minify.js'\n\nexport interface ElectronBuildResult {\n rendererOutDir: string\n mainFile: string\n preloadFiles: string[]\n}\n\n/**\n * Performs a full Electron build pipeline producing renderer, main, and preload artifacts.\n *\n * Resolves an Electron-targeted config, validates the installed Electron version, prepares the output directory, builds the renderer (via the web build), bundles the main process, bundles configured preload scripts, and logs a summary.\n *\n * @param inlineConfig - Optional config overrides merged into the resolved build configuration\n * @returns An object with `rendererOutDir` (renderer output directory), `mainFile` (bundled main process file path), and `preloadFiles` (array of bundled preload file paths)\n * @throws Error if the configured Electron main entry file does not exist\n */\nexport async function buildElectron(inlineConfig: NastiConfig = {}): Promise<ElectronBuildResult> {\n const config = await resolveConfig({ ...inlineConfig, target: 'electron' }, 'build')\n const startTime = performance.now()\n\n assertElectronVersion(config)\n\n console.log(pc.cyan('\\n⚡ nasti build (electron)') + pc.dim(` v${__NASTI_VERSION__}`))\n console.log(pc.dim(` root: ${config.root}`))\n console.log(pc.dim(` mode: ${config.mode}`))\n console.log(pc.dim(` target: electron (≥ ${config.electron.minVersion})`))\n\n const outDir = path.resolve(config.root, config.build.outDir)\n if (config.build.emptyOutDir && fs.existsSync(outDir)) {\n fs.rmSync(outDir, { recursive: true, force: true })\n }\n fs.mkdirSync(outDir, { recursive: true })\n\n // ---- 1. 渲染进程(复用 Web 构建)----\n const rendererOutDir = path.join(outDir, 'renderer')\n const { build } = await import('./index.js')\n await build(createElectronRendererConfig(config, inlineConfig, {\n build: {\n ...inlineConfig.build,\n outDir: rendererOutDir,\n emptyOutDir: false,\n },\n }))\n\n // ---- 2. 主进程 ----\n const mainEntry = path.resolve(config.root, config.electron.main)\n if (!fs.existsSync(mainEntry)) {\n throw new Error(\n `Electron main entry not found: ${config.electron.main}\\n` +\n `在 nasti.config.ts 的 electron.main 指定主进程入口文件。`,\n )\n }\n const mainFile = await bundleNode(config, mainEntry, {\n outFile: outFileName(outDir, 'main', config.electron.mainFormat),\n format: config.electron.mainFormat,\n label: 'main',\n })\n\n // ---- 3. Preload ----\n const preloadEntries = normalizePreload(config.electron.preload, config.root)\n const preloadFiles: string[] = []\n for (const entry of preloadEntries) {\n if (!fs.existsSync(entry)) {\n console.warn(pc.yellow(` ⚠ preload entry not found, skipped: ${entry}`))\n continue\n }\n const base = path.basename(entry).replace(/\\.[^.]+$/, '')\n const out = outFileName(outDir, base, config.electron.preloadFormat)\n await bundleNode(config, entry, {\n outFile: out,\n format: config.electron.preloadFormat,\n label: `preload (${base})`,\n })\n preloadFiles.push(out)\n }\n\n const elapsed = ((performance.now() - startTime) / 1000).toFixed(2)\n console.log(pc.green(`\\n✓ Electron build complete in ${elapsed}s`))\n console.log(pc.dim(` renderer: ${path.relative(config.root, rendererOutDir)}/`))\n console.log(pc.dim(` main: ${path.relative(config.root, mainFile)}`))\n for (const pf of preloadFiles) {\n console.log(pc.dim(` preload: ${path.relative(config.root, pf)}`))\n }\n console.log()\n\n return { rendererOutDir, mainFile, preloadFiles }\n}\n\ninterface BundleNodeOptions {\n outFile: string\n format: 'cjs' | 'esm'\n label: string\n}\n\n/**\n * Bundles a Node-targeted entry for Electron into a single output file using Rolldown and the OXC transform.\n *\n * @param config - Fully resolved build configuration used to drive transforms, defines, plugins, and output options\n * @param entry - Absolute path to the entry file to bundle\n * @param opts - Bundle options; expects `outFile` (destination path), `format` (`'cjs'` or `'esm'`), and `label` (human-readable name for logging)\n * @returns The path to the written bundle file (`opts.outFile`)\n */\nasync function bundleNode(\n config: ResolvedConfig,\n entry: string,\n opts: BundleNodeOptions,\n): Promise<string> {\n const env = loadEnv(config.mode, config.root, config.envPrefix)\n const envDefine = {\n ...buildEnvDefine(env, config.mode),\n __ELECTRON__: 'true',\n __NASTI_TARGET__: JSON.stringify('electron'),\n }\n\n const oxcTransformPlugin = {\n name: 'nasti:oxc-transform',\n async transform(code: string, id: string) {\n const result = config.framework === 'react'\n ? await transformReactCode(id, code, {\n react: config.react,\n consumer: 'server',\n development: config.mode === 'development',\n sourcemap: !!config.build.sourcemap,\n target: config.electron.nodeTarget,\n onWarning: (message) => config.logger.warn(`[nasti:react] ${message}`),\n })\n : shouldTransform(id)\n ? transformCode(id, code, {\n sourcemap: !!config.build.sourcemap,\n jsxRuntime: 'automatic',\n jsxImportSource: 'vue',\n target: config.electron.nodeTarget,\n })\n : null\n if (!result) return null\n return { code: result.code, map: result.map ? JSON.parse(result.map) : undefined }\n },\n }\n\n // 从 build.rolldownOptions 拆出 output(合并进 bundle.write())与 transform\n // (需与 envDefine 合并),其余 input 选项(treeshake 等)随 restInputOptions 透传。\n // Nasti 自管的 input / platform / transform / plugins 放在 spread 之后确保覆盖。\n const { output: userOutput, transform: userTransform, ...restInputOptions } =\n config.build.rolldownOptions\n // 合并用户的 transform.define 和 envDefine,确保 envDefine 优先级更高\n const mergedDefine = { ...(userTransform?.define ?? {}), ...envDefine }\n const bundle = await rolldown({\n ...restInputOptions,\n input: entry,\n platform: 'node',\n transform: {\n ...userTransform,\n target: config.electron.nodeTarget,\n define: mergedDefine,\n },\n plugins: [oxcTransformPlugin, electronPlugin(config), resolvePlugin(config)] as any,\n })\n\n fs.mkdirSync(path.dirname(opts.outFile), { recursive: true })\n\n await bundle.write({\n sourcemap: !!config.build.sourcemap,\n minify: resolveMinifyOption(config.build.minify),\n // 允许用户微调 output;但主进程 / preload 的单文件约束由下方键强制保证\n ...userOutput,\n file: opts.outFile,\n format: opts.format === 'cjs' ? 'cjs' : 'esm',\n codeSplitting: false,\n })\n\n await bundle.close()\n\n console.log(pc.dim(` ✓ ${opts.label} → ${path.relative(config.root, opts.outFile)}`))\n return opts.outFile\n}\n\n/**\n * 从 Electron 配置派生 renderer 的普通 Web 构建配置。\n *\n * renderer 使用指定 HTML 入口;本地文件加载场景默认把 `/` base 收敛为\n * `./`,避免产物中的 `/assets/*` 被解析到文件系统根目录。\n */\nexport function createElectronRendererConfig(\n config: ResolvedConfig,\n inlineConfig: NastiConfig = {},\n overrides: Pick<NastiConfig, 'build'> = {},\n): NastiConfig {\n const inlineClient = inlineConfig.environments?.client ?? {}\n return {\n ...inlineConfig,\n ...overrides,\n root: config.root,\n mode: config.mode,\n target: 'web',\n framework: config.framework,\n base: config.base === '/' ? './' : config.base,\n environments: {\n ...(inlineConfig.environments ?? {}),\n client: {\n ...inlineClient,\n html: config.electron.renderer,\n },\n },\n }\n}\n\n/**\n * Compute an output file path for a given base name and module format.\n *\n * @param outDir - Directory where the output file will be placed\n * @param base - Base filename without extension\n * @param format - Module format; `cjs` yields a `.cjs` extension, `esm` yields a `.mjs` extension\n * @returns The resolved file path combining `outDir` and `base` with the appropriate extension\n */\nfunction outFileName(outDir: string, base: string, format: 'cjs' | 'esm'): string {\n const ext = format === 'cjs' ? '.cjs' : '.mjs'\n return path.join(outDir, base + ext)\n}\n\n/**\n * Normalize Electron preload entries into absolute file paths.\n *\n * @param preload - A single preload path or an array of preload paths; a falsy value produces an empty list\n * @param root - Root directory used to resolve relative preload entries\n * @returns An array of absolute file paths for each preload entry\n */\nexport function normalizePreload(preload: string | string[] | undefined, root: string): string[] {\n const list = Array.isArray(preload) ? preload : preload ? [preload] : []\n return list.map((p) => path.resolve(root, p))\n}\n\n/**\n * Warns when the detected Electron major version is lower than the configured minimum.\n *\n * Checks the project for an installed Electron package and, if its major version\n * is less than `config.electron.minVersion`, logs a yellow warning describing the mismatch.\n *\n * @param config - Resolved build configuration that contains `electron.minVersion` and project `root`\n */\nfunction assertElectronVersion(config: ResolvedConfig): void {\n const min = config.electron.minVersion\n const installed = detectInstalledElectron(config.root)\n if (installed && installed < min) {\n console.warn(\n pc.yellow(\n ` ⚠ 检测到 Electron ${installed},Nasti 要求 ≥ ${min}。旧版本可能缺少 ESM 主进程支持。`,\n ),\n )\n }\n}\n\n/**\n * Detects the installed Electron major version in the given project root.\n *\n * @param root - Project root directory used to locate electron via Node resolution\n * @returns The major Electron version as a number, or `null` if Electron is not installed or the version cannot be determined\n */\nexport function detectInstalledElectron(root: string): number | null {\n try {\n // Node resolution handles pnpm/yarn symlink layouts; plain path join is a\n // fallback for installers that still flatten electron under node_modules.\n const require = createRequire(path.resolve(root, 'package.json'))\n const pkgPath = require.resolve('electron/package.json')\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))\n const major = parseInt(String(pkg.version).split('.')[0], 10)\n return Number.isFinite(major) ? major : null\n } catch {\n try {\n const pkgPath = path.resolve(root, 'node_modules/electron/package.json')\n if (!fs.existsSync(pkgPath)) return null\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))\n const major = parseInt(String(pkg.version).split('.')[0], 10)\n return Number.isFinite(major) ? major : null\n } catch {\n return null\n }\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,272 @@
1
+ const require_logger = require("./logger-M7AuVVTV.cjs");
2
+ const require_env = require("./env-lOj3yw8v.cjs");
3
+ const require_electron = require("./electron-D0PSXz-a.cjs");
4
+ let node_path = require("node:path");
5
+ node_path = require_logger.__toESM(node_path, 1);
6
+ let node_fs = require("node:fs");
7
+ node_fs = require_logger.__toESM(node_fs, 1);
8
+ let picocolors = require("picocolors");
9
+ picocolors = require_logger.__toESM(picocolors, 1);
10
+ let node_module = require("node:module");
11
+ let rolldown = require("rolldown");
12
+ let chokidar = require("chokidar");
13
+ chokidar = require_logger.__toESM(chokidar, 1);
14
+ let node_child_process = require("node:child_process");
15
+ //#region src/server/electron-dev.ts
16
+ var electron_dev_exports = /* @__PURE__ */ require_logger.__exportAll({
17
+ electronRendererDevPath: () => electronRendererDevPath,
18
+ startElectronDev: () => startElectronDev
19
+ });
20
+ /**
21
+ * Start an Electron-focused development workflow: run the renderer dev server, build the main and preload bundles into .nasti/, and optionally spawn Electron with automatic restarts.
22
+ *
23
+ * @param inlineConfig - Development options; when `inlineConfig.noSpawn` is `true`, only compiles main/preload into `.nasti/` and does not start the Electron process.
24
+ */
25
+ async function startElectronDev(inlineConfig = {}) {
26
+ const { noSpawn, ...rest } = inlineConfig;
27
+ const config = await require_env.resolveConfig({
28
+ ...rest,
29
+ target: "electron"
30
+ }, "serve");
31
+ warnElectronVersion(config);
32
+ console.log(picocolors.default.cyan("\n⚡ nasti electron dev") + picocolors.default.dim(` v2.5.2`));
33
+ const { createServer } = await Promise.resolve().then(() => require("./server-BynlMdFc.cjs")).then((n) => n.server_exports);
34
+ const server = await createServer({
35
+ ...rest,
36
+ target: "electron",
37
+ framework: config.framework
38
+ });
39
+ await server.listen();
40
+ const devUrl = `http://localhost:${server.config.server.port}` + electronRendererDevPath(config.electron.renderer);
41
+ console.log(picocolors.default.dim(` renderer: ${devUrl}`));
42
+ const stageDir = node_path.default.resolve(config.root, ".nasti");
43
+ node_fs.default.mkdirSync(stageDir, { recursive: true });
44
+ const mainEntry = node_path.default.resolve(config.root, config.electron.main);
45
+ const preloadEntries = require_electron.normalizePreload(config.electron.preload, config.root);
46
+ const builtMainFile = node_path.default.join(stageDir, "main" + extFor(config.electron.mainFormat));
47
+ const builtPreloadFiles = [];
48
+ const compileAll = async () => {
49
+ await compileNode(config, mainEntry, {
50
+ outFile: builtMainFile,
51
+ format: config.electron.mainFormat,
52
+ devUrl
53
+ });
54
+ builtPreloadFiles.length = 0;
55
+ for (const entry of preloadEntries) {
56
+ if (!node_fs.default.existsSync(entry)) continue;
57
+ const base = node_path.default.basename(entry).replace(/\.[^.]+$/, "");
58
+ const out = node_path.default.join(stageDir, base + extFor(config.electron.preloadFormat));
59
+ await compileNode(config, entry, {
60
+ outFile: out,
61
+ format: config.electron.preloadFormat,
62
+ devUrl
63
+ });
64
+ builtPreloadFiles.push(out);
65
+ }
66
+ };
67
+ await compileAll();
68
+ if (noSpawn) {
69
+ console.log(picocolors.default.dim(" (noSpawn) 已编译主/preload,跳过启动 Electron。"));
70
+ return;
71
+ }
72
+ const electronBin = resolveElectronBinary(config);
73
+ if (!electronBin) {
74
+ console.warn(picocolors.default.yellow(" ⚠ 未找到 Electron 可执行文件,请先安装:npm install -D electron\n 已编译主/preload 至 .nasti/,可手动运行。"));
75
+ return;
76
+ }
77
+ let child = null;
78
+ const spawnElectron = () => {
79
+ const args = [builtMainFile, ...config.electron.electronArgs];
80
+ child = (0, node_child_process.spawn)(electronBin, args, {
81
+ stdio: "inherit",
82
+ env: {
83
+ ...process.env,
84
+ NASTI_DEV_SERVER_URL: devUrl,
85
+ NASTI_TARGET: "electron"
86
+ }
87
+ });
88
+ child.on("exit", (code) => {
89
+ if (code !== null && child && child.__nastiKilled !== true) {
90
+ console.log(picocolors.default.dim(` Electron exited (${code}).`));
91
+ process.exit(code ?? 0);
92
+ }
93
+ });
94
+ };
95
+ spawnElectron();
96
+ if (config.electron.autoRestart) {
97
+ const watchTargets = [mainEntry, ...preloadEntries].filter(node_fs.default.existsSync);
98
+ const watcher = chokidar.default.watch(watchTargets, { ignoreInitial: true });
99
+ let restarting = null;
100
+ let pending = false;
101
+ const restart = async () => {
102
+ if (restarting) {
103
+ pending = true;
104
+ return;
105
+ }
106
+ restarting = (async () => {
107
+ do {
108
+ pending = false;
109
+ console.log(picocolors.default.cyan("\n ♻ 主/preload 变更,重启 Electron..."));
110
+ if (child && !child.killed) {
111
+ child.__nastiKilled = true;
112
+ const dying = child;
113
+ await new Promise((resolve) => {
114
+ const timer = setTimeout(() => resolve(), 3e3);
115
+ dying.once("exit", () => {
116
+ clearTimeout(timer);
117
+ resolve();
118
+ });
119
+ dying.kill();
120
+ });
121
+ }
122
+ try {
123
+ await compileAll();
124
+ spawnElectron();
125
+ } catch (e) {
126
+ console.warn(picocolors.default.yellow(` ⚠ 重启编译失败,保留上一次进程: ${e.message}`));
127
+ }
128
+ } while (pending);
129
+ restarting = null;
130
+ })();
131
+ return restarting;
132
+ };
133
+ let debounceTimer = null;
134
+ watcher.on("all", () => {
135
+ if (debounceTimer) clearTimeout(debounceTimer);
136
+ debounceTimer = setTimeout(() => {
137
+ debounceTimer = null;
138
+ restart();
139
+ }, 80);
140
+ });
141
+ }
142
+ }
143
+ /**
144
+ * Map a module format identifier to its corresponding output file extension.
145
+ *
146
+ * @param format - 'cjs' for CommonJS or 'esm' for ECMAScript modules
147
+ * @returns The file extension to use: '.cjs' for `cjs`, '.mjs' for `esm`
148
+ */
149
+ function extFor(format) {
150
+ return format === "cjs" ? ".cjs" : ".mjs";
151
+ }
152
+ /**
153
+ * Compile a Node-target entry (Electron main or preload) into a single output file for development.
154
+ *
155
+ * Injects environment defines (including `__ELECTRON__`, `__NASTI_TARGET__`, and `__NASTI_DEV_SERVER_URL__`), applies source transforms, and writes a bundled file using rolldown.
156
+ *
157
+ * @param config - Resolved Nasti configuration used for transforms and plugin resolution
158
+ * @param entry - Path to the entry file to bundle
159
+ * @param opts - Compilation options
160
+ * @param opts.outFile - Destination path for the bundled output
161
+ * @param opts.format - Output module format, either `'cjs'` or `'esm'`
162
+ * @param opts.devUrl - Dev server URL injected into the bundle as `__NASTI_DEV_SERVER_URL__`
163
+ */
164
+ async function compileNode(config, entry, opts) {
165
+ const env = require_env.loadEnv(config.mode, config.root, config.envPrefix);
166
+ const envDefine = {
167
+ ...require_env.buildEnvDefine(env, config.mode),
168
+ __ELECTRON__: "true",
169
+ __NASTI_TARGET__: JSON.stringify("electron"),
170
+ __NASTI_DEV_SERVER_URL__: JSON.stringify(opts.devUrl)
171
+ };
172
+ const bundle = await (0, rolldown.rolldown)({
173
+ input: entry,
174
+ transform: {
175
+ target: config.electron.nodeTarget,
176
+ define: envDefine
177
+ },
178
+ platform: "node",
179
+ plugins: [
180
+ {
181
+ name: "nasti:oxc-transform",
182
+ async transform(code, id) {
183
+ const result = config.framework === "react" ? await require_env.transformReactCode(id, code, {
184
+ react: config.react,
185
+ consumer: "server",
186
+ development: true,
187
+ sourcemap: true,
188
+ target: config.electron.nodeTarget,
189
+ onWarning: (message) => config.logger.warn(`[nasti:react] ${message}`)
190
+ }) : require_env.shouldTransform(id) ? require_env.transformCode(id, code, {
191
+ sourcemap: true,
192
+ jsxRuntime: "automatic",
193
+ jsxImportSource: "vue",
194
+ target: config.electron.nodeTarget
195
+ }) : null;
196
+ if (!result) return null;
197
+ return {
198
+ code: result.code,
199
+ map: result.map ? JSON.parse(result.map) : void 0
200
+ };
201
+ }
202
+ },
203
+ require_electron.electronPlugin(config),
204
+ require_env.resolvePlugin(config)
205
+ ]
206
+ });
207
+ node_fs.default.mkdirSync(node_path.default.dirname(opts.outFile), { recursive: true });
208
+ await bundle.write({
209
+ file: opts.outFile,
210
+ format: opts.format === "cjs" ? "cjs" : "esm",
211
+ sourcemap: true,
212
+ minify: false,
213
+ codeSplitting: false
214
+ });
215
+ await bundle.close();
216
+ }
217
+ /** 将 renderer HTML 路径转换为 Electron 开发服务器 URL path。 */
218
+ function electronRendererDevPath(renderer) {
219
+ const normalized = renderer.split(node_path.default.sep).join("/").replace(/^\.?\//, "");
220
+ return normalized === "index.html" ? "/" : `/${normalized}`;
221
+ }
222
+ /**
223
+ * Locate the Electron executable for the given project configuration.
224
+ *
225
+ * Checks `config.electron.electronPath` first (returns it if the file exists), otherwise attempts to resolve the `electron` package export from the project's dependencies and returns that path if it exists.
226
+ *
227
+ * @param config - Resolved project configuration used to determine project root and configured Electron path
228
+ * @returns The file system path to the Electron executable if found, `null` otherwise
229
+ */
230
+ function resolveElectronBinary(config) {
231
+ if (config.electron.electronPath && node_fs.default.existsSync(config.electron.electronPath)) return config.electron.electronPath;
232
+ try {
233
+ const require$1 = (0, node_module.createRequire)(node_path.default.resolve(config.root, "package.json"));
234
+ const electronModule = require$1(require$1.resolve("electron"));
235
+ if (typeof electronModule === "string" && node_fs.default.existsSync(electronModule)) return electronModule;
236
+ } catch {}
237
+ return null;
238
+ }
239
+ /**
240
+ * Emit console warnings when Electron is missing or its installed version is lower than configured.
241
+ *
242
+ * @param config - Resolved configuration containing the project root and `electron.minVersion` to check against
243
+ */
244
+ function warnElectronVersion(config) {
245
+ const installed = require_electron.detectInstalledElectron(config.root);
246
+ if (installed === null) {
247
+ console.warn(picocolors.default.yellow(` ⚠ 未检测到 Electron,请安装:npm install -D electron@^${config.electron.minVersion}`));
248
+ return;
249
+ }
250
+ if (installed < config.electron.minVersion) console.warn(picocolors.default.yellow(` ⚠ Electron ${installed} 低于 Nasti 要求的 ${config.electron.minVersion},某些特性(如 ESM 主进程)不可用。`));
251
+ }
252
+ //#endregion
253
+ Object.defineProperty(exports, "electronRendererDevPath", {
254
+ enumerable: true,
255
+ get: function() {
256
+ return electronRendererDevPath;
257
+ }
258
+ });
259
+ Object.defineProperty(exports, "electron_dev_exports", {
260
+ enumerable: true,
261
+ get: function() {
262
+ return electron_dev_exports;
263
+ }
264
+ });
265
+ Object.defineProperty(exports, "startElectronDev", {
266
+ enumerable: true,
267
+ get: function() {
268
+ return startElectronDev;
269
+ }
270
+ });
271
+
272
+ //# sourceMappingURL=electron-dev-BzI2IFnR.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"electron-dev-BzI2IFnR.cjs","names":["resolveConfig","pc","path","normalizePreload","fs","spawn","loadEnv","buildEnvDefine","transformReactCode","shouldTransform","transformCode","electronPlugin","resolvePlugin","require","createRequire","detectInstalledElectron"],"sources":["../src/server/electron-dev.ts"],"sourcesContent":["// Electron 开发模式\n//\n// 启动流程:\n// 1. 启动 Nasti dev server(渲染进程由 Chromium 加载 http://localhost:PORT/)\n// 2. 编译主进程与 preload 到 .nasti/ 临时目录(watch 模式)\n// 3. spawn Electron 可执行文件,传入主进程入口\n// 4. 监听主/preload 变化 -> 重编译 -> kill & respawn\n//\n// 渲染进程的 HMR 由标准 Nasti dev server 提供,无需额外处理\nimport path from 'node:path'\nimport fs from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { spawn, type ChildProcess } from 'node:child_process'\nimport chokidar from 'chokidar'\nimport pc from 'picocolors'\nimport type { NastiConfig, ResolvedConfig } from '../types.js'\nimport { resolveConfig } from '../config/index.js'\nimport { rolldown } from 'rolldown'\nimport { electronPlugin } from '../plugins/electron.js'\nimport { resolvePlugin } from '../plugins/resolve.js'\nimport { transformCode, transformReactCode, shouldTransform } from '../core/transformer.js'\nimport { loadEnv, buildEnvDefine } from '../core/env.js'\nimport { detectInstalledElectron, normalizePreload } from '../build/electron.js'\n\nexport interface ElectronDevOptions extends NastiConfig {\n /** 不启动 Electron 进程,只编译主/preload(CI 场景) */\n noSpawn?: boolean\n}\n\n/**\n * Start an Electron-focused development workflow: run the renderer dev server, build the main and preload bundles into .nasti/, and optionally spawn Electron with automatic restarts.\n *\n * @param inlineConfig - Development options; when `inlineConfig.noSpawn` is `true`, only compiles main/preload into `.nasti/` and does not start the Electron process.\n */\nexport async function startElectronDev(inlineConfig: ElectronDevOptions = {}): Promise<void> {\n const { noSpawn, ...rest } = inlineConfig\n const config = await resolveConfig({ ...rest, target: 'electron' }, 'serve')\n\n warnElectronVersion(config)\n\n console.log(pc.cyan('\\n⚡ nasti electron dev') + pc.dim(` v${__NASTI_VERSION__}`))\n\n // 1. 启动 dev server(渲染进程)\n const { createServer } = await import('./index.js')\n const server = await createServer({\n ...rest,\n target: 'electron',\n framework: config.framework,\n })\n await server.listen()\n\n const devUrl =\n `http://localhost:${server.config.server.port}` +\n electronRendererDevPath(config.electron.renderer)\n console.log(pc.dim(` renderer: ${devUrl}`))\n\n // 2. 编译主进程 + preload 到 .nasti/\n const stageDir = path.resolve(config.root, '.nasti')\n fs.mkdirSync(stageDir, { recursive: true })\n\n const mainEntry = path.resolve(config.root, config.electron.main)\n const preloadEntries = normalizePreload(config.electron.preload, config.root)\n\n const builtMainFile = path.join(stageDir, 'main' + extFor(config.electron.mainFormat))\n const builtPreloadFiles: string[] = []\n\n const compileAll = async () => {\n await compileNode(config, mainEntry, {\n outFile: builtMainFile,\n format: config.electron.mainFormat,\n devUrl,\n })\n builtPreloadFiles.length = 0\n for (const entry of preloadEntries) {\n if (!fs.existsSync(entry)) continue\n const base = path.basename(entry).replace(/\\.[^.]+$/, '')\n const out = path.join(stageDir, base + extFor(config.electron.preloadFormat))\n await compileNode(config, entry, {\n outFile: out,\n format: config.electron.preloadFormat,\n devUrl,\n })\n builtPreloadFiles.push(out)\n }\n }\n\n await compileAll()\n\n if (noSpawn) {\n console.log(pc.dim(' (noSpawn) 已编译主/preload,跳过启动 Electron。'))\n return\n }\n\n // 3. 启动 Electron\n const electronBin = resolveElectronBinary(config)\n if (!electronBin) {\n console.warn(\n pc.yellow(\n ' ⚠ 未找到 Electron 可执行文件,请先安装:npm install -D electron\\n 已编译主/preload 至 .nasti/,可手动运行。',\n ),\n )\n return\n }\n\n let child: ChildProcess | null = null\n const spawnElectron = () => {\n const args = [builtMainFile, ...config.electron.electronArgs]\n child = spawn(electronBin, args, {\n stdio: 'inherit',\n env: { ...process.env, NASTI_DEV_SERVER_URL: devUrl, NASTI_TARGET: 'electron' },\n })\n child.on('exit', (code) => {\n if (code !== null && child && (child as any).__nastiKilled !== true) {\n console.log(pc.dim(` Electron exited (${code}).`))\n process.exit(code ?? 0)\n }\n })\n }\n spawnElectron()\n\n // 4. 监听主/preload 变化并重启\n if (config.electron.autoRestart) {\n const watchTargets = [mainEntry, ...preloadEntries].filter(fs.existsSync)\n const watcher = chokidar.watch(watchTargets, { ignoreInitial: true })\n // 旧实现:重启进行中就 return,丢弃变更。场景:改主进程后立刻改 preload\n // 会漏掉第二次。现在用 pending 标记 coalesce:等本轮完成后若 pending\n // 为真再跑一次,保证最后一次编辑必被编进去。\n let restarting: Promise<void> | null = null\n let pending = false\n const restart = async (): Promise<void> => {\n if (restarting) {\n pending = true\n return\n }\n restarting = (async () => {\n do {\n pending = false\n console.log(pc.cyan('\\n ♻ 主/preload 变更,重启 Electron...'))\n if (child && !child.killed) {\n ;(child as any).__nastiKilled = true\n const dying = child\n await new Promise<void>((resolve) => {\n const timer = setTimeout(() => resolve(), 3000)\n dying.once('exit', () => {\n clearTimeout(timer)\n resolve()\n })\n dying.kill()\n })\n }\n try {\n await compileAll()\n spawnElectron()\n } catch (e: any) {\n console.warn(pc.yellow(` ⚠ 重启编译失败,保留上一次进程: ${e.message}`))\n }\n // 若 pending 在本轮期间被再次置位,立即再跑一轮\n } while (pending)\n restarting = null\n })()\n return restarting\n }\n // chokidar 会对一次保存触发多次事件;小窗口去抖避免过早发起重启\n let debounceTimer: NodeJS.Timeout | null = null\n watcher.on('all', () => {\n if (debounceTimer) clearTimeout(debounceTimer)\n debounceTimer = setTimeout(() => {\n debounceTimer = null\n void restart()\n }, 80)\n })\n }\n}\n\n/**\n * Map a module format identifier to its corresponding output file extension.\n *\n * @param format - 'cjs' for CommonJS or 'esm' for ECMAScript modules\n * @returns The file extension to use: '.cjs' for `cjs`, '.mjs' for `esm`\n */\nfunction extFor(format: 'cjs' | 'esm'): string {\n return format === 'cjs' ? '.cjs' : '.mjs'\n}\n\ninterface CompileNodeOpts {\n outFile: string\n format: 'cjs' | 'esm'\n devUrl: string\n}\n\n/**\n * Compile a Node-target entry (Electron main or preload) into a single output file for development.\n *\n * Injects environment defines (including `__ELECTRON__`, `__NASTI_TARGET__`, and `__NASTI_DEV_SERVER_URL__`), applies source transforms, and writes a bundled file using rolldown.\n *\n * @param config - Resolved Nasti configuration used for transforms and plugin resolution\n * @param entry - Path to the entry file to bundle\n * @param opts - Compilation options\n * @param opts.outFile - Destination path for the bundled output\n * @param opts.format - Output module format, either `'cjs'` or `'esm'`\n * @param opts.devUrl - Dev server URL injected into the bundle as `__NASTI_DEV_SERVER_URL__`\n */\nasync function compileNode(config: ResolvedConfig, entry: string, opts: CompileNodeOpts): Promise<void> {\n const env = loadEnv(config.mode, config.root, config.envPrefix)\n const envDefine = {\n ...buildEnvDefine(env, config.mode),\n __ELECTRON__: 'true',\n __NASTI_TARGET__: JSON.stringify('electron'),\n __NASTI_DEV_SERVER_URL__: JSON.stringify(opts.devUrl),\n }\n\n const oxcTransformPlugin = {\n name: 'nasti:oxc-transform',\n async transform(code: string, id: string) {\n const result = config.framework === 'react'\n ? await transformReactCode(id, code, {\n react: config.react,\n consumer: 'server',\n development: true,\n sourcemap: true,\n target: config.electron.nodeTarget,\n onWarning: (message) => config.logger.warn(`[nasti:react] ${message}`),\n })\n : shouldTransform(id)\n ? transformCode(id, code, {\n sourcemap: true,\n jsxRuntime: 'automatic',\n jsxImportSource: 'vue',\n target: config.electron.nodeTarget,\n })\n : null\n if (!result) return null\n return { code: result.code, map: result.map ? JSON.parse(result.map) : undefined }\n },\n }\n\n const bundle = await rolldown({\n input: entry,\n transform: {\n target: config.electron.nodeTarget,\n define: envDefine,\n },\n platform: 'node',\n plugins: [oxcTransformPlugin, electronPlugin(config), resolvePlugin(config)] as any,\n })\n fs.mkdirSync(path.dirname(opts.outFile), { recursive: true })\n await bundle.write({\n file: opts.outFile,\n format: opts.format === 'cjs' ? 'cjs' : 'esm',\n sourcemap: true,\n minify: false,\n // rolldown 已弃用 inlineDynamicImports,改用 codeSplitting:false 表达\n // 同样语义(单 chunk、内联 dynamic import)\n codeSplitting: false,\n })\n await bundle.close()\n}\n\n/** 将 renderer HTML 路径转换为 Electron 开发服务器 URL path。 */\nexport function electronRendererDevPath(renderer: string): string {\n const normalized = renderer\n .split(path.sep)\n .join('/')\n .replace(/^\\.?\\//, '')\n return normalized === 'index.html' ? '/' : `/${normalized}`\n}\n\n/**\n * Locate the Electron executable for the given project configuration.\n *\n * Checks `config.electron.electronPath` first (returns it if the file exists), otherwise attempts to resolve the `electron` package export from the project's dependencies and returns that path if it exists.\n *\n * @param config - Resolved project configuration used to determine project root and configured Electron path\n * @returns The file system path to the Electron executable if found, `null` otherwise\n */\nfunction resolveElectronBinary(config: ResolvedConfig): string | null {\n if (config.electron.electronPath && fs.existsSync(config.electron.electronPath)) {\n return config.electron.electronPath\n }\n try {\n const require = createRequire(path.resolve(config.root, 'package.json'))\n // electron 包导出其可执行文件路径\n const pathFile = require.resolve('electron')\n const electronModule = require(pathFile)\n if (typeof electronModule === 'string' && fs.existsSync(electronModule)) {\n return electronModule\n }\n } catch {\n // ignore\n }\n return null\n}\n\n/**\n * Emit console warnings when Electron is missing or its installed version is lower than configured.\n *\n * @param config - Resolved configuration containing the project root and `electron.minVersion` to check against\n */\nfunction warnElectronVersion(config: ResolvedConfig): void {\n const installed = detectInstalledElectron(config.root)\n if (installed === null) {\n console.warn(\n pc.yellow(\n ` ⚠ 未检测到 Electron,请安装:npm install -D electron@^${config.electron.minVersion}`,\n ),\n )\n return\n }\n if (installed < config.electron.minVersion) {\n console.warn(\n pc.yellow(\n ` ⚠ Electron ${installed} 低于 Nasti 要求的 ${config.electron.minVersion},某些特性(如 ESM 主进程)不可用。`,\n ),\n )\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,251 @@
1
+ import { a as __exportAll } from "./logger-BVZwdqqT.js";
2
+ import { c as transformReactCode, f as resolveConfig, l as resolvePlugin, n as loadEnv, o as shouldTransform, s as transformCode, t as buildEnvDefine } from "./env-GY7K1zPK.js";
3
+ import { a as normalizePreload, o as electronPlugin, r as detectInstalledElectron } from "./electron-Bj8H-BJ6.js";
4
+ import { createRequire } from "node:module";
5
+ import path from "node:path";
6
+ import fs from "node:fs";
7
+ import pc from "picocolors";
8
+ import { rolldown } from "rolldown";
9
+ import chokidar from "chokidar";
10
+ import { spawn } from "node:child_process";
11
+ //#region src/server/electron-dev.ts
12
+ var electron_dev_exports = /* @__PURE__ */ __exportAll({
13
+ electronRendererDevPath: () => electronRendererDevPath,
14
+ startElectronDev: () => startElectronDev
15
+ });
16
+ /**
17
+ * Start an Electron-focused development workflow: run the renderer dev server, build the main and preload bundles into .nasti/, and optionally spawn Electron with automatic restarts.
18
+ *
19
+ * @param inlineConfig - Development options; when `inlineConfig.noSpawn` is `true`, only compiles main/preload into `.nasti/` and does not start the Electron process.
20
+ */
21
+ async function startElectronDev(inlineConfig = {}) {
22
+ const { noSpawn, ...rest } = inlineConfig;
23
+ const config = await resolveConfig({
24
+ ...rest,
25
+ target: "electron"
26
+ }, "serve");
27
+ warnElectronVersion(config);
28
+ console.log(pc.cyan("\n⚡ nasti electron dev") + pc.dim(` v2.5.2`));
29
+ const { createServer } = await import("./server-Bqgl25z0.js").then((n) => n.n);
30
+ const server = await createServer({
31
+ ...rest,
32
+ target: "electron",
33
+ framework: config.framework
34
+ });
35
+ await server.listen();
36
+ const devUrl = `http://localhost:${server.config.server.port}` + electronRendererDevPath(config.electron.renderer);
37
+ console.log(pc.dim(` renderer: ${devUrl}`));
38
+ const stageDir = path.resolve(config.root, ".nasti");
39
+ fs.mkdirSync(stageDir, { recursive: true });
40
+ const mainEntry = path.resolve(config.root, config.electron.main);
41
+ const preloadEntries = normalizePreload(config.electron.preload, config.root);
42
+ const builtMainFile = path.join(stageDir, "main" + extFor(config.electron.mainFormat));
43
+ const builtPreloadFiles = [];
44
+ const compileAll = async () => {
45
+ await compileNode(config, mainEntry, {
46
+ outFile: builtMainFile,
47
+ format: config.electron.mainFormat,
48
+ devUrl
49
+ });
50
+ builtPreloadFiles.length = 0;
51
+ for (const entry of preloadEntries) {
52
+ if (!fs.existsSync(entry)) continue;
53
+ const base = path.basename(entry).replace(/\.[^.]+$/, "");
54
+ const out = path.join(stageDir, base + extFor(config.electron.preloadFormat));
55
+ await compileNode(config, entry, {
56
+ outFile: out,
57
+ format: config.electron.preloadFormat,
58
+ devUrl
59
+ });
60
+ builtPreloadFiles.push(out);
61
+ }
62
+ };
63
+ await compileAll();
64
+ if (noSpawn) {
65
+ console.log(pc.dim(" (noSpawn) 已编译主/preload,跳过启动 Electron。"));
66
+ return;
67
+ }
68
+ const electronBin = resolveElectronBinary(config);
69
+ if (!electronBin) {
70
+ console.warn(pc.yellow(" ⚠ 未找到 Electron 可执行文件,请先安装:npm install -D electron\n 已编译主/preload 至 .nasti/,可手动运行。"));
71
+ return;
72
+ }
73
+ let child = null;
74
+ const spawnElectron = () => {
75
+ const args = [builtMainFile, ...config.electron.electronArgs];
76
+ child = spawn(electronBin, args, {
77
+ stdio: "inherit",
78
+ env: {
79
+ ...process.env,
80
+ NASTI_DEV_SERVER_URL: devUrl,
81
+ NASTI_TARGET: "electron"
82
+ }
83
+ });
84
+ child.on("exit", (code) => {
85
+ if (code !== null && child && child.__nastiKilled !== true) {
86
+ console.log(pc.dim(` Electron exited (${code}).`));
87
+ process.exit(code ?? 0);
88
+ }
89
+ });
90
+ };
91
+ spawnElectron();
92
+ if (config.electron.autoRestart) {
93
+ const watchTargets = [mainEntry, ...preloadEntries].filter(fs.existsSync);
94
+ const watcher = chokidar.watch(watchTargets, { ignoreInitial: true });
95
+ let restarting = null;
96
+ let pending = false;
97
+ const restart = async () => {
98
+ if (restarting) {
99
+ pending = true;
100
+ return;
101
+ }
102
+ restarting = (async () => {
103
+ do {
104
+ pending = false;
105
+ console.log(pc.cyan("\n ♻ 主/preload 变更,重启 Electron..."));
106
+ if (child && !child.killed) {
107
+ child.__nastiKilled = true;
108
+ const dying = child;
109
+ await new Promise((resolve) => {
110
+ const timer = setTimeout(() => resolve(), 3e3);
111
+ dying.once("exit", () => {
112
+ clearTimeout(timer);
113
+ resolve();
114
+ });
115
+ dying.kill();
116
+ });
117
+ }
118
+ try {
119
+ await compileAll();
120
+ spawnElectron();
121
+ } catch (e) {
122
+ console.warn(pc.yellow(` ⚠ 重启编译失败,保留上一次进程: ${e.message}`));
123
+ }
124
+ } while (pending);
125
+ restarting = null;
126
+ })();
127
+ return restarting;
128
+ };
129
+ let debounceTimer = null;
130
+ watcher.on("all", () => {
131
+ if (debounceTimer) clearTimeout(debounceTimer);
132
+ debounceTimer = setTimeout(() => {
133
+ debounceTimer = null;
134
+ restart();
135
+ }, 80);
136
+ });
137
+ }
138
+ }
139
+ /**
140
+ * Map a module format identifier to its corresponding output file extension.
141
+ *
142
+ * @param format - 'cjs' for CommonJS or 'esm' for ECMAScript modules
143
+ * @returns The file extension to use: '.cjs' for `cjs`, '.mjs' for `esm`
144
+ */
145
+ function extFor(format) {
146
+ return format === "cjs" ? ".cjs" : ".mjs";
147
+ }
148
+ /**
149
+ * Compile a Node-target entry (Electron main or preload) into a single output file for development.
150
+ *
151
+ * Injects environment defines (including `__ELECTRON__`, `__NASTI_TARGET__`, and `__NASTI_DEV_SERVER_URL__`), applies source transforms, and writes a bundled file using rolldown.
152
+ *
153
+ * @param config - Resolved Nasti configuration used for transforms and plugin resolution
154
+ * @param entry - Path to the entry file to bundle
155
+ * @param opts - Compilation options
156
+ * @param opts.outFile - Destination path for the bundled output
157
+ * @param opts.format - Output module format, either `'cjs'` or `'esm'`
158
+ * @param opts.devUrl - Dev server URL injected into the bundle as `__NASTI_DEV_SERVER_URL__`
159
+ */
160
+ async function compileNode(config, entry, opts) {
161
+ const env = loadEnv(config.mode, config.root, config.envPrefix);
162
+ const envDefine = {
163
+ ...buildEnvDefine(env, config.mode),
164
+ __ELECTRON__: "true",
165
+ __NASTI_TARGET__: JSON.stringify("electron"),
166
+ __NASTI_DEV_SERVER_URL__: JSON.stringify(opts.devUrl)
167
+ };
168
+ const bundle = await rolldown({
169
+ input: entry,
170
+ transform: {
171
+ target: config.electron.nodeTarget,
172
+ define: envDefine
173
+ },
174
+ platform: "node",
175
+ plugins: [
176
+ {
177
+ name: "nasti:oxc-transform",
178
+ async transform(code, id) {
179
+ const result = config.framework === "react" ? await transformReactCode(id, code, {
180
+ react: config.react,
181
+ consumer: "server",
182
+ development: true,
183
+ sourcemap: true,
184
+ target: config.electron.nodeTarget,
185
+ onWarning: (message) => config.logger.warn(`[nasti:react] ${message}`)
186
+ }) : shouldTransform(id) ? transformCode(id, code, {
187
+ sourcemap: true,
188
+ jsxRuntime: "automatic",
189
+ jsxImportSource: "vue",
190
+ target: config.electron.nodeTarget
191
+ }) : null;
192
+ if (!result) return null;
193
+ return {
194
+ code: result.code,
195
+ map: result.map ? JSON.parse(result.map) : void 0
196
+ };
197
+ }
198
+ },
199
+ electronPlugin(config),
200
+ resolvePlugin(config)
201
+ ]
202
+ });
203
+ fs.mkdirSync(path.dirname(opts.outFile), { recursive: true });
204
+ await bundle.write({
205
+ file: opts.outFile,
206
+ format: opts.format === "cjs" ? "cjs" : "esm",
207
+ sourcemap: true,
208
+ minify: false,
209
+ codeSplitting: false
210
+ });
211
+ await bundle.close();
212
+ }
213
+ /** 将 renderer HTML 路径转换为 Electron 开发服务器 URL path。 */
214
+ function electronRendererDevPath(renderer) {
215
+ const normalized = renderer.split(path.sep).join("/").replace(/^\.?\//, "");
216
+ return normalized === "index.html" ? "/" : `/${normalized}`;
217
+ }
218
+ /**
219
+ * Locate the Electron executable for the given project configuration.
220
+ *
221
+ * Checks `config.electron.electronPath` first (returns it if the file exists), otherwise attempts to resolve the `electron` package export from the project's dependencies and returns that path if it exists.
222
+ *
223
+ * @param config - Resolved project configuration used to determine project root and configured Electron path
224
+ * @returns The file system path to the Electron executable if found, `null` otherwise
225
+ */
226
+ function resolveElectronBinary(config) {
227
+ if (config.electron.electronPath && fs.existsSync(config.electron.electronPath)) return config.electron.electronPath;
228
+ try {
229
+ const require = createRequire(path.resolve(config.root, "package.json"));
230
+ const electronModule = require(require.resolve("electron"));
231
+ if (typeof electronModule === "string" && fs.existsSync(electronModule)) return electronModule;
232
+ } catch {}
233
+ return null;
234
+ }
235
+ /**
236
+ * Emit console warnings when Electron is missing or its installed version is lower than configured.
237
+ *
238
+ * @param config - Resolved configuration containing the project root and `electron.minVersion` to check against
239
+ */
240
+ function warnElectronVersion(config) {
241
+ const installed = detectInstalledElectron(config.root);
242
+ if (installed === null) {
243
+ console.warn(pc.yellow(` ⚠ 未检测到 Electron,请安装:npm install -D electron@^${config.electron.minVersion}`));
244
+ return;
245
+ }
246
+ if (installed < config.electron.minVersion) console.warn(pc.yellow(` ⚠ Electron ${installed} 低于 Nasti 要求的 ${config.electron.minVersion},某些特性(如 ESM 主进程)不可用。`));
247
+ }
248
+ //#endregion
249
+ export { electron_dev_exports as n, startElectronDev as r, electronRendererDevPath as t };
250
+
251
+ //# sourceMappingURL=electron-dev-CM15gsFp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"electron-dev-CM15gsFp.js","names":[],"sources":["../src/server/electron-dev.ts"],"sourcesContent":["// Electron 开发模式\n//\n// 启动流程:\n// 1. 启动 Nasti dev server(渲染进程由 Chromium 加载 http://localhost:PORT/)\n// 2. 编译主进程与 preload 到 .nasti/ 临时目录(watch 模式)\n// 3. spawn Electron 可执行文件,传入主进程入口\n// 4. 监听主/preload 变化 -> 重编译 -> kill & respawn\n//\n// 渲染进程的 HMR 由标准 Nasti dev server 提供,无需额外处理\nimport path from 'node:path'\nimport fs from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { spawn, type ChildProcess } from 'node:child_process'\nimport chokidar from 'chokidar'\nimport pc from 'picocolors'\nimport type { NastiConfig, ResolvedConfig } from '../types.js'\nimport { resolveConfig } from '../config/index.js'\nimport { rolldown } from 'rolldown'\nimport { electronPlugin } from '../plugins/electron.js'\nimport { resolvePlugin } from '../plugins/resolve.js'\nimport { transformCode, transformReactCode, shouldTransform } from '../core/transformer.js'\nimport { loadEnv, buildEnvDefine } from '../core/env.js'\nimport { detectInstalledElectron, normalizePreload } from '../build/electron.js'\n\nexport interface ElectronDevOptions extends NastiConfig {\n /** 不启动 Electron 进程,只编译主/preload(CI 场景) */\n noSpawn?: boolean\n}\n\n/**\n * Start an Electron-focused development workflow: run the renderer dev server, build the main and preload bundles into .nasti/, and optionally spawn Electron with automatic restarts.\n *\n * @param inlineConfig - Development options; when `inlineConfig.noSpawn` is `true`, only compiles main/preload into `.nasti/` and does not start the Electron process.\n */\nexport async function startElectronDev(inlineConfig: ElectronDevOptions = {}): Promise<void> {\n const { noSpawn, ...rest } = inlineConfig\n const config = await resolveConfig({ ...rest, target: 'electron' }, 'serve')\n\n warnElectronVersion(config)\n\n console.log(pc.cyan('\\n⚡ nasti electron dev') + pc.dim(` v${__NASTI_VERSION__}`))\n\n // 1. 启动 dev server(渲染进程)\n const { createServer } = await import('./index.js')\n const server = await createServer({\n ...rest,\n target: 'electron',\n framework: config.framework,\n })\n await server.listen()\n\n const devUrl =\n `http://localhost:${server.config.server.port}` +\n electronRendererDevPath(config.electron.renderer)\n console.log(pc.dim(` renderer: ${devUrl}`))\n\n // 2. 编译主进程 + preload 到 .nasti/\n const stageDir = path.resolve(config.root, '.nasti')\n fs.mkdirSync(stageDir, { recursive: true })\n\n const mainEntry = path.resolve(config.root, config.electron.main)\n const preloadEntries = normalizePreload(config.electron.preload, config.root)\n\n const builtMainFile = path.join(stageDir, 'main' + extFor(config.electron.mainFormat))\n const builtPreloadFiles: string[] = []\n\n const compileAll = async () => {\n await compileNode(config, mainEntry, {\n outFile: builtMainFile,\n format: config.electron.mainFormat,\n devUrl,\n })\n builtPreloadFiles.length = 0\n for (const entry of preloadEntries) {\n if (!fs.existsSync(entry)) continue\n const base = path.basename(entry).replace(/\\.[^.]+$/, '')\n const out = path.join(stageDir, base + extFor(config.electron.preloadFormat))\n await compileNode(config, entry, {\n outFile: out,\n format: config.electron.preloadFormat,\n devUrl,\n })\n builtPreloadFiles.push(out)\n }\n }\n\n await compileAll()\n\n if (noSpawn) {\n console.log(pc.dim(' (noSpawn) 已编译主/preload,跳过启动 Electron。'))\n return\n }\n\n // 3. 启动 Electron\n const electronBin = resolveElectronBinary(config)\n if (!electronBin) {\n console.warn(\n pc.yellow(\n ' ⚠ 未找到 Electron 可执行文件,请先安装:npm install -D electron\\n 已编译主/preload 至 .nasti/,可手动运行。',\n ),\n )\n return\n }\n\n let child: ChildProcess | null = null\n const spawnElectron = () => {\n const args = [builtMainFile, ...config.electron.electronArgs]\n child = spawn(electronBin, args, {\n stdio: 'inherit',\n env: { ...process.env, NASTI_DEV_SERVER_URL: devUrl, NASTI_TARGET: 'electron' },\n })\n child.on('exit', (code) => {\n if (code !== null && child && (child as any).__nastiKilled !== true) {\n console.log(pc.dim(` Electron exited (${code}).`))\n process.exit(code ?? 0)\n }\n })\n }\n spawnElectron()\n\n // 4. 监听主/preload 变化并重启\n if (config.electron.autoRestart) {\n const watchTargets = [mainEntry, ...preloadEntries].filter(fs.existsSync)\n const watcher = chokidar.watch(watchTargets, { ignoreInitial: true })\n // 旧实现:重启进行中就 return,丢弃变更。场景:改主进程后立刻改 preload\n // 会漏掉第二次。现在用 pending 标记 coalesce:等本轮完成后若 pending\n // 为真再跑一次,保证最后一次编辑必被编进去。\n let restarting: Promise<void> | null = null\n let pending = false\n const restart = async (): Promise<void> => {\n if (restarting) {\n pending = true\n return\n }\n restarting = (async () => {\n do {\n pending = false\n console.log(pc.cyan('\\n ♻ 主/preload 变更,重启 Electron...'))\n if (child && !child.killed) {\n ;(child as any).__nastiKilled = true\n const dying = child\n await new Promise<void>((resolve) => {\n const timer = setTimeout(() => resolve(), 3000)\n dying.once('exit', () => {\n clearTimeout(timer)\n resolve()\n })\n dying.kill()\n })\n }\n try {\n await compileAll()\n spawnElectron()\n } catch (e: any) {\n console.warn(pc.yellow(` ⚠ 重启编译失败,保留上一次进程: ${e.message}`))\n }\n // 若 pending 在本轮期间被再次置位,立即再跑一轮\n } while (pending)\n restarting = null\n })()\n return restarting\n }\n // chokidar 会对一次保存触发多次事件;小窗口去抖避免过早发起重启\n let debounceTimer: NodeJS.Timeout | null = null\n watcher.on('all', () => {\n if (debounceTimer) clearTimeout(debounceTimer)\n debounceTimer = setTimeout(() => {\n debounceTimer = null\n void restart()\n }, 80)\n })\n }\n}\n\n/**\n * Map a module format identifier to its corresponding output file extension.\n *\n * @param format - 'cjs' for CommonJS or 'esm' for ECMAScript modules\n * @returns The file extension to use: '.cjs' for `cjs`, '.mjs' for `esm`\n */\nfunction extFor(format: 'cjs' | 'esm'): string {\n return format === 'cjs' ? '.cjs' : '.mjs'\n}\n\ninterface CompileNodeOpts {\n outFile: string\n format: 'cjs' | 'esm'\n devUrl: string\n}\n\n/**\n * Compile a Node-target entry (Electron main or preload) into a single output file for development.\n *\n * Injects environment defines (including `__ELECTRON__`, `__NASTI_TARGET__`, and `__NASTI_DEV_SERVER_URL__`), applies source transforms, and writes a bundled file using rolldown.\n *\n * @param config - Resolved Nasti configuration used for transforms and plugin resolution\n * @param entry - Path to the entry file to bundle\n * @param opts - Compilation options\n * @param opts.outFile - Destination path for the bundled output\n * @param opts.format - Output module format, either `'cjs'` or `'esm'`\n * @param opts.devUrl - Dev server URL injected into the bundle as `__NASTI_DEV_SERVER_URL__`\n */\nasync function compileNode(config: ResolvedConfig, entry: string, opts: CompileNodeOpts): Promise<void> {\n const env = loadEnv(config.mode, config.root, config.envPrefix)\n const envDefine = {\n ...buildEnvDefine(env, config.mode),\n __ELECTRON__: 'true',\n __NASTI_TARGET__: JSON.stringify('electron'),\n __NASTI_DEV_SERVER_URL__: JSON.stringify(opts.devUrl),\n }\n\n const oxcTransformPlugin = {\n name: 'nasti:oxc-transform',\n async transform(code: string, id: string) {\n const result = config.framework === 'react'\n ? await transformReactCode(id, code, {\n react: config.react,\n consumer: 'server',\n development: true,\n sourcemap: true,\n target: config.electron.nodeTarget,\n onWarning: (message) => config.logger.warn(`[nasti:react] ${message}`),\n })\n : shouldTransform(id)\n ? transformCode(id, code, {\n sourcemap: true,\n jsxRuntime: 'automatic',\n jsxImportSource: 'vue',\n target: config.electron.nodeTarget,\n })\n : null\n if (!result) return null\n return { code: result.code, map: result.map ? JSON.parse(result.map) : undefined }\n },\n }\n\n const bundle = await rolldown({\n input: entry,\n transform: {\n target: config.electron.nodeTarget,\n define: envDefine,\n },\n platform: 'node',\n plugins: [oxcTransformPlugin, electronPlugin(config), resolvePlugin(config)] as any,\n })\n fs.mkdirSync(path.dirname(opts.outFile), { recursive: true })\n await bundle.write({\n file: opts.outFile,\n format: opts.format === 'cjs' ? 'cjs' : 'esm',\n sourcemap: true,\n minify: false,\n // rolldown 已弃用 inlineDynamicImports,改用 codeSplitting:false 表达\n // 同样语义(单 chunk、内联 dynamic import)\n codeSplitting: false,\n })\n await bundle.close()\n}\n\n/** 将 renderer HTML 路径转换为 Electron 开发服务器 URL path。 */\nexport function electronRendererDevPath(renderer: string): string {\n const normalized = renderer\n .split(path.sep)\n .join('/')\n .replace(/^\\.?\\//, '')\n return normalized === 'index.html' ? '/' : `/${normalized}`\n}\n\n/**\n * Locate the Electron executable for the given project configuration.\n *\n * Checks `config.electron.electronPath` first (returns it if the file exists), otherwise attempts to resolve the `electron` package export from the project's dependencies and returns that path if it exists.\n *\n * @param config - Resolved project configuration used to determine project root and configured Electron path\n * @returns The file system path to the Electron executable if found, `null` otherwise\n */\nfunction resolveElectronBinary(config: ResolvedConfig): string | null {\n if (config.electron.electronPath && fs.existsSync(config.electron.electronPath)) {\n return config.electron.electronPath\n }\n try {\n const require = createRequire(path.resolve(config.root, 'package.json'))\n // electron 包导出其可执行文件路径\n const pathFile = require.resolve('electron')\n const electronModule = require(pathFile)\n if (typeof electronModule === 'string' && fs.existsSync(electronModule)) {\n return electronModule\n }\n } catch {\n // ignore\n }\n return null\n}\n\n/**\n * Emit console warnings when Electron is missing or its installed version is lower than configured.\n *\n * @param config - Resolved configuration containing the project root and `electron.minVersion` to check against\n */\nfunction warnElectronVersion(config: ResolvedConfig): void {\n const installed = detectInstalledElectron(config.root)\n if (installed === null) {\n console.warn(\n pc.yellow(\n ` ⚠ 未检测到 Electron,请安装:npm install -D electron@^${config.electron.minVersion}`,\n ),\n )\n return\n }\n if (installed < config.electron.minVersion) {\n console.warn(\n pc.yellow(\n ` ⚠ Electron ${installed} 低于 Nasti 要求的 ${config.electron.minVersion},某些特性(如 ESM 主进程)不可用。`,\n ),\n )\n }\n}\n"],"mappings":"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"}