@nasti-toolchain/nasti 2.4.2 → 2.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -22,7 +22,7 @@
22
22
  - **OXC 转译** - Rust 编写的 TS/JSX/TSX 转译器,比 Babel 快 20-50x
23
23
  - **Vite 插件兼容** - 直接使用现有 Vite/Rollup 插件(resolveId / load / transform)
24
24
  - **内置 React 支持** - JSX 自动转换 + React Fast Refresh HMR
25
- - **内置 Vue 支持** - SFC 编译 + Vue HMR(可选依赖 `@vue/compiler-sfc`)
25
+ - **内置 Vue 支持** - SFC 编译 + Vue HMR;测试版支持 Vue 3.6 Vapor Mode(可选依赖 `@vue/compiler-sfc`)
26
26
  - **Electron 41+ 支持** - React/Vue renderer + 主进程 / Preload,支持 ESM 主进程
27
27
  - **Environment Driver** - 可桥接 Rspeedy 等非 Rolldown 工具链,含 build/dev/watch 生命周期
28
28
  - **Monaco Editor 集成** - 内置 `monacoEditorPlugin`,预打包 Web Worker,修复 HMR 期间的 EMFILE
@@ -198,6 +198,39 @@ export default defineConfig({
198
198
  })
199
199
  ```
200
200
 
201
+ ### Vapor Mode(测试版,需 Vue / `@vue/compiler-sfc` ≥ 3.6)
202
+
203
+ 若仍在使用 Vue 3.5,请先升级依赖(详见 [website/pages/vue.html](./website/pages/vue.html)):
204
+
205
+ ```bash
206
+ npm install vue@^3.6.0-rc.2
207
+ npm install -D @vue/compiler-sfc@^3.6.0-rc.2
208
+ ```
209
+
210
+ [Vapor Mode](https://github.com/vuejs/core/releases) 是 Vue 3.6 的 opt-in 编译模式,跳过 Virtual DOM,生成直接 DOM 操作。Nasti 支持单文件标记与环境级强制:
211
+
212
+ ```vue
213
+ <!-- 单文件 opt-in -->
214
+ <script setup vapor>
215
+ import { ref } from 'vue'
216
+ const count = ref(0)
217
+ </script>
218
+ ```
219
+
220
+ ```ts
221
+ // 环境级强制:对 <script setup> / 纯 template SFC 启用 Vapor
222
+ export default defineConfig({
223
+ framework: 'vue',
224
+ environments: {
225
+ client: {
226
+ vue: { features: { vapor: true } },
227
+ },
228
+ },
229
+ })
230
+ ```
231
+
232
+ 启用后,终端与浏览器控制台会打印测试版免责声明。Vapor Mode **不适合 SSR**;生产环境无崩溃保证,仅建议用于试验或局部性能热点。
233
+
201
234
  ## Electron 支持
202
235
 
203
236
  Nasti 原生支持 Electron,**最低 Electron 41**(对应 Node 22 / Chromium 138,完整 ESM 主进程)。
package/dist/cli.cjs CHANGED
@@ -4349,8 +4349,10 @@ function vuePlugin(config, environmentName = "client") {
4349
4349
  });
4350
4350
  const scopeId = hashId(id);
4351
4351
  const wantsSourceMap = !!config.build.sourcemap || transformedSfc.map != null;
4352
+ const vapor = resolveVaporMode(descriptor, vueOptions, sfc, config);
4352
4353
  let scriptCode = "";
4353
4354
  let scriptMap;
4355
+ let scriptBindings;
4354
4356
  if (descriptor.script || descriptor.scriptSetup) {
4355
4357
  const inlineTemplate = vueOptions.script?.inlineTemplate !== false;
4356
4358
  const compiled = sfc.compileScript(descriptor, {
@@ -4362,9 +4364,11 @@ function vuePlugin(config, environmentName = "client") {
4362
4364
  // 让 compileScript 产出 `const __sfc__ = ...`(而非默认的 `export default {...}`)。
4363
4365
  // 否则下方追加的 `__sfc__.render` / `__sfc__.__scopeId` / HMR 记录会引用一个
4364
4366
  // 不存在的 `__sfc__`,并与 compileScript 自带的 `export default` 形成双重默认导出。
4365
- genDefaultAs: "__sfc__"
4367
+ genDefaultAs: "__sfc__",
4368
+ vapor
4366
4369
  });
4367
4370
  scriptCode = compiled.content;
4371
+ scriptBindings = compiled.bindings;
4368
4372
  scriptMap = composeSourceMapChain(
4369
4373
  [compiled.map, transformedSfc.map],
4370
4374
  { filename: id, environmentName, type: "sfc" }
@@ -4378,7 +4382,9 @@ function vuePlugin(config, environmentName = "client") {
4378
4382
  }
4379
4383
  let templateCode = "";
4380
4384
  let templateMap;
4385
+ let templateMultiRoot;
4381
4386
  const scriptSetupIsInline = !!descriptor.scriptSetup && vueOptions.script?.inlineTemplate !== false;
4387
+ const isTemplateOnlyVapor = vapor && !descriptor.script && !descriptor.scriptSetup;
4382
4388
  if (descriptor.template && !scriptSetupIsInline) {
4383
4389
  const transformedTemplate = await applySourceTransform(
4384
4390
  vueOptions.transformTemplate,
@@ -4400,12 +4406,16 @@ function vuePlugin(config, environmentName = "client") {
4400
4406
  filename: id,
4401
4407
  id: scopeId,
4402
4408
  inMap: templateInputMap,
4409
+ vapor,
4403
4410
  compilerOptions: {
4404
4411
  ...customCompilerOptions,
4412
+ // Vapor 在 bindingMetadata 缺失时需要空对象(与 Vite / compiler-sfc 一致)
4413
+ bindingMetadata: customCompilerOptions.bindingMetadata ?? scriptBindings ?? (vapor ? {} : void 0),
4405
4414
  scopeId: `data-v-${scopeId}`
4406
4415
  }
4407
4416
  });
4408
4417
  templateCode = compiled.code;
4418
+ templateMultiRoot = compiled.multiRoot;
4409
4419
  if (wantsSourceMap || transformedTemplate.map != null) {
4410
4420
  templateMap = compiled.map;
4411
4421
  }
@@ -4443,12 +4453,20 @@ function vuePlugin(config, environmentName = "client") {
4443
4453
  outputNode.add(fragment);
4444
4454
  }
4445
4455
  };
4446
- append(scriptCode || "const __sfc__ = {}", scriptMap);
4456
+ append(
4457
+ scriptCode || (vapor ? "const __sfc__ = { __vapor: true }" : "const __sfc__ = {}"),
4458
+ scriptMap
4459
+ );
4447
4460
  if (templateCode) {
4448
4461
  append("\n");
4449
4462
  append(templateCode, templateMap);
4450
4463
  append("\n");
4451
4464
  append("\n__sfc__.render = render\n");
4465
+ if (isTemplateOnlyVapor && templateMultiRoot !== void 0) {
4466
+ append(`
4467
+ __sfc__.__multiRoot = ${JSON.stringify(templateMultiRoot)}
4468
+ `);
4469
+ }
4452
4470
  }
4453
4471
  if (descriptor.styles.length > 0) {
4454
4472
  for (let i = 0; i < descriptor.styles.length; i++) {
@@ -4457,6 +4475,16 @@ import "${id}?vue&type=style&index=${i}&lang.css"
4457
4475
  `);
4458
4476
  }
4459
4477
  }
4478
+ if (vapor) {
4479
+ append(
4480
+ `
4481
+ if (!globalThis.__NASTI_VAPOR_BETA_WARNED__) {
4482
+ globalThis.__NASTI_VAPOR_BETA_WARNED__ = true
4483
+ console.warn(${JSON.stringify(VAPOR_BETA_WARNING)})
4484
+ }
4485
+ `
4486
+ );
4487
+ }
4460
4488
  append(`
4461
4489
  __sfc__.__scopeId = "data-v-${scopeId}"
4462
4490
  `);
@@ -4516,6 +4544,32 @@ if (import.meta.hot) {
4516
4544
  }
4517
4545
  };
4518
4546
  }
4547
+ function resolveVaporMode(descriptor, vueOptions, sfc, config) {
4548
+ const requested = !!descriptor.vapor || !!vueOptions.features?.vapor && canForceVaporMode(descriptor);
4549
+ if (!requested) return false;
4550
+ if (!supportsVaporCompiler(sfc)) {
4551
+ config.logger.warnOnce(
4552
+ "[nasti:vue] Vapor Mode requires @vue/compiler-sfc >= 3.6. Install it: npm install @vue/compiler-sfc@^3.6.0-0"
4553
+ );
4554
+ return false;
4555
+ }
4556
+ config.logger.warnOnce(VAPOR_BETA_WARNING);
4557
+ return true;
4558
+ }
4559
+ function canForceVaporMode(descriptor) {
4560
+ if (typeof descriptor.filename === "string" && descriptor.filename.endsWith(".vue")) {
4561
+ if (descriptor.script && !descriptor.scriptSetup) return false;
4562
+ return !!(descriptor.scriptSetup || descriptor.template);
4563
+ }
4564
+ return true;
4565
+ }
4566
+ function supportsVaporCompiler(sfc) {
4567
+ const version = sfc.version;
4568
+ if (!version) return false;
4569
+ const [major, minor] = version.split(".").map((part) => Number.parseInt(part, 10));
4570
+ if (!Number.isFinite(major) || !Number.isFinite(minor)) return false;
4571
+ return major > 3 || major === 3 && minor >= 6;
4572
+ }
4519
4573
  async function applySourceTransform(transform2, source, context) {
4520
4574
  if (!transform2) return { code: source };
4521
4575
  const result = await transform2(source, context);
@@ -4576,7 +4630,7 @@ function warnUnchainableMap(context, reason) {
4576
4630
  function hashId(filename) {
4577
4631
  return import_node_crypto2.default.createHash("sha256").update(filename).digest("hex").slice(0, 8);
4578
4632
  }
4579
- var import_node_crypto2, import_source_map_js2, VUE_FILE_RE, VUE_QUERY_RE, debug3, compiler;
4633
+ var import_node_crypto2, import_source_map_js2, VUE_FILE_RE, VUE_QUERY_RE, debug3, VAPOR_BETA_WARNING, compiler;
4580
4634
  var init_vue = __esm({
4581
4635
  "src/plugins/vue.ts"() {
4582
4636
  "use strict";
@@ -4587,6 +4641,7 @@ var init_vue = __esm({
4587
4641
  VUE_FILE_RE = /\.vue$/;
4588
4642
  VUE_QUERY_RE = /\.vue\?vue&type=(script|template|style)(&index=\d+)?(&lang[.=]\w+)?/;
4589
4643
  debug3 = createDebugger("nasti:vue");
4644
+ VAPOR_BETA_WARNING = "Vapor Mode is a beta feature; Zixiao Labs and the Vue team do not provide guarantees against crashes in production environments, and it is not suitable for server-side rendering environments.";
4590
4645
  compiler = null;
4591
4646
  }
4592
4647
  });
@@ -5385,7 +5440,7 @@ async function build(inlineConfig = {}) {
5385
5440
  const startTime = performance.now();
5386
5441
  logger.info(
5387
5442
  import_picocolors6.default.cyan(`
5388
- nasti v${"2.4.2"} `) + import_picocolors6.default.green(`building for ${config.mode}...`)
5443
+ nasti v${"2.4.3"} `) + import_picocolors6.default.green(`building for ${config.mode}...`)
5389
5444
  );
5390
5445
  debug6?.(`root: ${config.root}`);
5391
5446
  const buildableNames = Object.keys(config.environments).filter((name) => {
@@ -6408,7 +6463,7 @@ async function createServer(inlineConfig = {}) {
6408
6463
  const readyIn = Math.ceil(performance.now() - startTime);
6409
6464
  logger.info(
6410
6465
  `
6411
- ${import_picocolors8.default.cyan(import_picocolors8.default.bold("NASTI"))} ${import_picocolors8.default.cyan(`v${"2.4.2"}`)} ${import_picocolors8.default.dim("ready in")} ${import_picocolors8.default.bold(readyIn)} ${import_picocolors8.default.dim("ms")}
6466
+ ${import_picocolors8.default.cyan(import_picocolors8.default.bold("NASTI"))} ${import_picocolors8.default.cyan(`v${"2.4.3"}`)} ${import_picocolors8.default.dim("ready in")} ${import_picocolors8.default.bold(readyIn)} ${import_picocolors8.default.dim("ms")}
6412
6467
  `
6413
6468
  );
6414
6469
  printServerUrls(
@@ -6606,7 +6661,7 @@ async function buildElectron(inlineConfig = {}) {
6606
6661
  const config = await resolveConfig({ ...inlineConfig, target: "electron" }, "build");
6607
6662
  const startTime = performance.now();
6608
6663
  assertElectronVersion(config);
6609
- console.log(import_picocolors9.default.cyan("\n\u26A1 nasti build (electron)") + import_picocolors9.default.dim(` v${"2.4.2"}`));
6664
+ console.log(import_picocolors9.default.cyan("\n\u26A1 nasti build (electron)") + import_picocolors9.default.dim(` v${"2.4.3"}`));
6610
6665
  console.log(import_picocolors9.default.dim(` root: ${config.root}`));
6611
6666
  console.log(import_picocolors9.default.dim(` mode: ${config.mode}`));
6612
6667
  console.log(import_picocolors9.default.dim(` target: electron (\u2265 ${config.electron.minVersion})`));
@@ -6785,7 +6840,7 @@ async function startElectronDev(inlineConfig = {}) {
6785
6840
  const { noSpawn, ...rest } = inlineConfig;
6786
6841
  const config = await resolveConfig({ ...rest, target: "electron" }, "serve");
6787
6842
  warnElectronVersion(config);
6788
- console.log(import_picocolors10.default.cyan("\n\u26A1 nasti electron dev") + import_picocolors10.default.dim(` v${"2.4.2"}`));
6843
+ console.log(import_picocolors10.default.cyan("\n\u26A1 nasti electron dev") + import_picocolors10.default.dim(` v${"2.4.3"}`));
6789
6844
  const { createServer: createServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
6790
6845
  const server = await createServer2({
6791
6846
  ...rest,
@@ -7150,7 +7205,7 @@ cli.command("preview [root]", "Preview production build").option("--port <port>"
7150
7205
  const host = options.host === true ? "0.0.0.0" : options.host ?? "localhost";
7151
7206
  http2.createServer(app).listen(port, host, () => {
7152
7207
  logger.info(`
7153
- ${import_picocolors11.default.cyan(import_picocolors11.default.bold("NASTI"))} ${import_picocolors11.default.cyan(`v${"2.4.2"}`)} ${import_picocolors11.default.dim("preview")}
7208
+ ${import_picocolors11.default.cyan(import_picocolors11.default.bold("NASTI"))} ${import_picocolors11.default.cyan(`v${"2.4.3"}`)} ${import_picocolors11.default.dim("preview")}
7154
7209
  `);
7155
7210
  printServerUrls2(
7156
7211
  {
@@ -7167,6 +7222,6 @@ cli.command("preview [root]", "Preview production build").option("--port <port>"
7167
7222
  }
7168
7223
  });
7169
7224
  cli.help();
7170
- cli.version("2.4.2");
7225
+ cli.version("2.4.3");
7171
7226
  cli.parse();
7172
7227
  //# sourceMappingURL=cli.cjs.map