@vureact/compiler-core 1.0.3 → 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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @vureact/compiler-core v1.0.2
2
+ * @vureact/compiler-core v1.1.0
3
3
  * (c) 2025-present Ruihong Zhong (Ryan John)
4
4
  * @license MIT
5
5
  */
@@ -1401,14 +1401,17 @@ import less from "less";
1401
1401
  import * as sass from "sass";
1402
1402
  function resolveLessSass(source, options) {
1403
1403
  const { lang, enabled, filename } = options;
1404
- if (!enabled) return source;
1405
- if (lang === "less") {
1406
- return resolveLessSync(source, filename);
1404
+ const result = { code: source, fileExt: ".css" };
1405
+ if (!options.enabled) {
1406
+ result.fileExt = `.${options.lang}`;
1407
1407
  }
1408
- if (lang === "scss" || lang === "sass") {
1409
- return resolveSassSync(source, filename);
1408
+ if (!enabled) return result;
1409
+ if (lang === "less") {
1410
+ result.code = resolveLessSync(source, filename);
1411
+ } else if (lang === "scss" || lang === "sass") {
1412
+ result.code = resolveSassSync(source, filename);
1410
1413
  }
1411
- return source;
1414
+ return result;
1412
1415
  }
1413
1416
  function resolveLessSync(source, filename) {
1414
1417
  let result = "";
@@ -1478,13 +1481,12 @@ function resolveStyles(descriptor, ctx, result) {
1478
1481
  { file: filename }
1479
1482
  );
1480
1483
  }
1481
- const source = resolveLessSass(content, {
1484
+ const { code, fileExt } = resolveLessSass(content, {
1482
1485
  lang,
1483
1486
  filename,
1484
1487
  enabled: preprocessStyles
1485
1488
  });
1486
- const disablePreprocessing = lang !== "css" && !preprocessStyles;
1487
- let ext = disablePreprocessing ? `.${lang}` : ".css";
1489
+ let ext = fileExt;
1488
1490
  if (style.module) {
1489
1491
  ext = `-${fileId}.module${ext}`;
1490
1492
  styleData.moduleName = typeof style.module === "boolean" ? STYLE_MODULE_NAME : style.module;
@@ -1492,18 +1494,18 @@ function resolveStyles(descriptor, ctx, result) {
1492
1494
  ext = `-${fileId}${ext}`;
1493
1495
  }
1494
1496
  if (style.scoped) {
1495
- if (disablePreprocessing) {
1497
+ if (lang !== "css" && !preprocessStyles) {
1496
1498
  logger.warn(
1497
1499
  "Scoped styles are only supported for CSS. Preprocessing is disabled, so scoped styles will not be applied.",
1498
1500
  { file: filename }
1499
1501
  );
1500
1502
  return;
1501
1503
  }
1502
- const result2 = processScopedWithPostCss(source, fileId);
1504
+ const result2 = processScopedWithPostCss(code, fileId);
1503
1505
  style.content = result2.css.trim();
1504
1506
  styleData.scopeId = result2.scopeId;
1505
1507
  } else {
1506
- style.content = source;
1508
+ style.content = code;
1507
1509
  }
1508
1510
  const filePath = filename.replace(/\.vue$/i, ext);
1509
1511
  styleData.filePath = filePath;
@@ -1591,7 +1593,8 @@ import { traverse as traverse3 } from "@babel/core";
1591
1593
  // src/core/transform/sfc/script/syntax-processor/postprocess/insert-css-import.ts
1592
1594
  import * as t16 from "@babel/types";
1593
1595
  function insertCSSImport(ctx) {
1594
- if (ctx.inputType !== "sfc") return;
1596
+ const { inputType } = ctx;
1597
+ if (inputType !== "sfc") return;
1595
1598
  const { filePath, moduleName } = ctx.styleData;
1596
1599
  if (!filePath) return;
1597
1600
  const scriptIR = getScriptIR(ctx);
@@ -1634,6 +1637,44 @@ function insertRequiredImports(ctx) {
1634
1637
  const processedModules = /* @__PURE__ */ new Set();
1635
1638
  let hasProcessedImports = false;
1636
1639
  recordImport(ctx, PACKAGE_NAME.react, REACT_API_MAP.memo);
1640
+ function resolveRequiredImport(path7) {
1641
+ const { node } = path7;
1642
+ const moduleName = node.source.value.toLowerCase();
1643
+ const isVueLike = isVueEcosystemPackage(moduleName);
1644
+ mergeImports(node, ctx);
1645
+ if (processedModules.has(moduleName) && !path7.removed) {
1646
+ path7.remove();
1647
+ return;
1648
+ }
1649
+ processedModules.add(moduleName);
1650
+ if (!hasProcessedImports) {
1651
+ const required = createRequiredImports(ctx);
1652
+ if (isVueLike) {
1653
+ path7.replaceWithMultiple(required);
1654
+ } else if (moduleName === PACKAGE_NAME.react) {
1655
+ path7.insertAfter(required);
1656
+ } else {
1657
+ path7.insertBefore(required);
1658
+ }
1659
+ hasProcessedImports = true;
1660
+ }
1661
+ if (isVueLike && !path7.removed) {
1662
+ path7.remove();
1663
+ return;
1664
+ }
1665
+ replaceVueSuffix(ctx, node.source);
1666
+ }
1667
+ function resolveStyleFileExt(path7) {
1668
+ if (!ctx.preprocessStyles) return;
1669
+ const { node } = path7;
1670
+ if (!node || !node.source || !node.source.value) return;
1671
+ const importSource = node.source.value;
1672
+ if (typeof importSource !== "string") return;
1673
+ const styleExtRegex = /\.(less|sass|scss)$/i;
1674
+ if (!styleExtRegex.test(importSource)) return;
1675
+ const newSource = importSource.replace(styleExtRegex, ".css");
1676
+ node.source.value = newSource;
1677
+ }
1637
1678
  return {
1638
1679
  // 增加 Program.exit 兜底注入 required imports(处理无 ImportDeclaration 的 SFC)
1639
1680
  Program: {
@@ -1645,31 +1686,8 @@ function insertRequiredImports(ctx) {
1645
1686
  }
1646
1687
  },
1647
1688
  ImportDeclaration(path7) {
1648
- const { node } = path7;
1649
- const moduleName = node.source.value.toLowerCase();
1650
- const isVueLike = isVueEcosystemPackage(moduleName);
1651
- mergeImports(node, ctx);
1652
- if (processedModules.has(moduleName) && !path7.removed) {
1653
- path7.remove();
1654
- return;
1655
- }
1656
- processedModules.add(moduleName);
1657
- if (!hasProcessedImports) {
1658
- const required = createRequiredImports(ctx);
1659
- if (isVueLike) {
1660
- path7.replaceWithMultiple(required);
1661
- } else if (moduleName === PACKAGE_NAME.react) {
1662
- path7.insertAfter(required);
1663
- } else {
1664
- path7.insertBefore(required);
1665
- }
1666
- hasProcessedImports = true;
1667
- }
1668
- if (isVueLike && !path7.removed) {
1669
- path7.remove();
1670
- return;
1671
- }
1672
- replaceVueSuffix(ctx, node.source);
1689
+ resolveRequiredImport(path7);
1690
+ resolveStyleFileExt(path7);
1673
1691
  }
1674
1692
  };
1675
1693
  }
@@ -2964,7 +2982,7 @@ function buildSlotPropSignature(rawName, params, optional) {
2964
2982
  t27.tsTypeReference(t27.identifier(REACT_API_MAP.ReactNode))
2965
2983
  );
2966
2984
  let typeAnnotation;
2967
- if (rawName === SLOT_DEFAULT_NAME && params.length === 0) {
2985
+ if (rawName === SLOT_DEFAULT_NAME && params.length === 0 || params.length === 0) {
2968
2986
  typeAnnotation = reactNodeType;
2969
2987
  } else {
2970
2988
  const fnType = t27.tsFunctionType(null, params, reactNodeType);
@@ -5076,7 +5094,7 @@ function transform(ast, ctx, options) {
5076
5094
  }
5077
5095
 
5078
5096
  // package.json
5079
- var version = "1.0.2";
5097
+ var version = "1.1.0";
5080
5098
  var bin = {
5081
5099
  vureact: "./bin/vureact.js"
5082
5100
  };
@@ -5085,6 +5103,7 @@ var bin = {
5085
5103
  var CacheKey = /* @__PURE__ */ ((CacheKey2) => {
5086
5104
  CacheKey2["SFC"] = "sfc";
5087
5105
  CacheKey2["SCRIPT"] = "script";
5106
+ CacheKey2["STYLE"] = "style";
5088
5107
  CacheKey2["ASSET"] = "copied";
5089
5108
  return CacheKey2;
5090
5109
  })(CacheKey || {});
@@ -5190,6 +5209,7 @@ var Helper = class {
5190
5209
  "package-lock.json",
5191
5210
  "pnpm-lock.yaml",
5192
5211
  "index.html",
5212
+ "yarn-lock.",
5193
5213
  "tsconfig.",
5194
5214
  "vite.config.",
5195
5215
  "eslint.config.",
@@ -5325,6 +5345,7 @@ var Helper = class {
5325
5345
  source: {
5326
5346
  ["sfc" /* SFC */]: [],
5327
5347
  ["script" /* SCRIPT */]: [],
5348
+ ["style" /* STYLE */]: [],
5328
5349
  ["copied" /* ASSET */]: []
5329
5350
  }
5330
5351
  };
@@ -5465,6 +5486,39 @@ var Helper = class {
5465
5486
  }
5466
5487
  };
5467
5488
 
5489
+ // src/core/parse/style-only.ts
5490
+ function parseOnlyStyle(source, ctx, options) {
5491
+ const { filename } = ctx;
5492
+ let lang = filename.split(".").pop();
5493
+ if (!lang) {
5494
+ lang = "css";
5495
+ logger.warn(`The style file without an extension has been fallback to .css`, {
5496
+ file: filename
5497
+ });
5498
+ }
5499
+ const { code, fileExt } = resolveLessSass(source, {
5500
+ filename,
5501
+ lang,
5502
+ enabled: ctx.preprocessStyles
5503
+ });
5504
+ const result = {
5505
+ template: null,
5506
+ script: null,
5507
+ style: {
5508
+ source: {
5509
+ type: "style",
5510
+ content: code,
5511
+ lang: fileExt.replace(".", ""),
5512
+ attrs: {},
5513
+ loc: {}
5514
+ },
5515
+ ast: void 0
5516
+ }
5517
+ };
5518
+ executePlugins(options?.plugins, result, ctx);
5519
+ return result;
5520
+ }
5521
+
5468
5522
  // src/compiler/context/adapter.ts
5469
5523
  import path3 from "path";
5470
5524
 
@@ -5526,19 +5580,42 @@ var CompilationAdapter = class _CompilationAdapter {
5526
5580
  static createContext(input) {
5527
5581
  const ctx = createCompilationCtx();
5528
5582
  const inputType = _CompilationAdapter.detectInputType(input.filename);
5529
- if (inputType.startsWith("script-")) {
5530
- ctx.data.inputType = inputType;
5583
+ const initTemplateData = () => {
5531
5584
  ctx.data.templateData = {};
5585
+ };
5586
+ const initScriptData = () => {
5587
+ ctx.data.scriptData = {};
5588
+ };
5589
+ const initStyleData = () => {
5532
5590
  ctx.data.styleData = {};
5591
+ };
5592
+ if (inputType.startsWith("script-")) {
5593
+ initTemplateData();
5594
+ initStyleData();
5595
+ } else if (inputType === "style") {
5596
+ initTemplateData();
5597
+ initScriptData();
5533
5598
  }
5534
5599
  ctx.init({ inputType, ...input });
5535
5600
  return ctx;
5536
5601
  }
5537
5602
  static detectInputType(filename) {
5538
- const ext = path3.extname(filename);
5539
- if (ext === ".vue") return "sfc";
5540
- if (ext === ".ts") return "script-ts";
5541
- return "script-js";
5603
+ const ext = path3.extname(filename).toLowerCase();
5604
+ switch (ext) {
5605
+ case ".vue":
5606
+ return "sfc";
5607
+ case ".js":
5608
+ return "script-js";
5609
+ case ".ts":
5610
+ return "script-ts";
5611
+ case ".css":
5612
+ case ".less":
5613
+ case ".sass":
5614
+ case ".scss":
5615
+ return "style";
5616
+ default:
5617
+ return "unknow";
5618
+ }
5542
5619
  }
5543
5620
  };
5544
5621
 
@@ -5597,8 +5674,7 @@ var BaseCompiler = class extends Helper {
5597
5674
  * `;
5598
5675
  *
5599
5676
  * const result = compiler.compile(vueCode, 'MyComponent.vue');
5600
- * console.log(result.code); // 生成的 React JSX 代码
5601
- * console.log(result.fileInfo.jsx.file); // 输出文件路径
5677
+ * console.log(result);
5602
5678
  * ```
5603
5679
  *
5604
5680
  * @throws 不会直接抛出异常,错误通过日志系统记录
@@ -5610,21 +5686,29 @@ var BaseCompiler = class extends Helper {
5610
5686
  compile(source, filename) {
5611
5687
  const { plugins, preprocessStyles = true } = this.options;
5612
5688
  const fileId = this.genHash(filename);
5613
- const genOptions = this.prepareGenerateOptions(filename);
5614
5689
  const ctx = this.createContext({
5615
5690
  fileId,
5616
5691
  filename,
5617
5692
  source,
5618
5693
  preprocessStyles
5619
5694
  });
5620
- try {
5695
+ const genOptions = this.prepareGenerateOptions(filename);
5696
+ const resolveSFCAndScriptFile = () => {
5621
5697
  const ast = parse(source, ctx.data, { plugins: plugins?.parser });
5622
5698
  const ir = transform(ast, ctx.data, { plugins: plugins?.transformer });
5623
5699
  const gen = generate(ir, ctx.data, {
5624
5700
  ...genOptions,
5625
5701
  plugins: plugins?.codegen
5626
5702
  });
5627
- const result = this.resolveResult(ir, gen, ctx.data);
5703
+ return this.resolveMainResult(ir, gen, ctx.data);
5704
+ };
5705
+ const resolveStyleFile = () => {
5706
+ const result = parseOnlyStyle(source, ctx.data, { plugins: plugins?.parser });
5707
+ return this.resolveStyleResult(result, ctx.data);
5708
+ };
5709
+ try {
5710
+ const isStyleFile = ctx.data.inputType === "style";
5711
+ const result = isStyleFile ? resolveStyleFile() : resolveSFCAndScriptFile();
5628
5712
  if (plugins) {
5629
5713
  const { parser, transformer, codegen, ...rest } = plugins;
5630
5714
  executePlugins(rest, result, ctx);
@@ -5634,67 +5718,6 @@ var BaseCompiler = class extends Helper {
5634
5718
  ctx.clear();
5635
5719
  }
5636
5720
  }
5637
- /**
5638
- * 处理 SFC 和 Script 文件的编译结果
5639
- *
5640
- * 根据编译上下文中的输入类型(SFC 或 Script),整理并返回相应的编译结果。
5641
- * 对于 SFC 文件,返回包含 JSX 和 CSS 信息的完整结果;
5642
- * 对于 Script 文件,返回仅包含脚本信息的结果。
5643
- *
5644
- * 关键处理逻辑:
5645
- * 1. 构建基础结果:包含文件ID、路由信息和生成的代码
5646
- * 2. 确定输出路径:根据源文件路径和语言类型生成输出文件路径
5647
- * 3. 区分文件类型:
5648
- * - SFC 文件:生成 .tsx 扩展名,包含样式信息
5649
- * - Script 文件:保持原扩展名,不包含样式信息
5650
- * 4. 样式处理:提取样式文件路径、作用域ID和样式代码
5651
- *
5652
- * @private
5653
- * @param ir - React 中间表示(IR)描述符,包含转换后的组件信息
5654
- * @param gen - 代码生成结果,包含生成的代码和源码映射
5655
- * @param ctxData - 编译上下文数据,包含文件信息和编译状态
5656
- * @returns {CompilationResult} 整理后的编译结果
5657
- *
5658
- * @remarks
5659
- * - 文件扩展名处理:Vue SFC 文件会转换为 .tsx 或 .jsx 文件
5660
- * - 样式分离:SFC 中的样式会被提取到独立的 CSS 文件中
5661
- * - 路由检测:自动检测组件是否使用路由,用于依赖注入
5662
- * - 作用域样式:为 Scoped CSS 生成唯一的作用域ID
5663
- *
5664
- * @see {@link SFCCompilationResult} SFC 编译结果类型
5665
- * @see {@link ScriptCompilationResult} Script 编译结果类型
5666
- */
5667
- resolveResult(ir, gen, ctxData) {
5668
- const { fileId, filename, scriptData, styleData } = ctxData;
5669
- const base = {
5670
- fileId,
5671
- hasRoute: ctxData.route,
5672
- ...gen
5673
- };
5674
- const { lang } = scriptData;
5675
- const file = this.resolveOutputPath(filename, lang);
5676
- if (ctxData.inputType === "sfc") {
5677
- return {
5678
- fileInfo: {
5679
- jsx: {
5680
- file: `${file}x`,
5681
- // 'xxx.ts' + 'x' => 'xxx.tsx'
5682
- lang
5683
- },
5684
- css: {
5685
- file: styleData?.filePath,
5686
- hash: styleData?.scopeId,
5687
- code: ir?.style
5688
- }
5689
- },
5690
- ...base
5691
- };
5692
- }
5693
- return {
5694
- fileInfo: { script: { file, lang } },
5695
- ...base
5696
- };
5697
- }
5698
5721
  /**
5699
5722
  * 初始化 Babel 代码生成选项
5700
5723
  *
@@ -5759,6 +5782,87 @@ var BaseCompiler = class extends Helper {
5759
5782
  }
5760
5783
  return mergedOptions;
5761
5784
  }
5785
+ /**
5786
+ * 处理 SFC 和 Script 文件的编译结果
5787
+ *
5788
+ * 根据编译上下文中的输入类型(SFC 或 Script),整理并返回相应的编译结果。
5789
+ * 对于 SFC 文件,返回包含 JSX 和 CSS 信息的完整结果;
5790
+ * 对于 Script 文件,返回仅包含脚本信息的结果。
5791
+ *
5792
+ * 关键处理逻辑:
5793
+ * 1. 构建基础结果:包含文件ID、路由信息和生成的代码
5794
+ * 2. 确定输出路径:根据源文件路径和语言类型生成输出文件路径
5795
+ * 3. 区分文件类型:
5796
+ * - SFC 文件:生成 .tsx 扩展名,包含样式信息
5797
+ * - Script 文件:保持原扩展名,不包含样式信息
5798
+ * 4. 样式处理:提取样式文件路径、作用域ID和样式代码
5799
+ *
5800
+ * @private
5801
+ * @param ir - React 中间表示(IR)描述符,包含转换后的组件信息
5802
+ * @param gen - 代码生成结果,包含生成的代码和源码映射
5803
+ * @param ctxData - 编译上下文数据,包含文件信息和编译状态
5804
+ * @returns {CompilationResult} 整理后的编译结果
5805
+ *
5806
+ * @remarks
5807
+ * - 文件扩展名处理:Vue SFC 文件会转换为 .tsx 或 .jsx 文件
5808
+ * - 样式分离:SFC 中的样式会被提取到独立的 CSS 文件中
5809
+ * - 路由检测:自动检测组件是否使用路由,用于依赖注入
5810
+ * - 作用域样式:为 Scoped CSS 生成唯一的作用域ID
5811
+ *
5812
+ * @see {@link SFCCompilationResult} SFC 编译结果类型
5813
+ * @see {@link ScriptCompilationResult} Script 编译结果类型
5814
+ */
5815
+ resolveMainResult(ir, gen, ctxData) {
5816
+ const { fileId, filename, scriptData, styleData, inputType } = ctxData;
5817
+ const base = {
5818
+ fileId,
5819
+ hasRoute: ctxData.route,
5820
+ ...gen
5821
+ };
5822
+ const { lang } = scriptData;
5823
+ const file = this.resolveOutputPath(filename, lang);
5824
+ if (inputType === "sfc") {
5825
+ return {
5826
+ fileInfo: {
5827
+ jsx: {
5828
+ file: `${file}x`,
5829
+ // 'xxx.ts' + 'x' => 'xxx.tsx'
5830
+ lang
5831
+ },
5832
+ css: {
5833
+ file: styleData?.filePath,
5834
+ hash: styleData?.scopeId,
5835
+ code: ir?.style
5836
+ }
5837
+ },
5838
+ ...base
5839
+ };
5840
+ }
5841
+ return {
5842
+ fileInfo: { script: { file, lang } },
5843
+ ...base
5844
+ };
5845
+ }
5846
+ /**
5847
+ * 处理 Style 文件的编译结果
5848
+ * @param data style 文件解析结果
5849
+ * @param ctxData 上下文数据
5850
+ */
5851
+ resolveStyleResult(data, ctxData) {
5852
+ const { fileId, filename } = ctxData;
5853
+ const { lang, content = "" } = data.style.source;
5854
+ const file = this.resolveOutputPath(filename, lang);
5855
+ return {
5856
+ fileId,
5857
+ fileInfo: {
5858
+ style: {
5859
+ file,
5860
+ lang
5861
+ }
5862
+ },
5863
+ code: content
5864
+ };
5865
+ }
5762
5866
  };
5763
5867
 
5764
5868
  // src/utils/calc-elapsed-time.ts
@@ -5788,6 +5892,8 @@ var AssetManager = class {
5788
5892
  this.fileCompiler = fileCompiler;
5789
5893
  this.cleanupManager = cleanupManager;
5790
5894
  }
5895
+ // 需要经过管线编译处理的文件类型
5896
+ pipelineFiles = [".js", ".ts", ".less", ".scss", ".sass"];
5791
5897
  /**
5792
5898
  * 运行资源文件处理管线
5793
5899
  */
@@ -5801,7 +5907,16 @@ var AssetManager = class {
5801
5907
  const filename = path4.basename(p).toLowerCase();
5802
5908
  const ext = path4.extname(p).toLowerCase();
5803
5909
  if (!this.fileCompiler.options.output?.ignoreAssets) {
5804
- if (!relativeToRoot.includes(path4.sep) && exclusions.has(filename)) {
5910
+ const shouldExclude = Array.from(exclusions).some((pattern) => {
5911
+ if (pattern.endsWith(".")) {
5912
+ return filename.startsWith(pattern);
5913
+ }
5914
+ if (pattern.startsWith(".")) {
5915
+ return ext === pattern;
5916
+ }
5917
+ return relativeToRoot === pattern || filename === pattern;
5918
+ });
5919
+ if (shouldExclude) {
5805
5920
  return false;
5806
5921
  }
5807
5922
  } else if (exclusions.has(relativeToRoot) || exclusions.has(filename)) {
@@ -5809,7 +5924,7 @@ var AssetManager = class {
5809
5924
  }
5810
5925
  if (ext === ".vue") return false;
5811
5926
  const isInsideSrc = p.startsWith(inputPath + path4.sep);
5812
- if (isInsideSrc && (ext === ".js" || ext === ".ts")) {
5927
+ if (isInsideSrc && this.pipelineFiles.includes(ext)) {
5813
5928
  return false;
5814
5929
  }
5815
5930
  return true;
@@ -5969,33 +6084,47 @@ var CompilationUnitProcessor = class {
5969
6084
  }
5970
6085
  // 处理编译结果
5971
6086
  resolveResult(result, unit, key) {
5972
- const { fileId, code, hasRoute } = result;
6087
+ const { fileId, code } = result;
5973
6088
  unit.fileId = fileId;
5974
- unit.hasRoute = hasRoute;
5975
- if (key === "sfc" /* SFC */) {
5976
- const component = result;
5977
- const { jsx, css } = component.fileInfo;
5978
- if (css.file) {
5979
- css.file = this.fileCompiler.resolveOutputPath(css.file);
5980
- }
5981
- unit.output = {
5982
- // 添加 jsx 文件信息
5983
- jsx: {
5984
- file: jsx.file,
5985
- code
5986
- },
5987
- css
5988
- // 添加 css 文件信息
5989
- };
5990
- } else if (key === "script" /* SCRIPT */) {
5991
- const { script } = result.fileInfo;
5992
- unit.output = {
5993
- script: {
5994
- file: script.file,
5995
- code
6089
+ const isSFC = key === "sfc" /* SFC */;
6090
+ const isScriptFile = key === "script" /* SCRIPT */;
6091
+ const isStyleFile = key === "style" /* STYLE */;
6092
+ if (isSFC || isScriptFile) {
6093
+ unit.hasRoute = result?.hasRoute;
6094
+ }
6095
+ const resolveFileInfo = () => {
6096
+ if (isSFC) {
6097
+ const component = result;
6098
+ const { jsx, css } = component.fileInfo;
6099
+ if (css.file) {
6100
+ css.file = this.fileCompiler.resolveOutputPath(css.file);
5996
6101
  }
5997
- };
5998
- }
6102
+ unit.output = {
6103
+ jsx: {
6104
+ file: jsx.file,
6105
+ code
6106
+ },
6107
+ css
6108
+ };
6109
+ } else if (isScriptFile) {
6110
+ const { script } = result.fileInfo;
6111
+ unit.output = {
6112
+ script: {
6113
+ file: script?.file,
6114
+ code
6115
+ }
6116
+ };
6117
+ } else if (isStyleFile) {
6118
+ const { style } = result.fileInfo;
6119
+ unit.output = {
6120
+ style: {
6121
+ file: style.file,
6122
+ code
6123
+ }
6124
+ };
6125
+ }
6126
+ };
6127
+ resolveFileInfo();
5999
6128
  }
6000
6129
  /**
6001
6130
  * 将编译产物写入磁盘
@@ -6012,6 +6141,10 @@ var CompilationUnitProcessor = class {
6012
6141
  if (css.file && css.code) {
6013
6142
  await this.fileCompiler.writeFileWithDir(css.file, css.code);
6014
6143
  }
6144
+ } else if (key === "style" /* STYLE */) {
6145
+ const { style } = output;
6146
+ file = style.file;
6147
+ code = style.code;
6015
6148
  } else {
6016
6149
  const { script } = output;
6017
6150
  file = script.file;
@@ -6047,6 +6180,12 @@ var FileProcessor = class {
6047
6180
  async processScript(filePath, existingCache) {
6048
6181
  return this.processFile("script" /* SCRIPT */, filePath, existingCache);
6049
6182
  }
6183
+ /**
6184
+ * Process a single style file (this method is called directly in CLI Watch mode)
6185
+ */
6186
+ async processStyle(filePath, existingCache) {
6187
+ return this.processFile("style" /* STYLE */, filePath, existingCache);
6188
+ }
6050
6189
  async processFile(key, filePath, existingCache) {
6051
6190
  const absPath = this.fileCompiler.getAbsPath(filePath);
6052
6191
  const fileMeta = await this.fileCompiler.getFileMeta(absPath);
@@ -6062,17 +6201,20 @@ var FileProcessor = class {
6062
6201
  if (!source.trim()) return;
6063
6202
  const initUnit = {
6064
6203
  ...fileMeta,
6065
- file: absPath,
6066
- fileId: "",
6067
6204
  source,
6068
- hash: hash || this.fileCompiler.genHash(source),
6069
- output: null
6205
+ type: key,
6206
+ fileId: "",
6207
+ file: absPath,
6208
+ output: void 0,
6209
+ hash: hash || this.fileCompiler.genHash(source)
6070
6210
  };
6071
6211
  const processed = await this.compilationUnitProcessor.resolve(initUnit, key);
6072
6212
  if (processed?.output) {
6073
6213
  await this.compilationUnitProcessor.saveCompiledFiles(processed, key);
6074
- if (processed.hasRoute) {
6075
- await this.injectVuReactRouteDep();
6214
+ if (key === "sfc" /* SFC */ || key === "script" /* SCRIPT */) {
6215
+ if (processed?.hasRoute) {
6216
+ await this.injectVuReactRouteDep();
6217
+ }
6076
6218
  }
6077
6219
  await this.cacheManager.updateCacheIncrementally(processed, key);
6078
6220
  }
@@ -6109,15 +6251,30 @@ var PipelineManager = class {
6109
6251
  async runScriptPipeline() {
6110
6252
  return this.runCorePipeline("script" /* SCRIPT */);
6111
6253
  }
6254
+ /**
6255
+ * 运行 Style 编译管线
6256
+ */
6257
+ async runStylePipeline() {
6258
+ return this.runCorePipeline("style" /* STYLE */);
6259
+ }
6112
6260
  /**
6113
6261
  * 核心编译管线
6114
6262
  */
6115
6263
  async runCorePipeline(key) {
6116
6264
  const inputPath = this.fileCompiler.getInputPath();
6265
+ const scriptExtRegex = /\.(js|ts)$/i;
6266
+ const styleExtRegex = /\.(css|less|sass|scss)$/i;
6117
6267
  const files = this.fileCompiler.scanFiles(inputPath, (p) => {
6118
6268
  const ext = path6.extname(p);
6119
- if (key === "sfc" /* SFC */) return ext === ".vue";
6120
- if (key === "script" /* SCRIPT */) return ext === ".js" || ext === ".ts";
6269
+ if (key === "sfc" /* SFC */) {
6270
+ return ext === ".vue";
6271
+ }
6272
+ if (key === "script" /* SCRIPT */) {
6273
+ return scriptExtRegex.test(ext);
6274
+ }
6275
+ if (key === "style" /* STYLE */) {
6276
+ return styleExtRegex.test(ext);
6277
+ }
6121
6278
  return false;
6122
6279
  });
6123
6280
  if (!files.length) return 0;
@@ -6327,13 +6484,16 @@ var FileCompiler = class extends BaseCompiler {
6327
6484
  this.spinner.start("Compiling script files...");
6328
6485
  const scriptCount = await this.pipelineManager.runScriptPipeline();
6329
6486
  this.spinner.stop();
6487
+ this.spinner.start("Compiling style files...");
6488
+ const styleCount = await this.pipelineManager.runStylePipeline();
6489
+ this.spinner.stop();
6330
6490
  this.spinner.start("Copying assets...");
6331
6491
  const assetCount = await this.assetManager.runAssetPipeline();
6332
6492
  this.spinner.stop();
6333
6493
  await this.options.onSuccess?.();
6334
6494
  const endTime = calcElapsedTime(startTime);
6335
6495
  this.printCoreLogs();
6336
- this.showCompileStats(endTime, sfcCount, scriptCount, assetCount);
6496
+ this.showCompileStats(endTime, sfcCount, scriptCount, styleCount, assetCount);
6337
6497
  } catch (error) {
6338
6498
  this.spinner.stop();
6339
6499
  const endTime = calcElapsedTime(startTime);
@@ -6371,6 +6531,21 @@ var FileCompiler = class extends BaseCompiler {
6371
6531
  async processScript(filePath, existingCache) {
6372
6532
  return this.fileProcessor.processScript(filePath, existingCache);
6373
6533
  }
6534
+ /**
6535
+ * 处理单个 CSS/LESS/SCSS 样式文件
6536
+ *
6537
+ * 此方法主要用于 CLI 的 watch 模式,当检测到文件变更时调用。
6538
+ * 支持增量编译,如果文件未变更则跳过编译。
6539
+ *
6540
+ * @async
6541
+ * @param filePath - style 文件的绝对路径
6542
+ * @param existingCache - 可选的预加载缓存对象,用于增量编译
6543
+ * @returns {Promise<ScriptUnit | undefined>} 编译单元对象,如果跳过编译则返回 undefined
6544
+ * @see {@link FileProcessor.processStyle}
6545
+ */
6546
+ async processStyle(filePath, existingCache) {
6547
+ return this.fileProcessor.processStyle(filePath, existingCache);
6548
+ }
6374
6549
  /**
6375
6550
  * 处理单个文件(Vue 或 Script)
6376
6551
  *
@@ -6460,7 +6635,7 @@ var FileCompiler = class extends BaseCompiler {
6460
6635
  * @param scriptCount - 处理的脚本文件数量
6461
6636
  * @param assetCount - 处理的资源文件数量
6462
6637
  */
6463
- showCompileStats(endTime, sfcCount, scriptCount, assetCount) {
6638
+ showCompileStats(endTime, sfcCount, scriptCount, styleCount, assetCount) {
6464
6639
  const dir = normalizePath(this.relativePath(this.getOuputPath()));
6465
6640
  this.spinner.succeed(`Build completed in ${endTime}`);
6466
6641
  console.info(` Output directory: ${kleur6.dim(dir)}`);
@@ -6471,10 +6646,11 @@ var FileCompiler = class extends BaseCompiler {
6471
6646
  console.info(kleur6.dim(`Skipped ${this.skippedCount} unchanged file(s)`));
6472
6647
  this.resetSkippedCount();
6473
6648
  }
6474
- if (sfcCount || scriptCount || assetCount) {
6649
+ if (sfcCount || scriptCount || styleCount || assetCount) {
6475
6650
  const stats = [];
6476
6651
  if (sfcCount) stats.push(`${sfcCount} SFC(s)`);
6477
6652
  if (scriptCount) stats.push(`${scriptCount} script(s)`);
6653
+ if (styleCount) stats.push(`${styleCount} style(s)`);
6478
6654
  if (assetCount) stats.push(`${assetCount} asset(s)`);
6479
6655
  console.info(kleur6.gray(`Processed ${stats.join(", ")}`));
6480
6656
  }