@proteus-vue/plugin-vite 0.2.0-beta.2 → 0.2.0-beta.5

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.
@@ -20,6 +20,17 @@ export interface GenRoutesOptions {
20
20
  dependencies?: Record<string, string>;
21
21
  preload?: string[];
22
22
  }>;
23
+ /**
24
+ * ★Web 目标只生成**应用侧路由表**(2026-09-19 修外部实战报告的第 3 条阻断项):
25
+ * `auto-routes.ts` 是 **双端共用** 产物(Web 的 RouterView 与 MP 的 app.json 同源),
26
+ * 但此前 gen-routes 只在 MP 目标被调用(`needsGenRoutes: isMp`)→ 使用 `proteus build --target web`
27
+ * 的工程新增页面后路由表不更新 → **页面 404**(外部项目实测:新增 `src/pages/editor/` 后
28
+ * Web 构建成功但路由表未收录)。
29
+ * `webOnly: true` 时:只 `scanPages → buildRoutes → validate → writeAutoRoutes`,
30
+ * **跳过 MP 专属产物**(app.json/page.json/component.json/project.config.json,web 构建不需要)
31
+ * 且**不清理 `dist/mp-weixin`**(否则会把已构建的 MP 产物删掉——那不属于 web 构建的职责)。
32
+ */
33
+ webOnly?: boolean;
23
34
  }
24
35
  /**
25
36
  * 运行路由表生成(纯函数,可单测):清理 dist 产物 → 扫描页面 → 生成 auto-routes/app.json/page.json/component.json
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/plugin.ts
2
- import fs4 from "node:fs";
3
- import path4 from "node:path";
2
+ import fs5 from "node:fs";
3
+ import path5 from "node:path";
4
4
  import { createRequire as createRequire3 } from "node:module";
5
5
 
6
6
  // ../types/src/router-config.ts
@@ -294,6 +294,10 @@ function runGenRoutes(options) {
294
294
  return ` { ${parts.join(", ")} },`;
295
295
  }
296
296
  function writeAutoRoutes(routes2) {
297
+ if (!rc.routesOutput) {
298
+ console.log("[gen-routes] routesOutput \u4E3A\u7A7A \u2192 \u6309\u914D\u7F6E**\u8DF3\u8FC7**\u5E94\u7528\u4FA7\u8DEF\u7531\u8868\u751F\u6210\uFF08\u5DE5\u7A0B\u81EA\u5E26\u8DEF\u7531\u673A\u5236\uFF09");
299
+ return;
300
+ }
297
301
  const lines = [
298
302
  `// ${rc.routesOutput} \u2014\u2014 \u5E94\u7528\u4FA7\u8DEF\u7531\u8868\uFF08AUTO-GENERATED by scripts/gen-routes.ts\uFF0C\u52FF\u624B\u52A8\u7F16\u8F91\uFF09`,
299
303
  "// \u2605\u62C6\u5305\u6B65\u9AA4 4\uFF1Aauto-routes \u968F\u5E94\u7528\u5B58\u653E\uFF08\u5DE5\u5382\u5316\u540E\u8DEF\u7531\u8868\u7531\u5E94\u7528\u6CE8\u5165 createRouter\uFF09\uFF0C\u4E0D\u518D\u5C5E\u4E8E @proteus-vue/router \u5305",
@@ -555,7 +559,7 @@ function runGenRoutes(options) {
555
559
  "details",
556
560
  "summary"
557
561
  ]);
558
- function extractTemplateBody(src) {
562
+ function extractTemplateBody2(src) {
559
563
  const n = src.length;
560
564
  let i = 0;
561
565
  while (i < n) {
@@ -611,7 +615,7 @@ function runGenRoutes(options) {
611
615
  }
612
616
  function collectComponents(file, skipSemantic = false) {
613
617
  const src = fs2.readFileSync(file, "utf-8");
614
- const tpl = extractTemplateBody(src);
618
+ const tpl = extractTemplateBody2(src);
615
619
  const customTags = new Set(Object.keys(config.rules?.customTags ?? {}));
616
620
  const gridRuleDisabled = (config.rules?.disabled ?? []).includes("fluid/semantic-grid");
617
621
  const semanticTags = skipSemantic && !gridRuleDisabled ? /* @__PURE__ */ new Set(["p-grid"]) : /* @__PURE__ */ new Set();
@@ -715,10 +719,15 @@ function runGenRoutes(options) {
715
719
  fs2.writeFileSync(path2.join(OUT_DIR, "project.config.json"), JSON.stringify(projectConfig, null, 2) + "\n");
716
720
  console.log(`[gen-routes] \u5DF2\u751F\u6210 dist/mp-weixin/project.config.json\uFF08appid=${config.appid}\uFF0Cprojectname=${projectName}\uFF09`);
717
721
  }
718
- fs2.rmSync(OUT_DIR, { recursive: true, force: true });
719
722
  const pages = scanPages();
720
723
  const routes = buildRoutes(pages);
721
724
  validate(pages, routes);
725
+ if (options.webOnly) {
726
+ writeAutoRoutes(routes);
727
+ console.log(`[gen-routes] \uFF08web \u76EE\u6807\uFF09\u5DF2\u66F4\u65B0\u5E94\u7528\u4FA7\u8DEF\u7531\u8868\uFF1A\u5171 ${pages.length} \u4E2A\u9875\u9762`);
728
+ return;
729
+ }
730
+ fs2.rmSync(OUT_DIR, { recursive: true, force: true });
722
731
  writeAutoRoutes(routes);
723
732
  writeAppJson(pages, routes);
724
733
  writePageJsons(pages);
@@ -898,6 +907,138 @@ function createBundleCache(cacheDir) {
898
907
  };
899
908
  }
900
909
 
910
+ // src/tag-scan.ts
911
+ import fs4 from "node:fs";
912
+ import path4 from "node:path";
913
+ function extractTemplateBody(src) {
914
+ const n = src.length;
915
+ let i = 0;
916
+ while (i < n) {
917
+ const lt = src.indexOf("<", i);
918
+ if (lt < 0) return "";
919
+ if (src.startsWith("<!--", lt)) {
920
+ const e = src.indexOf("-->", lt + 4);
921
+ i = e < 0 ? n : e + 3;
922
+ continue;
923
+ }
924
+ const block = /^<(script|style)\b/i.exec(src.slice(lt, lt + 32));
925
+ if (block) {
926
+ const tag = block[1].toLowerCase();
927
+ const end = src.toLowerCase().indexOf(`</${tag}`, lt + block[0].length);
928
+ if (end < 0) return "";
929
+ const gt = src.indexOf(">", end);
930
+ i = gt < 0 ? n : gt + 1;
931
+ continue;
932
+ }
933
+ if (/^<template[\s>]/i.test(src.slice(lt, lt + 16))) {
934
+ const gt = src.indexOf(">", lt);
935
+ if (gt < 0) return "";
936
+ let depth = 1;
937
+ let j = gt + 1;
938
+ while (j < n) {
939
+ const l2 = src.indexOf("<", j);
940
+ if (l2 < 0) return src.slice(gt + 1);
941
+ if (src.startsWith("<!--", l2)) {
942
+ const e = src.indexOf("-->", l2 + 4);
943
+ j = e < 0 ? n : e + 3;
944
+ continue;
945
+ }
946
+ const seg = src.slice(l2, l2 + 16);
947
+ if (/^<\/template[\s>]/i.test(seg)) {
948
+ depth--;
949
+ if (depth === 0) return src.slice(gt + 1, l2);
950
+ j = l2 + "</template>".length;
951
+ continue;
952
+ }
953
+ if (/^<template[\s>]/i.test(seg)) {
954
+ depth++;
955
+ const g2 = src.indexOf(">", l2);
956
+ j = g2 < 0 ? n : g2 + 1;
957
+ continue;
958
+ }
959
+ j = l2 + 1;
960
+ }
961
+ return src.slice(gt + 1);
962
+ }
963
+ i = lt + 1;
964
+ }
965
+ return "";
966
+ }
967
+ function extractTags(tpl) {
968
+ const out = /* @__PURE__ */ new Set();
969
+ let idx = 0;
970
+ while (idx < tpl.length) {
971
+ const lt = tpl.indexOf("<", idx);
972
+ if (lt < 0) break;
973
+ if (tpl.startsWith("<!--", lt)) {
974
+ const e = tpl.indexOf("-->", lt + 4);
975
+ idx = e < 0 ? tpl.length : e + 3;
976
+ continue;
977
+ }
978
+ if (tpl.startsWith("</", lt)) {
979
+ idx = lt + 2;
980
+ continue;
981
+ }
982
+ const mm = /^([A-Za-z][\w-]*)/.exec(tpl.slice(lt + 1));
983
+ if (!mm) {
984
+ idx = lt + 1;
985
+ continue;
986
+ }
987
+ const raw = mm[1];
988
+ out.add(/[A-Z]/.test(raw) ? raw.replace(/\B([A-Z])/g, "-$1").toLowerCase() : raw);
989
+ idx = lt + 1 + mm[0].length;
990
+ }
991
+ return out;
992
+ }
993
+ function collectCompilerEmittedTags(templateBody) {
994
+ const out = /* @__PURE__ */ new Set();
995
+ if (/<(?:svg|circle|rect|ellipse|path|line|polyline|polygon)[\s>][\s\S]*?<animate\b/i.test(templateBody)) {
996
+ const shapeAnim = /<animate\s[^>]*attributeName\s*=\s*["'](cx|cy|r|rx|ry|x|y|width|height|d|points|stroke-dashoffset|stroke-dasharray)["']/i;
997
+ if (shapeAnim.test(templateBody)) out.add("p-svg-canvas");
998
+ }
999
+ return out;
1000
+ }
1001
+ function resolveComponentFile(componentsDir, tag) {
1002
+ const dirIndex = path4.join(componentsDir, tag, "index.vue");
1003
+ if (fs4.existsSync(dirIndex)) return dirIndex;
1004
+ const flat = path4.join(componentsDir, `${tag}.vue`);
1005
+ if (fs4.existsSync(flat)) return flat;
1006
+ return null;
1007
+ }
1008
+ function collectUsedFrameworkComponents(pageFiles, componentsDir) {
1009
+ const used = /* @__PURE__ */ new Set();
1010
+ const visitedFiles = /* @__PURE__ */ new Set();
1011
+ const queue = [...pageFiles];
1012
+ while (queue.length) {
1013
+ const file = queue.pop();
1014
+ if (visitedFiles.has(file)) continue;
1015
+ visitedFiles.add(file);
1016
+ if (!fs4.existsSync(file)) continue;
1017
+ let tags;
1018
+ const body = (() => {
1019
+ try {
1020
+ return extractTemplateBody(fs4.readFileSync(file, "utf-8"));
1021
+ } catch {
1022
+ return "";
1023
+ }
1024
+ })();
1025
+ try {
1026
+ tags = extractTags(body);
1027
+ for (const t of collectCompilerEmittedTags(body)) tags.add(t);
1028
+ } catch {
1029
+ continue;
1030
+ }
1031
+ for (const tag of tags) {
1032
+ if (used.has(tag)) continue;
1033
+ const compFile = resolveComponentFile(componentsDir, tag);
1034
+ if (!compFile) continue;
1035
+ used.add(tag);
1036
+ queue.push(compFile);
1037
+ }
1038
+ }
1039
+ return used;
1040
+ }
1041
+
901
1042
  // src/plugin.ts
902
1043
  var MP_TAG_MAP = {
903
1044
  view: "proteus-view",
@@ -925,7 +1066,15 @@ var MP_ONLY_TAGS = /* @__PURE__ */ new Set([
925
1066
  "share-element",
926
1067
  "keyboard-accessory",
927
1068
  "cover-view",
928
- "cover-image"
1069
+ "cover-image",
1070
+ // ★端对齐批次3(2026-09-16):宿主能力组件的 MP 原生标签——其模板用 v-if 双分支
1071
+ // (MP 原生 / Web 降级),Web 端死分支不渲染但会被 resolveComponent 提升解析 → 声明为自定义元素消除告警。
1072
+ "rich-text",
1073
+ "map",
1074
+ "camera",
1075
+ "canvas",
1076
+ "ad",
1077
+ "web-view"
929
1078
  ]);
930
1079
  function defaultScopedPlugin() {
931
1080
  return {
@@ -960,13 +1109,13 @@ function resolvePkgPath(projectRoot, modPath) {
960
1109
  const m = modPath.match(/^node_modules\/((?:@[^/]+\/)?[^/]+)\/([\s\S]+)$/);
961
1110
  if (m) {
962
1111
  try {
963
- const pkgRequire = createRequire3(path4.join(projectRoot, "package.json"));
964
- const pkgRoot = path4.dirname(pkgRequire.resolve(`${m[1]}/package.json`));
965
- return path4.join(pkgRoot, m[2]);
1112
+ const pkgRequire = createRequire3(path5.join(projectRoot, "package.json"));
1113
+ const pkgRoot = path5.dirname(pkgRequire.resolve(`${m[1]}/package.json`));
1114
+ return path5.join(pkgRoot, m[2]);
966
1115
  } catch {
967
1116
  }
968
1117
  }
969
- return path4.join(projectRoot, modPath);
1118
+ return path5.join(projectRoot, modPath);
970
1119
  }
971
1120
  function preprocessStyle(lang, content) {
972
1121
  if (lang === "scss" || lang === "sass") {
@@ -984,11 +1133,11 @@ function preprocessStyle(lang, content) {
984
1133
  return content;
985
1134
  }
986
1135
  function loadStyleSrcWithVariant(src, fromFilename, platform = "mp") {
987
- const base = path4.resolve(path4.dirname(fromFilename), src);
988
- const hit = resolvePlatformVariant(base, platform, fs4.existsSync);
1136
+ const base = path5.resolve(path5.dirname(fromFilename), src);
1137
+ const hit = resolvePlatformVariant(base, platform, fs5.existsSync);
989
1138
  if (!hit) return null;
990
1139
  try {
991
- return fs4.readFileSync(hit, "utf-8");
1140
+ return fs5.readFileSync(hit, "utf-8");
992
1141
  } catch {
993
1142
  return null;
994
1143
  }
@@ -997,10 +1146,10 @@ var VENDOR_SINGLETONS = ["pinia", "vue", "@vue/devtools-api"];
997
1146
  function resolveSharedModule(appDir, absFrom, source, frameworkDir, resolveFrom, platform = "mp") {
998
1147
  if (source.startsWith("@proteus-vue/")) {
999
1148
  try {
1000
- const resolver = resolveFrom ? createRequire3(path4.join(resolveFrom, "package.json")) : require2;
1001
- const pkgRoot = path4.dirname(resolver.resolve(`${source}/package.json`));
1002
- const entry = path4.join(pkgRoot, "dist", "index.js");
1003
- if (!fs4.existsSync(entry)) return null;
1149
+ const resolver = resolveFrom ? createRequire3(path5.join(resolveFrom, "package.json")) : require2;
1150
+ const pkgRoot = path5.dirname(resolver.resolve(`${source}/package.json`));
1151
+ const entry = path5.join(pkgRoot, "dist", "index.js");
1152
+ if (!fs5.existsSync(entry)) return null;
1004
1153
  return { file: entry, relNoExt: `_proteus/${source.replace("@proteus-vue/", "")}` };
1005
1154
  } catch {
1006
1155
  return null;
@@ -1014,34 +1163,34 @@ function resolveSharedModule(appDir, absFrom, source, frameworkDir, resolveFrom,
1014
1163
  return null;
1015
1164
  }
1016
1165
  };
1017
- const entry = tryResolve(path4.join(resolveFrom ?? appDir, "package.json")) ?? tryResolve(absFrom);
1018
- if (!entry || !fs4.existsSync(entry)) return null;
1166
+ const entry = tryResolve(path5.join(resolveFrom ?? appDir, "package.json")) ?? tryResolve(absFrom);
1167
+ if (!entry || !fs5.existsSync(entry)) return null;
1019
1168
  return { file: entry, relNoExt: `_proteus/${source}` };
1020
1169
  }
1021
1170
  if (!source.startsWith(".")) return null;
1022
- const base = path4.resolve(path4.dirname(absFrom), source);
1171
+ const base = path5.resolve(path5.dirname(absFrom), source);
1023
1172
  const JS_EXTS = /* @__PURE__ */ new Set([".ts", ".js", ".mjs", ".cjs"]);
1024
1173
  const variantHit = resolvePlatformVariantWithExts(base, [...JS_EXTS], platform, (p) => {
1025
1174
  try {
1026
- return fs4.statSync(p).isFile();
1175
+ return fs5.statSync(p).isFile();
1027
1176
  } catch {
1028
1177
  return false;
1029
1178
  }
1030
1179
  });
1031
- const candidates = variantHit ? [variantHit] : [base, `${base}.ts`, `${base}.js`, path4.join(base, "index.ts"), path4.join(base, "index.js")];
1180
+ const candidates = variantHit ? [variantHit] : [base, `${base}.ts`, `${base}.js`, path5.join(base, "index.ts"), path5.join(base, "index.js")];
1032
1181
  for (const cand of candidates) {
1033
1182
  if (cand.endsWith(".vue")) continue;
1034
- if (!JS_EXTS.has(path4.extname(cand).toLowerCase())) continue;
1183
+ if (!JS_EXTS.has(path5.extname(cand).toLowerCase())) continue;
1035
1184
  let isFile = false;
1036
1185
  try {
1037
- isFile = fs4.statSync(cand).isFile();
1186
+ isFile = fs5.statSync(cand).isFile();
1038
1187
  } catch {
1039
1188
  isFile = false;
1040
1189
  }
1041
1190
  if (isFile) {
1042
- let relNoExt = path4.relative(appDir, cand).replace(/\\/g, "/").replace(/\.(ts|js)$/, "");
1043
- if (relNoExt.startsWith("../") && frameworkDir && !path4.relative(frameworkDir, cand).startsWith("..")) {
1044
- relNoExt = `proteus/${path4.relative(frameworkDir, cand).replace(/\\/g, "/").replace(/\.(ts|js)$/, "")}`;
1191
+ let relNoExt = path5.relative(appDir, cand).replace(/\\/g, "/").replace(/\.(ts|js)$/, "");
1192
+ if (relNoExt.startsWith("../") && frameworkDir && !path5.relative(frameworkDir, cand).startsWith("..")) {
1193
+ relNoExt = `proteus/${path5.relative(frameworkDir, cand).replace(/\\/g, "/").replace(/\.(ts|js)$/, "")}`;
1045
1194
  }
1046
1195
  return { file: cand, relNoExt };
1047
1196
  }
@@ -1053,10 +1202,10 @@ function rewriteRootToPage(css) {
1053
1202
  }
1054
1203
  function rewriteFrameworkRequires(js, relOutput) {
1055
1204
  if (!js.includes("require('@proteus-vue/")) return js;
1056
- const pageDir = path4.posix.dirname(relOutput);
1205
+ const pageDir = path5.posix.dirname(relOutput);
1057
1206
  return js.replace(/require\('@proteus-vue\/([A-Za-z0-9_-]+)'\)/g, (m, name) => {
1058
1207
  const pkgRel = `_proteus/${name}.js`;
1059
- let rel = path4.posix.relative(pageDir, pkgRel);
1208
+ let rel = path5.posix.relative(pageDir, pkgRel);
1060
1209
  if (!rel.startsWith(".")) rel = `./${rel}`;
1061
1210
  return `require('${rel}')`;
1062
1211
  });
@@ -1105,11 +1254,11 @@ async function loadPresetBuilders(projectRoot, cfg) {
1105
1254
  for (const d of duplicates) console.warn(`[mp-transform] \u8DEF\u7531\u5B57\u6BB5 "${d}" \u5728\u9876\u5C42\u4E0E router \u6BB5\u540C\u65F6\u58F0\u660E\u2014\u2014\u5DF2\u53D6 router.${d}\uFF08#492 \u7EDF\u4E00\u8DEF\u7531\u7BA1\u7406\uFF1A\u5EFA\u8BAE\u5220\u9664\u9876\u5C42\u9057\u7559\u5199\u6CD5\uFF09`);
1106
1255
  for (const [name, modPath] of Object.entries(rc.customRoute.builders)) {
1107
1256
  const abs = resolvePkgPath(projectRoot, modPath);
1108
- if (!fs4.existsSync(abs)) {
1257
+ if (!fs5.existsSync(abs)) {
1109
1258
  console.warn(`[mp-transform] \u9884\u8BBE builder ${name} \u4E0D\u5B58\u5728\uFF1A${modPath}`);
1110
1259
  continue;
1111
1260
  }
1112
- const { code } = await esbuildTransform(fs4.readFileSync(abs, "utf-8"), { loader: "ts", charset: "utf8" });
1261
+ const { code } = await esbuildTransform(fs5.readFileSync(abs, "utf-8"), { loader: "ts", charset: "utf8" });
1113
1262
  const fnName = extractBuilderFnName(code);
1114
1263
  if (!fnName) {
1115
1264
  console.warn(`[mp-transform] \u9884\u8BBE builder ${name} \u672A\u627E\u5230\u51FD\u6570\u58F0\u660E\uFF0C\u5DF2\u8DF3\u8FC7`);
@@ -1120,10 +1269,10 @@ async function loadPresetBuilders(projectRoot, cfg) {
1120
1269
  return presets;
1121
1270
  }
1122
1271
  function walkVueFiles(dir, acc = []) {
1123
- if (!fs4.existsSync(dir)) return acc;
1124
- for (const entry of fs4.readdirSync(dir, { withFileTypes: true })) {
1272
+ if (!fs5.existsSync(dir)) return acc;
1273
+ for (const entry of fs5.readdirSync(dir, { withFileTypes: true })) {
1125
1274
  if (entry.name.startsWith(".")) continue;
1126
- const full = path4.join(dir, entry.name);
1275
+ const full = path5.join(dir, entry.name);
1127
1276
  if (entry.isDirectory()) walkVueFiles(full, acc);
1128
1277
  else if (entry.name.endsWith(".vue")) acc.push(full);
1129
1278
  }
@@ -1139,19 +1288,26 @@ function collectMpEntries(opts) {
1139
1288
  onSkipWebOnly?.(f);
1140
1289
  continue;
1141
1290
  }
1142
- const relBase = path4.relative(appDir, splitVariant2(f).base).replace(/\\/g, "/");
1291
+ const relBase = path5.relative(appDir, splitVariant2(f).base).replace(/\\/g, "/");
1143
1292
  out.push({ file: f, rel: relBase.replace(/\.vue$/, ""), isComponent });
1144
1293
  }
1145
1294
  };
1146
- pushRel(path4.join(projectRoot, pagesDir), false);
1147
- for (const sp of subPackages) pushRel(path4.join(projectRoot, sp.root), false);
1148
- pushRel(path4.join(appDir, "components"), true);
1295
+ pushRel(path5.join(projectRoot, pagesDir), false);
1296
+ for (const sp of subPackages) pushRel(path5.join(projectRoot, sp.root), false);
1297
+ pushRel(path5.join(appDir, "components"), true);
1298
+ const pageFiles = out.filter((t) => !t.isComponent).map((t) => t.file);
1299
+ const emitAll = (opts.componentEmit ?? "used") === "all";
1300
+ const usedComponents = emitAll ? null : collectUsedFrameworkComponents(pageFiles, componentsDir);
1149
1301
  for (const f of effectiveVariants2(walkVueFiles(componentsDir), platform)) {
1150
1302
  if (webOnlyPages?.has(f)) {
1151
1303
  onSkipWebOnly?.(f);
1152
1304
  continue;
1153
1305
  }
1154
- const relIn = path4.relative(componentsDir, splitVariant2(f).base).replace(/\\/g, "/").replace(/\.vue$/, "");
1306
+ if (usedComponents) {
1307
+ const compName = path5.relative(componentsDir, splitVariant2(f).base).replace(/\\/g, "/").split("/")[0];
1308
+ if (!usedComponents.has(compName)) continue;
1309
+ }
1310
+ const relIn = path5.relative(componentsDir, splitVariant2(f).base).replace(/\\/g, "/").replace(/\.vue$/, "");
1155
1311
  out.push({ file: f, rel: `proteus/${relIn}`, isComponent: true });
1156
1312
  }
1157
1313
  return out;
@@ -1184,13 +1340,13 @@ function mpTransform(opts) {
1184
1340
  projectRoot = resolved.root;
1185
1341
  },
1186
1342
  async buildStart() {
1187
- const appDir = path4.join(projectRoot, path4.dirname(cfg.pagesDir));
1188
- const compileCache = createCompileCache(path4.join(projectRoot, "node_modules", ".cache", "proteus", "compile"));
1189
- const bundleCache = createBundleCache(path4.join(projectRoot, "node_modules", ".cache", "proteus", "bundle"));
1343
+ const appDir = path5.join(projectRoot, path5.dirname(cfg.pagesDir));
1344
+ const compileCache = createCompileCache(path5.join(projectRoot, "node_modules", ".cache", "proteus", "compile"));
1345
+ const bundleCache = createBundleCache(path5.join(projectRoot, "node_modules", ".cache", "proteus", "bundle"));
1190
1346
  const webOnlyPages = /* @__PURE__ */ new Set();
1191
1347
  const detectWebOnly = (file) => {
1192
1348
  try {
1193
- const src = fs4.readFileSync(file, "utf-8");
1349
+ const src = fs5.readFileSync(file, "utf-8");
1194
1350
  const m = src.match(/<route>\s*([\s\S]*?)<\/route>/);
1195
1351
  if (!m) return;
1196
1352
  if (/"?webOnly"?\s*:\s*true/.test(m[1])) {
@@ -1210,10 +1366,10 @@ function mpTransform(opts) {
1210
1366
  } catch {
1211
1367
  }
1212
1368
  };
1213
- for (const pagesRoot of [path4.join(projectRoot, cfg.pagesDir), ...(effectiveSubPackages ?? []).map((sp) => path4.join(projectRoot, sp.root))]) {
1369
+ for (const pagesRoot of [path5.join(projectRoot, cfg.pagesDir), ...(effectiveSubPackages ?? []).map((sp) => path5.join(projectRoot, sp.root))]) {
1214
1370
  for (const f of walkVueFiles(pagesRoot)) detectWebOnly(f);
1215
1371
  }
1216
- const frameworkComponents = opts.componentsDir ? path4.resolve(projectRoot, opts.componentsDir) : resolveComponentsRoot(projectRoot);
1372
+ const frameworkComponents = opts.componentsDir ? path5.resolve(projectRoot, opts.componentsDir) : resolveComponentsRoot(projectRoot);
1217
1373
  const files = collectMpEntries({
1218
1374
  projectRoot,
1219
1375
  appDir,
@@ -1221,20 +1377,22 @@ function mpTransform(opts) {
1221
1377
  subPackages: effectiveSubPackages ?? [],
1222
1378
  componentsDir: frameworkComponents,
1223
1379
  webOnlyPages,
1224
- onSkipWebOnly: (f) => console.log(`[mp-transform] \u8DF3\u8FC7 webOnly \u9875\u9762\uFF1A${path4.relative(projectRoot, f).replace(/\\/g, "/")}`)
1380
+ onSkipWebOnly: (f) => console.log(`[mp-transform] \u8DF3\u8FC7 webOnly \u9875\u9762\uFF1A${path5.relative(projectRoot, f).replace(/\\/g, "/")}`),
1381
+ // ★框架组件按引用输出(2026-09-18);PROTEUS_COMPONENTS_EMIT=all 回退全量(非常规用法逃生舱)
1382
+ componentEmit: process.env.PROTEUS_COMPONENTS_EMIT === "all" ? "all" : "used"
1225
1383
  });
1226
1384
  const appUsesStore = files.some((f) => {
1227
1385
  try {
1228
- const s = fs4.readFileSync(f.file, "utf-8");
1386
+ const s = fs5.readFileSync(f.file, "utf-8");
1229
1387
  const script = s.includes("<script") ? s.match(/<script[^>]*>([\s\S]*?)<\/script>/i)?.[1] ?? "" : s;
1230
1388
  return /\buse[A-Z]\w*Store\s*\(/.test(script);
1231
1389
  } catch {
1232
1390
  return false;
1233
1391
  }
1234
1392
  });
1235
- const mpEntry = path4.join(appDir, "main.mp.ts");
1236
- if (fs4.existsSync(mpEntry)) {
1237
- const src = fs4.readFileSync(mpEntry, "utf-8");
1393
+ const mpEntry = path5.join(appDir, "main.mp.ts");
1394
+ if (fs5.existsSync(mpEntry)) {
1395
+ const src = fs5.readFileSync(mpEntry, "utf-8");
1238
1396
  const { code } = await esbuildTransform(src, { loader: "ts", charset: "utf8" });
1239
1397
  const presets = filterOverriddenPresets(code, await loadPresetBuilders(projectRoot, cfg));
1240
1398
  const piniaInstall = appUsesStore ? " // \u2605\u9875\u9762\u4F7F\u7528 store \u2192 \u5B89\u88C5\u5E76\u6FC0\u6D3B Pinia\uFF08\u5C0F\u7A0B\u5E8F\u65E0 createApp\uFF0C\u5FC5\u987B setActivePinia\uFF09\n var __proteusPiniaMod = require('./_proteus/runtime.js')\n if (__proteusPiniaMod && __proteusPiniaMod.createMpPinia) __proteusPiniaMod.createMpPinia()" : " // \uFF08\u672A\u68C0\u6D4B\u5230 store \u4F7F\u7528\u2014\u2014\u8DF3\u8FC7 Pinia \u5B89\u88C5\uFF09";
@@ -1243,19 +1401,19 @@ function mpTransform(opts) {
1243
1401
  console.log(`[mp-transform] app.js \u5DF2\u76F4\u51FA\uFF08${isDebug ? "debug" : "\u6B63\u5F0F"}\uFF09\uFF0C\u5185\u7F6E\u9884\u8BBE\uFF1A${presets.map((p) => p.name).join("/") || "\u65E0"}${appUsesStore ? "\uFF0CPinia \u5DF2\u5B89\u88C5" : ""}`);
1244
1402
  }
1245
1403
  {
1246
- const explicit = cfg.globalStyle ? path4.resolve(projectRoot, cfg.globalStyle) : void 0;
1404
+ const explicit = cfg.globalStyle ? path5.resolve(projectRoot, cfg.globalStyle) : void 0;
1247
1405
  const candidates = [
1248
1406
  explicit,
1249
- path4.join(appDir, "app.wxss"),
1250
- path4.join(projectRoot, "app.wxss")
1407
+ path5.join(appDir, "app.wxss"),
1408
+ path5.join(projectRoot, "app.wxss")
1251
1409
  ].filter((p) => Boolean(p));
1252
- const globalStylePath = candidates.find((p) => fs4.existsSync(p));
1410
+ const globalStylePath = candidates.find((p) => fs5.existsSync(p));
1253
1411
  if (globalStylePath) {
1254
- const raw = fs4.readFileSync(globalStylePath, "utf-8");
1412
+ const raw = fs5.readFileSync(globalStylePath, "utf-8");
1255
1413
  const normalized = rewriteRootToPage(raw);
1256
1414
  const wxss = transformStyleToWxss(normalized, { px2rpx: cfg.style?.px2rpx ?? true, rpxRatio: cfg.style?.rpxRatio ?? 2, rules: cfg.rules });
1257
1415
  this.emitFile({ type: "asset", fileName: "app.wxss", source: wxss });
1258
- console.log(`[mp-transform] app.wxss \u5DF2\u4EA7\u51FA\uFF08${path4.relative(projectRoot, globalStylePath).replace(/\\/g, "/")}\u2014\u2014\u5168\u5C40\u8BBE\u8BA1 token/\u91CD\u7F6E\uFF0C:root\u2192page\uFF09`);
1416
+ console.log(`[mp-transform] app.wxss \u5DF2\u4EA7\u51FA\uFF08${path5.relative(projectRoot, globalStylePath).replace(/\\/g, "/")}\u2014\u2014\u5168\u5C40\u8BBE\u8BA1 token/\u91CD\u7F6E\uFF0C:root\u2192page\uFF09`);
1259
1417
  }
1260
1418
  }
1261
1419
  const moduleImportsByFile = /* @__PURE__ */ new Map();
@@ -1263,7 +1421,7 @@ function mpTransform(opts) {
1263
1421
  const sharedRelNoExt = /* @__PURE__ */ new Map();
1264
1422
  const resolveShared = (absFrom, source) => resolveSharedModule(appDir, absFrom, source, frameworkComponents, projectRoot, "mp");
1265
1423
  const scanImports = (absFile) => {
1266
- const src = fs4.readFileSync(absFile, "utf-8");
1424
+ const src = fs5.readFileSync(absFile, "utf-8");
1267
1425
  const script = src.includes("<script") ? src.match(/<script[^>]*>([\s\S]*?)<\/script>/i)?.[1] ?? "" : src;
1268
1426
  return scanSourceImports(script);
1269
1427
  };
@@ -1327,21 +1485,21 @@ function mpTransform(opts) {
1327
1485
  name: "proteus-pkg-require-path",
1328
1486
  setup(b) {
1329
1487
  const mapExternal = (target) => {
1330
- const dir = path4.posix.dirname(relNoExt);
1331
- let rel = path4.posix.relative(dir, target);
1488
+ const dir = path5.posix.dirname(relNoExt);
1489
+ let rel = path5.posix.relative(dir, target);
1332
1490
  if (!rel.startsWith(".")) rel = `./${rel}`;
1333
1491
  return { path: rel, external: true };
1334
1492
  };
1335
1493
  b.onResolve({ filter: /^@proteus-vue\// }, (args) => mapExternal(`_proteus/${args.path.replace("@proteus-vue/", "")}.js`));
1336
1494
  b.onResolve({ filter: new RegExp(`^(${VENDOR_SINGLETONS.join("|")})$`) }, (args) => mapExternal(`_proteus/${args.path}.js`));
1337
1495
  b.onLoad({ filter: /\.(md|txt|json)$/ }, (args) => ({
1338
- contents: `export default ${JSON.stringify(fs4.readFileSync(args.path, "utf-8"))}`,
1496
+ contents: `export default ${JSON.stringify(fs5.readFileSync(args.path, "utf-8"))}`,
1339
1497
  loader: "js"
1340
1498
  }));
1341
1499
  b.onLoad({ filter: /\.(png|jpe?g|gif|webp|svg|ico|woff2?|ttf|eot|mp3|mp4|wav|zip)$/ }, (args) => ({
1342
1500
  errors: [
1343
1501
  {
1344
- text: `MP \u4EA7\u7269\u4E0D\u652F\u6301\u4E8C\u8FDB\u5236\u8D44\u6E90 import\uFF1A${path4.relative(projectRoot, args.path)}\u2014\u2014\u8BF7\u6539\u7528\u7F51\u7EDC URL \u6216 base64 \u5185\u8054`
1502
+ text: `MP \u4EA7\u7269\u4E0D\u652F\u6301\u4E8C\u8FDB\u5236\u8D44\u6E90 import\uFF1A${path5.relative(projectRoot, args.path)}\u2014\u2014\u8BF7\u6539\u7528\u7F51\u7EDC URL \u6216 base64 \u5185\u8054`
1345
1503
  }
1346
1504
  ]
1347
1505
  }));
@@ -1400,7 +1558,7 @@ function mpTransform(opts) {
1400
1558
  const inputFiles = Object.keys(build.metafile.inputs);
1401
1559
  const inputs = inputFiles.map((f) => {
1402
1560
  try {
1403
- const st = fs4.statSync(f);
1561
+ const st = fs5.statSync(f);
1404
1562
  return { file: f, mtimeMs: st.mtimeMs, size: st.size };
1405
1563
  } catch {
1406
1564
  return null;
@@ -1415,18 +1573,18 @@ function mpTransform(opts) {
1415
1573
  for (const [file, list] of moduleImportsByFile) {
1416
1574
  const entry = files.find((f) => f.file === file);
1417
1575
  if (!entry) continue;
1418
- const pageDir = path4.posix.dirname(entry.rel);
1576
+ const pageDir = path5.posix.dirname(entry.rel);
1419
1577
  for (const item of list) {
1420
1578
  const shared = resolveShared(file, item.source);
1421
1579
  if (!shared) continue;
1422
1580
  const sharedRel = `${shared.relNoExt}.js`;
1423
- let rel = path4.posix.relative(pageDir, sharedRel);
1581
+ let rel = path5.posix.relative(pageDir, sharedRel);
1424
1582
  if (!rel.startsWith(".")) rel = `./${rel}`;
1425
1583
  item.requirePath = rel;
1426
1584
  }
1427
1585
  }
1428
1586
  for (const { file, rel, isComponent } of files) {
1429
- const source = fs4.readFileSync(file, "utf-8");
1587
+ const source = fs5.readFileSync(file, "utf-8");
1430
1588
  if (rustCompiler) {
1431
1589
  const v = verifyDualCompilerEquivalence(source, { rustBin: rustCliBin, filename: file });
1432
1590
  if (v.status === "ok") {
@@ -1546,21 +1704,21 @@ function mpTransform(opts) {
1546
1704
  console.log(`[mp-transform] ${rel} \u2192 wxml/js/wxss \u5DF2\u8F93\u51FA`);
1547
1705
  }
1548
1706
  {
1549
- const publicDir = path4.join(projectRoot, "public");
1550
- if (fs4.existsSync(publicDir)) {
1707
+ const publicDir = path5.join(projectRoot, "public");
1708
+ if (fs5.existsSync(publicDir)) {
1551
1709
  const rels = [];
1552
1710
  const walk = (dir) => {
1553
- for (const e of fs4.readdirSync(dir, { withFileTypes: true })) {
1711
+ for (const e of fs5.readdirSync(dir, { withFileTypes: true })) {
1554
1712
  if (e.name.startsWith(".")) continue;
1555
- const full = path4.join(dir, e.name);
1713
+ const full = path5.join(dir, e.name);
1556
1714
  if (e.isDirectory()) walk(full);
1557
- else rels.push(path4.relative(publicDir, full).replace(/\\/g, "/"));
1715
+ else rels.push(path5.relative(publicDir, full).replace(/\\/g, "/"));
1558
1716
  }
1559
1717
  };
1560
1718
  walk(publicDir);
1561
1719
  let assetN = 0;
1562
1720
  for (const { from, to } of mapPublicAssetVariants(rels, "mp")) {
1563
- this.emitFile({ type: "asset", fileName: to, source: fs4.readFileSync(path4.join(publicDir, from)) });
1721
+ this.emitFile({ type: "asset", fileName: to, source: fs5.readFileSync(path5.join(publicDir, from)) });
1564
1722
  assetN++;
1565
1723
  }
1566
1724
  if (assetN) console.log(`[mp-transform] public \u9759\u6001\u8D44\u6E90 \u2192 ${assetN} \u4E2A\uFF08\u5E73\u53F0\u53D8\u4F53\u5DF2\u6309 mp \u89E3\u6790\uFF09`);
@@ -1592,8 +1750,8 @@ function mpTransform(opts) {
1592
1750
  }
1593
1751
 
1594
1752
  // src/devtools-plugin.ts
1595
- import fs5 from "node:fs";
1596
- import path5 from "node:path";
1753
+ import fs6 from "node:fs";
1754
+ import path6 from "node:path";
1597
1755
  import { createRequire as createRequire4 } from "node:module";
1598
1756
  import { WebSocketServer } from "ws";
1599
1757
 
@@ -1664,7 +1822,7 @@ function isOriginAllowed(origin, allowFrom) {
1664
1822
  return allowFrom.indexOf(origin) >= 0;
1665
1823
  }
1666
1824
  function resolveDevtoolsDir() {
1667
- return path5.dirname(require_.resolve("@proteus-vue/devtools/package.json"));
1825
+ return path6.dirname(require_.resolve("@proteus-vue/devtools/package.json"));
1668
1826
  }
1669
1827
  function createPanelPageHandler(devtoolsDir) {
1670
1828
  return (req, res) => {
@@ -1673,19 +1831,19 @@ function createPanelPageHandler(devtoolsDir) {
1673
1831
  if (pathname === base || pathname === base + "/") {
1674
1832
  const host = req.headers?.host ?? "localhost";
1675
1833
  const proto = req.headers?.["x-forwarded-proto"] === "https" ? "wss" : "ws";
1676
- const html = fs5.readFileSync(path5.join(devtoolsDir, "panel.html"), "utf8").replace(/__PROTEUS_DEFAULT_WS__/g, `'${proto}://${host}/proteus-panel'`).replace("./style.css", base + "/style.css").replace("./dist/panel.js", base + "/panel.js");
1834
+ const html = fs6.readFileSync(path6.join(devtoolsDir, "panel.html"), "utf8").replace(/__PROTEUS_DEFAULT_WS__/g, `'${proto}://${host}/proteus-panel'`).replace("./style.css", base + "/style.css").replace("./dist/panel.js", base + "/panel.js");
1677
1835
  res.setHeader("content-type", "text/html; charset=utf-8");
1678
1836
  res.end(html);
1679
1837
  return true;
1680
1838
  }
1681
1839
  if (pathname === base + "/style.css") {
1682
1840
  res.setHeader("content-type", "text/css; charset=utf-8");
1683
- res.end(fs5.readFileSync(path5.join(devtoolsDir, "style.css")));
1841
+ res.end(fs6.readFileSync(path6.join(devtoolsDir, "style.css")));
1684
1842
  return true;
1685
1843
  }
1686
1844
  if (pathname === base + "/panel.js") {
1687
1845
  res.setHeader("content-type", "application/javascript; charset=utf-8");
1688
- res.end(fs5.readFileSync(path5.join(devtoolsDir, "dist", "panel.js")));
1846
+ res.end(fs6.readFileSync(path6.join(devtoolsDir, "dist", "panel.js")));
1689
1847
  return true;
1690
1848
  }
1691
1849
  return false;
@@ -1759,8 +1917,8 @@ function devtoolsRelayPlugin(opts = {}) {
1759
1917
  }
1760
1918
 
1761
1919
  // src/vite-config.ts
1762
- import path6 from "node:path";
1763
- import fs6 from "node:fs";
1920
+ import path7 from "node:path";
1921
+ import fs7 from "node:fs";
1764
1922
  import { pathToFileURL } from "node:url";
1765
1923
  import { createRequire as createRequire5 } from "node:module";
1766
1924
  import {
@@ -1794,25 +1952,25 @@ function platformVariantPlugin(root, platform) {
1794
1952
  const bare = qIdx >= 0 ? id.slice(0, qIdx) : id;
1795
1953
  if (!bare) return null;
1796
1954
  let base;
1797
- if (bare.startsWith(".")) base = path6.resolve(path6.dirname(importer), bare);
1798
- else if (bare.startsWith("@/")) base = path6.resolve(root, "src", bare.slice(2));
1955
+ if (bare.startsWith(".")) base = path7.resolve(path7.dirname(importer), bare);
1956
+ else if (bare.startsWith("@/")) base = path7.resolve(root, "src", bare.slice(2));
1799
1957
  else return null;
1800
- const hasExt = path6.extname(base) !== "";
1958
+ const hasExt = path7.extname(base) !== "";
1801
1959
  if (hasExt) {
1802
- const r2 = resolvePlatformVariant2(base, platform, fs6.existsSync);
1960
+ const r2 = resolvePlatformVariant2(base, platform, fs7.existsSync);
1803
1961
  return r2 && r2 !== base ? r2 + query : null;
1804
1962
  }
1805
- const r = resolvePlatformVariantWithExts2(base, CODE_EXTS, platform, fs6.existsSync);
1963
+ const r = resolvePlatformVariantWithExts2(base, CODE_EXTS, platform, fs7.existsSync);
1806
1964
  return r ? r + query : null;
1807
1965
  }
1808
1966
  };
1809
1967
  }
1810
1968
  function hasPublicVariants(publicDir) {
1811
- if (!fs6.existsSync(publicDir)) return false;
1969
+ if (!fs7.existsSync(publicDir)) return false;
1812
1970
  const walk = (dir) => {
1813
- for (const e of fs6.readdirSync(dir, { withFileTypes: true })) {
1971
+ for (const e of fs7.readdirSync(dir, { withFileTypes: true })) {
1814
1972
  if (e.name.startsWith(".")) continue;
1815
- const full = path6.join(dir, e.name);
1973
+ const full = path7.join(dir, e.name);
1816
1974
  if (e.isDirectory()) {
1817
1975
  if (walk(full)) return true;
1818
1976
  } else if (splitVariant3(e.name).platform !== void 0) {
@@ -1828,20 +1986,20 @@ function platformPublicAssetsPlugin(root, platform) {
1828
1986
  name: "proteus-platform-public-assets",
1829
1987
  apply: "build",
1830
1988
  generateBundle() {
1831
- const publicDir = path6.join(root, "public");
1832
- if (!fs6.existsSync(publicDir)) return;
1989
+ const publicDir = path7.join(root, "public");
1990
+ if (!fs7.existsSync(publicDir)) return;
1833
1991
  const rels = [];
1834
1992
  const walk = (dir) => {
1835
- for (const e of fs6.readdirSync(dir, { withFileTypes: true })) {
1993
+ for (const e of fs7.readdirSync(dir, { withFileTypes: true })) {
1836
1994
  if (e.name.startsWith(".")) continue;
1837
- const full = path6.join(dir, e.name);
1995
+ const full = path7.join(dir, e.name);
1838
1996
  if (e.isDirectory()) walk(full);
1839
- else rels.push(path6.relative(publicDir, full).replace(/\\/g, "/"));
1997
+ else rels.push(path7.relative(publicDir, full).replace(/\\/g, "/"));
1840
1998
  }
1841
1999
  };
1842
2000
  walk(publicDir);
1843
2001
  for (const { from, to } of mapPublicAssetVariants2(rels, platform)) {
1844
- this.emitFile({ type: "asset", fileName: to, source: fs6.readFileSync(path6.join(publicDir, from)) });
2002
+ this.emitFile({ type: "asset", fileName: to, source: fs7.readFileSync(path7.join(publicDir, from)) });
1845
2003
  }
1846
2004
  }
1847
2005
  };
@@ -1869,7 +2027,7 @@ function virtualMpEntryPlugin() {
1869
2027
  };
1870
2028
  }
1871
2029
  async function importFromRoot(root, spec) {
1872
- const req = createRequire5(path6.join(root, "package.json"));
2030
+ const req = createRequire5(path7.join(root, "package.json"));
1873
2031
  const resolved = req.resolve(spec);
1874
2032
  return import(pathToFileURL(resolved).href);
1875
2033
  }
@@ -1906,15 +2064,15 @@ async function resolveProteusViteConfig(ctx, config) {
1906
2064
  // ★平台变体·静态资源 Web 通道(第 3 层):public/ 含平台变体(logo.web.png)时,
1907
2065
  // 关掉 Vite 默认逐字拷贝(会把他端变体也拷进产物),改由 platformPublicAssetsPlugin 按 web 解析;
1908
2066
  // 无变体时保持默认(零侵入,避免改变既有工程行为)。
1909
- publicDir: hasPublicVariants(path6.join(root, "public")) ? false : void 0,
2067
+ publicDir: hasPublicVariants(path7.join(root, "public")) ? false : void 0,
1910
2068
  resolve: {
1911
- alias: [{ find: "@", replacement: path6.join(root, "src") }]
2069
+ alias: [{ find: "@", replacement: path7.join(root, "src") }]
1912
2070
  },
1913
2071
  build: {
1914
2072
  target: "es2018",
1915
2073
  cssCodeSplit: false,
1916
2074
  minify: isMp ? false : void 0,
1917
- outDir: path6.join(root, "dist", platform),
2075
+ outDir: path7.join(root, "dist", platform),
1918
2076
  emptyOutDir: !isMp,
1919
2077
  rollupOptions: isMp ? { input: "proteus:mp-entry", output: { entryFileNames: "mp-entry.js" } } : void 0
1920
2078
  }
package/dist/plugin.d.ts CHANGED
@@ -119,6 +119,8 @@ export declare function collectMpEntries(opts: {
119
119
  onSkipWebOnly?: (file: string) => void;
120
120
  /** ★平台变体(2026-09-13):按该平台解析 `foo.mp.vue`/`foo.web.vue`(缺省 mp) */
121
121
  platform?: VariantPlatform;
122
+ /** ★框架组件输出策略(2026-09-18):'used'(缺省,按引用闭包输出)/ 'all'(全量,逃生舱) */
123
+ componentEmit?: 'used' | 'all';
122
124
  }): Array<{
123
125
  file: string;
124
126
  rel: string;
@@ -0,0 +1,28 @@
1
+ /** 抽取 `<template>` 块正文(跳过注释 / script / style / 嵌套 template 深度) */
2
+ export declare function extractTemplateBody(src: string): string;
3
+ /**
4
+ * 扫描模板正文中出现的全部标签名(kebab 化;跳过注释块)。
5
+ * PascalCase(`<PSafe>`)→ kebab(`p-safe`)——★旧正则只匹配小写开头,
6
+ * 导致 `<PSafe>` 完全跳过扫描(既不注册也不告警 → 组件静默不渲染,showcase 踩坑)。
7
+ */
8
+ export declare function extractTags(tpl: string): Set<string>;
9
+ /**
10
+ * ★编译器**产出**的组件标签(源码模板中不存在,由 compiler lowering 生成)。
11
+ * 必须计入引用集合,否则按需输出会把这些组件漏掉 → 运行时不渲染(真 bug)。
12
+ *
13
+ * 与 gen-routes 的 collectComponents 同源规则(两处必须一致):
14
+ * 源码含**形状变化动画**的 SVG(cx/r/d/stroke-dashoffset 等)→ 编译器 lowering 为 `<p-svg-canvas>`。
15
+ */
16
+ export declare function collectCompilerEmittedTags(templateBody: string): Set<string>;
17
+ /**
18
+ * 计算页面**实际引用**的框架组件目录集合(含组件间传递依赖闭包)。
19
+ *
20
+ * 用途:MP 产物只输出用到的组件(而非全量 76 个 = 607 KB)——对每个真实应用都是可观瘦身。
21
+ * ★诚实边界:仅识别**静态模板标签**。运行时动态拼标签不受支持(MP 本就无 `<component :is>`,
22
+ * 编译器已对 `<component :is>` 显式告警)——若确有非常规用法,可用 `components.emit: 'all'` 关闭本优化。
23
+ *
24
+ * @param pageFiles 页面源文件绝对路径(主包 + 分包,**须已排除 webOnly 页面**)
25
+ * @param componentsDir 框架组件根目录
26
+ * @returns 组件目录名集合(如 `p-view`);空集表示「未引用任何框架组件」
27
+ */
28
+ export declare function collectUsedFrameworkComponents(pageFiles: readonly string[], componentsDir: string): Set<string>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proteus-vue/plugin-vite",
3
- "version": "0.2.0-beta.2",
3
+ "version": "0.2.0-beta.5",
4
4
  "description": "Proteus Vite 插件(mp-weixin 编译管线适配层)+ gen-routes 路由表生成器",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -19,14 +19,14 @@
19
19
  "README.md"
20
20
  ],
21
21
  "dependencies": {
22
- "@proteus-vue/compiler": "0.3.0-beta.2",
23
- "@proteus-vue/module": "0.1.0",
24
- "@proteus-vue/router": "0.2.0-beta.2",
25
- "@proteus-vue/types": "0.2.0-beta.1",
22
+ "@proteus-vue/compiler": "0.3.0-beta.3",
23
+ "@proteus-vue/module": "0.1.1-beta.0",
24
+ "@proteus-vue/router": "0.2.0-beta.5",
25
+ "@proteus-vue/types": "0.2.0-beta.2",
26
26
  "esbuild": "^0.28.2",
27
27
  "sass": "^1.103.1",
28
28
  "ws": "^8.0.0",
29
- "@proteus-vue/compiler-backend": "0.1.1-beta.0"
29
+ "@proteus-vue/compiler-backend": "0.1.1-beta.1"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "vite": "^5.0.0"