@nasti-toolchain/nasti 1.6.4 → 1.7.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/index.d.cts CHANGED
@@ -1,3 +1,5 @@
1
+ import { InputOptions, OutputOptions } from 'rolldown';
2
+
1
3
  interface NastiConfig {
2
4
  /** 项目根目录 */
3
5
  root?: string;
@@ -87,8 +89,33 @@ interface BuildConfig {
87
89
  minify?: boolean | 'oxc';
88
90
  sourcemap?: boolean | 'inline' | 'hidden';
89
91
  target?: string | string[];
90
- rolldownOptions?: Record<string, unknown>;
92
+ /**
93
+ * 透传给 Rolldown 的底层选项,供生产应用手动控制代码拆分与 Tree-shaking。
94
+ *
95
+ * - input 侧(`treeshake`、`resolve`、`external`、`platform` 等)会合并进 `rolldown()`;
96
+ * - `output` 会合并进 `bundle.write()`,用于控制代码拆分
97
+ * (`output.advancedChunks` / `output.codeSplitting`)、chunk 命名等。
98
+ *
99
+ * 注:`input` 与 `plugins` 由 Nasti 管理,故不在此暴露;`output.dir` 始终由
100
+ * `build.outDir` 决定(HTML 改写依赖产物路径),传入会被忽略。
101
+ */
102
+ rolldownOptions?: NastiRolldownOptions;
91
103
  emptyOutDir?: boolean;
104
+ css?: CssConfig;
105
+ }
106
+ /**
107
+ * Nasti 暴露的 Rolldown 选项:在 Rolldown {@link InputOptions} 基础上去掉由 Nasti
108
+ * 接管的 `input` / `plugins`,并补充一个 `output` 出口用于 `bundle.write()`。
109
+ */
110
+ type NastiRolldownOptions = Omit<InputOptions, 'input' | 'plugins'> & {
111
+ /** 传给 `bundle.write()` 的输出选项:代码拆分(`advancedChunks` / `codeSplitting`)、chunk 命名等 */
112
+ output?: OutputOptions;
113
+ };
114
+ interface CssConfig {
115
+ /** CSP nonce to add to inline <style> tags */
116
+ nonce?: string;
117
+ /** Emit CSS as separate files instead of inline injection (CSP-friendly) */
118
+ emitCssFile?: boolean;
92
119
  }
93
120
  interface NastiPlugin {
94
121
  name: string;
@@ -143,6 +170,7 @@ type LoadResult = string | null | undefined | {
143
170
  type TransformResult = string | null | undefined | {
144
171
  code: string;
145
172
  map?: unknown;
173
+ moduleType?: string;
146
174
  };
147
175
  interface EmittedFile {
148
176
  type: 'asset' | 'chunk';
@@ -322,4 +350,4 @@ interface MonacoEditorPluginOptions {
322
350
  }
323
351
  declare function monacoEditorPlugin(options?: MonacoEditorPluginOptions): NastiPlugin;
324
352
 
325
- export { type DevServer, type ElectronConfig, type HmrPayload, type ModuleNode, type MonacoCustomWorker, type MonacoEditorLanguageWorker, type MonacoEditorPluginOptions, type NastiConfig, type NastiPlugin, type ResolvedConfig, type TransformResult, build, buildElectron, createServer, defineConfig, electronPlugin, monacoEditorPlugin, resolveConfig, startElectronDev };
353
+ export { type BuildConfig, type DevServer, type ElectronConfig, type HmrPayload, type ModuleNode, type MonacoCustomWorker, type MonacoEditorLanguageWorker, type MonacoEditorPluginOptions, type NastiConfig, type NastiPlugin, type NastiRolldownOptions, type ResolvedConfig, type TransformResult, build, buildElectron, createServer, defineConfig, electronPlugin, monacoEditorPlugin, resolveConfig, startElectronDev };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { InputOptions, OutputOptions } from 'rolldown';
2
+
1
3
  interface NastiConfig {
2
4
  /** 项目根目录 */
3
5
  root?: string;
@@ -87,8 +89,33 @@ interface BuildConfig {
87
89
  minify?: boolean | 'oxc';
88
90
  sourcemap?: boolean | 'inline' | 'hidden';
89
91
  target?: string | string[];
90
- rolldownOptions?: Record<string, unknown>;
92
+ /**
93
+ * 透传给 Rolldown 的底层选项,供生产应用手动控制代码拆分与 Tree-shaking。
94
+ *
95
+ * - input 侧(`treeshake`、`resolve`、`external`、`platform` 等)会合并进 `rolldown()`;
96
+ * - `output` 会合并进 `bundle.write()`,用于控制代码拆分
97
+ * (`output.advancedChunks` / `output.codeSplitting`)、chunk 命名等。
98
+ *
99
+ * 注:`input` 与 `plugins` 由 Nasti 管理,故不在此暴露;`output.dir` 始终由
100
+ * `build.outDir` 决定(HTML 改写依赖产物路径),传入会被忽略。
101
+ */
102
+ rolldownOptions?: NastiRolldownOptions;
91
103
  emptyOutDir?: boolean;
104
+ css?: CssConfig;
105
+ }
106
+ /**
107
+ * Nasti 暴露的 Rolldown 选项:在 Rolldown {@link InputOptions} 基础上去掉由 Nasti
108
+ * 接管的 `input` / `plugins`,并补充一个 `output` 出口用于 `bundle.write()`。
109
+ */
110
+ type NastiRolldownOptions = Omit<InputOptions, 'input' | 'plugins'> & {
111
+ /** 传给 `bundle.write()` 的输出选项:代码拆分(`advancedChunks` / `codeSplitting`)、chunk 命名等 */
112
+ output?: OutputOptions;
113
+ };
114
+ interface CssConfig {
115
+ /** CSP nonce to add to inline <style> tags */
116
+ nonce?: string;
117
+ /** Emit CSS as separate files instead of inline injection (CSP-friendly) */
118
+ emitCssFile?: boolean;
92
119
  }
93
120
  interface NastiPlugin {
94
121
  name: string;
@@ -143,6 +170,7 @@ type LoadResult = string | null | undefined | {
143
170
  type TransformResult = string | null | undefined | {
144
171
  code: string;
145
172
  map?: unknown;
173
+ moduleType?: string;
146
174
  };
147
175
  interface EmittedFile {
148
176
  type: 'asset' | 'chunk';
@@ -322,4 +350,4 @@ interface MonacoEditorPluginOptions {
322
350
  }
323
351
  declare function monacoEditorPlugin(options?: MonacoEditorPluginOptions): NastiPlugin;
324
352
 
325
- export { type DevServer, type ElectronConfig, type HmrPayload, type ModuleNode, type MonacoCustomWorker, type MonacoEditorLanguageWorker, type MonacoEditorPluginOptions, type NastiConfig, type NastiPlugin, type ResolvedConfig, type TransformResult, build, buildElectron, createServer, defineConfig, electronPlugin, monacoEditorPlugin, resolveConfig, startElectronDev };
353
+ export { type BuildConfig, type DevServer, type ElectronConfig, type HmrPayload, type ModuleNode, type MonacoCustomWorker, type MonacoEditorLanguageWorker, type MonacoEditorPluginOptions, type NastiConfig, type NastiPlugin, type NastiRolldownOptions, type ResolvedConfig, type TransformResult, build, buildElectron, createServer, defineConfig, electronPlugin, monacoEditorPlugin, resolveConfig, startElectronDev };
package/dist/index.js CHANGED
@@ -35,7 +35,8 @@ var init_defaults = __esm({
35
35
  sourcemap: false,
36
36
  target: "es2022",
37
37
  rolldownOptions: {},
38
- emptyOutDir: true
38
+ emptyOutDir: true,
39
+ css: {}
39
40
  };
40
41
  defaultElectron = {
41
42
  main: "src/electron/main.ts",
@@ -282,20 +283,29 @@ import { createRequire } from "module";
282
283
  function resolvePlugin(config) {
283
284
  const { alias, extensions } = config.resolve;
284
285
  const require2 = createRequire(path2.resolve(config.root, "package.json"));
286
+ const aliasEntries = Object.entries(alias).sort(
287
+ ([a], [b]) => b.length - a.length
288
+ );
285
289
  return {
286
290
  name: "nasti:resolve",
287
291
  enforce: "pre",
288
292
  resolveId(source, importer) {
289
- for (const [key, value] of Object.entries(alias)) {
293
+ for (const [key, value] of aliasEntries) {
290
294
  if (source === key || source.startsWith(key + "/")) {
291
- source = source.replace(key, value);
292
- if (!path2.isAbsolute(source)) {
293
- source = path2.resolve(config.root, source);
294
- }
295
+ const aliasBase = resolveAliasTarget(value, config.root);
296
+ const sub = source.slice(key.length).replace(/^\//, "");
297
+ const target = sub ? path2.join(aliasBase, sub) : aliasBase;
298
+ const resolved = tryResolveFile(target, extensions);
299
+ if (resolved) return resolved;
295
300
  break;
296
301
  }
297
302
  }
298
- if (path2.isAbsolute(source)) {
303
+ if (source.startsWith("/") && !source.startsWith("//")) {
304
+ const rootRelative = path2.join(config.root, source.slice(1));
305
+ const resolved = tryResolveFile(rootRelative, extensions);
306
+ if (resolved) return resolved;
307
+ }
308
+ if (path2.isAbsolute(source) && fs2.existsSync(source)) {
299
309
  const resolved = tryResolveFile(source, extensions);
300
310
  if (resolved) return resolved;
301
311
  }
@@ -318,6 +328,7 @@ function resolvePlugin(config) {
318
328
  return null;
319
329
  },
320
330
  load(id) {
331
+ if (id.startsWith("\0")) return null;
321
332
  if (!fs2.existsSync(id)) return null;
322
333
  if (id.endsWith(".json")) {
323
334
  const content = fs2.readFileSync(id, "utf-8");
@@ -327,6 +338,11 @@ function resolvePlugin(config) {
327
338
  }
328
339
  };
329
340
  }
341
+ function resolveAliasTarget(value, root) {
342
+ if (path2.isAbsolute(value) && fs2.existsSync(value)) return value;
343
+ if (value.startsWith("/")) return path2.join(root, value.slice(1));
344
+ return path2.resolve(root, value);
345
+ }
330
346
  function tryResolveFile(file, extensions) {
331
347
  if (fs2.existsSync(file) && fs2.statSync(file).isFile()) {
332
348
  return file;
@@ -423,8 +439,8 @@ function cssPlugin(config) {
423
439
  cssSource = compiled.css;
424
440
  }
425
441
  const rewritten = rewriteCssUrls(cssSource, id, config.root);
442
+ const escaped = JSON.stringify(rewritten);
426
443
  if (config.command === "serve") {
427
- const escaped = JSON.stringify(rewritten);
428
444
  return {
429
445
  code: `
430
446
  const css = ${escaped};
@@ -448,7 +464,42 @@ export default css;
448
464
  `
449
465
  };
450
466
  }
451
- return rewritten !== code ? { code: rewritten } : null;
467
+ const cssConfig = config.build.css || {};
468
+ const nonce = cssConfig.nonce;
469
+ const emitCssFile = cssConfig.emitCssFile;
470
+ if (emitCssFile) {
471
+ const fileName = `assets/${path4.basename(id, ".css")}.css`;
472
+ this.emitFile({
473
+ type: "asset",
474
+ fileName,
475
+ source: rewritten
476
+ });
477
+ return {
478
+ code: `
479
+ const link = document.createElement('link');
480
+ link.rel = 'stylesheet';
481
+ link.href = ${JSON.stringify("/" + fileName)};
482
+ document.head.appendChild(link);
483
+
484
+ export default ${escaped};
485
+ `,
486
+ moduleType: "js"
487
+ };
488
+ }
489
+ const nonceAttr = nonce ? `style.setAttribute('nonce', ${JSON.stringify(nonce)});` : "";
490
+ return {
491
+ code: `
492
+ const css = ${escaped};
493
+ const style = document.createElement('style');
494
+ style.setAttribute('data-nasti-css', ${JSON.stringify(id)});
495
+ ${nonceAttr}
496
+ style.textContent = css;
497
+ document.head.appendChild(style);
498
+
499
+ export default css;
500
+ `,
501
+ moduleType: "js"
502
+ };
452
503
  }
453
504
  };
454
505
  }
@@ -852,7 +903,7 @@ import pc from "picocolors";
852
903
  async function build(inlineConfig = {}) {
853
904
  const config = await resolveConfig(inlineConfig, "build");
854
905
  const startTime = performance.now();
855
- console.log(pc.cyan("\n\u{1F528} nasti build") + pc.dim(` v${"1.6.4"}`));
906
+ console.log(pc.cyan("\n\u{1F528} nasti build") + pc.dim(` v${"1.7.0"}`));
856
907
  console.log(pc.dim(` root: ${config.root}`));
857
908
  console.log(pc.dim(` mode: ${config.mode}`));
858
909
  const outDir = path8.resolve(config.root, config.build.outDir);
@@ -906,9 +957,12 @@ async function build(inlineConfig = {}) {
906
957
  };
907
958
  const env = loadEnv(config.mode, config.root, config.envPrefix);
908
959
  const envDefine = buildEnvDefine(env, config.mode);
960
+ const { output: userOutput, transform: userTransform, ...restInputOptions } = config.build.rolldownOptions;
961
+ const mergedDefine = { ...userTransform?.define ?? {}, ...envDefine };
909
962
  const bundle = await rolldown({
963
+ ...restInputOptions,
910
964
  input: entryPoints,
911
- define: envDefine,
965
+ transform: { ...userTransform, define: mergedDefine },
912
966
  plugins: [
913
967
  oxcTransformPlugin,
914
968
  // 转换 Nasti 插件为 Rolldown 插件格式
@@ -924,17 +978,19 @@ async function build(inlineConfig = {}) {
924
978
  // manifest/SW writers) rely on for final-stage artifact emission.
925
979
  closeBundle: p.closeBundle
926
980
  }))
927
- ],
928
- ...config.build.rolldownOptions
981
+ ]
929
982
  });
930
983
  const { output } = await bundle.write({
931
- dir: outDir,
932
984
  format: "esm",
933
985
  sourcemap: !!config.build.sourcemap,
934
986
  minify: !!config.build.minify,
935
987
  entryFileNames: "assets/[name].[hash].js",
936
988
  chunkFileNames: "assets/[name].[hash].js",
937
- assetFileNames: "assets/[name].[hash][extname]"
989
+ assetFileNames: "assets/[name].[hash][extname]",
990
+ // 用户可覆盖默认输出:代码拆分(advancedChunks / codeSplitting)、chunk 命名等
991
+ ...userOutput,
992
+ // dir 始终由 Nasti 掌管 —— 下方 HTML 改写依赖固定的产物目录,故放在最后强制生效
993
+ dir: outDir
938
994
  });
939
995
  await bundle.close();
940
996
  await pluginContainer.buildEnd();
@@ -1603,7 +1659,7 @@ function rewriteImports(code, config, filePath) {
1603
1659
  const baseSpec = suffix ? spec.slice(0, -suffix.length) : spec;
1604
1660
  for (const [key, value] of aliasEntries) {
1605
1661
  if (baseSpec === key || baseSpec.startsWith(key + "/")) {
1606
- const aliasBase = resolveAliasTarget(value, root);
1662
+ const aliasBase = resolveAliasTarget2(value, root);
1607
1663
  const sub = baseSpec.slice(key.length).replace(/^\//, "");
1608
1664
  const target = sub ? path10.join(aliasBase, sub) : aliasBase;
1609
1665
  const resolved = tryResolveDiskPath(target);
@@ -1634,7 +1690,7 @@ function rewriteImports(code, config, filePath) {
1634
1690
  (_m, q, s) => `import(${q}${transformSpec(s)}${q})`
1635
1691
  );
1636
1692
  }
1637
- function resolveAliasTarget(value, root) {
1693
+ function resolveAliasTarget2(value, root) {
1638
1694
  if (path10.isAbsolute(value) && fs8.existsSync(value)) return value;
1639
1695
  if (value.startsWith("/")) return path10.join(root, value.slice(1));
1640
1696
  return path10.resolve(root, value);
@@ -2059,7 +2115,7 @@ async function createServer(inlineConfig = {}) {
2059
2115
  const localUrl = `http://localhost:${actualPort}`;
2060
2116
  const networkUrl = host === "0.0.0.0" ? `http://${getNetworkAddress()}:${actualPort}` : null;
2061
2117
  console.log();
2062
- console.log(pc3.cyan(" nasti dev server") + pc3.dim(` v${"1.6.4"}`));
2118
+ console.log(pc3.cyan(" nasti dev server") + pc3.dim(` v${"1.7.0"}`));
2063
2119
  console.log();
2064
2120
  console.log(` ${pc3.green(">")} Local: ${pc3.cyan(localUrl)}`);
2065
2121
  if (networkUrl) {
@@ -2183,7 +2239,7 @@ async function buildElectron(inlineConfig = {}) {
2183
2239
  const config = await resolveConfig({ ...inlineConfig, target: "electron" }, "build");
2184
2240
  const startTime = performance.now();
2185
2241
  assertElectronVersion(config);
2186
- console.log(pc2.cyan("\n\u26A1 nasti build (electron)") + pc2.dim(` v${"1.6.4"}`));
2242
+ console.log(pc2.cyan("\n\u26A1 nasti build (electron)") + pc2.dim(` v${"1.7.0"}`));
2187
2243
  console.log(pc2.dim(` root: ${config.root}`));
2188
2244
  console.log(pc2.dim(` mode: ${config.mode}`));
2189
2245
  console.log(pc2.dim(` target: electron (\u2265 ${config.electron.minVersion})`));
@@ -2261,19 +2317,23 @@ async function bundleNode(config, entry, opts) {
2261
2317
  return { code: result.code, map: result.map ? JSON.parse(result.map) : void 0 };
2262
2318
  }
2263
2319
  };
2320
+ const { output: userOutput, transform: userTransform, ...restInputOptions } = config.build.rolldownOptions;
2321
+ const mergedDefine = { ...userTransform?.define ?? {}, ...envDefine };
2264
2322
  const bundle = await rolldown2({
2323
+ ...restInputOptions,
2265
2324
  input: entry,
2266
- define: envDefine,
2267
2325
  platform: "node",
2268
- plugins: [oxcTransformPlugin, electronPlugin(config), resolvePlugin(config)],
2269
- ...config.build.rolldownOptions
2326
+ transform: { ...userTransform, define: mergedDefine },
2327
+ plugins: [oxcTransformPlugin, electronPlugin(config), resolvePlugin(config)]
2270
2328
  });
2271
2329
  fs7.mkdirSync(path9.dirname(opts.outFile), { recursive: true });
2272
2330
  await bundle.write({
2273
- file: opts.outFile,
2274
- format: opts.format === "cjs" ? "cjs" : "esm",
2275
2331
  sourcemap: !!config.build.sourcemap,
2276
2332
  minify: !!config.build.minify,
2333
+ // 允许用户微调 output;但主进程 / preload 的单文件约束由下方键强制保证
2334
+ ...userOutput,
2335
+ file: opts.outFile,
2336
+ format: opts.format === "cjs" ? "cjs" : "esm",
2277
2337
  codeSplitting: false
2278
2338
  });
2279
2339
  await bundle.close();
@@ -2330,7 +2390,7 @@ async function startElectronDev(inlineConfig = {}) {
2330
2390
  const { noSpawn, ...rest } = inlineConfig;
2331
2391
  const config = await resolveConfig({ ...rest, target: "electron" }, "serve");
2332
2392
  warnElectronVersion(config);
2333
- console.log(pc4.cyan("\n\u26A1 nasti electron dev") + pc4.dim(` v${"1.6.4"}`));
2393
+ console.log(pc4.cyan("\n\u26A1 nasti electron dev") + pc4.dim(` v${"1.7.0"}`));
2334
2394
  const { createServer: createServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
2335
2395
  const server = await createServer2({ ...rest, target: "electron" });
2336
2396
  await server.listen();