@vureact/compiler-core 1.3.0 → 1.5.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.3.0
2
+ * @vureact/compiler-core v1.5.0
3
3
  * (c) 2025-present Ruihong Zhong (Ryan John)
4
4
  * @license MIT
5
5
  */
@@ -32,6 +32,16 @@ var PACKAGE_NAME = {
32
32
  runtime: "@vureact/runtime-core",
33
33
  router: "@vureact/router"
34
34
  };
35
+ var RUNTIME_PACKAGES = {
36
+ router: {
37
+ name: PACKAGE_NAME.router,
38
+ version: "^2.0.1"
39
+ },
40
+ runtime: {
41
+ name: PACKAGE_NAME.runtime,
42
+ version: "^1.0.1"
43
+ }
44
+ };
35
45
  var STYLE_MODULE_NAME = "$style";
36
46
  var MACRO_API_NAMES = {
37
47
  props: "defineProps",
@@ -418,8 +428,8 @@ function getBabelParseOptions(lang = "js", context = "script", filename) {
418
428
  function stringToExpr(input, lang, filename = "") {
419
429
  return parseExpression(input, getBabelParseOptions(lang, "expression", filename));
420
430
  }
421
- function atComponentOrHookRoot(path8, rootScope, inScriptFile = false) {
422
- const { parentPath, scope } = path8;
431
+ function atComponentOrHookRoot(path9, rootScope, inScriptFile = false) {
432
+ const { parentPath, scope } = path9;
423
433
  const parentBlock = scope.block;
424
434
  if (!parentPath) return !inScriptFile;
425
435
  if (parentBlock === rootScope) {
@@ -1650,9 +1660,9 @@ function resolveScriptMeta(result, ctx) {
1650
1660
  const scriptAST = result.script?.ast;
1651
1661
  if (!scriptAST) return;
1652
1662
  traverse(scriptAST, {
1653
- VariableDeclarator(path8) {
1654
- const { node } = path8;
1655
- if (!atComponentOrHookRoot(path8, scriptAST.program) || !t16.isIdentifier(node.id)) {
1663
+ VariableDeclarator(path9) {
1664
+ const { node } = path9;
1665
+ if (!atComponentOrHookRoot(path9, scriptAST.program) || !t16.isIdentifier(node.id)) {
1656
1666
  return;
1657
1667
  }
1658
1668
  if (node.init && t16.isCallExpression(node.init) && t16.isIdentifier(node.init.callee)) {
@@ -1817,12 +1827,6 @@ function resolveStyles(descriptor, ctx, result) {
1817
1827
  { file: filename }
1818
1828
  );
1819
1829
  }
1820
- if (content.includes("@import")) {
1821
- logger.warn(
1822
- "Detected @import in scoped style. Imported styles remain global. Consider inlining them to preserve scoping.",
1823
- { file: filename }
1824
- );
1825
- }
1826
1830
  const { code, fileExt } = resolveLessSass(content, {
1827
1831
  lang,
1828
1832
  filename,
@@ -1949,7 +1953,7 @@ function insertCSSImport(ctx) {
1949
1953
  scriptIR.imports.push(importDecl);
1950
1954
  }
1951
1955
 
1952
- // src/core/transform/sfc/script/syntax-processor/postprocess/insert-required-imports.ts
1956
+ // src/core/transform/sfc/script/syntax-processor/postprocess/resolve-required-imports/index.ts
1953
1957
  import * as t18 from "@babel/types";
1954
1958
 
1955
1959
  // src/core/transform/shared.ts
@@ -1981,79 +1985,161 @@ function replaceVueSuffix(ctx, node) {
1981
1985
  node.extra = { rawValue: jsxFile, raw: jsxFile };
1982
1986
  }
1983
1987
 
1984
- // src/core/transform/sfc/script/syntax-processor/postprocess/insert-required-imports.ts
1985
- function insertRequiredImports(ctx) {
1988
+ // src/core/transform/sfc/script/syntax-processor/postprocess/resolve-required-imports/import-strategies.ts
1989
+ var VueRouterStrategy = class {
1990
+ matches(moduleName) {
1991
+ return moduleName === "vue-router" || moduleName.startsWith("vue-router/");
1992
+ }
1993
+ process() {
1994
+ return {
1995
+ shouldReplaceSource: true,
1996
+ newSource: PACKAGE_NAME.router,
1997
+ shouldRemove: false,
1998
+ shouldInjectRuntimeImports: false
1999
+ };
2000
+ }
2001
+ };
2002
+ var VueEcosystemStrategy = class {
2003
+ matches(moduleName) {
2004
+ if (moduleName.startsWith(".") || moduleName.startsWith("/") || moduleName.startsWith("file:")) {
2005
+ return false;
2006
+ }
2007
+ if (moduleName === "vue-router" || moduleName.startsWith("vue-router/")) {
2008
+ return false;
2009
+ }
2010
+ if (moduleName.startsWith("@vue/")) {
2011
+ return true;
2012
+ }
2013
+ for (const pkg of VUE_PACKAGES) {
2014
+ if (moduleName === pkg || moduleName.startsWith(`${pkg}/`)) {
2015
+ return true;
2016
+ }
2017
+ }
2018
+ return false;
2019
+ }
2020
+ process() {
2021
+ return {
2022
+ shouldReplaceSource: false,
2023
+ shouldRemove: true,
2024
+ shouldInjectRuntimeImports: true
2025
+ };
2026
+ }
2027
+ };
2028
+ var StyleFileStrategy = class {
2029
+ regExp = /\.(less|sass|scss)$/i;
2030
+ matches(moduleName) {
2031
+ return this.regExp.test(moduleName);
2032
+ }
2033
+ process(path9, ctx) {
2034
+ if (!ctx.preprocessStyles) {
2035
+ return {};
2036
+ }
2037
+ const importSource = path9.node.source.value;
2038
+ if (typeof importSource !== "string") {
2039
+ return {};
2040
+ }
2041
+ return {
2042
+ shouldReplaceSource: true,
2043
+ newSource: importSource.replace(this.regExp, ".css"),
2044
+ shouldRemove: false,
2045
+ shouldInjectRuntimeImports: false
2046
+ };
2047
+ }
2048
+ };
2049
+
2050
+ // src/core/transform/sfc/script/syntax-processor/postprocess/resolve-required-imports/import-strategy-manager.ts
2051
+ var ImportStrategyManager = class {
2052
+ strategies = [];
2053
+ constructor() {
2054
+ this.strategies.push(new VueRouterStrategy());
2055
+ this.strategies.push(new VueEcosystemStrategy());
2056
+ this.strategies.push(new StyleFileStrategy());
2057
+ }
2058
+ /** 添加自定义策略 */
2059
+ addStrategy(strategy) {
2060
+ this.strategies.push(strategy);
2061
+ }
2062
+ /** 查找匹配的策略 */
2063
+ findStrategy(moduleName) {
2064
+ for (const strategy of this.strategies) {
2065
+ if (strategy.matches(moduleName)) {
2066
+ return strategy;
2067
+ }
2068
+ }
2069
+ return null;
2070
+ }
2071
+ };
2072
+
2073
+ // src/core/transform/sfc/script/syntax-processor/postprocess/resolve-required-imports/index.ts
2074
+ function resolveRequiredImports(ctx) {
1986
2075
  const processedModules = /* @__PURE__ */ new Set();
1987
2076
  let hasProcessedImports = false;
1988
- recordImport(ctx, PACKAGE_NAME.react, REACT_API_MAP.memo);
1989
- function resolveRequiredImport(path8) {
1990
- const { node } = path8;
1991
- const moduleName = node.source.value.toLowerCase();
1992
- const isVueLike = isVueEcosystemPackage(moduleName);
1993
- mergeImports(node, ctx);
1994
- if (processedModules.has(moduleName) && !path8.removed) {
1995
- path8.remove();
2077
+ const strategyManager = new ImportStrategyManager();
2078
+ if (ctx.inputType === "sfc") {
2079
+ recordImport(ctx, PACKAGE_NAME.react, REACT_API_MAP.memo);
2080
+ }
2081
+ function resolveRequiredImport(path9) {
2082
+ const { node } = path9;
2083
+ const originalModuleName = node.source.value.toLowerCase();
2084
+ const strategy = strategyManager.findStrategy(originalModuleName);
2085
+ if (strategy) {
2086
+ const result = strategy.process(path9, ctx, originalModuleName);
2087
+ if (result.shouldReplaceSource && result.newSource) {
2088
+ node.source.value = result.newSource;
2089
+ }
2090
+ }
2091
+ const normalizedModuleName = node.source.value.toLowerCase();
2092
+ mergeImports(node, ctx, normalizedModuleName);
2093
+ if (processedModules.has(normalizedModuleName) && !path9.removed) {
2094
+ path9.remove();
1996
2095
  return;
1997
2096
  }
1998
- processedModules.add(moduleName);
2097
+ processedModules.add(normalizedModuleName);
1999
2098
  if (!hasProcessedImports) {
2000
2099
  const required = createRequiredImports(ctx);
2001
- if (isVueLike) {
2002
- path8.replaceWithMultiple(required);
2003
- } else if (moduleName === PACKAGE_NAME.react) {
2004
- path8.insertAfter(required);
2100
+ if (strategy) {
2101
+ const result = strategy.process(path9, ctx, originalModuleName);
2102
+ if (result.shouldRemove) {
2103
+ path9.replaceWithMultiple(required);
2104
+ } else if (normalizedModuleName === PACKAGE_NAME.react) {
2105
+ path9.insertAfter(required);
2106
+ } else {
2107
+ path9.insertBefore(required);
2108
+ }
2005
2109
  } else {
2006
- path8.insertBefore(required);
2110
+ if (normalizedModuleName === PACKAGE_NAME.react) {
2111
+ path9.insertAfter(required);
2112
+ } else {
2113
+ path9.insertBefore(required);
2114
+ }
2007
2115
  }
2008
2116
  hasProcessedImports = true;
2009
2117
  }
2010
- if (isVueLike && !path8.removed) {
2011
- path8.remove();
2012
- return;
2118
+ if (strategy) {
2119
+ const result = strategy.process(path9, ctx, originalModuleName);
2120
+ if (result.shouldRemove && !path9.removed) {
2121
+ path9.remove();
2122
+ return;
2123
+ }
2013
2124
  }
2014
2125
  replaceVueSuffix(ctx, node.source);
2015
2126
  }
2016
- function resolveStyleFileExt(path8) {
2017
- if (!ctx.preprocessStyles) return;
2018
- const { node } = path8;
2019
- if (!node || !node.source || !node.source.value) return;
2020
- const importSource = node.source.value;
2021
- if (typeof importSource !== "string") return;
2022
- const styleExtRegex = /\.(less|sass|scss)$/i;
2023
- if (!styleExtRegex.test(importSource)) return;
2024
- const newSource = importSource.replace(styleExtRegex, ".css");
2025
- node.source.value = newSource;
2026
- }
2027
2127
  return {
2028
- // 增加 Program.exit 兜底注入 required imports(处理无 ImportDeclaration 的 SFC)
2128
+ // 兜底:无 ImportDeclaration 的文件也要能注入必需依赖。
2029
2129
  Program: {
2030
- exit(path8) {
2130
+ exit(path9) {
2031
2131
  if (hasProcessedImports) return;
2032
2132
  const required = createRequiredImports(ctx);
2033
- path8.unshiftContainer("body", required);
2133
+ path9.unshiftContainer("body", required);
2034
2134
  hasProcessedImports = true;
2035
2135
  }
2036
2136
  },
2037
- ImportDeclaration(path8) {
2038
- resolveRequiredImport(path8);
2039
- resolveStyleFileExt(path8);
2137
+ ImportDeclaration(path9) {
2138
+ resolveRequiredImport(path9);
2040
2139
  }
2041
2140
  };
2042
2141
  }
2043
- function isVueEcosystemPackage(moduleName) {
2044
- if (moduleName.startsWith(".") || moduleName.startsWith("/") || moduleName.startsWith("file:")) {
2045
- return false;
2046
- }
2047
- if (moduleName.startsWith("@vue/")) {
2048
- return true;
2049
- }
2050
- if (moduleName === "vue-router" || moduleName.startsWith("vue-router/")) {
2051
- return true;
2052
- }
2053
- return VUE_PACKAGES.some((name) => moduleName === name || moduleName.startsWith(`${name}/`));
2054
- }
2055
- function mergeImports(currentNode, ctx) {
2056
- const moduleName = currentNode.source.value.toLowerCase();
2142
+ function mergeImports(currentNode, ctx, moduleName) {
2057
2143
  const ctxImportItems = ctx.imports.get(moduleName);
2058
2144
  if (!ctxImportItems?.length) {
2059
2145
  return;
@@ -2068,7 +2154,9 @@ function mergeImports(currentNode, ctx) {
2068
2154
  }
2069
2155
  }
2070
2156
  for (const item of ctxImportItems) {
2071
- if (currentImports.has(item.name)) return;
2157
+ if (currentImports.has(item.name)) {
2158
+ continue;
2159
+ }
2072
2160
  const local = t18.identifier(item.name);
2073
2161
  const newNode = !item.onDemand ? t18.importDefaultSpecifier(local) : t18.importSpecifier(local, local);
2074
2162
  currentNode.specifiers.push(newNode);
@@ -2107,10 +2195,10 @@ import * as t20 from "@babel/types";
2107
2195
 
2108
2196
  // src/core/transform/sfc/script/shared/babel-utils.ts
2109
2197
  import * as t19 from "@babel/types";
2110
- function findRootVariablePath(path8) {
2111
- const rootId = findRootIdentifier(path8.node);
2198
+ function findRootVariablePath(path9) {
2199
+ const rootId = findRootIdentifier(path9.node);
2112
2200
  if (!rootId?.name) return null;
2113
- const binding = path8.scope.getBinding(rootId.name);
2201
+ const binding = path9.scope.getBinding(rootId.name);
2114
2202
  if (!binding) return null;
2115
2203
  const rootPath = getVariableDeclaratorPath(binding.path);
2116
2204
  return rootPath;
@@ -2122,14 +2210,14 @@ function findRootIdentifier(node) {
2122
2210
  }
2123
2211
  return t19.isIdentifier(current) ? current : null;
2124
2212
  }
2125
- function getVariableDeclaratorPath(path8) {
2126
- if (path8.isVariableDeclarator()) {
2127
- return path8;
2213
+ function getVariableDeclaratorPath(path9) {
2214
+ if (path9.isVariableDeclarator()) {
2215
+ return path9;
2128
2216
  }
2129
- return path8.findParent((p) => p.isVariableDeclarator());
2217
+ return path9.findParent((p) => p.isVariableDeclarator());
2130
2218
  }
2131
- function isVariableDeclTopLevel(path8) {
2132
- const variableDeclaratorPath = path8;
2219
+ function isVariableDeclTopLevel(path9) {
2220
+ const variableDeclaratorPath = path9;
2133
2221
  const variableDeclarationPath = variableDeclaratorPath.parentPath;
2134
2222
  if (!variableDeclarationPath) {
2135
2223
  return false;
@@ -2143,61 +2231,61 @@ function isVariableDeclTopLevel(path8) {
2143
2231
  }
2144
2232
  return false;
2145
2233
  }
2146
- function isRealVariableAccess(path8) {
2147
- return isIdentifierAccess(path8) && !isPropertyName(path8);
2234
+ function isRealVariableAccess(path9) {
2235
+ return isIdentifierAccess(path9) && !isPropertyName(path9);
2148
2236
  }
2149
- function isIdentifierAccess(path8) {
2150
- if (isIdentifierDeclaration(path8)) {
2237
+ function isIdentifierAccess(path9) {
2238
+ if (isIdentifierDeclaration(path9)) {
2151
2239
  return false;
2152
2240
  }
2153
- const binding = path8.scope.getBinding(path8.node.name);
2241
+ const binding = path9.scope.getBinding(path9.node.name);
2154
2242
  if (!binding) {
2155
2243
  return true;
2156
2244
  }
2157
- return binding.identifier !== path8.node;
2245
+ return binding.identifier !== path9.node;
2158
2246
  }
2159
- function isIdentifierDeclaration(path8) {
2160
- const parent = path8.parentPath;
2247
+ function isIdentifierDeclaration(path9) {
2248
+ const parent = path9.parentPath;
2161
2249
  if (!parent) return false;
2162
- if (parent.isVariableDeclarator() && parent.node.id === path8.node) {
2250
+ if (parent.isVariableDeclarator() && parent.node.id === path9.node) {
2163
2251
  return true;
2164
2252
  }
2165
- if (parent.isFunctionDeclaration() && parent.node.id === path8.node) {
2253
+ if (parent.isFunctionDeclaration() && parent.node.id === path9.node) {
2166
2254
  return true;
2167
2255
  }
2168
- if (parent.isFunctionExpression() && parent.node.id === path8.node) {
2256
+ if (parent.isFunctionExpression() && parent.node.id === path9.node) {
2169
2257
  return true;
2170
2258
  }
2171
- if (parent.isClassDeclaration() && parent.node.id === path8.node) {
2259
+ if (parent.isClassDeclaration() && parent.node.id === path9.node) {
2172
2260
  return true;
2173
2261
  }
2174
- if (parent.isImportSpecifier() && parent.node.local === path8.node) {
2262
+ if (parent.isImportSpecifier() && parent.node.local === path9.node) {
2175
2263
  return true;
2176
2264
  }
2177
- if (parent.isImportDefaultSpecifier() && parent.node.local === path8.node) {
2265
+ if (parent.isImportDefaultSpecifier() && parent.node.local === path9.node) {
2178
2266
  return true;
2179
2267
  }
2180
- if (parent.isImportNamespaceSpecifier() && parent.node.local === path8.node) {
2268
+ if (parent.isImportNamespaceSpecifier() && parent.node.local === path9.node) {
2181
2269
  return true;
2182
2270
  }
2183
- if (parent.isFunction() && parent.node.params.includes(path8.node)) {
2271
+ if (parent.isFunction() && parent.node.params.includes(path9.node)) {
2184
2272
  return true;
2185
2273
  }
2186
- if (parent.isCatchClause() && parent.node.param === path8.node) {
2274
+ if (parent.isCatchClause() && parent.node.param === path9.node) {
2187
2275
  return true;
2188
2276
  }
2189
2277
  return false;
2190
2278
  }
2191
- function isPropertyName(path8) {
2192
- const parent = path8.parentPath;
2279
+ function isPropertyName(path9) {
2280
+ const parent = path9.parentPath;
2193
2281
  if (!parent) return false;
2194
- if (parent.isObjectProperty() && parent.node.key === path8.node) {
2282
+ if (parent.isObjectProperty() && parent.node.key === path9.node) {
2195
2283
  return true;
2196
2284
  }
2197
- if (parent.isClassProperty() && parent.node.key === path8.node) {
2285
+ if (parent.isClassProperty() && parent.node.key === path9.node) {
2198
2286
  return true;
2199
2287
  }
2200
- if (parent.isMemberExpression() && parent.node.property === path8.node) {
2288
+ if (parent.isMemberExpression() && parent.node.property === path9.node) {
2201
2289
  return true;
2202
2290
  }
2203
2291
  return false;
@@ -2308,26 +2396,26 @@ function resolveStaticHoisting(ctx) {
2308
2396
  return {};
2309
2397
  }
2310
2398
  return {
2311
- "ImportDeclaration|ExportDeclaration"(path8) {
2312
- if (t20.isImportDeclaration(path8.node)) {
2313
- scriptIR.imports.push(path8.node);
2314
- } else if (t20.isExportDeclaration(path8.node)) {
2315
- scriptIR.exports.push(path8.node);
2399
+ "ImportDeclaration|ExportDeclaration"(path9) {
2400
+ if (t20.isImportDeclaration(path9.node)) {
2401
+ scriptIR.imports.push(path9.node);
2402
+ } else if (t20.isExportDeclaration(path9.node)) {
2403
+ scriptIR.exports.push(path9.node);
2316
2404
  }
2317
- path8.remove();
2405
+ path9.remove();
2318
2406
  },
2319
- "TSInterfaceDeclaration|TSTypeAliasDeclaration|TSEnumDeclaration|TSModuleDeclaration|TSModuleDeclaration"(path8) {
2320
- if (t20.isProgram(path8.parent)) {
2321
- scriptIR.tsTypes.push(path8.node);
2322
- path8.remove();
2407
+ "TSInterfaceDeclaration|TSTypeAliasDeclaration|TSEnumDeclaration|TSModuleDeclaration|TSModuleDeclaration"(path9) {
2408
+ if (t20.isProgram(path9.parent)) {
2409
+ scriptIR.tsTypes.push(path9.node);
2410
+ path9.remove();
2323
2411
  }
2324
2412
  },
2325
- VariableDeclarator(path8) {
2326
- const { node } = path8;
2327
- if (!isVariableDeclTopLevel(path8) || !isSimpleLiteral(node.init) || getScriptNodeMeta(node)) {
2413
+ VariableDeclarator(path9) {
2414
+ const { node } = path9;
2415
+ if (!isVariableDeclTopLevel(path9) || !isSimpleLiteral(node.init) || getScriptNodeMeta(node)) {
2328
2416
  return;
2329
2417
  }
2330
- const declarationPath = path8.findParent((p) => p.isVariableDeclaration());
2418
+ const declarationPath = path9.findParent((p) => p.isVariableDeclaration());
2331
2419
  if (!declarationPath) return;
2332
2420
  scriptIR.statement.global.push(declarationPath.node);
2333
2421
  declarationPath.remove();
@@ -2344,14 +2432,14 @@ function collectLocalStatements(ctx, ast) {
2344
2432
  import * as t21 from "@babel/types";
2345
2433
  function resolveDefineAsyncComponent(ctx) {
2346
2434
  return {
2347
- CallExpression(path8) {
2348
- const { node } = path8;
2435
+ CallExpression(path9) {
2436
+ const { node } = path9;
2349
2437
  if (!isCalleeNamed(node, VUE_API_MAP.defineAsyncComponent)) {
2350
2438
  return;
2351
2439
  }
2352
2440
  const [arg] = node.arguments;
2353
2441
  checkIsUnsupported(ctx, arg);
2354
- pushToGlobalScope(path8, ctx);
2442
+ pushToGlobalScope(path9, ctx);
2355
2443
  recordImport(ctx, PACKAGE_NAME.react, REACT_API_MAP.lazy);
2356
2444
  }
2357
2445
  };
@@ -2414,15 +2502,15 @@ function warnMultipleOptionsUsed(ctx, node) {
2414
2502
  }
2415
2503
  );
2416
2504
  }
2417
- function pushToGlobalScope(path8, ctx) {
2418
- const { node } = path8;
2505
+ function pushToGlobalScope(path9, ctx) {
2506
+ const { node } = path9;
2419
2507
  const callee = node.callee;
2420
2508
  callee.name = REACT_API_MAP.lazy;
2421
2509
  callee.loc.identifierName = REACT_API_MAP.lazy;
2422
2510
  if (node.typeParameters) {
2423
2511
  node.typeParameters = void 0;
2424
2512
  }
2425
- let declarationPath = path8.parentPath;
2513
+ let declarationPath = path9.parentPath;
2426
2514
  while (declarationPath) {
2427
2515
  if (declarationPath.isVariableDeclaration()) {
2428
2516
  break;
@@ -2433,12 +2521,12 @@ function pushToGlobalScope(path8, ctx) {
2433
2521
  if (declarationPath?.isVariableDeclaration()) {
2434
2522
  fullNode = declarationPath.node;
2435
2523
  declarationPath.remove();
2436
- } else if (path8.parentPath.isVariableDeclarator()) {
2437
- fullNode = path8.parent;
2438
- path8.parentPath.remove();
2524
+ } else if (path9.parentPath.isVariableDeclarator()) {
2525
+ fullNode = path9.parent;
2526
+ path9.parentPath.remove();
2439
2527
  } else {
2440
- fullNode = path8.node;
2441
- path8.remove();
2528
+ fullNode = path9.node;
2529
+ path9.remove();
2442
2530
  }
2443
2531
  const scriptIR = getScriptIR(ctx);
2444
2532
  scriptIR.statement.global.push(fullNode);
@@ -2469,15 +2557,15 @@ function createUseImperativeHandle(refId, init) {
2469
2557
  function resolveDefineExpose(ctx) {
2470
2558
  if (ctx.inputType !== "sfc") return {};
2471
2559
  return {
2472
- CallExpression(path8) {
2473
- const { node } = path8;
2560
+ CallExpression(path9) {
2561
+ const { node } = path9;
2474
2562
  const { filename, scriptData } = ctx;
2475
2563
  if (!isCalleeNamed(node, MACRO_API_NAMES.expose)) {
2476
2564
  return;
2477
2565
  }
2478
2566
  const [expose] = node.arguments;
2479
2567
  if (!expose) {
2480
- path8.remove();
2568
+ path9.remove();
2481
2569
  return;
2482
2570
  }
2483
2571
  const adapter = ADAPTER_RULES.react[MACRO_API_NAMES.expose];
@@ -2494,7 +2582,7 @@ function resolveDefineExpose(ctx) {
2494
2582
  const { forwardRef } = scriptData;
2495
2583
  const newNode = createUseImperativeHandle(t23.identifier(forwardRef.refField), init);
2496
2584
  forwardRef.enabled = true;
2497
- path8.replaceWith(newNode);
2585
+ path9.replaceWith(newNode);
2498
2586
  }
2499
2587
  };
2500
2588
  }
@@ -2503,8 +2591,8 @@ function resolveDefineExpose(ctx) {
2503
2591
  import * as t24 from "@babel/types";
2504
2592
  function resolveDefineOptions(ctx) {
2505
2593
  return {
2506
- CallExpression(path8) {
2507
- const { node } = path8;
2594
+ CallExpression(path9) {
2595
+ const { node } = path9;
2508
2596
  if (!isCalleeNamed(node, MACRO_API_NAMES.options)) {
2509
2597
  return;
2510
2598
  }
@@ -2516,7 +2604,7 @@ function resolveDefineOptions(ctx) {
2516
2604
  file: filename,
2517
2605
  loc: node?.loc
2518
2606
  });
2519
- path8.remove();
2607
+ path9.remove();
2520
2608
  return;
2521
2609
  }
2522
2610
  if (!t24.isObjectExpression(options)) {
@@ -2533,7 +2621,7 @@ function resolveDefineOptions(ctx) {
2533
2621
  extractName(prop, ctx);
2534
2622
  }
2535
2623
  }
2536
- path8.remove();
2624
+ path9.remove();
2537
2625
  }
2538
2626
  };
2539
2627
  }
@@ -2565,8 +2653,8 @@ function resolveEmitCalls(ctx) {
2565
2653
  return `on${capitalize(camelCase(normalized))}`;
2566
2654
  };
2567
2655
  return {
2568
- CallExpression(path8) {
2569
- const { node } = path8;
2656
+ CallExpression(path9) {
2657
+ const { node } = path9;
2570
2658
  const { filename, templateData, scriptData } = ctx;
2571
2659
  if (!t25.isIdentifier(node.callee)) return;
2572
2660
  const { name } = node.callee;
@@ -2577,7 +2665,7 @@ function resolveEmitCalls(ctx) {
2577
2665
  result = meta.source === MACRO_API_NAMES.emits;
2578
2666
  }
2579
2667
  if (!result) {
2580
- const binding = path8.scope.getBinding(name);
2668
+ const binding = path9.scope.getBinding(name);
2581
2669
  if (binding) {
2582
2670
  const parent = binding.path.node;
2583
2671
  if (t25.isVariableDeclarator(parent) && t25.isCallExpression(parent.init) && t25.isIdentifier(parent.init.callee)) {
@@ -2596,7 +2684,7 @@ function resolveEmitCalls(ctx) {
2596
2684
  source: scriptData.source,
2597
2685
  loc: callee?.loc
2598
2686
  });
2599
- path8.remove();
2687
+ path9.remove();
2600
2688
  return;
2601
2689
  }
2602
2690
  const propCall = t25.optionalCallExpression(
@@ -2609,7 +2697,7 @@ function resolveEmitCalls(ctx) {
2609
2697
  args,
2610
2698
  true
2611
2699
  );
2612
- path8.replaceWith(propCall);
2700
+ path9.replaceWith(propCall);
2613
2701
  }
2614
2702
  };
2615
2703
  }
@@ -2647,9 +2735,9 @@ function cloneCallableParams(params) {
2647
2735
  // src/core/transform/sfc/script/syntax-processor/preprocess/resolve-props-interface/resolve-emits.ts
2648
2736
  function resolveEmitsTopLevelTypes(ctx) {
2649
2737
  return {
2650
- "TSInterfaceDeclaration|TSTypeAliasDeclaration"(path8) {
2651
- if (!t27.isProgram(path8.parent)) return;
2652
- const { node } = path8;
2738
+ "TSInterfaceDeclaration|TSTypeAliasDeclaration"(path9) {
2739
+ if (!t27.isProgram(path9.parent)) return;
2740
+ const { node } = path9;
2653
2741
  if (t27.isTSInterfaceDeclaration(node)) {
2654
2742
  const typeLiteral = t27.tsTypeLiteral(node.body.body);
2655
2743
  if (!hasEmitsSignatureInType(typeLiteral)) return;
@@ -2714,8 +2802,8 @@ function resolveTopLevelEmitType(tsType) {
2714
2802
  }
2715
2803
  return tsType;
2716
2804
  }
2717
- function resolveDefineEmitsIface(path8, ctx) {
2718
- const { node } = path8;
2805
+ function resolveDefineEmitsIface(path9, ctx) {
2806
+ const { node } = path9;
2719
2807
  const [runtimeArg] = node.arguments;
2720
2808
  const tsParams = node.typeParameters?.params;
2721
2809
  if (tsParams?.length) {
@@ -3015,8 +3103,8 @@ function resolveTupleElementParam(element, index) {
3015
3103
 
3016
3104
  // src/core/transform/sfc/script/syntax-processor/preprocess/resolve-props-interface/resolve-props.ts
3017
3105
  import * as t28 from "@babel/types";
3018
- function resolveDefinePropsIface(path8, ctx) {
3019
- const { node } = path8;
3106
+ function resolveDefinePropsIface(path9, ctx) {
3107
+ const { node } = path9;
3020
3108
  const [runtimeArg] = node.arguments;
3021
3109
  const tsParams = node.typeParameters?.params;
3022
3110
  if (tsParams?.length) {
@@ -3161,10 +3249,13 @@ var SLOT_DEFAULT_NAME = "default";
3161
3249
  var SLOT_CHILDREN_NAME = "children";
3162
3250
  var SLOT_FN_PARAM_NAME = "props";
3163
3251
  function resolveSlotsTopLevelTypes(ctx) {
3252
+ if (ctx.inputType !== "sfc") {
3253
+ return {};
3254
+ }
3164
3255
  return {
3165
- "TSInterfaceDeclaration|TSTypeAliasDeclaration"(path8) {
3166
- if (!t29.isProgram(path8.parent)) return;
3167
- const { node } = path8;
3256
+ "TSInterfaceDeclaration|TSTypeAliasDeclaration"(path9) {
3257
+ if (!t29.isProgram(path9.parent)) return;
3258
+ const { node } = path9;
3168
3259
  if (t29.isTSInterfaceDeclaration(node)) {
3169
3260
  const typeLiteral = t29.tsTypeLiteral(node.body.body);
3170
3261
  if (!hasSlotsSignatureInType(typeLiteral)) return;
@@ -3186,8 +3277,8 @@ function resolveSlotsTopLevelTypes(ctx) {
3186
3277
  }
3187
3278
  };
3188
3279
  }
3189
- function resolveDefineSlotsIface(path8, ctx) {
3190
- const { node } = path8;
3280
+ function resolveDefineSlotsIface(path9, ctx) {
3281
+ const { node } = path9;
3191
3282
  const tsParams = node.typeParameters?.params;
3192
3283
  if (!tsParams?.length) return;
3193
3284
  const {
@@ -3378,8 +3469,8 @@ function resolvePropName2(key) {
3378
3469
  function resolvePropsIface(ctx) {
3379
3470
  const isTS = ctx.scriptData.lang.startsWith("ts");
3380
3471
  return {
3381
- CallExpression(path8) {
3382
- const { node, parentPath } = path8;
3472
+ CallExpression(path9) {
3473
+ const { node, parentPath } = path9;
3383
3474
  const name = node.callee.name;
3384
3475
  if (!isCalleeNamed(node, MACRO_API_NAMES.props) && !isCalleeNamed(node, MACRO_API_NAMES.emits) && !isCalleeNamed(node, MACRO_API_NAMES.slots)) {
3385
3476
  return;
@@ -3388,7 +3479,7 @@ function resolvePropsIface(ctx) {
3388
3479
  if (parentPath.isVariableDeclaration() || parentPath.isVariableDeclarator()) {
3389
3480
  parentPath.remove();
3390
3481
  } else {
3391
- path8.remove();
3482
+ path9.remove();
3392
3483
  }
3393
3484
  };
3394
3485
  if (ctx.inputType !== "sfc") {
@@ -3402,11 +3493,11 @@ function resolvePropsIface(ctx) {
3402
3493
  propsTSIface.hasPropsInJsEnv = true;
3403
3494
  } else {
3404
3495
  if (name === MACRO_API_NAMES.props) {
3405
- resolveDefinePropsIface(path8, ctx);
3496
+ resolveDefinePropsIface(path9, ctx);
3406
3497
  } else if (name === MACRO_API_NAMES.emits) {
3407
- resolveDefineEmitsIface(path8, ctx);
3498
+ resolveDefineEmitsIface(path9, ctx);
3408
3499
  } else if (name === MACRO_API_NAMES.slots) {
3409
- resolveDefineSlotsIface(path8, ctx);
3500
+ resolveDefineSlotsIface(path9, ctx);
3410
3501
  }
3411
3502
  }
3412
3503
  removePath();
@@ -3451,15 +3542,27 @@ function analyzeDeps(node, ctx, parentPath) {
3451
3542
  function addDependency(exp) {
3452
3543
  deps.set(getDependencyKey(exp), exp);
3453
3544
  }
3545
+ const analyzeTargetPath = parentPath && parentPath.node === analyzeTarget ? parentPath : null;
3546
+ if (analyzeTargetPath) {
3547
+ if (t31.isMemberExpression(analyzeTarget) || t31.isOptionalMemberExpression(analyzeTarget)) {
3548
+ const rootId = findRootIdentifier(analyzeTarget);
3549
+ if (rootId) {
3550
+ tryAddDependency(analyzeTargetPath, rootId.name, analyzeTargetPath.scope);
3551
+ processedIdentifiers.add(rootId);
3552
+ }
3553
+ } else if (t31.isIdentifier(analyzeTarget)) {
3554
+ tryAddDependency(analyzeTargetPath, analyzeTarget.name, analyzeTargetPath.scope);
3555
+ }
3556
+ }
3454
3557
  traverse2(
3455
3558
  analyzeTarget,
3456
3559
  {
3457
3560
  "MemberExpression|OptionalMemberExpression"(memberPath) {
3458
- const path8 = memberPath;
3459
- if (isNestedMemberObject(path8)) return;
3460
- const rootId = findRootIdentifier(path8.node);
3561
+ const path9 = memberPath;
3562
+ if (isNestedMemberObject(path9)) return;
3563
+ const rootId = findRootIdentifier(path9.node);
3461
3564
  if (!rootId) return;
3462
- tryAddDependency(path8, rootId.name, path8.scope);
3565
+ tryAddDependency(path9, rootId.name, path9.scope);
3463
3566
  processedIdentifiers.add(rootId);
3464
3567
  },
3465
3568
  Identifier(idPath) {
@@ -3491,16 +3594,19 @@ function analyzeDeps(node, ctx, parentPath) {
3491
3594
  }
3492
3595
  const sourcedExpression = traceBindingSource(binding, /* @__PURE__ */ new Set(), TRACE_MAX_DEPTH);
3493
3596
  if (sourcedExpression) {
3494
- addDependency(sourcedExpression);
3597
+ const normalizedSource = normalizeSourcedDependency(sourcedExpression);
3598
+ if (normalizedSource) {
3599
+ addDependency(normalizedSource);
3600
+ }
3495
3601
  }
3496
3602
  }
3497
- function normalizeDependencyExpr(path8, rootName) {
3498
- if (t31.isIdentifier(path8.node)) {
3499
- return t31.identifier(path8.node.name);
3603
+ function normalizeDependencyExpr(path9, rootName) {
3604
+ if (t31.isIdentifier(path9.node)) {
3605
+ return t31.identifier(path9.node.name);
3500
3606
  }
3501
- if (t31.isMemberExpression(path8.node) || t31.isOptionalMemberExpression(path8.node)) {
3502
- const normalizedExp = normalizeMemberForCallSite(path8, path8.node);
3503
- const safeExp = t31.isMemberExpression(normalizedExp) || t31.isOptionalMemberExpression(normalizedExp) ? ensureOptionalForRefValue(normalizedExp) : normalizedExp;
3607
+ if (t31.isMemberExpression(path9.node) || t31.isOptionalMemberExpression(path9.node)) {
3608
+ const normalizedExp = normalizeMemberForCallSite(path9, path9.node);
3609
+ const safeExp = t31.isMemberExpression(normalizedExp) || t31.isOptionalMemberExpression(normalizedExp) ? ensureOptionalForMemberChain(normalizedExp) : normalizedExp;
3504
3610
  if (isReactValidDependencyExpr(safeExp)) {
3505
3611
  return t31.cloneNode(safeExp, true);
3506
3612
  }
@@ -3508,6 +3614,21 @@ function analyzeDeps(node, ctx, parentPath) {
3508
3614
  }
3509
3615
  return null;
3510
3616
  }
3617
+ function normalizeSourcedDependency(exp) {
3618
+ if (t31.isIdentifier(exp)) {
3619
+ return t31.identifier(exp.name);
3620
+ }
3621
+ if (t31.isMemberExpression(exp) || t31.isOptionalMemberExpression(exp)) {
3622
+ const root = findRootIdentifier(exp);
3623
+ if (!root) return null;
3624
+ const safeExp = t31.isMemberExpression(exp) || t31.isOptionalMemberExpression(exp) ? ensureOptionalForMemberChain(exp) : exp;
3625
+ if (isReactValidDependencyExpr(safeExp)) {
3626
+ return t31.cloneNode(safeExp, true);
3627
+ }
3628
+ return t31.identifier(root.name);
3629
+ }
3630
+ return null;
3631
+ }
3511
3632
  function isReactValidDependencyExpr(node2) {
3512
3633
  if (t31.isIdentifier(node2)) {
3513
3634
  return true;
@@ -3540,8 +3661,8 @@ function analyzeDeps(node, ctx, parentPath) {
3540
3661
  }
3541
3662
  return false;
3542
3663
  }
3543
- function normalizeMemberForCallSite(path8, node2) {
3544
- const parent = path8.parentPath;
3664
+ function normalizeMemberForCallSite(path9, node2) {
3665
+ const parent = path9.parentPath;
3545
3666
  const isDirectCallee = !!parent && (parent.isCallExpression() && parent.node.callee === node2 || parent.isOptionalCallExpression() && parent.node.callee === node2);
3546
3667
  if (!isDirectCallee) {
3547
3668
  return node2;
@@ -3551,8 +3672,8 @@ function analyzeDeps(node, ctx, parentPath) {
3551
3672
  }
3552
3673
  return node2.object;
3553
3674
  }
3554
- function ensureOptionalForRefValue(node2) {
3555
- if (!hasRefValueAccess(node2)) {
3675
+ function ensureOptionalForMemberChain(node2) {
3676
+ if (!hasTrailingMemberAccess(node2)) {
3556
3677
  return node2;
3557
3678
  }
3558
3679
  if (t31.isOptionalMemberExpression(node2) && node2.optional) {
@@ -3562,19 +3683,8 @@ function analyzeDeps(node, ctx, parentPath) {
3562
3683
  const property = t31.cloneNode(node2.property, true);
3563
3684
  return t31.optionalMemberExpression(object, property, node2.computed, true);
3564
3685
  }
3565
- function hasRefValueAccess(node2) {
3566
- let current = node2;
3567
- while (t31.isMemberExpression(current) || t31.isOptionalMemberExpression(current)) {
3568
- if (current.computed) {
3569
- if (t31.isStringLiteral(current.property) && current.property.value === "value") {
3570
- return true;
3571
- }
3572
- } else if (t31.isIdentifier(current.property) && current.property.name === "value") {
3573
- return true;
3574
- }
3575
- current = current.object;
3576
- }
3577
- return false;
3686
+ function hasTrailingMemberAccess(node2) {
3687
+ return t31.isMemberExpression(node2.object) || t31.isOptionalMemberExpression(node2.object);
3578
3688
  }
3579
3689
  function isEligibleBindingSource(binding) {
3580
3690
  if (binding.kind === "param") {
@@ -3582,16 +3692,13 @@ function analyzeDeps(node, ctx, parentPath) {
3582
3692
  }
3583
3693
  const bindingPath = binding.path;
3584
3694
  const declaratorPath = getVariableDeclaratorPath(bindingPath);
3585
- const isImportBinding = bindingPath.isImportSpecifier() || bindingPath.isImportDefaultSpecifier() || bindingPath.isImportNamespaceSpecifier();
3586
3695
  const isReactiveVarBinding = !!declaratorPath && isReactiveBinding(declaratorPath.node);
3587
3696
  const nodeInit = declaratorPath?.node.init;
3588
3697
  const isReactiveApiCallVarBinding = !!declaratorPath && t31.isCallExpression(nodeInit) && t31.isIdentifier(nodeInit.callee) && reactiveStateApis.has(nodeInit.callee.name);
3589
3698
  const isHookCallVarBinding = !!declaratorPath && t31.isCallExpression(nodeInit) && isHookLikeCallee(nodeInit.callee);
3590
3699
  const isFunctionBinding = bindingPath.isFunctionDeclaration() || !!declaratorPath && !!nodeInit && (t31.isArrowFunctionExpression(nodeInit) || t31.isFunctionExpression(nodeInit));
3591
- if (declaratorPath && nodeInit && (t31.isArrowFunctionExpression(nodeInit) || t31.isFunctionExpression(nodeInit))) {
3592
- markAsAnalyzed(nodeInit, false);
3593
- }
3594
- return isImportBinding || isReactiveVarBinding || isReactiveApiCallVarBinding || isHookCallVarBinding || isFunctionBinding;
3700
+ const isReactiveFunctionBinding = isFunctionBinding && (isReactiveBinding(declaratorPath?.node) || isReactiveBinding(bindingPath.node));
3701
+ return isReactiveVarBinding || isReactiveApiCallVarBinding || isHookCallVarBinding || isReactiveFunctionBinding;
3595
3702
  }
3596
3703
  function isHookLikeCallee(callee) {
3597
3704
  if (t31.isIdentifier(callee)) {
@@ -3687,11 +3794,11 @@ function getDependencyKey(exp) {
3687
3794
  }
3688
3795
  return exp.type;
3689
3796
  }
3690
- function isNestedMemberObject(path8) {
3691
- const parent = path8.parentPath;
3797
+ function isNestedMemberObject(path9) {
3798
+ const parent = path9.parentPath;
3692
3799
  if (!parent) return false;
3693
3800
  if (parent.isMemberExpression() || parent.isOptionalMemberExpression()) {
3694
- return parent.node.object === path8.node;
3801
+ return parent.node.object === path9.node;
3695
3802
  }
3696
3803
  return false;
3697
3804
  }
@@ -3699,27 +3806,22 @@ function isReactiveBinding(node) {
3699
3806
  if (!node) return false;
3700
3807
  return !!getScriptNodeMeta(node)?.is_reactive;
3701
3808
  }
3702
- function markAsAnalyzed(node, flag = true) {
3703
- const analyzed = getIsAnalyzed(node);
3704
- if (analyzed) return;
3705
- setScriptNodeMeta(node, { is_deps_analyzed: flag });
3706
- }
3707
- function getIsAnalyzed(node) {
3708
- return getScriptNodeMeta(node)?.is_deps_analyzed;
3709
- }
3710
3809
 
3711
3810
  // src/core/transform/sfc/script/syntax-processor/process/resolve-analysis-only-adapter.ts
3712
3811
  function resolveAnalysisOnlyAdapter(ctx) {
3713
3812
  return {
3714
- "CallExpression|Identifier"(path8) {
3715
- const node = path8.node;
3813
+ "CallExpression|Identifier"(path9) {
3814
+ const node = path9.node;
3716
3815
  const apiName = getApiName(node);
3717
3816
  const adapter = ADAPTER_RULES.runtime[apiName];
3718
3817
  if (!adapter || adapter.type !== "analyzed-deps") {
3719
3818
  return;
3720
3819
  }
3820
+ if (!isVueApiReference(path9, apiName)) {
3821
+ return;
3822
+ }
3721
3823
  if (t32.isCallExpression(node)) {
3722
- resolveCallNode(path8, adapter, ctx);
3824
+ resolveCallNode(path9, adapter, ctx);
3723
3825
  } else {
3724
3826
  replaceIdName(node, adapter.target);
3725
3827
  }
@@ -3737,59 +3839,77 @@ function getApiName(node) {
3737
3839
  }
3738
3840
  return apiName;
3739
3841
  }
3740
- function resolveCallNode(path8, adapter, ctx) {
3741
- const { node } = path8;
3842
+ function resolveCallNode(path9, adapter, ctx) {
3843
+ const { node } = path9;
3742
3844
  const { arguments: args } = node;
3743
3845
  if (!args.length) return;
3744
3846
  const fn = args[0];
3745
3847
  if (!t32.isArrowFunctionExpression(fn) && !t32.isFunctionExpression(fn)) {
3746
3848
  return;
3747
3849
  }
3748
- const fnPath = path8.get("arguments")[0];
3850
+ const fnPath = path9.get("arguments")[0];
3749
3851
  const deps = analyzeDeps(fn, ctx, fnPath);
3750
3852
  args.push(deps);
3751
3853
  replaceCallName(node, adapter.target);
3752
3854
  recordImport(ctx, adapter.package, adapter.target);
3753
3855
  }
3856
+ function isVueApiReference(path9, apiName) {
3857
+ if (path9.isIdentifier()) {
3858
+ if (path9.parentPath.isCallExpression() && path9.parentPath.node.callee === path9.node) {
3859
+ return false;
3860
+ }
3861
+ if (!path9.isReferencedIdentifier()) {
3862
+ return false;
3863
+ }
3864
+ }
3865
+ if (path9.isCallExpression()) {
3866
+ const callee = path9.get("callee");
3867
+ if (!callee.isIdentifier()) return false;
3868
+ return isVueImportBinding(callee.scope.getBinding(apiName));
3869
+ }
3870
+ return isVueImportBinding(path9.scope.getBinding(apiName));
3871
+ }
3872
+ function isVueImportBinding(binding) {
3873
+ if (!binding) return false;
3874
+ const bindingPath = binding.path;
3875
+ if (!bindingPath.isImportSpecifier() && !bindingPath.isImportDefaultSpecifier() && !bindingPath.isImportNamespaceSpecifier()) {
3876
+ return false;
3877
+ }
3878
+ const parent = bindingPath.parentPath?.node;
3879
+ if (!parent || !t32.isImportDeclaration(parent)) {
3880
+ return false;
3881
+ }
3882
+ const source = parent.source.value.toLowerCase();
3883
+ if (source.startsWith("@vue/")) {
3884
+ return true;
3885
+ }
3886
+ if (source === "vue-router" || source.startsWith("vue-router/")) {
3887
+ return true;
3888
+ }
3889
+ return VUE_PACKAGES.some((name) => source === name || source.startsWith(`${name}/`));
3890
+ }
3754
3891
 
3755
3892
  // src/core/transform/sfc/script/syntax-processor/process/resolve-arrow-deps.ts
3756
3893
  function resolveArrowFnDeps(ctx, ast) {
3757
3894
  return {
3758
- ArrowFunctionExpression(path8) {
3759
- const { node, parentPath } = path8;
3760
- if (isSkip(path8) || !atComponentOrHookRoot(parentPath, ast.program)) {
3895
+ ArrowFunctionExpression(path9) {
3896
+ const { node, parentPath } = path9;
3897
+ if (isSkip(path9) || !atComponentOrHookRoot(parentPath, ast.program)) {
3761
3898
  return;
3762
3899
  }
3763
- const deps = analyzeDeps(node, ctx, path8);
3900
+ const deps = analyzeDeps(node, ctx, path9);
3764
3901
  if (!deps.elements.length) return;
3765
3902
  const newNode = createUseCallback(node, deps);
3766
- const declaratorPath = getVariableDeclaratorPath(path8);
3767
- markAsAnalyzed(newNode.arguments[0]);
3903
+ const declaratorPath = getVariableDeclaratorPath(path9);
3768
3904
  recordImport(ctx, PACKAGE_NAME.react, REACT_API_MAP.useCallback);
3769
3905
  setScriptNodeMeta(declaratorPath?.node, { is_reactive: true, reactive_type: "indirect" });
3770
- path8.replaceWith(newNode);
3771
- }
3772
- };
3773
- }
3774
- function resolveUnanalyzedArrow(ctx) {
3775
- return {
3776
- ArrowFunctionExpression(path8) {
3777
- const { node } = path8;
3778
- const analyzed = getIsAnalyzed(node);
3779
- if (typeof analyzed === "undefined" || analyzed) return;
3780
- const newNode = createUseCallback(node);
3781
- const declaratorPath = getVariableDeclaratorPath(path8);
3782
- if (declaratorPath?.node) {
3783
- setScriptNodeMeta(declaratorPath.node, { is_reactive: true, reactive_type: "indirect" });
3784
- }
3785
- markAsAnalyzed(newNode.arguments[0]);
3786
- path8.replaceWith(newNode);
3906
+ path9.replaceWith(newNode);
3787
3907
  }
3788
3908
  };
3789
3909
  }
3790
- function isSkip(path8) {
3791
- const { parentPath } = path8;
3792
- const isVariableDecl = () => getVariableDeclaratorPath(path8) !== null;
3910
+ function isSkip(path9) {
3911
+ const { parentPath } = path9;
3912
+ const isVariableDecl = () => getVariableDeclaratorPath(path9) !== null;
3793
3913
  const isReturnFunc = () => !isVariableDecl() && parentPath.isReturnStatement();
3794
3914
  const isCallback = () => {
3795
3915
  if (!parentPath) {
@@ -3798,12 +3918,12 @@ function isSkip(path8) {
3798
3918
  if (parentPath.isCallExpression()) {
3799
3919
  const callExpressionPath = parentPath;
3800
3920
  const args = callExpressionPath.node.arguments;
3801
- return args.some((arg) => arg === path8.node);
3921
+ return args.some((arg) => arg === path9.node);
3802
3922
  }
3803
3923
  if (parentPath.isArrayExpression()) {
3804
3924
  const arrayExpressionPath = parentPath;
3805
3925
  const elements = arrayExpressionPath.node.elements;
3806
- return elements.some((element) => element === path8.node);
3926
+ return elements.some((element) => element === path9.node);
3807
3927
  }
3808
3928
  return false;
3809
3929
  };
@@ -3827,13 +3947,13 @@ function isSkip(path8) {
3827
3947
  import * as t33 from "@babel/types";
3828
3948
  function resolveElementRef(ctx) {
3829
3949
  return {
3830
- CallExpression(path8) {
3950
+ CallExpression(path9) {
3831
3951
  const {
3832
3952
  inputType,
3833
3953
  templateData: { refBindings }
3834
3954
  } = ctx;
3835
3955
  if (inputType !== "sfc") return;
3836
- const { node } = path8;
3956
+ const { node } = path9;
3837
3957
  const isUseTemplateRef = isCalleeNamed(node, VUE_API_MAP.useTemplateRef);
3838
3958
  const isCompRefBindings = Object.keys(refBindings.componentRefs).length > 0 && isCalleeNamed(node, VUE_API_MAP.ref);
3839
3959
  const shouldProcess = isUseTemplateRef || isCompRefBindings;
@@ -3841,7 +3961,7 @@ function resolveElementRef(ctx) {
3841
3961
  return;
3842
3962
  }
3843
3963
  if (isCompRefBindings) {
3844
- const varDeclaratorPath = getVariableDeclaratorPath(path8)?.node;
3964
+ const varDeclaratorPath = getVariableDeclaratorPath(path9)?.node;
3845
3965
  if (!t33.isIdentifier(varDeclaratorPath?.id)) {
3846
3966
  return;
3847
3967
  }
@@ -3850,22 +3970,22 @@ function resolveElementRef(ctx) {
3850
3970
  if (!compRef) return;
3851
3971
  }
3852
3972
  node.arguments[0] = t33.identifier("null");
3853
- resolveTypeParameters(ctx, path8);
3973
+ resolveTypeParameters(ctx, path9);
3854
3974
  replaceCallName(node, REACT_API_MAP.useRef);
3855
3975
  recordImport(ctx, PACKAGE_NAME.react, REACT_API_MAP.useRef);
3856
3976
  },
3857
- MemberExpression(path8) {
3858
- resolveRefValueToCurrent(path8);
3977
+ MemberExpression(path9) {
3978
+ resolveRefValueToCurrent(path9);
3859
3979
  }
3860
3980
  };
3861
3981
  }
3862
- function resolveTypeParameters(ctx, path8) {
3982
+ function resolveTypeParameters(ctx, path9) {
3863
3983
  const {
3864
3984
  templateData: { refBindings },
3865
3985
  scriptData
3866
3986
  } = ctx;
3867
- const { node } = path8;
3868
- const varDeclaratorNode = getVariableDeclaratorPath(path8)?.node;
3987
+ const { node } = path9;
3988
+ const varDeclaratorNode = getVariableDeclaratorPath(path9)?.node;
3869
3989
  if (!scriptData.lang.startsWith("ts") || !varDeclaratorNode) {
3870
3990
  return;
3871
3991
  }
@@ -3877,12 +3997,12 @@ function resolveTypeParameters(ctx, path8) {
3877
3997
  node.typeParameters = t33.tsTypeParameterInstantiation([t33.tsTypeReference(t33.identifier(type))]);
3878
3998
  }
3879
3999
  }
3880
- function resolveRefValueToCurrent(path8) {
3881
- const { node } = path8;
4000
+ function resolveRefValueToCurrent(path9) {
4001
+ const { node } = path9;
3882
4002
  if (node.computed || !t33.isIdentifier(node.property) || node.property.name !== "value") {
3883
4003
  return;
3884
4004
  }
3885
- const rootPath = findRootVariablePath(path8);
4005
+ const rootPath = findRootVariablePath(path9);
3886
4006
  if (!rootPath?.node || !t33.isCallExpression(rootPath.node.init) || !isCalleeNamed(rootPath.node.init, REACT_API_MAP.useRef)) {
3887
4007
  return;
3888
4008
  }
@@ -3898,21 +4018,25 @@ import * as t34 from "@babel/types";
3898
4018
  function resolveExprMemo(ctx, ast) {
3899
4019
  const isScriptFile = ctx.inputType !== "sfc";
3900
4020
  return {
3901
- VariableDeclarator(path8) {
3902
- const { node } = path8;
4021
+ VariableDeclarator(path9) {
4022
+ const { node } = path9;
3903
4023
  const { init } = node;
3904
- if (!init) return;
3905
4024
  const shouldMemo = () => {
3906
- if (!atComponentOrHookRoot(path8, ast.program, isScriptFile)) {
4025
+ if (!init) return false;
4026
+ if (!atComponentOrHookRoot(path9, ast.program, isScriptFile)) {
4027
+ return false;
4028
+ }
4029
+ if (!t34.isVariableDeclaration(path9.parent) || path9.parent.kind !== "const") {
3907
4030
  return false;
3908
4031
  }
4032
+ if (t34.isFunction(init)) return false;
3909
4033
  if (t34.isCallExpression(init) && t34.isIdentifier(init.callee) && init.callee.name.startsWith("use")) {
3910
4034
  return false;
3911
4035
  }
3912
4036
  return true;
3913
4037
  };
3914
4038
  if (!shouldMemo()) return;
3915
- const initPath = path8.get("init");
4039
+ const initPath = path9.get("init");
3916
4040
  if (!initPath.isExpression()) return;
3917
4041
  const deps = analyzeDeps(initPath.node, ctx, initPath);
3918
4042
  if (!deps.elements.length) return;
@@ -3928,8 +4052,8 @@ import * as t35 from "@babel/types";
3928
4052
  function resolveLintRules(ctx, ast) {
3929
4053
  const inScriptFile = ctx.inputType !== "sfc";
3930
4054
  return {
3931
- CallExpression(path8) {
3932
- const { node, parentPath } = path8;
4055
+ CallExpression(path9) {
4056
+ const { node, parentPath } = path9;
3933
4057
  if (!t35.isIdentifier(node.callee)) return;
3934
4058
  const { name: callName } = node.callee;
3935
4059
  const addLog = (t41) => {
@@ -3948,7 +4072,7 @@ function resolveLintRules(ctx, ast) {
3948
4072
  );
3949
4073
  return;
3950
4074
  }
3951
- if (!atComponentOrHookRoot(path8, ast.program)) {
4075
+ if (!atComponentOrHookRoot(path9, ast.program)) {
3952
4076
  addLog(
3953
4077
  `The ${macro} must be defined at the top level of the component, not inside blocks or functions.`
3954
4078
  );
@@ -3964,7 +4088,7 @@ function resolveLintRules(ctx, ast) {
3964
4088
  };
3965
4089
  const lintHooks = () => {
3966
4090
  if (!callName.startsWith("use")) return;
3967
- if (!atComponentOrHookRoot(path8, ast.program, inScriptFile)) {
4091
+ if (!atComponentOrHookRoot(path9, ast.program, inScriptFile)) {
3968
4092
  addLog(
3969
4093
  `The ${callName} hook must be called at the top level, not inside loops, conditions, or nested functions.`
3970
4094
  );
@@ -3982,8 +4106,8 @@ import * as t36 from "@babel/types";
3982
4106
  function resolveProvide(ctx) {
3983
4107
  if (ctx.inputType === "style") return {};
3984
4108
  return {
3985
- CallExpression(path8) {
3986
- const { node } = path8;
4109
+ CallExpression(path9) {
4110
+ const { node } = path9;
3987
4111
  const providerTarget = ADAPTER_RULES.runtime[VUE_API_MAP.provide]?.target;
3988
4112
  const isProvideCall = isCalleeNamed(node, VUE_API_MAP.provide) || providerTarget && isCalleeNamed(node, providerTarget);
3989
4113
  if (!isProvideCall) return;
@@ -3993,7 +4117,7 @@ function resolveProvide(ctx) {
3993
4117
  const adapter = ADAPTER_RULES.runtime[VUE_API_MAP.provide];
3994
4118
  assignProviderValue(target, key, value);
3995
4119
  recordImport(ctx, adapter.package, adapter.target);
3996
- path8.parentPath.remove();
4120
+ path9.parentPath.remove();
3997
4121
  }
3998
4122
  };
3999
4123
  }
@@ -4035,8 +4159,8 @@ function assignProviderValue(target, key, value) {
4035
4159
  import * as t37 from "@babel/types";
4036
4160
  function resolveRenameAdapter(ctx) {
4037
4161
  return {
4038
- "CallExpression|Identifier"(path8) {
4039
- const node = path8.node;
4162
+ "CallExpression|Identifier"(path9) {
4163
+ const node = path9.node;
4040
4164
  const isCallNode = t37.isCallExpression(node);
4041
4165
  let apiName = "";
4042
4166
  if (t37.isIdentifier(node)) {
@@ -4044,15 +4168,21 @@ function resolveRenameAdapter(ctx) {
4044
4168
  } else if (isCallNode && t37.isIdentifier(node.callee)) {
4045
4169
  apiName = node.callee.name;
4046
4170
  }
4171
+ if (!apiName) {
4172
+ return;
4173
+ }
4047
4174
  const runtimeAdapter = ADAPTER_RULES.runtime[apiName];
4048
4175
  const routerAdapter = ADAPTER_RULES.router[apiName];
4049
4176
  const adapter = runtimeAdapter || routerAdapter;
4050
4177
  if (!adapter || adapter.type !== "rename") {
4051
4178
  return;
4052
4179
  }
4180
+ if (!isVueApiReference2(path9, apiName)) {
4181
+ return;
4182
+ }
4053
4183
  if (adapter.isTrackable) {
4054
4184
  const reactiveType = getReactiveType(apiName);
4055
- const declaratorPath = getVariableDeclaratorPath(path8);
4185
+ const declaratorPath = getVariableDeclaratorPath(path9);
4056
4186
  setScriptNodeMeta(declaratorPath?.node, {
4057
4187
  is_reactive: true,
4058
4188
  reactive_type: reactiveType
@@ -4070,6 +4200,41 @@ function resolveRenameAdapter(ctx) {
4070
4200
  }
4071
4201
  };
4072
4202
  }
4203
+ function isVueApiReference2(path9, apiName) {
4204
+ if (path9.isIdentifier()) {
4205
+ if (path9.parentPath.isCallExpression() && path9.parentPath.node.callee === path9.node) {
4206
+ return false;
4207
+ }
4208
+ if (!path9.isReferencedIdentifier()) {
4209
+ return false;
4210
+ }
4211
+ }
4212
+ if (path9.isCallExpression()) {
4213
+ const callee = path9.get("callee");
4214
+ if (!callee.isIdentifier()) return false;
4215
+ return isVueImportBinding2(callee.scope.getBinding(apiName));
4216
+ }
4217
+ return isVueImportBinding2(path9.scope.getBinding(apiName));
4218
+ }
4219
+ function isVueImportBinding2(binding) {
4220
+ if (!binding) return false;
4221
+ const bindingPath = binding.path;
4222
+ if (!bindingPath.isImportSpecifier() && !bindingPath.isImportDefaultSpecifier() && !bindingPath.isImportNamespaceSpecifier()) {
4223
+ return false;
4224
+ }
4225
+ const parent = bindingPath.parentPath?.node;
4226
+ if (!parent || !t37.isImportDeclaration(parent)) {
4227
+ return false;
4228
+ }
4229
+ const source = parent.source.value.toLowerCase();
4230
+ if (source.startsWith("@vue/")) {
4231
+ return true;
4232
+ }
4233
+ if (source === "vue-router" || source.startsWith("vue-router/")) {
4234
+ return true;
4235
+ }
4236
+ return VUE_PACKAGES.some((name) => source === name || source.startsWith(`${name}/`));
4237
+ }
4073
4238
 
4074
4239
  // src/core/transform/sfc/script/syntax-processor/index.ts
4075
4240
  function processVueSyntax2(ast, ctx) {
@@ -4091,16 +4256,17 @@ function processVueSyntax2(ast, ctx) {
4091
4256
  // provide 需要在 rename 之前收集并移除原始调用,避免被重命名后失配
4092
4257
  resolveProvide,
4093
4258
  resolveRenameAdapter,
4259
+ // fix:在分析函数前分析可优化为 useMemo 的顶层变量声明,
4260
+ // 使得后续能够被函数依赖分析
4261
+ resolveExprMemo,
4094
4262
  resolveArrowFnDeps,
4095
- resolveUnanalyzedArrow,
4096
4263
  resolveAnalysisOnlyAdapter,
4097
- resolveExprMemo,
4098
4264
  resolveLintRules
4099
4265
  ],
4100
4266
  excludeBabel: [resolveTemplateSlotIface, resolveCompIProps]
4101
4267
  },
4102
4268
  postprocess: {
4103
- applyBabel: [insertRequiredImports, resolveStaticHoisting],
4269
+ applyBabel: [resolveRequiredImports, resolveStaticHoisting],
4104
4270
  excludeBabel: [insertCSSImport, collectLocalStatements]
4105
4271
  }
4106
4272
  });
@@ -4241,7 +4407,7 @@ function isIdentifier20(code) {
4241
4407
  function isStringLiteral12(code) {
4242
4408
  try {
4243
4409
  const node = parseExpression3(code);
4244
- return t38.isStringLiteral(node) || t38.isTemplateLiteral(node);
4410
+ return t38.isStringLiteral(node);
4245
4411
  } catch {
4246
4412
  return false;
4247
4413
  }
@@ -4314,7 +4480,10 @@ function normalizePropName(rawName, name) {
4314
4480
  case "for":
4315
4481
  return "htmlFor";
4316
4482
  default:
4317
- return whitelist.test(name) ? name : camelCase(name);
4483
+ if (!isVBind(rawName) && whitelist.test(name)) {
4484
+ return name;
4485
+ }
4486
+ return camelCase(name);
4318
4487
  }
4319
4488
  }
4320
4489
  function isVOn(name) {
@@ -4330,7 +4499,7 @@ function isVModel(name) {
4330
4499
  return /^v-model/.test(name ?? "");
4331
4500
  }
4332
4501
  function isClassAttr(name) {
4333
- return /^(class|:class|v-bind:class|className)$/.test(name ?? "");
4502
+ return /^(class|:class|v-bind:class|className|class-name)$/.test(name ?? "");
4334
4503
  }
4335
4504
  function isStyleAttr(name) {
4336
4505
  return /^(style|:style|v-bind:style)$/.test(name ?? "");
@@ -4524,7 +4693,6 @@ function resolveDefaultStyleModuleName(node) {
4524
4693
 
4525
4694
  // src/core/transform/sfc/template/syntax-processor/preprocess/resolve-style-scope-attribute.ts
4526
4695
  import {
4527
- ElementTypes,
4528
4696
  isSlotOutlet,
4529
4697
  isTemplateNode,
4530
4698
  NodeTypes as NodeTypes3
@@ -4548,21 +4716,30 @@ function walkElementNodes2(node, onElement) {
4548
4716
  }
4549
4717
  function injectStyleScopeAttribute(node, ctx) {
4550
4718
  const { scopeId } = ctx.styleData;
4551
- if (!scopeId || isComponentElement(node) || isSlotOutlet(node) || isTemplateNode(node)) {
4719
+ if (!scopeId || isSlotOutlet(node) || isTemplateNode(node)) {
4552
4720
  return;
4553
4721
  }
4554
- const hasDynamicIs = node.props.some((prop) => {
4555
- if (prop.type !== NodeTypes3.DIRECTIVE || prop.arg?.type !== NodeTypes3.SIMPLE_EXPRESSION) {
4556
- return false;
4722
+ let hasScopeId = false;
4723
+ let hasClassOrId = false;
4724
+ for (const prop of node.props) {
4725
+ if (prop.type === NodeTypes3.ATTRIBUTE) {
4726
+ if (prop.name === scopeId) {
4727
+ hasScopeId = true;
4728
+ break;
4729
+ }
4730
+ if (getHasClassOrId(prop.name)) {
4731
+ hasClassOrId = true;
4732
+ break;
4733
+ }
4734
+ }
4735
+ if (prop.type === NodeTypes3.DIRECTIVE && prop.arg?.type === NodeTypes3.SIMPLE_EXPRESSION) {
4736
+ if (getHasClassOrId(prop.arg.content)) {
4737
+ hasClassOrId = true;
4738
+ break;
4739
+ }
4557
4740
  }
4558
- return prop.arg.content === "is";
4559
- });
4560
- const hasScopeId = node.props.some(
4561
- (prop) => prop.type === NodeTypes3.ATTRIBUTE && prop.name === scopeId
4562
- );
4563
- if (hasDynamicIs || hasScopeId) {
4564
- return;
4565
4741
  }
4742
+ if (hasScopeId || !hasClassOrId) return;
4566
4743
  const attr = {
4567
4744
  type: NodeTypes3.ATTRIBUTE,
4568
4745
  name: scopeId,
@@ -4572,11 +4749,8 @@ function injectStyleScopeAttribute(node, ctx) {
4572
4749
  };
4573
4750
  node.props.push(attr);
4574
4751
  }
4575
- function isComponentElement(node) {
4576
- if (node.tagType !== ElementTypes.COMPONENT) {
4577
- return camelCase(node.tag) !== node.tag;
4578
- }
4579
- return node.tagType === ElementTypes.COMPONENT;
4752
+ function getHasClassOrId(ns) {
4753
+ return isClassAttr(ns) || ns === "id";
4580
4754
  }
4581
4755
 
4582
4756
  // src/core/transform/sfc/template/shared/prop-merge-utils.ts
@@ -4767,7 +4941,12 @@ function resolvePropertyIR(node, ir, ctx, nodeIR, isDynamic = false) {
4767
4941
  content = node.value.content = parseStyleString(content);
4768
4942
  }
4769
4943
  if (isDynamic) {
4770
- node.value.isStringLiteral = strCodeTypes.isStringLiteral(content);
4944
+ const isStringLiteral13 = strCodeTypes.isStringLiteral(content);
4945
+ if (isStringLiteral13) {
4946
+ content = normalizeString(content);
4947
+ node.value.content = content;
4948
+ }
4949
+ node.value.isStringLiteral = isStringLiteral13;
4771
4950
  }
4772
4951
  const existing = findSameProp(nodeIR.props, node);
4773
4952
  if (existing) {
@@ -4777,6 +4956,12 @@ function resolvePropertyIR(node, ir, ctx, nodeIR, isDynamic = false) {
4777
4956
  }
4778
4957
  resolvePropAsBabelExp(existing ?? node, ctx);
4779
4958
  }
4959
+ function normalizeString(s) {
4960
+ if (s.startsWith("'") && s.endsWith("'")) {
4961
+ return s.slice(1, -1);
4962
+ }
4963
+ return s;
4964
+ }
4780
4965
 
4781
4966
  // src/core/transform/sfc/template/syntax-processor/process/props/resolve-attribute-prop.ts
4782
4967
  function resolveAttributeProp(node, ir, ctx, nodeIR) {
@@ -4916,7 +5101,7 @@ function resolveVMemo(node, _ir, ctx, nodeIR) {
4916
5101
 
4917
5102
  // src/core/transform/sfc/template/syntax-processor/process/props/resolve-v-model.ts
4918
5103
  import {
4919
- ElementTypes as ElementTypes2,
5104
+ ElementTypes,
4920
5105
  NodeTypes as NodeTypes6
4921
5106
  } from "@vue/compiler-core";
4922
5107
  function resolveVModel(node, _ir, ctx, elementNode, nodeIR) {
@@ -4924,7 +5109,7 @@ function resolveVModel(node, _ir, ctx, elementNode, nodeIR) {
4924
5109
  const exp = node.exp;
4925
5110
  const modifiers = node.modifiers.map((item) => item.content);
4926
5111
  const getterName = exp.content;
4927
- const isComponent = elementNode.tagType === ElementTypes2.COMPONENT;
5112
+ const isComponent = elementNode.tagType === ElementTypes.COMPONENT;
4928
5113
  const inputType = resolveHtmlInput(elementNode, isComponent);
4929
5114
  const propName = arg?.content ?? resolveModelPropName(inputType, isComponent);
4930
5115
  let valuePropIR;
@@ -5001,7 +5186,7 @@ function resolveVOn(node, _ir, ctx, nodeIR) {
5001
5186
  const exp = node.exp;
5002
5187
  const modifiers = node.modifiers.map((item) => item.content);
5003
5188
  const captureIndex = modifiers.findIndex((modifier) => modifier === "capture");
5004
- let eventName = `on${camelCase(capitalize(arg.content))}`;
5189
+ let eventName = normalizeVOnEventName(arg.content);
5005
5190
  let handler = resolveSpecialExpressions(exp.content.trim(), ctx);
5006
5191
  if (captureIndex > -1) {
5007
5192
  eventName = modifiers[captureIndex] ? `${eventName}Capture` : eventName;
@@ -5012,7 +5197,7 @@ function resolveVOn(node, _ir, ctx, nodeIR) {
5012
5197
  originalVueEventName = `${arg.content}.${modifiers.join(".")}`;
5013
5198
  } else {
5014
5199
  const expr = stringToExpr(handler);
5015
- if (!t40.isFunctionExpression(expr) && !t40.isIdentifier(expr)) {
5200
+ if (!t40.isFunctionExpression(expr) && !t40.isArrowFunctionExpression(expr) && !t40.isIdentifier(expr)) {
5016
5201
  handler = `() => {${handler}}`;
5017
5202
  }
5018
5203
  }
@@ -5030,6 +5215,11 @@ function resolveVOn(node, _ir, ctx, nodeIR) {
5030
5215
  }
5031
5216
  nodeIR.props.push(eventIR);
5032
5217
  }
5218
+ function normalizeVOnEventName(rawEventName) {
5219
+ const segments = rawEventName.split(/[:-]/g).map((segment) => segment.trim()).filter(Boolean);
5220
+ const normalized = segments.map((segment) => capitalize(camelCase(segment))).join("");
5221
+ return `on${normalized}`;
5222
+ }
5033
5223
 
5034
5224
  // src/core/transform/sfc/template/syntax-processor/process/props/resolve-v-show.ts
5035
5225
  function resolveVShow(node, _ir, ctx, nodeIR) {
@@ -5203,7 +5393,7 @@ function resolveCommentNode(node, _ir, ctx, childrenIR) {
5203
5393
  }
5204
5394
 
5205
5395
  // src/core/transform/sfc/template/syntax-processor/process/resolve-element-node.ts
5206
- import { ElementTypes as ElementTypes3 } from "@vue/compiler-core";
5396
+ import { ElementTypes as ElementTypes2 } from "@vue/compiler-core";
5207
5397
  function resolveElementNode(node, ir, ctx, siblingNodesIR) {
5208
5398
  const isComponent = getIsComponent(node);
5209
5399
  const tag = isComponent ? capitalize(camelCase(node.tag)) : node.tag;
@@ -5238,10 +5428,10 @@ function createElementNodeIR(options) {
5238
5428
  };
5239
5429
  }
5240
5430
  function getIsComponent(node) {
5241
- if (node.tagType !== ElementTypes3.COMPONENT) {
5431
+ if (node.tagType !== ElementTypes2.COMPONENT) {
5242
5432
  return camelCase(node.tag) !== node.tag;
5243
5433
  }
5244
- return node.tagType === ElementTypes3.COMPONENT;
5434
+ return node.tagType === ElementTypes2.COMPONENT;
5245
5435
  }
5246
5436
 
5247
5437
  // src/core/transform/sfc/template/syntax-processor/process/resolve-interpolation-node.ts
@@ -5460,12 +5650,12 @@ function transform(ast, ctx, options) {
5460
5650
  }
5461
5651
 
5462
5652
  // package.json
5463
- var version = "1.3.0";
5653
+ var version = "1.5.0";
5464
5654
  var bin = {
5465
5655
  vureact: "./bin/vureact.js"
5466
5656
  };
5467
5657
 
5468
- // src/compiler/shared/types.ts
5658
+ // src/compiler/shared/types/cache-types.ts
5469
5659
  var CacheKey = /* @__PURE__ */ ((CacheKey2) => {
5470
5660
  CacheKey2["SFC"] = "sfc";
5471
5661
  CacheKey2["SCRIPT"] = "script";
@@ -5507,10 +5697,146 @@ function simpleFormat(code) {
5507
5697
  }
5508
5698
 
5509
5699
  // src/compiler/shared/helper.ts
5510
- import { execSync } from "child_process";
5700
+ import fs2 from "fs";
5701
+ import kleur4 from "kleur";
5702
+ import path3 from "path";
5703
+
5704
+ // src/compiler/shared/file-lock-manager.ts
5511
5705
  import fs from "fs";
5512
5706
  import kleur3 from "kleur";
5513
5707
  import path2 from "path";
5708
+ import lockfile from "proper-lockfile";
5709
+ var FileLockManager = class _FileLockManager {
5710
+ static instance;
5711
+ /**
5712
+ * 获取单例实例
5713
+ */
5714
+ static getInstance() {
5715
+ if (!_FileLockManager.instance) {
5716
+ _FileLockManager.instance = new _FileLockManager();
5717
+ }
5718
+ return _FileLockManager.instance;
5719
+ }
5720
+ /**
5721
+ * 获取文件锁并更新文件内容
5722
+ * @param filePath 文件路径
5723
+ * @param updater 更新函数,接收当前内容,返回更新后的内容
5724
+ * @param options 锁选项
5725
+ */
5726
+ async updateFile(filePath, updater, options = {}) {
5727
+ return this.withLock(
5728
+ filePath,
5729
+ async () => {
5730
+ let current = null;
5731
+ try {
5732
+ const content = await fs.promises.readFile(filePath, "utf-8");
5733
+ if (content.trim()) {
5734
+ try {
5735
+ current = JSON.parse(content);
5736
+ } catch {
5737
+ current = content;
5738
+ }
5739
+ }
5740
+ } catch (error) {
5741
+ console.error(kleur3.red("\u2716"), `Failed to read file ${filePath}`);
5742
+ console.error(error);
5743
+ }
5744
+ const updated = await updater(current);
5745
+ const contentToWrite = typeof updated === "string" ? updated : JSON.stringify(updated, null, 2);
5746
+ await this.writeFile(filePath, contentToWrite);
5747
+ return updated;
5748
+ },
5749
+ options
5750
+ );
5751
+ }
5752
+ /**
5753
+ * 获取文件锁并执行操作
5754
+ * @param filePath 文件路径
5755
+ * @param operation 要执行的操作函数
5756
+ * @param options 锁选项
5757
+ */
5758
+ async withLock(filePath, operation, options = {}) {
5759
+ const {
5760
+ stale = 1e4,
5761
+ update = stale / 2,
5762
+ retries = 5,
5763
+ realpath = true,
5764
+ lockfilePath
5765
+ } = options;
5766
+ await fs.promises.mkdir(path2.dirname(filePath), { recursive: true });
5767
+ try {
5768
+ await fs.promises.access(filePath);
5769
+ } catch {
5770
+ await this.writeFile(filePath, "");
5771
+ }
5772
+ const release = await lockfile.lock(filePath, {
5773
+ stale,
5774
+ update,
5775
+ retries,
5776
+ realpath,
5777
+ lockfilePath
5778
+ });
5779
+ try {
5780
+ return await operation();
5781
+ } finally {
5782
+ await release();
5783
+ }
5784
+ }
5785
+ /**
5786
+ * 检查文件是否被锁定
5787
+ * @param filePath 文件路径
5788
+ * @param options 锁选项
5789
+ */
5790
+ async isLocked(filePath, options = {}) {
5791
+ const { stale = 1e4, realpath = true, lockfilePath } = options;
5792
+ try {
5793
+ return await lockfile.check(filePath, { stale, realpath, lockfilePath });
5794
+ } catch {
5795
+ return false;
5796
+ }
5797
+ }
5798
+ /**
5799
+ * 尝试获取锁(非阻塞)
5800
+ * @param filePath 文件路径
5801
+ * @param options 锁选项
5802
+ * @returns 如果成功获取锁,返回释放函数;否则返回 null
5803
+ */
5804
+ async tryLock(filePath, options = {}) {
5805
+ const { stale = 1e4, update = stale / 2, realpath = true, lockfilePath } = options;
5806
+ try {
5807
+ const release = await lockfile.lock(filePath, {
5808
+ stale,
5809
+ update,
5810
+ retries: 0,
5811
+ // 不重试
5812
+ realpath,
5813
+ lockfilePath
5814
+ });
5815
+ return release;
5816
+ } catch {
5817
+ return null;
5818
+ }
5819
+ }
5820
+ /**
5821
+ * 释放文件锁
5822
+ * @param filePath 文件路径
5823
+ * @param options 锁选项
5824
+ */
5825
+ async unlock(filePath, options = {}) {
5826
+ const { realpath = true, lockfilePath } = options;
5827
+ try {
5828
+ await lockfile.unlock(filePath, { realpath, lockfilePath });
5829
+ } catch {
5830
+ }
5831
+ }
5832
+ async writeFile(filePath, content) {
5833
+ await fs.promises.mkdir(path2.dirname(filePath), { recursive: true });
5834
+ await fs.promises.writeFile(filePath, content, "utf-8");
5835
+ }
5836
+ };
5837
+ var fileLock = FileLockManager.getInstance();
5838
+
5839
+ // src/compiler/shared/helper.ts
5514
5840
  var Helper = class {
5515
5841
  compilerOpts;
5516
5842
  pathFilter;
@@ -5535,35 +5861,37 @@ var Helper = class {
5535
5861
  * 获取输入文件的路径
5536
5862
  */
5537
5863
  getInputPath() {
5538
- const { input } = this.compilerOpts;
5539
- return path2.resolve(this.getProjectRoot(), input || "src");
5864
+ const { input = "src" } = this.compilerOpts;
5865
+ return path3.resolve(this.getProjectRoot(), input);
5540
5866
  }
5541
5867
  /**
5542
5868
  * 检查 input 路径是否是单个文件
5543
5869
  */
5544
5870
  isSingleFile() {
5545
5871
  const inputPath = this.getInputPath();
5546
- return fs.existsSync(inputPath) && fs.statSync(inputPath).isFile();
5872
+ return fs2.existsSync(inputPath) && fs2.statSync(inputPath).isFile();
5547
5873
  }
5548
5874
  /**
5549
5875
  * 获取输出文件的路径。如:'[root]/.vureact/dist/'
5876
+ * @param addInput 会输出如:'[root]/.vureact/dist/[input]/'
5550
5877
  */
5551
- getOuputPath() {
5552
- return path2.resolve(this.getWorkspaceDir(), this.getOutDirName());
5878
+ getOuputPath(addInput = false) {
5879
+ const { input = "src" } = this.compilerOpts;
5880
+ return path3.resolve(this.getWorkspaceDir(), this.getOutDirName(), addInput ? input : "");
5553
5881
  }
5554
5882
  getOutDirName() {
5555
5883
  const { output } = this.compilerOpts;
5556
5884
  return output?.outDir || this.outDir;
5557
5885
  }
5558
5886
  getWorkspaceDir() {
5559
- return path2.resolve(this.getProjectRoot(), this.workspaceDir);
5887
+ return path3.resolve(this.getProjectRoot(), this.workspaceDir);
5560
5888
  }
5561
5889
  /**
5562
5890
  * 根据相对输出路径反推源文件路径
5563
5891
  */
5564
5892
  getSourcePath(outputPath) {
5565
- const relativePath2 = path2.relative(this.getOuputPath(), outputPath);
5566
- return path2.resolve(this.getProjectRoot(), relativePath2);
5893
+ const relativePath2 = path3.relative(this.getOuputPath(), outputPath);
5894
+ return path3.resolve(this.getProjectRoot(), relativePath2);
5567
5895
  }
5568
5896
  getIgnoreAssets() {
5569
5897
  const { output } = this.compilerOpts;
@@ -5581,7 +5909,9 @@ var Helper = class {
5581
5909
  "eslint.config.",
5582
5910
  "readme.",
5583
5911
  "vue.",
5584
- "vureact.config.js"
5912
+ ".vue",
5913
+ "vureact.config.js",
5914
+ "vureact.config.ts"
5585
5915
  ]);
5586
5916
  }
5587
5917
  getIsCache() {
@@ -5591,19 +5921,19 @@ var Helper = class {
5591
5921
  * 返回原始目录下的 package.json 路径
5592
5922
  */
5593
5923
  getRootPkgPath() {
5594
- return path2.join(this.getProjectRoot(), "package.json");
5924
+ return path3.join(this.getProjectRoot(), "package.json");
5595
5925
  }
5596
5926
  /**
5597
5927
  * 返回 output 的 package.json 路径
5598
5928
  */
5599
5929
  getOutputPkgPath() {
5600
- return path2.join(this.getOuputPath(), "package.json");
5930
+ return path3.join(this.getOuputPath(), "package.json");
5601
5931
  }
5602
5932
  /**
5603
5933
  * 返回文件相对工作区的路径
5604
5934
  */
5605
5935
  relativePath(filePath) {
5606
- return path2.relative(this.getProjectRoot(), filePath);
5936
+ return path3.relative(this.getProjectRoot(), filePath);
5607
5937
  }
5608
5938
  /**
5609
5939
  * 替换 .vue 文件名后缀为 .jsx/.tsx
@@ -5615,8 +5945,8 @@ var Helper = class {
5615
5945
  const relativePath2 = this.relativePath(filePath);
5616
5946
  let newRelativePath = relativePath2.replace(/\.vue$/i, ext);
5617
5947
  if (newRelativePath === relativePath2) {
5618
- const { name, dir } = path2.parse(relativePath2);
5619
- newRelativePath = path2.join(dir, `${name}${ext}`);
5948
+ const { name, dir } = path3.parse(relativePath2);
5949
+ newRelativePath = path3.join(dir, `${name}${ext}`);
5620
5950
  }
5621
5951
  return newRelativePath;
5622
5952
  }
@@ -5624,7 +5954,7 @@ var Helper = class {
5624
5954
  * 判断是否应该跳过不需要进行文件搜索的路径
5625
5955
  */
5626
5956
  shouldSkipPath(filePath) {
5627
- const baseName = path2.basename(filePath);
5957
+ const baseName = path3.basename(filePath);
5628
5958
  const defaultExcludes = ["node_modules", "dist", "build", ".git", ".DS_Store"];
5629
5959
  if (defaultExcludes.includes(baseName)) {
5630
5960
  return true;
@@ -5640,7 +5970,7 @@ var Helper = class {
5640
5970
  */
5641
5971
  resolveOutputPath(filePath, extname) {
5642
5972
  const newRelativePath = extname ? this.replaceVueFileExt(filePath, `.${extname}`) : this.relativePath(filePath);
5643
- const outputPath = path2.resolve(this.getOuputPath(), newRelativePath);
5973
+ const outputPath = path3.resolve(this.getOuputPath(), newRelativePath);
5644
5974
  return outputPath;
5645
5975
  }
5646
5976
  /**
@@ -5680,20 +6010,34 @@ var Helper = class {
5680
6010
  return a.fileSize === b.fileSize && a.mtime === b.mtime;
5681
6011
  }
5682
6012
  /**
5683
- * 统一的写文件方法,包含自动创建目录
6013
+ * 统一的写文件方法,包含自动创建目录(带文件互斥锁可选)
6014
+ * @param filePath - 要写入的文件路径
6015
+ * @param content - 要写入的内容
6016
+ * @param options - 可选配置项
6017
+ * @param options.lock - 是否启用文件锁(默认false)
5684
6018
  */
5685
- async writeFileWithDir(filePath, content) {
5686
- await fs.promises.mkdir(path2.dirname(filePath), { recursive: true });
5687
- await fs.promises.writeFile(filePath, content, "utf-8");
6019
+ async writeFileWithDir(filePath, content, options) {
6020
+ if (options?.lock) {
6021
+ await fileLock.updateFile(filePath, async () => content, options);
6022
+ } else {
6023
+ await fs2.promises.mkdir(path3.dirname(filePath), { recursive: true });
6024
+ await fs2.promises.writeFile(filePath, content, "utf-8");
6025
+ }
6026
+ }
6027
+ async rmFile(filePath) {
6028
+ try {
6029
+ await fs2.promises.rm(filePath, { recursive: true, force: true });
6030
+ } catch {
6031
+ }
5688
6032
  }
5689
6033
  async loadCache(key) {
5690
6034
  const cacheFile = this.getCachePath();
5691
6035
  const defaultData = this.createCacheData(key);
5692
- if (!fs.existsSync(cacheFile)) {
6036
+ if (!fs2.existsSync(cacheFile)) {
5693
6037
  return defaultData;
5694
6038
  }
5695
6039
  try {
5696
- const content = await fs.promises.readFile(cacheFile, "utf-8");
6040
+ const content = await fs2.promises.readFile(cacheFile, "utf-8");
5697
6041
  const data = JSON.parse(content);
5698
6042
  return {
5699
6043
  key,
@@ -5721,15 +6065,25 @@ var Helper = class {
5721
6065
  */
5722
6066
  getCachePath() {
5723
6067
  const filename = "_metadata";
5724
- return path2.resolve(this.getProjectRoot(), this.workspaceDir, "cache", `${filename}.json`);
6068
+ return path3.resolve(this.getProjectRoot(), this.workspaceDir, "cache", `${filename}.json`);
5725
6069
  }
5726
6070
  async saveCache(data) {
5727
6071
  if (!this.getIsCache() || !data) {
5728
6072
  return;
5729
6073
  }
5730
- const { key, source, target } = data;
5731
- source[key] = target;
5732
- await this.writeFileWithDir(this.getCachePath(), JSON.stringify(source));
6074
+ const getDefaultValue = () => ({
6075
+ ["sfc" /* SFC */]: [],
6076
+ ["script" /* SCRIPT */]: [],
6077
+ ["style" /* STYLE */]: [],
6078
+ ["copied" /* ASSET */]: []
6079
+ });
6080
+ const cachePath = this.getCachePath();
6081
+ await fileLock.updateFile(cachePath, (currentData) => {
6082
+ const { key, target } = data;
6083
+ const mergedData = currentData || getDefaultValue();
6084
+ mergedData[key] = target;
6085
+ return mergedData;
6086
+ });
5733
6087
  }
5734
6088
  genHash(content) {
5735
6089
  return genHashByXXH(content);
@@ -5741,18 +6095,18 @@ var Helper = class {
5741
6095
  */
5742
6096
  scanFiles(dir, filter) {
5743
6097
  const results = [];
5744
- if (!fs.existsSync(dir)) {
6098
+ if (!fs2.existsSync(dir)) {
5745
6099
  return results;
5746
6100
  }
5747
- const stats = fs.statSync(dir);
6101
+ const stats = fs2.statSync(dir);
5748
6102
  if (stats.isFile()) {
5749
6103
  return filter(dir) ? [dir] : [];
5750
6104
  }
5751
- const list = fs.readdirSync(dir);
6105
+ const list = fs2.readdirSync(dir);
5752
6106
  for (const file of list) {
5753
- const fullPath = path2.resolve(dir, file);
6107
+ const fullPath = path3.resolve(dir, file);
5754
6108
  if (this.shouldSkipPath(fullPath)) continue;
5755
- const stat = fs.statSync(fullPath);
6109
+ const stat = fs2.statSync(fullPath);
5756
6110
  if (stat.isDirectory() && this.compilerOpts.recursive !== false) {
5757
6111
  results.push(...this.scanFiles(fullPath, filter));
5758
6112
  } else if (filter(fullPath)) {
@@ -5762,19 +6116,19 @@ var Helper = class {
5762
6116
  return results;
5763
6117
  }
5764
6118
  getAbsPath(filePath) {
5765
- return path2.isAbsolute(filePath) ? filePath : path2.resolve(this.getProjectRoot(), filePath);
6119
+ return path3.isAbsolute(filePath) ? filePath : path3.resolve(this.getProjectRoot(), filePath);
5766
6120
  }
5767
6121
  async getFileMeta(filePath) {
5768
- const stats = await fs.promises.stat(filePath);
6122
+ const stats = await fs2.promises.stat(filePath);
5769
6123
  return {
5770
6124
  fileSize: stats.size,
5771
6125
  mtime: stats.mtimeMs
5772
6126
  };
5773
6127
  }
5774
6128
  async removeOutputFile(filePath, resolveOutputPath) {
5775
- const path8 = resolveOutputPath ? this.resolveOutputPath(filePath) : filePath;
5776
- if (!fs.existsSync(path8)) return;
5777
- await fs.promises.unlink(path8);
6129
+ const path9 = resolveOutputPath ? this.resolveOutputPath(filePath) : filePath;
6130
+ if (!fs2.existsSync(path9)) return;
6131
+ await fs2.promises.unlink(path9);
5778
6132
  }
5779
6133
  updateCache(targetFile, newData, cache) {
5780
6134
  const index = cache.target.findIndex((c) => c.file === targetFile);
@@ -5784,18 +6138,6 @@ var Helper = class {
5784
6138
  cache.target.push(newData);
5785
6139
  }
5786
6140
  }
5787
- resolveViteCreateApp() {
5788
- const { output } = this.compilerOpts;
5789
- const config = output?.bootstrapVite;
5790
- const template = typeof config === "object" ? config.template : "react-ts";
5791
- const outDirName = this.getOutDirName();
5792
- const cmd = `npm create vite@latest ${outDirName} -- --template ${template}`;
5793
- execSync(cmd, {
5794
- cwd: this.getWorkspaceDir(),
5795
- stdio: "ignore"
5796
- // 隐藏 create-vite 内部的输出日志,保持终端整洁
5797
- });
5798
- }
5799
6141
  /**
5800
6142
  * 获取需要排除编译的文件
5801
6143
  */
@@ -5823,15 +6165,15 @@ var Helper = class {
5823
6165
  }
5824
6166
  printCompileInfo(file, duration) {
5825
6167
  this.print(
5826
- kleur3.green("Compiled"),
5827
- kleur3.dim(normalizePath(this.relativePath(file))),
5828
- kleur3.gray(`(${duration})`)
6168
+ kleur4.green("Compiled"),
6169
+ kleur4.dim(normalizePath(this.relativePath(file))),
6170
+ kleur4.gray(`(${duration})`)
5829
6171
  );
5830
6172
  }
5831
6173
  print(...message) {
5832
6174
  if (this.compilerOpts.watch) {
5833
6175
  const time = (/* @__PURE__ */ new Date()).toLocaleTimeString();
5834
- console.info(kleur3.dim(time), kleur3.cyan(kleur3.bold("[vureact]")), ...message);
6176
+ console.info(kleur4.dim(time), kleur4.cyan(kleur4.bold("[vureact]")), ...message);
5835
6177
  return;
5836
6178
  }
5837
6179
  console.info(...message);
@@ -5839,17 +6181,34 @@ var Helper = class {
5839
6181
  /**
5840
6182
  * 读取 package.json 文件内容,并处理成对象返回
5841
6183
  */
5842
- async resolvePackageFile(path8) {
6184
+ async resolvePackageFile(path9) {
6185
+ if (!fs2.existsSync(path9)) {
6186
+ return {};
6187
+ }
5843
6188
  try {
5844
- if (!fs.existsSync(path8)) {
6189
+ const content = await fs2.promises.readFile(path9, "utf-8");
6190
+ if (!content.trim()) {
5845
6191
  return {};
5846
6192
  }
5847
- return JSON.parse(await fs.promises.readFile(path8, "utf-8"));
6193
+ return JSON.parse(content);
5848
6194
  } catch (error) {
5849
- console.error(error);
6195
+ console.error(kleur4.red("\u274C"), `Failed to parse JSON file ${path9}:
6196
+ `, error);
5850
6197
  return {};
5851
6198
  }
5852
6199
  }
6200
+ /**
6201
+ * 获取目录到文件的相对路径
6202
+ * @returns 结果路径不包含文件拓展名,并以诸如 ./ 开头
6203
+ */
6204
+ resolveRelativePath(from, to) {
6205
+ let relativePath2 = path3.relative(from, to);
6206
+ relativePath2 = relativePath2.substring(0, relativePath2.indexOf("."));
6207
+ if (!relativePath2.startsWith(".")) {
6208
+ relativePath2 = `./${relativePath2}`;
6209
+ }
6210
+ return normalizePath(relativePath2);
6211
+ }
5853
6212
  };
5854
6213
 
5855
6214
  // src/core/parse/style-only.ts
@@ -5886,7 +6245,7 @@ function parseOnlyStyle(source, ctx, options) {
5886
6245
  }
5887
6246
 
5888
6247
  // src/compiler/context/adapter.ts
5889
- import path3 from "path";
6248
+ import path4 from "path";
5890
6249
 
5891
6250
  // src/compiler/context/creator.ts
5892
6251
  function createCompilationCtx() {
@@ -5973,7 +6332,7 @@ var CompilationAdapter = class _CompilationAdapter {
5973
6332
  return ctx;
5974
6333
  }
5975
6334
  static detectInputType(filename) {
5976
- const ext = path3.extname(filename).toLowerCase();
6335
+ const ext = path4.extname(filename).toLowerCase();
5977
6336
  switch (ext) {
5978
6337
  case ".vue":
5979
6338
  return "sfc";
@@ -5997,65 +6356,11 @@ var BaseCompiler = class extends Helper {
5997
6356
  version = version;
5998
6357
  options;
5999
6358
  createContext = CompilationAdapter.createContext;
6000
- /**
6001
- * 创建基础编译器实例
6002
- *
6003
- * @param options - 编译器配置选项,包括缓存设置、插件配置、样式预处理等,
6004
- * 所有选项都会传递给父类 {@link Helper} 进行初始化
6005
- *
6006
- * @see {@link CompilerOptions} 查看完整的配置选项说明
6007
- */
6008
6359
  constructor(options = {}) {
6009
6360
  super(options);
6010
6361
  this.options = options;
6011
6362
  }
6012
- /**
6013
- * 编译 Vue 源代码为 React 代码
6014
- *
6015
- * 该方法执行完整的编译流程,包括:
6016
- * 1. 上下文初始化:创建独立的编译上下文
6017
- * 2. 解析阶段:将 Vue 源代码解析为抽象语法树(AST)
6018
- * 3. 转换阶段:将 Vue AST 转换为 React 中间表示(IR)
6019
- * 4. 生成阶段:将 React IR 生成为目标代码
6020
- * 5. 插件执行:在各阶段执行注册的插件
6021
- * 6. 结果处理:整理编译结果并返回
6022
- *
6023
- * 编译过程使用 try-finally 确保上下文资源被正确清理,
6024
- * 即使编译过程中发生错误也不会导致资源泄漏。
6025
- *
6026
- * @param source - Vue 源代码字符串
6027
- * @param filename - 源文件名,用于生成文件ID和输出路径
6028
- * @returns {CompilationResult} 编译结果,包含生成的代码和文件信息
6029
- *
6030
- * @example
6031
- * ```typescript
6032
- * const vueCode = `
6033
- * <template>
6034
- * <div class="container">{{ message }}</div>
6035
- * </template>
6036
- *
6037
- * <script setup lang="ts">
6038
- * import { ref } from 'vue;
6039
- * const message = ref('Hello Vue')
6040
- * </script>
6041
- *
6042
- * <style scoped>
6043
- * .container {
6044
- * padding: 20px;
6045
- * }
6046
- * </style>
6047
- * `;
6048
- *
6049
- * const result = compiler.compile(vueCode, 'MyComponent.vue');
6050
- * console.log(result);
6051
- * ```
6052
- *
6053
- * @throws 不会直接抛出异常,错误通过日志系统记录
6054
- * @see {@link parse} 解析函数
6055
- * @see {@link transform} 转换函数
6056
- * @see {@link generate} 生成函数
6057
- * @see {@link executePlugins} 插件执行函数
6058
- */
6363
+ /** 编译 Vue 源代码为 React 代码 */
6059
6364
  compile(source, filename) {
6060
6365
  const { plugins, preprocessStyles = true } = this.options;
6061
6366
  const fileId = this.genHash(filename);
@@ -6091,61 +6396,12 @@ var BaseCompiler = class extends Helper {
6091
6396
  ctx.clear();
6092
6397
  }
6093
6398
  }
6094
- /**
6095
- * 初始化 Babel 代码生成选项
6096
- *
6097
- * 合并用户自定义的生成选项与默认选项,确保代码生成的一致性和正确性。
6098
- * 默认配置优化了代码的可读性和性能:
6099
- * 1. 最小化 Unicode 转义:只转义必要的字符,保持代码可读性
6100
- * 2. 统一引号风格:使用单引号,符合 JavaScript 社区常见约定
6101
- * 3. 代码压缩:启用最小化,移除不必要的空白字符
6102
- *
6103
- * 如果用户提供了自定义选项,会与默认选项进行深度合并,
6104
- * 用户选项的优先级高于默认选项。
6105
- *
6106
- * @private
6107
- * @param filename - 可选的文件名,用于源码映射配置
6108
- * @returns {GeneratorOptions} 合并后的 Babel 生成选项
6109
- *
6110
- * @remarks
6111
- * - 源码映射支持:如果启用了 sourceMaps 且提供了文件名,会自动设置 sourceFileName
6112
- * - 选项合并策略:用户选项会覆盖默认选项,实现灵活的配置
6113
- * - 性能优化:默认启用 minified 以减少生成代码的体积
6114
- * - 编码安全:使用 jsesc 确保特殊字符的正确转义
6115
- *
6116
- * @example
6117
- * ```typescript
6118
- * // 默认选项
6119
- * const defaultOptions = {
6120
- * jsescOption: { minimal: true, quotes: 'single' },
6121
- * minified: true
6122
- * };
6123
- *
6124
- * // 用户自定义选项
6125
- * const userOptions = {
6126
- * minified: false, // 覆盖默认值
6127
- * sourceMaps: true // 新增选项
6128
- * };
6129
- *
6130
- * // 合并结果
6131
- * const merged = {
6132
- * jsescOption: { minimal: true, quotes: 'single' },
6133
- * minified: false, // 用户值
6134
- * sourceMaps: true // 用户值
6135
- * };
6136
- * ```
6137
- *
6138
- * @see {@link GeneratorOptions} Babel 生成选项类型定义
6139
- */
6140
6399
  prepareGenerateOptions(filename) {
6141
6400
  const userOptions = this.options.generate || {};
6142
6401
  const mergedOptions = {
6143
- // 配置 jsesc 避免 Unicode 转义
6144
6402
  jsescOption: {
6145
6403
  minimal: true,
6146
- // 只转义必要的字符
6147
6404
  quotes: "single"
6148
- // 使用单引号
6149
6405
  },
6150
6406
  minified: true,
6151
6407
  ...userOptions
@@ -6155,36 +6411,6 @@ var BaseCompiler = class extends Helper {
6155
6411
  }
6156
6412
  return mergedOptions;
6157
6413
  }
6158
- /**
6159
- * 处理 SFC 和 Script 文件的编译结果
6160
- *
6161
- * 根据编译上下文中的输入类型(SFC 或 Script),整理并返回相应的编译结果。
6162
- * 对于 SFC 文件,返回包含 JSX 和 CSS 信息的完整结果;
6163
- * 对于 Script 文件,返回仅包含脚本信息的结果。
6164
- *
6165
- * 关键处理逻辑:
6166
- * 1. 构建基础结果:包含文件ID、路由信息和生成的代码
6167
- * 2. 确定输出路径:根据源文件路径和语言类型生成输出文件路径
6168
- * 3. 区分文件类型:
6169
- * - SFC 文件:生成 .tsx 扩展名,包含样式信息
6170
- * - Script 文件:保持原扩展名,不包含样式信息
6171
- * 4. 样式处理:提取样式文件路径、作用域ID和样式代码
6172
- *
6173
- * @private
6174
- * @param ir - React 中间表示(IR)描述符,包含转换后的组件信息
6175
- * @param gen - 代码生成结果,包含生成的代码和源码映射
6176
- * @param ctxData - 编译上下文数据,包含文件信息和编译状态
6177
- * @returns {CompilationResult} 整理后的编译结果
6178
- *
6179
- * @remarks
6180
- * - 文件扩展名处理:Vue SFC 文件会转换为 .tsx 或 .jsx 文件
6181
- * - 样式分离:SFC 中的样式会被提取到独立的 CSS 文件中
6182
- * - 路由检测:自动检测组件是否使用路由,用于依赖注入
6183
- * - 作用域样式:为 Scoped CSS 生成唯一的作用域ID
6184
- *
6185
- * @see {@link SFCCompilationResult} SFC 编译结果类型
6186
- * @see {@link ScriptCompilationResult} Script 编译结果类型
6187
- */
6188
6414
  resolveMainResult(ir, gen, ctxData) {
6189
6415
  const { fileId, filename, scriptData, styleData, inputType } = ctxData;
6190
6416
  const base = {
@@ -6199,7 +6425,6 @@ var BaseCompiler = class extends Helper {
6199
6425
  fileInfo: {
6200
6426
  jsx: {
6201
6427
  file: `${file}x`,
6202
- // 'xxx.ts' + 'x' => 'xxx.tsx'
6203
6428
  lang
6204
6429
  },
6205
6430
  css: {
@@ -6216,11 +6441,6 @@ var BaseCompiler = class extends Helper {
6216
6441
  ...base
6217
6442
  };
6218
6443
  }
6219
- /**
6220
- * 处理 Style 文件的编译结果
6221
- * @param data style 文件解析结果
6222
- * @param ctxData 上下文数据
6223
- */
6224
6444
  resolveStyleResult(data, ctxData) {
6225
6445
  const { fileId, filename } = ctxData;
6226
6446
  const { lang, content = "" } = data.style.source;
@@ -6253,13 +6473,12 @@ function formattDuration(n) {
6253
6473
  }
6254
6474
 
6255
6475
  // src/compiler/shared/file-compiler/index.ts
6256
- import fs5 from "fs";
6257
- import kleur6 from "kleur";
6476
+ import kleur8 from "kleur";
6258
6477
  import ora2 from "ora";
6259
6478
 
6260
6479
  // src/compiler/shared/file-compiler/asset-manager.ts
6261
- import fs2 from "fs";
6262
- import path4 from "path";
6480
+ import fs3 from "fs";
6481
+ import path5 from "path";
6263
6482
  var AssetManager = class {
6264
6483
  constructor(fileCompiler, cleanupManager) {
6265
6484
  this.fileCompiler = fileCompiler;
@@ -6267,19 +6486,21 @@ var AssetManager = class {
6267
6486
  }
6268
6487
  // 需要经过管线编译处理的文件类型
6269
6488
  pipelineFiles = [".js", ".ts", ".less", ".scss", ".sass"];
6489
+ skippedCount = 0;
6270
6490
  /**
6271
6491
  * 运行资源文件处理管线
6272
6492
  */
6273
6493
  async runAssetPipeline() {
6494
+ const { options } = this.fileCompiler;
6274
6495
  const rootPath = this.fileCompiler.getProjectRoot();
6275
6496
  const inputPath = this.fileCompiler.getInputPath();
6276
6497
  const exclusions = this.fileCompiler.getIgnoreAssets();
6277
6498
  const assetFiles = this.fileCompiler.scanFiles(rootPath, (p) => {
6278
6499
  if (this.fileCompiler.shouldSkipPath(p)) return false;
6279
6500
  const relativeToRoot = normalizePath(this.fileCompiler.relativePath(p));
6280
- const filename = path4.basename(p).toLowerCase();
6281
- const ext = path4.extname(p).toLowerCase();
6282
- if (!this.fileCompiler.options.output?.ignoreAssets) {
6501
+ const filename = path5.basename(p).toLowerCase();
6502
+ const ext = path5.extname(p).toLowerCase();
6503
+ if (!options.output?.ignoreAssets) {
6283
6504
  const shouldExclude = Array.from(exclusions).some((pattern) => {
6284
6505
  if (pattern.endsWith(".")) {
6285
6506
  return filename.startsWith(pattern);
@@ -6289,34 +6510,28 @@ var AssetManager = class {
6289
6510
  }
6290
6511
  return relativeToRoot === pattern || filename === pattern;
6291
6512
  });
6292
- if (shouldExclude) {
6293
- return false;
6294
- }
6513
+ if (shouldExclude) return false;
6295
6514
  } else if (exclusions.has(relativeToRoot) || exclusions.has(filename)) {
6296
6515
  return false;
6297
6516
  }
6298
6517
  if (ext === ".vue") return false;
6299
- const isInsideSrc = p.startsWith(inputPath + path4.sep);
6518
+ const isInsideSrc = p.startsWith(inputPath + path5.sep);
6300
6519
  if (isInsideSrc && this.pipelineFiles.includes(ext)) {
6301
6520
  return false;
6302
6521
  }
6303
6522
  return true;
6304
6523
  });
6305
6524
  const absFiles = new Set(assetFiles.map((f) => this.fileCompiler.getAbsPath(f)));
6306
- const cache = await this.fileCompiler.loadCache("copied" /* ASSET */);
6307
6525
  await this.cleanupManager.cleanupOldOutput("copied" /* ASSET */, (u) => !absFiles.has(u.file));
6308
- await this.updateAssetCaches(assetFiles, cache);
6309
- return assetFiles.length;
6310
- }
6311
- /**
6312
- * 更新资源文件缓存
6313
- */
6314
- async updateAssetCaches(files, cache) {
6315
- for (const file of files) {
6316
- const meta = await this.processAsset(file, cache);
6317
- this.updateCache(file, meta, cache);
6318
- }
6526
+ if (!assetFiles.length) return 0;
6527
+ const cache = await this.fileCompiler.loadCache("copied" /* ASSET */);
6528
+ const copied = await Promise.all(
6529
+ assetFiles.map((file) => {
6530
+ return this.processAsset(file, cache);
6531
+ })
6532
+ );
6319
6533
  await this.fileCompiler.saveCache(cache);
6534
+ return copied.filter(Boolean).length;
6320
6535
  }
6321
6536
  /**
6322
6537
  * Process single asset file, compare with cache and decide whether to copy.
@@ -6330,11 +6545,16 @@ var AssetManager = class {
6330
6545
  const cache = (this.fileCompiler.getIsCache() ? existingCache : void 0) || await this.fileCompiler.loadCache("copied" /* ASSET */);
6331
6546
  const record = cache.target.find((f) => f.file === absPath);
6332
6547
  if (record && this.fileCompiler.compareFileMeta(record, fileMeta)) {
6333
- return fileMeta;
6548
+ this.skippedCount++;
6549
+ return;
6334
6550
  }
6335
6551
  const outputPath = this.fileCompiler.resolveOutputPath(absPath);
6336
- await fs2.promises.mkdir(path4.dirname(outputPath), { recursive: true });
6337
- await fs2.promises.copyFile(absPath, outputPath);
6552
+ await fs3.promises.mkdir(path5.dirname(outputPath), { recursive: true });
6553
+ await fs3.promises.copyFile(absPath, outputPath);
6554
+ this.updateCache(absPath, fileMeta, cache);
6555
+ if (this.fileCompiler.getIsCache() && !existingCache) {
6556
+ await this.fileCompiler.saveCache(cache);
6557
+ }
6338
6558
  return fileMeta;
6339
6559
  }
6340
6560
  /**
@@ -6349,6 +6569,18 @@ var AssetManager = class {
6349
6569
  cache.target.push(newData);
6350
6570
  }
6351
6571
  }
6572
+ /**
6573
+ * 获取跳过的文件数量
6574
+ */
6575
+ getSkippedCount() {
6576
+ return this.skippedCount;
6577
+ }
6578
+ /**
6579
+ * 重置跳过的文件数量
6580
+ */
6581
+ resetSkippedCount() {
6582
+ this.skippedCount = 0;
6583
+ }
6352
6584
  };
6353
6585
 
6354
6586
  // src/compiler/shared/file-compiler/cache-manager.ts
@@ -6356,12 +6588,12 @@ var CacheManager = class {
6356
6588
  constructor(fileCompiler) {
6357
6589
  this.fileCompiler = fileCompiler;
6358
6590
  }
6591
+ pendingUpdates = /* @__PURE__ */ new Map();
6359
6592
  /**
6360
- * 增量更新缓存记录
6593
+ * 批量更新缓存记录
6361
6594
  */
6362
6595
  async updateCacheIncrementally(unit, key) {
6363
6596
  if (!this.fileCompiler.getIsCache()) return;
6364
- const cache = await this.fileCompiler.loadCache(key);
6365
6597
  const meta = { ...unit };
6366
6598
  delete meta.source;
6367
6599
  if (key === "sfc" /* SFC */) {
@@ -6372,8 +6604,26 @@ var CacheManager = class {
6372
6604
  } else if (key === "style" /* STYLE */) {
6373
6605
  delete meta.output?.style.code;
6374
6606
  }
6375
- this.updateCache(unit.file, meta, cache);
6607
+ if (!this.pendingUpdates.has(key)) {
6608
+ this.pendingUpdates.set(key, []);
6609
+ }
6610
+ this.pendingUpdates.get(key).push({ unit, meta });
6611
+ }
6612
+ /**
6613
+ * 批量保存缓存
6614
+ */
6615
+ async flushCache(key) {
6616
+ if (!this.fileCompiler.getIsCache() || !this.pendingUpdates.has(key)) {
6617
+ return;
6618
+ }
6619
+ const updates = this.pendingUpdates.get(key);
6620
+ if (updates.length === 0) return;
6621
+ const cache = await this.fileCompiler.loadCache(key);
6622
+ for (const { unit, meta } of updates) {
6623
+ this.updateCache(unit.file, meta, cache);
6624
+ }
6376
6625
  await this.fileCompiler.saveCache(cache);
6626
+ this.pendingUpdates.set(key, []);
6377
6627
  }
6378
6628
  /**
6379
6629
  * 更新缓存
@@ -6389,7 +6639,7 @@ var CacheManager = class {
6389
6639
  };
6390
6640
 
6391
6641
  // src/compiler/shared/file-compiler/cleanup-manager.ts
6392
- import path5 from "path";
6642
+ import path6 from "path";
6393
6643
  var CleanupManager = class {
6394
6644
  constructor(fileCompiler) {
6395
6645
  this.fileCompiler = fileCompiler;
@@ -6404,7 +6654,7 @@ var CleanupManager = class {
6404
6654
  (u) => u.file === absPath || // 加 path.sep 是因为假如删除了 src/components 文件夹,
6405
6655
  // 为了防止误删名为 src/components-old 的文件夹,
6406
6656
  // 所以必须确保路径后跟着一个分隔符,确保精准匹配子目录内容。
6407
- u.file.startsWith(absPath + path5.sep)
6657
+ u.file.startsWith(absPath + path6.sep)
6408
6658
  );
6409
6659
  }
6410
6660
  /**
@@ -6416,25 +6666,43 @@ var CleanupManager = class {
6416
6666
  const toRemove = cache.target.filter(filter);
6417
6667
  if (!toRemove.length) return;
6418
6668
  const removeFn = async (m) => {
6419
- if (key === "sfc" /* SFC */) {
6420
- const meta = m;
6421
- if (!meta?.output) return;
6422
- const { jsx, css } = meta.output;
6423
- await this.fileCompiler.removeOutputFile(jsx.file);
6424
- if (css?.file) {
6425
- await this.fileCompiler.removeOutputFile(css.file);
6669
+ let meta;
6670
+ switch (key) {
6671
+ case "sfc" /* SFC */: {
6672
+ meta = m;
6673
+ const { jsx, css } = meta.output;
6674
+ await this.fileCompiler.removeOutputFile(jsx.file);
6675
+ if (css?.file) {
6676
+ await this.fileCompiler.removeOutputFile(css.file);
6677
+ }
6678
+ break;
6679
+ }
6680
+ case "script" /* SCRIPT */: {
6681
+ meta = m;
6682
+ await this.fileCompiler.removeOutputFile(meta.output.script.file);
6683
+ break;
6684
+ }
6685
+ case "style" /* STYLE */: {
6686
+ meta = m;
6687
+ await this.fileCompiler.removeOutputFile(meta.output.style.file);
6688
+ break;
6689
+ }
6690
+ // 静态资产缓存直接删除对应文件
6691
+ default: {
6692
+ await this.fileCompiler.removeOutputFile(m.file, true);
6693
+ break;
6426
6694
  }
6427
- } else if (key === "script" /* SCRIPT */ || key === "copied" /* ASSET */) {
6428
- await this.fileCompiler.removeOutputFile(m.file, true);
6429
6695
  }
6430
6696
  };
6431
6697
  await Promise.all(toRemove.map(removeFn));
6698
+ const removedFiles = new Set(toRemove.map((m) => m.file));
6699
+ cache.target = cache.target.filter((m) => !removedFiles.has(m.file));
6432
6700
  await this.fileCompiler.saveCache(cache);
6433
6701
  }
6434
6702
  };
6435
6703
 
6436
6704
  // src/compiler/shared/file-compiler/compilation-unit.ts
6437
- import kleur4 from "kleur";
6705
+ import kleur5 from "kleur";
6438
6706
  var CompilationUnitProcessor = class {
6439
6707
  constructor(fileCompiler) {
6440
6708
  this.fileCompiler = fileCompiler;
@@ -6449,7 +6717,7 @@ var CompilationUnitProcessor = class {
6449
6717
  this.resolveResult(result, unit, key);
6450
6718
  } catch (err) {
6451
6719
  console.info(
6452
- kleur4.red(`\u2716`),
6720
+ kleur5.red(`\u2716`),
6453
6721
  `Failed to compile ${this.fileCompiler.relativePath(unit.file)}
6454
6722
  `,
6455
6723
  err
@@ -6530,20 +6798,16 @@ var CompilationUnitProcessor = class {
6530
6798
  };
6531
6799
 
6532
6800
  // src/compiler/shared/file-compiler/file-processor.ts
6533
- import fs3 from "fs";
6534
- import path6 from "path";
6801
+ import fs4 from "fs";
6802
+ import kleur6 from "kleur";
6803
+ import path7 from "path";
6535
6804
  var FileProcessor = class {
6536
6805
  constructor(fileCompiler, compilationUnitProcessor, cacheManager) {
6537
6806
  this.fileCompiler = fileCompiler;
6538
6807
  this.compilationUnitProcessor = compilationUnitProcessor;
6539
6808
  this.cacheManager = cacheManager;
6540
6809
  }
6541
- pkgs = {
6542
- router: {
6543
- name: PACKAGE_NAME.router,
6544
- version: "^1.0.0"
6545
- }
6546
- };
6810
+ skippedCount = 0;
6547
6811
  /**
6548
6812
  * Process a single Vue file (this method is called directly in CLI Watch mode)
6549
6813
  */
@@ -6570,10 +6834,13 @@ var FileProcessor = class {
6570
6834
  const { shouldCompile, hash } = await this.fileCompiler.checkCacheStatus(
6571
6835
  fileMeta,
6572
6836
  record,
6573
- () => fs3.promises.readFile(absPath, "utf-8")
6837
+ () => fs4.promises.readFile(absPath, "utf-8")
6574
6838
  );
6575
- if (!shouldCompile) return;
6576
- const source = await fs3.promises.readFile(absPath, "utf-8");
6839
+ if (!shouldCompile) {
6840
+ this.skippedCount++;
6841
+ return;
6842
+ }
6843
+ const source = await fs4.promises.readFile(absPath, "utf-8");
6577
6844
  if (!source.trim()) return;
6578
6845
  const initUnit = {
6579
6846
  ...fileMeta,
@@ -6589,46 +6856,108 @@ var FileProcessor = class {
6589
6856
  await this.compilationUnitProcessor.saveCompiledFiles(processed, key);
6590
6857
  if (key === "sfc" /* SFC */ || key === "script" /* SCRIPT */) {
6591
6858
  if (processed?.hasRoute) {
6592
- await this.injectVuReactRouteDep();
6593
- await this.copyRouteSetupNotes();
6859
+ await this.addRouterToPackageJson();
6860
+ await this.updateEntryWithRouterProvider();
6594
6861
  }
6595
6862
  }
6596
6863
  await this.cacheManager.updateCacheIncrementally(processed, key);
6597
6864
  }
6598
6865
  return processed;
6599
6866
  }
6600
- async injectVuReactRouteDep() {
6601
- const pkgPath = this.fileCompiler.getOutputPkgPath();
6602
- const pkg = await this.fileCompiler.resolvePackageFile(pkgPath);
6603
- const { router } = this.pkgs;
6604
- if (!pkg["dependencies"]) {
6605
- pkg["dependencies"] = {};
6867
+ /**
6868
+ * package.json 注入路由依赖项
6869
+ */
6870
+ async addRouterToPackageJson() {
6871
+ const { output } = this.fileCompiler.options;
6872
+ if (output?.bootstrapVite === false) {
6873
+ return;
6606
6874
  }
6607
- pkg["dependencies"][router.name] = router.version;
6608
- await fs3.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2), "utf-8");
6875
+ const { router } = RUNTIME_PACKAGES;
6876
+ const filePath = this.fileCompiler.getOutputPkgPath();
6877
+ const packageJson = await this.fileCompiler.resolvePackageFile(filePath);
6878
+ if (packageJson?.dependencies?.[router.name]) {
6879
+ return;
6880
+ }
6881
+ if (!packageJson.dependencies) {
6882
+ packageJson.dependencies = {};
6883
+ }
6884
+ packageJson.dependencies[router.name] = router.version;
6885
+ const newData = JSON.stringify(packageJson, null, 2);
6886
+ await this.fileCompiler.writeFileWithDir(filePath, newData, {
6887
+ lock: true
6888
+ });
6609
6889
  }
6610
6890
  /**
6611
- * 如果使用了路由,则拷贝路由配置说明文档到输出目录根部。
6891
+ * 注入路由提供器到 React 应用的入口文件(如 main.tsx)
6612
6892
  */
6613
- async copyRouteSetupNotes() {
6614
- const outputDir = this.fileCompiler.getOuputPath();
6615
- const packageRoot = path6.resolve(getDirname(import.meta.url), "../");
6616
- const templateDir = path6.join(packageRoot, "templates");
6617
- if (!fs3.existsSync(templateDir)) {
6893
+ async updateEntryWithRouterProvider() {
6894
+ const { exclude, output, router } = this.fileCompiler.options;
6895
+ const inputPath = this.fileCompiler.getInputPath();
6896
+ const outputPath = this.fileCompiler.getOuputPath(true);
6897
+ if (output?.bootstrapVite === false || router?.autoUpdateEntry === false || !router?.configFile) {
6618
6898
  return;
6619
6899
  }
6620
- const files = ["route-setup-notes.md", "route-setup-notes.zh.md"];
6621
- for (const file of files) {
6622
- const srcPath = path6.join(templateDir, file);
6623
- if (!fs3.existsSync(srcPath)) continue;
6624
- const destPath = path6.join(outputDir, file);
6625
- await fs3.promises.copyFile(srcPath, destPath);
6900
+ const getMainFile = async (filename) => {
6901
+ const filePath = path7.resolve(outputPath, filename);
6902
+ try {
6903
+ return { filePath, content: await fs4.promises.readFile(filePath, "utf-8") };
6904
+ } catch {
6905
+ return;
6906
+ }
6907
+ };
6908
+ const fileData = await getMainFile("main.tsx") || await getMainFile("main.jsx");
6909
+ if (!fileData) {
6910
+ console.warn(
6911
+ `${kleur6.yellow("\u26A0\uFE0F")} React application entry file not found, please confirm the filename is main.tsx or main.jsx?`
6912
+ );
6913
+ return;
6626
6914
  }
6915
+ const prepareRouterEntry = () => {
6916
+ const importPath = this.fileCompiler.resolveRelativePath(inputPath, router.configFile);
6917
+ let content = fileData.content;
6918
+ if (exclude?.includes(router.configFile) || exclude?.includes(importPath)) {
6919
+ return content;
6920
+ }
6921
+ const routerModule = "RouterInstance";
6922
+ if (content.includes(routerModule) || content.includes(importPath)) {
6923
+ return content;
6924
+ }
6925
+ const routerImport = `import ${routerModule} from '${importPath}'`;
6926
+ if (content.includes("./App")) {
6927
+ const appImportRegex = /import\s+(?:App|.*)\s+from\s+['"]\.\/App\.(?:tsx|jsx)['"]/;
6928
+ content = content.replace(appImportRegex, routerImport);
6929
+ } else {
6930
+ const lastImportMatch = content.match(/import.*from.*\n/g);
6931
+ if (lastImportMatch) {
6932
+ const lastImport = lastImportMatch.pop();
6933
+ content = content.replace(lastImport, `${lastImport}
6934
+ ${routerImport}`);
6935
+ } else {
6936
+ content = `${routerImport}${content}`;
6937
+ }
6938
+ }
6939
+ const providerTag = `<${routerModule}.RouterProvider />`;
6940
+ fileData.content = content.replace(/<App\s*\/?>/g, providerTag);
6941
+ };
6942
+ prepareRouterEntry();
6943
+ await this.fileCompiler.writeFileWithDir(fileData.filePath, fileData.content);
6944
+ }
6945
+ /**
6946
+ * 获取跳过的文件数量
6947
+ */
6948
+ getSkippedCount() {
6949
+ return this.skippedCount;
6950
+ }
6951
+ /**
6952
+ * 重置跳过的文件数量
6953
+ */
6954
+ resetSkippedCount() {
6955
+ this.skippedCount = 0;
6627
6956
  }
6628
6957
  };
6629
6958
 
6630
6959
  // src/compiler/shared/file-compiler/pipeline-manager.ts
6631
- import path7 from "path";
6960
+ import path8 from "path";
6632
6961
  var PipelineManager = class {
6633
6962
  constructor(fileCompiler, fileProcessor) {
6634
6963
  this.fileCompiler = fileCompiler;
@@ -6663,7 +6992,7 @@ var PipelineManager = class {
6663
6992
  const scriptExtRegex = /\.(js|ts)$/i;
6664
6993
  const styleExtRegex = /\.(css|less|sass|scss)$/i;
6665
6994
  const files = this.fileCompiler.scanFiles(inputPath, (p) => {
6666
- const ext = path7.extname(p);
6995
+ const ext = path8.extname(p);
6667
6996
  if (key === "sfc" /* SFC */) {
6668
6997
  return ext === ".vue";
6669
6998
  }
@@ -6675,28 +7004,21 @@ var PipelineManager = class {
6675
7004
  }
6676
7005
  return false;
6677
7006
  });
6678
- if (!files.length) return 0;
6679
- const cache = await this.fileCompiler.loadCache(key);
6680
7007
  const absFiles = new Set(files.map((f) => this.fileCompiler.getAbsPath(f)));
6681
7008
  await this.cleanupManager.cleanupOldOutput(key, (c) => !absFiles.has(c.file));
6682
- const results = await Promise.all(
6683
- files.map(async (f) => {
6684
- if (key === "sfc" /* SFC */) {
6685
- return this.fileProcessor.processFile(key, f, cache);
6686
- } else {
6687
- return this.fileProcessor.processFile(key, f, cache);
6688
- }
6689
- })
7009
+ if (!files.length) return 0;
7010
+ const cache = await this.fileCompiler.loadCache(key);
7011
+ const compiled = await Promise.all(
7012
+ files.map(async (f) => this.fileProcessor.processFile(key, f, cache))
6690
7013
  );
6691
- const compiledCount = results.filter(Boolean).length;
6692
- this.skippedCount += files.length - compiledCount;
6693
- return compiledCount;
7014
+ await this.fileCompiler.flushCache(key);
7015
+ return compiled.filter(Boolean).length;
6694
7016
  }
6695
7017
  /**
6696
7018
  * 获取跳过的文件数量
6697
7019
  */
6698
7020
  getSkippedCount() {
6699
- return this.skippedCount;
7021
+ return this.skippedCount += this.fileProcessor.getSkippedCount();
6700
7022
  }
6701
7023
  /**
6702
7024
  * 重置跳过的文件数量
@@ -6707,9 +7029,9 @@ var PipelineManager = class {
6707
7029
  };
6708
7030
 
6709
7031
  // src/compiler/shared/file-compiler/vite-bootstrapper.ts
6710
- import { execSync as execSync2 } from "child_process";
6711
- import fs4 from "fs";
6712
- import kleur5 from "kleur";
7032
+ import { execSync } from "child_process";
7033
+ import fs5 from "fs";
7034
+ import kleur7 from "kleur";
6713
7035
  import ora from "ora";
6714
7036
  var ViteBootstrapper = class {
6715
7037
  constructor(fileCompiler, options) {
@@ -6717,52 +7039,50 @@ var ViteBootstrapper = class {
6717
7039
  this.options = options;
6718
7040
  }
6719
7041
  spinner = ora();
6720
- pkgs = {
6721
- runtime: {
6722
- name: PACKAGE_NAME.runtime,
6723
- version: "^1.0.0"
6724
- }
7042
+ defaultConfig = {
7043
+ template: "react-ts",
7044
+ viteVersion: "@latest"
6725
7045
  };
6726
7046
  /**
6727
7047
  * 检查是否需要初始化 Vite 环境
6728
7048
  */
6729
7049
  isSingleFile() {
6730
7050
  const inputPath = this.fileCompiler.getInputPath();
6731
- if (!fs4.existsSync(inputPath)) {
7051
+ if (!fs5.existsSync(inputPath)) {
6732
7052
  return false;
6733
7053
  }
6734
- const stat = fs4.statSync(inputPath);
7054
+ const stat = fs5.statSync(inputPath);
6735
7055
  return stat.isFile();
6736
7056
  }
6737
7057
  /**
6738
7058
  * 利用 Vite 官方脚手架创建标准 React 环境
6739
7059
  */
6740
7060
  async bootstrapIfNeeded() {
6741
- const { bootstrapVite } = this.options.output || {};
6742
- if (bootstrapVite === false) {
7061
+ const { output } = this.options;
7062
+ const workspaceDir = this.fileCompiler.getWorkspaceDir();
7063
+ await fs5.promises.mkdir(workspaceDir, { recursive: true });
7064
+ if (output?.bootstrapVite === false) {
6743
7065
  return false;
6744
7066
  }
6745
7067
  if (this.isSingleFile()) {
6746
- console.info(kleur5.dim("Skipping Vite initialization for single file compilation"));
6747
- return false;
7068
+ console.info("Skipping Vite initialization for single file compilation");
7069
+ return;
6748
7070
  }
6749
- const workspaceDir = this.fileCompiler.getWorkspaceDir();
6750
7071
  const outputPkgPath = this.fileCompiler.getOutputPkgPath();
6751
- if (fs4.existsSync(outputPkgPath)) {
6752
- return false;
7072
+ if (fs5.existsSync(outputPkgPath)) {
7073
+ return;
6753
7074
  }
6754
- this.spinner.start("Bootstrapping Vite React environment...");
6755
- await fs4.promises.mkdir(workspaceDir, { recursive: true });
6756
7075
  try {
6757
- this.resolveViteCreateApp();
7076
+ this.spinner.start("Bootstrapping Vite React environment...");
7077
+ await this.resolveViteCreateApp();
6758
7078
  } catch (err) {
6759
- this.spinner.stop();
6760
7079
  console.error(
6761
- kleur5.red("\u2716"),
6762
- "Failed to bootstrap Vite environment. Please check npm/network\n",
7080
+ kleur7.red("\u2716"),
7081
+ "Failed to bootstrap Vite environment. Please check npm/network.\n",
6763
7082
  err
6764
7083
  );
6765
- return false;
7084
+ this.spinner.stop();
7085
+ return;
6766
7086
  }
6767
7087
  const removeVuePackages = (deps) => {
6768
7088
  for (const name in deps) {
@@ -6779,103 +7099,134 @@ var ViteBootstrapper = class {
6779
7099
  ...vite
6780
7100
  };
6781
7101
  if (!isDev) {
6782
- const { runtime } = this.pkgs;
7102
+ const { runtime } = RUNTIME_PACKAGES;
6783
7103
  deps[runtime.name] = runtime.version;
6784
7104
  }
6785
7105
  return deps;
6786
7106
  };
6787
- const rootPkgPath = this.fileCompiler.getRootPkgPath();
6788
- const rootPkg = await this.fileCompiler.resolvePackageFile(rootPkgPath);
6789
- const vitePkg = await this.fileCompiler.resolvePackageFile(outputPkgPath);
6790
- const newDeps = resolveDeps(rootPkg.dependencies, vitePkg.dependencies);
6791
- const newDevDeps = resolveDeps(rootPkg.devDependencies, vitePkg.devDependencies, true);
6792
- vitePkg.dependencies = newDeps;
6793
- vitePkg.devDependencies = newDevDeps;
6794
- await fs4.promises.writeFile(outputPkgPath, JSON.stringify(vitePkg, null, 2), "utf-8");
7107
+ const sourcePkgPath = this.fileCompiler.getRootPkgPath();
7108
+ const sourcePkg = await this.fileCompiler.resolvePackageFile(sourcePkgPath);
7109
+ let newPkg = await this.fileCompiler.resolvePackageFile(outputPkgPath);
7110
+ const newDeps = resolveDeps(sourcePkg.dependencies, newPkg.dependencies);
7111
+ const newDevDeps = resolveDeps(sourcePkg.devDependencies, newPkg.devDependencies, true);
7112
+ newPkg.dependencies = newDeps;
7113
+ newPkg.devDependencies = newDevDeps;
7114
+ newPkg = output?.packageJson?.(newPkg) || newPkg;
7115
+ await this.fileCompiler.writeFileWithDir(outputPkgPath, JSON.stringify(newPkg, null, 2));
6795
7116
  this.spinner.succeed("Standard Vite React environment initialized");
6796
7117
  return true;
6797
7118
  }
6798
7119
  /**
6799
7120
  * 执行 vite 创建命令
6800
7121
  */
6801
- resolveViteCreateApp() {
7122
+ async resolveViteCreateApp() {
6802
7123
  const { output } = this.options;
6803
- const config = output?.bootstrapVite;
6804
- const template = typeof config === "object" ? config.template : "react-ts";
7124
+ const { viteVersion, template: tmpl } = this.defaultConfig;
7125
+ const bootstrapVite = output?.bootstrapVite;
6805
7126
  const outDirName = this.fileCompiler.getOutDirName();
6806
- const cmd = `npm create vite@latest ${outDirName} -- --template ${template}`;
6807
- execSync2(cmd, {
7127
+ const configObject = typeof bootstrapVite === "object" ? bootstrapVite : null;
7128
+ const viteVer = configObject?.vite || viteVersion;
7129
+ const reactVer = configObject?.react;
7130
+ const template = configObject?.template || tmpl;
7131
+ const cmd = `npm create vite${viteVer} ${outDirName} -- --template ${template}`;
7132
+ execSync(cmd, {
6808
7133
  cwd: this.fileCompiler.getWorkspaceDir(),
6809
7134
  stdio: "ignore"
6810
7135
  // 隐藏 create-vite 内部的输出日志,保持终端整洁
6811
7136
  });
7137
+ if (reactVer) {
7138
+ await this.resolveReactVersion(reactVer);
7139
+ }
6812
7140
  }
6813
- };
6814
-
6815
- // src/compiler/shared/file-compiler/index.ts
6816
- var FileCompiler = class extends BaseCompiler {
6817
- skippedCount = 0;
6818
- spinner = ora2();
6819
- // 管理器实例
6820
- viteBootstrapper;
6821
- pipelineManager;
6822
- fileProcessor;
6823
- compilationUnitProcessor;
6824
- cacheManager;
6825
- assetManager;
6826
- cleanupManager;
6827
7141
  /**
6828
- * 创建文件系统编译器实例
6829
- *
6830
- * @param options - 编译器配置选项,继承自 BaseCompiler 的选项
6831
- * 包括输入路径、输出配置、缓存设置、排除模式等
6832
- * @see {@link CompilerOptions} 查看完整的选项说明
7142
+ * 处理 React 包版本
7143
+ * @param ver 版本号
6833
7144
  */
6834
- constructor(options = {}) {
6835
- super(options);
6836
- this.initializeManagers();
7145
+ async resolveReactVersion(ver) {
7146
+ const outputPkgPath = this.fileCompiler.getOutputPkgPath();
7147
+ const curPkg = await this.fileCompiler.resolvePackageFile(outputPkgPath);
7148
+ const mainVer = Number(ver.split(".")[0]);
7149
+ const typeVer = !isNaN(mainVer) ? `^${mainVer.toString().replace(/@|\^|~|>=|>|/, "")}.0.0` : "^19.0.0";
7150
+ curPkg.dependencies.react = ver;
7151
+ curPkg.dependencies["react-dom"] = ver;
7152
+ curPkg.devDependencies["@types/react"] = typeVer;
7153
+ curPkg.devDependencies["@types/react-dom"] = typeVer;
7154
+ await this.fileCompiler.writeFileWithDir(outputPkgPath, JSON.stringify(curPkg, null, 2));
7155
+ return curPkg;
7156
+ }
7157
+ };
7158
+
7159
+ // src/compiler/shared/file-compiler/setup-manager.ts
7160
+ var SetupManager = class {
7161
+ constructor(getCompiler) {
7162
+ this.getCompiler = getCompiler;
7163
+ this.setup();
6837
7164
  }
6838
7165
  /**
6839
7166
  * 初始化所有管理器实例
6840
7167
  *
6841
- * 按照依赖关系顺序创建各个管理器:
7168
+ * !必须按照依赖关系顺序创建各个管理器:
6842
7169
  * 1. 基础管理器(无依赖)
6843
7170
  * 2. 依赖基础管理器的管理器
6844
7171
  * 3. 依赖其他管理器的管理器
6845
- *
6846
- * @private
6847
7172
  */
6848
- initializeManagers() {
6849
- this.cacheManager = new CacheManager(this);
6850
- this.cleanupManager = new CleanupManager(this);
6851
- this.compilationUnitProcessor = new CompilationUnitProcessor(this);
6852
- this.fileProcessor = new FileProcessor(this, this.compilationUnitProcessor, this.cacheManager);
6853
- this.viteBootstrapper = new ViteBootstrapper(this, this.options);
6854
- this.pipelineManager = new PipelineManager(this, this.fileProcessor);
6855
- this.assetManager = new AssetManager(this, this.cleanupManager);
7173
+ setup() {
7174
+ const fileCompiler = this.getCompiler();
7175
+ this.setupBaseManager(fileCompiler);
7176
+ this.setupManagedByBase(fileCompiler);
7177
+ this.setupCompositeManager(fileCompiler);
6856
7178
  }
6857
7179
  /**
6858
- * 执行完整的编译流程
6859
- *
6860
- * 该方法执行以下步骤:
6861
- * 1. 环境初始化:清理旧输出(如果禁用缓存)并初始化 Vite 项目环境
6862
- * 2. Vue 文件编译:批量处理所有 .vue 文件
6863
- * 3. Script 文件编译:批量处理所有 .js/.ts 文件
6864
- * 4. 资源文件拷贝:处理剩余的非编译文件
6865
- * 5. 统计输出:显示编译结果和性能统计
6866
- *
6867
- * @async
6868
- * @throws {Error} 当编译过程中发生致命错误时抛出
6869
- * @returns {Promise<void>}
7180
+ * 创建基础的管理器(无外部依赖)
6870
7181
  */
7182
+ setupBaseManager(fileCompiler) {
7183
+ fileCompiler.manager = {};
7184
+ const manager = fileCompiler.manager;
7185
+ manager.cacheManager = new CacheManager(fileCompiler);
7186
+ manager.cleanupManager = new CleanupManager(fileCompiler);
7187
+ }
7188
+ /**
7189
+ * 创建依赖基础管理器的管理器
7190
+ */
7191
+ setupManagedByBase(fileCompiler) {
7192
+ const manager = fileCompiler.manager;
7193
+ manager.fileProcessor = new FileProcessor(
7194
+ fileCompiler,
7195
+ new CompilationUnitProcessor(fileCompiler),
7196
+ manager.cacheManager
7197
+ );
7198
+ }
7199
+ /**
7200
+ * 创建依赖其他管理器的管理器
7201
+ */
7202
+ setupCompositeManager(fileCompiler) {
7203
+ const manager = fileCompiler.manager;
7204
+ manager.viteBootstrapper = new ViteBootstrapper(fileCompiler, fileCompiler.options);
7205
+ manager.pipelineManager = new PipelineManager(fileCompiler, manager.fileProcessor);
7206
+ manager.assetManager = new AssetManager(fileCompiler, manager.cleanupManager);
7207
+ }
7208
+ };
7209
+
7210
+ // src/compiler/shared/file-compiler/index.ts
7211
+ var FileCompiler = class extends BaseCompiler {
7212
+ manager;
7213
+ spinner = ora2();
7214
+ constructor(options = {}) {
7215
+ super(options);
7216
+ new SetupManager(() => this);
7217
+ }
7218
+ /** 执行完整的编译流程 */
6871
7219
  async execute() {
6872
- console.info("\n\n", kleur6.magenta(`${kleur6.bold("VUREACT")} v${this.version}`), "\n");
7220
+ console.info("\n\n", kleur8.magenta(`${kleur8.bold("VUREACT")} v${this.version}`), "\n");
6873
7221
  const startTime = performance.now();
6874
- if (!this.getIsCache()) {
6875
- await fs5.promises.rm(this.getWorkspaceDir(), { recursive: true, force: true });
6876
- }
6877
- await this.viteBootstrapper.bootstrapIfNeeded();
7222
+ const rmWorkspace = async () => {
7223
+ await this.rmFile(this.getWorkspaceDir());
7224
+ };
6878
7225
  try {
7226
+ if (!this.getIsCache()) {
7227
+ await rmWorkspace();
7228
+ }
7229
+ await this.manager.viteBootstrapper.bootstrapIfNeeded();
6879
7230
  const sfcCount = await this.runPipelineWithSpinner("sfc" /* SFC */);
6880
7231
  const scriptCount = await this.runPipelineWithSpinner("script" /* SCRIPT */);
6881
7232
  const styleCount = await this.runPipelineWithSpinner("style" /* STYLE */);
@@ -6886,35 +7237,73 @@ var FileCompiler = class extends BaseCompiler {
6886
7237
  this.showCompileStats(endTime, sfcCount, scriptCount, styleCount, assetCount);
6887
7238
  } catch (error) {
6888
7239
  const endTime = calcElapsedTime(startTime);
6889
- console.error(kleur6.red("\u2716"), `Build failed in ${endTime}`);
7240
+ await rmWorkspace();
7241
+ console.error(kleur8.red("\u2716"), `Build failed in ${endTime}`);
6890
7242
  console.error(error);
7243
+ process.exit(-1);
7244
+ } finally {
7245
+ this.spinner.stop();
7246
+ this.resetSkippedCount();
6891
7247
  }
6892
7248
  }
6893
- /**
6894
- * 运行管线并显示加载动画
6895
- *
6896
- * @private
6897
- * @param text - 加载动画显示的文本
6898
- * @param pipelineFn - 要执行的管线函数
6899
- * @returns 返回的处理的文件数
6900
- */
7249
+ /** 处理单个 Vue 单文件组件(SFC) */
7250
+ async processSFC(filePath, existingCache) {
7251
+ return this.manager.fileProcessor.processSFC(filePath, existingCache);
7252
+ }
7253
+ /** 处理单个 JavaScript/TypeScript 脚本文件 */
7254
+ async processScript(filePath, existingCache) {
7255
+ return this.manager.fileProcessor.processScript(filePath, existingCache);
7256
+ }
7257
+ /** 处理单个 CSS/LESS/SCSS 样式文件 */
7258
+ async processStyle(filePath, existingCache) {
7259
+ return this.manager.fileProcessor.processStyle(filePath, existingCache);
7260
+ }
7261
+ /** 处理单个文件(Vue 或 Script) */
7262
+ async processFile(key, filePath, existingCache) {
7263
+ if (key === "sfc" /* SFC */) {
7264
+ return this.manager.fileProcessor.processFile(key, filePath, existingCache);
7265
+ } else if (key === "script" /* SCRIPT */) {
7266
+ return this.manager.fileProcessor.processFile(
7267
+ key,
7268
+ filePath,
7269
+ existingCache
7270
+ );
7271
+ }
7272
+ return this.manager.fileProcessor.processFile(
7273
+ key,
7274
+ filePath,
7275
+ existingCache
7276
+ );
7277
+ }
7278
+ /** 处理单个资源文件 */
7279
+ async processAsset(filePath, existingCache) {
7280
+ return this.manager.assetManager.processAsset(filePath, existingCache);
7281
+ }
7282
+ /** 批量保存缓存 */
7283
+ async flushCache(key) {
7284
+ await this.manager.cacheManager.flushCache(key);
7285
+ }
7286
+ /** 删除指定路径对应的输出文件和缓存 */
7287
+ async removeOutputPath(targetPath, type) {
7288
+ return await this.manager.cleanupManager.removeOutputPath(targetPath, type);
7289
+ }
6901
7290
  async runPipelineWithSpinner(name) {
6902
7291
  const options = {
6903
7292
  ["sfc" /* SFC */]: {
6904
7293
  text: "Compiling Vue files...",
6905
- pipeline: () => this.pipelineManager.runSfcPipeline()
7294
+ pipeline: () => this.manager.pipelineManager.runSfcPipeline()
6906
7295
  },
6907
7296
  ["script" /* SCRIPT */]: {
6908
7297
  text: "Compiling script files...",
6909
- pipeline: () => this.pipelineManager.runScriptPipeline()
7298
+ pipeline: () => this.manager.pipelineManager.runScriptPipeline()
6910
7299
  },
6911
7300
  ["style" /* STYLE */]: {
6912
7301
  text: "Compiling style files...",
6913
- pipeline: () => this.pipelineManager.runStylePipeline()
7302
+ pipeline: () => this.manager.pipelineManager.runStylePipeline()
6914
7303
  },
6915
7304
  ["copied" /* ASSET */]: {
6916
7305
  text: "Copying assets...",
6917
- pipeline: () => this.assetManager.runAssetPipeline()
7306
+ pipeline: () => this.manager.assetManager.runAssetPipeline()
6918
7307
  }
6919
7308
  };
6920
7309
  const { text, pipeline } = options[name];
@@ -6927,166 +7316,36 @@ var FileCompiler = class extends BaseCompiler {
6927
7316
  this.spinner.stop();
6928
7317
  }
6929
7318
  }
6930
- /**
6931
- * 处理单个 Vue 单文件组件(SFC)
6932
- *
6933
- * 此方法主要用于 CLI 的 watch 模式,当检测到文件变更时调用。
6934
- * 支持增量编译,如果文件未变更则跳过编译。
6935
- *
6936
- * @async
6937
- * @param filePath - Vue 文件的绝对路径
6938
- * @param existingCache - 可选的预加载缓存对象,用于增量编译
6939
- * @returns {Promise<SFCUnit | undefined>} 编译单元对象,如果跳过编译则返回 undefined
6940
- * @see {@link FileProcessor.processSFC}
6941
- */
6942
- async processSFC(filePath, existingCache) {
6943
- return this.fileProcessor.processSFC(filePath, existingCache);
6944
- }
6945
- /**
6946
- * 处理单个 JavaScript/TypeScript 脚本文件
6947
- *
6948
- * 此方法主要用于 CLI 的 watch 模式,当检测到文件变更时调用。
6949
- * 支持增量编译,如果文件未变更则跳过编译。
6950
- *
6951
- * @async
6952
- * @param filePath - 脚本文件的绝对路径
6953
- * @param existingCache - 可选的预加载缓存对象,用于增量编译
6954
- * @returns {Promise<ScriptUnit | undefined>} 编译单元对象,如果跳过编译则返回 undefined
6955
- * @see {@link FileProcessor.processScript}
6956
- */
6957
- async processScript(filePath, existingCache) {
6958
- return this.fileProcessor.processScript(filePath, existingCache);
6959
- }
6960
- /**
6961
- * 处理单个 CSS/LESS/SCSS 样式文件
6962
- *
6963
- * 此方法主要用于 CLI 的 watch 模式,当检测到文件变更时调用。
6964
- * 支持增量编译,如果文件未变更则跳过编译。
6965
- *
6966
- * @async
6967
- * @param filePath - style 文件的绝对路径
6968
- * @param existingCache - 可选的预加载缓存对象,用于增量编译
6969
- * @returns {Promise<ScriptUnit | undefined>} 编译单元对象,如果跳过编译则返回 undefined
6970
- * @see {@link FileProcessor.processStyle}
6971
- */
6972
- async processStyle(filePath, existingCache) {
6973
- return this.fileProcessor.processStyle(filePath, existingCache);
6974
- }
6975
- /**
6976
- * 处理单个文件(Vue 或 Script)
6977
- *
6978
- * 通用的文件处理方法,根据 CacheKey 类型自动选择处理逻辑。
6979
- * 主要用于内部调用和统一的文件处理接口。
6980
- *
6981
- * @async
6982
- * @param key - 文件类型标识(SFC 或 SCRIPT)
6983
- * @param filePath - 文件的绝对路径
6984
- * @param existingCache - 可选的预加载缓存对象
6985
- * @returns {Promise<SFCUnit | ScriptUnit | undefined>} 编译单元对象
6986
- * @see {@link FileProcessor.processFile}
6987
- */
6988
- async processFile(key, filePath, existingCache) {
6989
- if (key === "sfc" /* SFC */) {
6990
- return this.fileProcessor.processFile(key, filePath, existingCache);
6991
- } else if (key === "script" /* SCRIPT */) {
6992
- return this.fileProcessor.processFile(key, filePath, existingCache);
6993
- }
6994
- return this.fileProcessor.processFile(
6995
- key,
6996
- filePath,
6997
- existingCache
6998
- );
6999
- }
7000
- /**
7001
- * 处理单个资源文件
7002
- *
7003
- * 比较文件与缓存,决定是否需要拷贝。
7004
- * 资源文件包括图片、字体、样式文件等非编译文件。
7005
- *
7006
- * @async
7007
- * @param filePath - 资源文件的绝对路径
7008
- * @param existingCache - 可选的预加载缓存对象
7009
- * @returns {Promise<FileCacheMeta>} 资源文件的缓存元数据
7010
- * @see {@link AssetManager.processAsset}
7011
- */
7012
- async processAsset(filePath, existingCache) {
7013
- return this.assetManager.processAsset(filePath, existingCache);
7014
- }
7015
- /**
7016
- * 删除指定路径对应的输出文件和缓存
7017
- *
7018
- * 当源文件被删除或重命名时,需要清理对应的输出文件。
7019
- * 主要用于 watch 模式下的文件删除处理。
7020
- *
7021
- * @async
7022
- * @param targetPath - 源代码的路径(文件或文件夹)
7023
- * @param type - 清理类型,指定是 SFC、SCRIPT 还是 ASSET
7024
- * @returns {Promise<void>}
7025
- * @see {@link CleanupManager.removeOutputPath}
7026
- */
7027
- async removeOutputPath(targetPath, type) {
7028
- return this.cleanupManager.removeOutputPath(targetPath, type);
7029
- }
7030
- /**
7031
- * 获取跳过的文件数量
7032
- *
7033
- * 在增量编译中,未变更的文件会被跳过。
7034
- * 此方法返回当前编译会话中跳过的文件数量。
7035
- *
7036
- * @returns {number} 跳过的文件数量
7037
- */
7038
- getSkippedCount() {
7039
- return this.skippedCount;
7040
- }
7041
- /**
7042
- * 重置跳过的文件数量
7043
- *
7044
- * 在每次新的编译会话开始时调用,重置计数器。
7045
- */
7046
- resetSkippedCount() {
7047
- this.skippedCount = 0;
7048
- }
7049
- /**
7050
- * 显示编译统计信息
7051
- *
7052
- * 在编译完成后调用,显示以下信息:
7053
- * 1. 编译耗时
7054
- * 2. 输出目录
7055
- * 3. 跳过的文件数量
7056
- * 4. 处理的文件分类统计
7057
- *
7058
- * @private
7059
- * @param endTime - 格式化后的编译耗时字符串
7060
- * @param sfcCount - 处理的 Vue 文件数量
7061
- * @param scriptCount - 处理的脚本文件数量
7062
- * @param assetCount - 处理的资源文件数量
7063
- */
7064
7319
  showCompileStats(endTime, sfcCount, scriptCount, styleCount, assetCount) {
7065
- const dir = normalizePath(this.relativePath(this.getOuputPath()));
7066
7320
  this.spinner.succeed(`Build completed in ${endTime}`);
7067
- console.info(` Output directory: ${kleur6.dim(dir)}`);
7068
7321
  console.info();
7069
- this.skippedCount += this.pipelineManager.getSkippedCount();
7070
- this.pipelineManager.resetSkippedCount();
7071
- if (this.skippedCount) {
7072
- console.info(kleur6.dim(`Skipped ${this.skippedCount} unchanged file(s)`));
7073
- this.resetSkippedCount();
7074
- }
7075
7322
  if (sfcCount || scriptCount || styleCount || assetCount) {
7076
7323
  const stats = [];
7077
7324
  if (sfcCount) stats.push(`${sfcCount} SFC(s)`);
7078
7325
  if (scriptCount) stats.push(`${scriptCount} script(s)`);
7079
7326
  if (styleCount) stats.push(`${styleCount} style(s)`);
7080
7327
  if (assetCount) stats.push(`${assetCount} asset(s)`);
7081
- console.info(kleur6.gray(`Processed ${stats.join(", ")}`));
7328
+ this.spinner.succeed(`Processed: ${stats.join(", ")}`);
7329
+ }
7330
+ const skippedCount = this.manager.pipelineManager.getSkippedCount() + this.manager.assetManager.getSkippedCount();
7331
+ if (skippedCount) {
7332
+ console.info(kleur8.gray(`\u21B7 Cached: ${kleur8.white(skippedCount)} unchanged file(s)`));
7333
+ this.resetSkippedCount();
7082
7334
  }
7335
+ const dir = normalizePath(this.relativePath(this.getOuputPath()));
7336
+ console.info();
7337
+ console.info(`\u{1F4E6} Output: ${dir}`);
7083
7338
  console.info();
7084
7339
  }
7340
+ resetSkippedCount() {
7341
+ this.manager.pipelineManager.resetSkippedCount();
7342
+ this.manager.assetManager.resetSkippedCount();
7343
+ }
7085
7344
  };
7086
7345
 
7087
7346
  // src/compiler/shared/define-config.ts
7088
- function defineConfig(options) {
7089
- return options;
7347
+ function defineConfig(config) {
7348
+ return typeof config === "function" ? config() : config;
7090
7349
  }
7091
7350
 
7092
7351
  // src/compiler/index.ts