@proteus-vue/plugin-vite 0.2.0-beta.0 → 0.2.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,176 +1,1033 @@
1
1
  // src/plugin.ts
2
- import fs2 from "node:fs";
3
- import path2 from "node:path";
4
- import { createRequire as createRequire2 } from "node:module";
2
+ import fs5 from "node:fs";
3
+ import path5 from "node:path";
4
+ import { createRequire as createRequire3 } from "node:module";
5
+
6
+ // ../types/src/router-config.ts
7
+ var DEFAULT_ROUTES_OUTPUT = "src/router/auto-routes.ts";
8
+ function resolveRouterConfig(config) {
9
+ const section = isObj(config) && isObj(config.router) ? config.router : {};
10
+ const cfg = isObj(config) ? config : {};
11
+ const duplicates = [];
12
+ let routesOutput;
13
+ if (section.routesOutput !== void 0) {
14
+ routesOutput = section.routesOutput;
15
+ if (cfg.routesOutput !== void 0) duplicates.push("routesOutput");
16
+ } else if (cfg.routesOutput !== void 0) {
17
+ routesOutput = cfg.routesOutput;
18
+ } else {
19
+ routesOutput = DEFAULT_ROUTES_OUTPUT;
20
+ }
21
+ let subPackages;
22
+ if (section.subPackages !== void 0) {
23
+ subPackages = section.subPackages;
24
+ if (cfg.subPackages !== void 0) duplicates.push("subPackages");
25
+ } else if (cfg.subPackages !== void 0) {
26
+ subPackages = cfg.subPackages;
27
+ } else {
28
+ subPackages = [];
29
+ }
30
+ let customRoute;
31
+ if (section.customRoute !== void 0) {
32
+ const cr = isObj(section.customRoute) ? section.customRoute : {};
33
+ customRoute = {
34
+ registerPresets: cr.registerPresets !== false,
35
+ builders: cr.builders ?? {}
36
+ };
37
+ if (cfg.customRoute !== void 0) duplicates.push("customRoute");
38
+ } else if (cfg.customRoute !== void 0) {
39
+ const cr = isObj(cfg.customRoute) ? cfg.customRoute : {};
40
+ customRoute = {
41
+ registerPresets: cr.registerPresets !== false,
42
+ builders: cr.builders ?? {}
43
+ };
44
+ } else {
45
+ customRoute = { registerPresets: true, builders: {} };
46
+ }
47
+ return {
48
+ router: {
49
+ routesOutput,
50
+ subPackages,
51
+ customRoute,
52
+ tabBar: section.tabBar,
53
+ meta: section.meta
54
+ },
55
+ duplicates
56
+ };
57
+ }
58
+ function isObj(v) {
59
+ return typeof v === "object" && v !== null;
60
+ }
61
+
62
+ // src/plugin.ts
5
63
  import { transform as esbuildTransform, build as esbuildBuild } from "esbuild";
6
64
  import * as sass from "sass";
7
- import { compileVueSfc } from "@proteus-vue/compiler";
65
+ import {
66
+ compileVueSfc,
67
+ transformStyleToWxss,
68
+ applyPlatformMacros,
69
+ platformDefines,
70
+ effectiveVariants as effectiveVariants2,
71
+ splitVariant as splitVariant2,
72
+ resolvePlatformVariantWithExts,
73
+ mapPublicAssetVariants,
74
+ resolvePlatformVariant
75
+ } from "@proteus-vue/compiler";
76
+ import { resolveRustCliBin, verifyDualCompilerEquivalence } from "@proteus-vue/compiler-backend";
8
77
 
9
- // src/appSkeleton.ts
10
- var APP_LAUNCH_SKELETON = `App({
11
- onLaunch() {
12
- // \u5168\u94FE\u8DEF\u8C03\u8BD5\u5F00\u5173\uFF08PROTEUS_DEBUG=1 \u6784\u5EFA\u65F6\u7531\u63D2\u4EF6\u66FF\u6362\u4E3A true\uFF09
13
- const debug = typeof __PROTEUS_DEBUG__ !== 'undefined' && __PROTEUS_DEBUG__
14
- if (debug) console.log('[proteus][app] \u542F\u52A8', Date.now())
15
- // \u5168\u5C40\u9519\u8BEF\u6355\u83B7\uFF08debug \u6784\u5EFA\u8F93\u51FA\uFF0C\u6B63\u5F0F\u6784\u5EFA\u5E38\u91CF\u6298\u53E0\u96F6\u6B8B\u7559\uFF09
16
- if (typeof wx !== 'undefined' && wx.onError) {
17
- wx.onError(function (err) {
18
- if (debug) console.error('[proteus][error]', err, Date.now())
19
- })
20
- }
21
- // \u5185\u7F6E\u9884\u8BBE\u6CE8\u518C\uFF08\u540C\u6587\u4EF6\u9759\u6001\u53EF\u5206\u6790\uFF1A\u51FD\u6570\u5B9A\u4E49\u5728\u524D\u3001\u6CE8\u518C\u5728\u540E\uFF0C\u63D2\u4EF6\u5DF2\u4FDD\u8BC1\u987A\u5E8F\uFF09
22
- if (typeof wx !== 'undefined' && wx.router) {
23
- __PRESET_REGISTRATION__
24
- }
25
- },
26
- // \u2605lifecycle-plan B4\uFF1AApp \u7EA7 onShow/onHide \u94A9\u5B50\uFF08\u8C03\u8BD5\u65E5\u5FD7\uFF1BWeb \u7AEF\u5BF9\u5E94 visibilitychange\uFF09
27
- onShow() {
28
- const debug = typeof __PROTEUS_DEBUG__ !== 'undefined' && __PROTEUS_DEBUG__
29
- if (debug) console.log('[proteus][app] onShow', Date.now())
30
- },
31
- onHide() {
32
- const debug = typeof __PROTEUS_DEBUG__ !== 'undefined' && __PROTEUS_DEBUG__
33
- if (debug) console.log('[proteus][app] onHide', Date.now())
34
- },
35
- })
36
- `;
78
+ // src/gen-routes.ts
79
+ import fs2 from "node:fs";
80
+ import path2 from "node:path";
81
+ import { mergeMeta } from "@proteus-vue/router/merge";
82
+ import { scanRoutes } from "@proteus-vue/router/scan";
83
+ import { buildRouteTree } from "@proteus-vue/router/tree";
84
+ import { effectiveVariants, splitVariant } from "@proteus-vue/compiler";
37
85
 
38
- // src/cache.ts
86
+ // src/resolve-components.ts
39
87
  import fs from "node:fs";
40
88
  import path from "node:path";
41
- import crypto from "node:crypto";
42
89
  import { createRequire } from "node:module";
43
- function getCompilerVersion(projectRoot) {
90
+ var COMPONENTS_PKG = "@proteus-vue/components";
91
+ function resolveComponentsRoot(projectRoot) {
44
92
  try {
45
- const require3 = createRequire(path.join(projectRoot, "package.json"));
46
- const pkgJsonPath = require3.resolve("@proteus-vue/compiler/package.json");
47
- const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
48
- const dist = path.join(path.dirname(pkgJsonPath), pkg.main ?? "dist/index.js");
49
- const h = crypto.createHash("sha1");
50
- h.update(fs.readFileSync(dist, "utf-8"));
51
- return `${pkg.version}-${h.digest("hex").slice(0, 8)}`;
93
+ const pkgRequire = createRequire(path.join(projectRoot, "package.json"));
94
+ const pkgJson = pkgRequire.resolve(`${COMPONENTS_PKG}/package.json`);
95
+ return path.dirname(pkgJson);
52
96
  } catch {
53
- return "unknown";
97
+ return path.join(projectRoot, "node_modules", COMPONENTS_PKG);
54
98
  }
55
99
  }
56
- function getEsbuildVersion(projectRoot) {
57
- try {
58
- const require3 = createRequire(path.join(projectRoot, "package.json"));
59
- const pkg = JSON.parse(fs.readFileSync(require3.resolve("esbuild/package.json"), "utf-8"));
60
- return pkg.version;
61
- } catch {
62
- return "unknown";
63
- }
100
+ function componentsRootExists(projectRoot) {
101
+ return fs.existsSync(resolveComponentsRoot(projectRoot));
64
102
  }
65
- function compileCacheKey(source, options, projectRoot) {
66
- const h = crypto.createHash("sha1");
67
- h.update(source);
68
- h.update("|");
69
- h.update(JSON.stringify({ ...options, compilerVersion: getCompilerVersion(projectRoot) }));
70
- return h.digest("hex");
103
+
104
+ // src/gen-routes.ts
105
+ function matchWebviewPage(list, rel, relInSub, mpPath) {
106
+ if (!Array.isArray(list) || !list.length) return false;
107
+ const norm = (x) => (x ?? "").replace(/^\/+/, "").replace(/\.vue$/, "").replace(/^pages\//, "");
108
+ const keys = new Set([norm(rel), relInSub ? norm(relInSub) : "", mpPath ? norm(mpPath) : ""].filter(Boolean));
109
+ return list.some((name) => keys.has(norm(name)));
71
110
  }
72
- function createCompileCache(cacheDir) {
73
- fs.mkdirSync(cacheDir, { recursive: true });
74
- const memory = /* @__PURE__ */ new Map();
75
- let hits = 0;
76
- let misses = 0;
77
- return {
78
- get(key) {
79
- const mem = memory.get(key);
80
- if (mem) {
81
- hits++;
82
- return mem;
83
- }
84
- const file = path.join(cacheDir, `${key}.json`);
85
- if (fs.existsSync(file)) {
86
- try {
87
- const entry = JSON.parse(fs.readFileSync(file, "utf-8"));
88
- memory.set(key, entry);
89
- hits++;
90
- return entry;
91
- } catch {
92
- }
93
- }
94
- misses++;
95
- return null;
96
- },
97
- set(key, entry) {
98
- memory.set(key, entry);
99
- try {
100
- fs.writeFileSync(path.join(cacheDir, `${key}.json`), JSON.stringify(entry));
101
- } catch {
102
- }
103
- },
104
- stats() {
105
- return { hits, misses };
111
+ function runGenRoutes(options) {
112
+ const config = options.config;
113
+ const ROOT = options.root ?? process.cwd();
114
+ const { router: rc, duplicates } = resolveRouterConfig(config);
115
+ for (const d of duplicates) console.warn(`[gen-routes] \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`);
116
+ const trace = options.trace ?? (() => {
117
+ });
118
+ const APP_DIR = path2.resolve(ROOT, path2.dirname(config.pagesDir));
119
+ const OUT_DIR = path2.join(ROOT, "dist", "mp-weixin");
120
+ const FW_COMPONENTS = options.componentsDir ? path2.resolve(ROOT, options.componentsDir) : resolveComponentsRoot(ROOT);
121
+ if (!fs2.existsSync(FW_COMPONENTS)) {
122
+ console.warn(
123
+ `[gen-routes] \u672A\u627E\u5230\u8BED\u4E49\u7EC4\u4EF6\u5E93 @proteus-vue/components\uFF08\u89E3\u6790\u4E3A ${FW_COMPONENTS}\uFF09\u2014\u2014p-* \u7EC4\u4EF6\u5C06\u4E0D\u88AB\u6CE8\u518C\uFF08\u9875\u9762 usingComponents \u7F3A\u5931 \u2192 WXML \u6574\u5757\u4E0D\u6E32\u67D3\uFF09\u3002\u8BF7\u786E\u8BA4\u5DF2\u5B89\u88C5\u4F9D\u8D56\uFF08npm i / pnpm i\uFF09\u3002`
124
+ );
125
+ }
126
+ const moduleChunks = /* @__PURE__ */ new Map();
127
+ for (const mc of options.moduleConfigs ?? []) moduleChunks.set(mc.name, mc.chunk ?? mc.name);
128
+ const subPackageModules = /* @__PURE__ */ new Map();
129
+ for (const mc of options.moduleConfigs ?? []) {
130
+ const chunk = mc.chunk ?? mc.name;
131
+ const matched = rc.subPackages.some((sp) => (sp.name ?? path2.basename(sp.root)) === chunk);
132
+ if (matched) subPackageModules.set(chunk, { deps: Object.keys(mc.dependencies ?? {}), preload: mc.preload ?? [] });
133
+ for (const dep of Object.keys(mc.dependencies ?? {})) {
134
+ if (!moduleChunks.has(dep)) console.warn(`[gen-routes] \u6A21\u5757 ${mc.name} \u4F9D\u8D56 "${dep}" \u672A\u627E\u5230\u5BF9\u5E94\u6A21\u5757\u5951\u7EA6\uFF08proteus-module.config.ts\uFF09\u2014\u2014\u4F9D\u8D56\u5C06\u4E0D\u751F\u6548`);
106
135
  }
136
+ }
137
+ const subPackageNameOf = (moduleName) => {
138
+ const chunk = moduleChunks.get(moduleName);
139
+ return rc.subPackages.some((sp) => (sp.name ?? path2.basename(sp.root)) === chunk) ? chunk : void 0;
107
140
  };
108
- }
109
- function bundleCacheKey(entryFile, projectRoot) {
110
- const h = crypto.createHash("sha1");
111
- h.update(entryFile);
112
- h.update("|");
113
- h.update(
114
- JSON.stringify({
115
- esbuild: getEsbuildVersion(projectRoot),
116
- target: "es2018",
117
- format: "cjs",
118
- charset: "utf8",
119
- minify: true,
120
- external: ["@proteus-vue/*"]
121
- })
122
- );
123
- return h.digest("hex");
124
- }
125
- function inputsValid(inputs) {
126
- for (let i = 0; i < inputs.length; i++) {
127
- try {
128
- const st = fs.statSync(inputs[i].file);
129
- if (st.mtimeMs !== inputs[i].mtimeMs || st.size !== inputs[i].size) return false;
130
- } catch {
131
- return false;
141
+ function walkVueFiles2(dir, acc = []) {
142
+ if (!fs2.existsSync(dir)) return acc;
143
+ for (const entry of fs2.readdirSync(dir, { withFileTypes: true })) {
144
+ if (entry.name.startsWith(".")) continue;
145
+ const full = path2.join(dir, entry.name);
146
+ if (entry.isDirectory()) walkVueFiles2(full, acc);
147
+ else if (entry.name.endsWith(".vue")) acc.push(full);
132
148
  }
149
+ return acc;
133
150
  }
134
- return true;
135
- }
136
- function createBundleCache(cacheDir) {
137
- fs.mkdirSync(cacheDir, { recursive: true });
138
- const memory = /* @__PURE__ */ new Map();
139
- let hits = 0;
140
- let misses = 0;
141
- return {
142
- get(key) {
143
- const mem = memory.get(key);
144
- if (mem && inputsValid(mem.inputs)) {
145
- hits++;
146
- return mem;
151
+ function resolveConfigMeta(configMeta, pageRel) {
152
+ if (!configMeta) return void 0;
153
+ let dirMeta;
154
+ const segs = pageRel.split("/");
155
+ for (let i = segs.length - 1; i >= 1; i--) {
156
+ const prefix = segs.slice(0, i).join("/");
157
+ if (configMeta[prefix]) {
158
+ dirMeta = configMeta[prefix];
159
+ break;
147
160
  }
148
- const file = path.join(cacheDir, `${key}.json`);
149
- if (fs.existsSync(file)) {
150
- try {
151
- const entry = JSON.parse(fs.readFileSync(file, "utf-8"));
152
- if (inputsValid(entry.inputs)) {
153
- memory.set(key, entry);
154
- hits++;
155
- return entry;
156
- }
157
- } catch {
161
+ }
162
+ const exact = configMeta[pageRel];
163
+ if (dirMeta && exact) return mergeMeta(dirMeta, exact);
164
+ return dirMeta ?? exact;
165
+ }
166
+ function scanPages() {
167
+ const pages2 = [];
168
+ const configMeta = rc.meta;
169
+ const keepMp = (blocks) => {
170
+ const effective = new Set(effectiveVariants(blocks.map((b) => b.componentPath), "mp"));
171
+ return blocks.filter((b) => effective.has(b.componentPath));
172
+ };
173
+ const mainBlocks = keepMp(scanRoutes(path2.join(ROOT, config.pagesDir), { derivePath: true, verbose: true, includeNoRoute: true }));
174
+ for (const b of mainBlocks) {
175
+ const relSrc = path2.relative(APP_DIR, splitVariant(b.componentPath).base).replace(/\\/g, "/").replace(/\.vue$/, "");
176
+ const pageRel = relSrc.replace(/^pages\//, "");
177
+ trace(`[route] ${relSrc} \u6765\u6E90\u767B\u8BB0\uFF08${b.loc.file}:${b.loc.line}\uFF0Croute/scan\uFF09`);
178
+ pages2.push({
179
+ file: b.componentPath,
180
+ relSrc,
181
+ mpPath: relSrc,
182
+ // ★集中 meta:config(精确/目录前缀)→ 页面 <route> 覆盖(mergeMeta 页面胜)
183
+ meta: mergeMeta(resolveConfigMeta(configMeta, pageRel), b.meta),
184
+ params: b.params,
185
+ pageJson: b.pageJson,
186
+ customRouteKeyName: b.customRouteKeyName,
187
+ webOnly: b.webOnly,
188
+ platforms: b.platforms
189
+ });
190
+ }
191
+ for (const sp of rc.subPackages) {
192
+ const spRootAbs = path2.join(ROOT, sp.root);
193
+ const spName = sp.name ?? path2.basename(sp.root);
194
+ const spBlocks = keepMp(scanRoutes(spRootAbs, { derivePath: true, verbose: true, includeNoRoute: true }));
195
+ for (const b of spBlocks) {
196
+ const basePath = splitVariant(b.componentPath).base;
197
+ const relSrc = path2.relative(APP_DIR, basePath).replace(/\\/g, "/").replace(/\.vue$/, "");
198
+ const relInSub = path2.relative(spRootAbs, basePath).replace(/\\/g, "/").replace(/\.vue$/, "");
199
+ const pageRel = relInSub.replace(/^pages\//, "");
200
+ pages2.push({
201
+ file: b.componentPath,
202
+ relSrc,
203
+ mpPath: relSrc,
204
+ subPackage: spName,
205
+ relInSub,
206
+ meta: mergeMeta(resolveConfigMeta(configMeta, pageRel), b.meta),
207
+ params: b.params,
208
+ pageJson: b.pageJson,
209
+ customRouteKeyName: b.customRouteKeyName,
210
+ chunk: b.chunk,
211
+ webOnly: b.webOnly,
212
+ platforms: b.platforms
213
+ });
214
+ if (b.chunk && b.chunk !== spName) {
215
+ console.warn(`[gen-routes] \u5206\u5305\u9875\u9762 ${relInSub} \u58F0\u660E chunk="${b.chunk}" \u4E0E\u5206\u5305\u540D "${spName}" \u4E0D\u4E00\u81F4\uFF08Router M7.1\uFF1A\u9875\u9762 chunk \u5E94\u5BF9\u9F50\u6A21\u5757\u5206\u5305\u540D\uFF0C\u89C1 module-plan 05\uFF09`);
158
216
  }
159
217
  }
160
- misses++;
161
- return null;
162
- },
163
- set(key, entry) {
164
- memory.set(key, entry);
165
- try {
166
- fs.writeFileSync(path.join(cacheDir, `${key}.json`), JSON.stringify(entry));
167
- } catch {
218
+ }
219
+ return pages2;
220
+ }
221
+ function buildRoutes(pages2) {
222
+ const routes2 = pages2.map((p) => {
223
+ const r = {
224
+ // ★统一后 name 由 scan 推导(derivePath 模式:index 归并目录名,与旧 toRouteName 一致)
225
+ name: deriveName(p),
226
+ path: p.mpPath,
227
+ // 相对 RouterView 所在目录({appDir}/router)的路径,Web 端 import.meta.glob 按此匹配
228
+ // ★平台变体(2026-09-13):路由表 Web/MP **共享**,component 必须写**基准路径**(去变体后缀)——
229
+ // 两端各自按平台解析到自己的变体(Web: x.web.vue / MP: x.mp.vue)。写变体路径会让 Web 拿到 MP 变体。
230
+ component: path2.relative(path2.join(APP_DIR, "router"), splitVariant(p.file).base).replace(/\\/g, "/")
231
+ };
232
+ if (p.subPackage) r.subPackage = p.subPackage;
233
+ if (p.meta && Object.keys(p.meta).length > 0) r.meta = p.meta;
234
+ if (p.customRouteKeyName) r.customRouteKeyName = p.customRouteKeyName;
235
+ if (p.params && Object.keys(p.params).length > 0) r.params = p.params;
236
+ if (p.platforms && p.platforms.length > 0) r.platforms = p.platforms;
237
+ return r;
238
+ });
239
+ const nameByRel = new Map(pages2.map((p) => [p.relSrc, deriveName(p)]));
240
+ const blocks = pages2.map((p) => ({
241
+ loc: { file: p.file, line: 1, column: 1 },
242
+ path: p.relSrc.endsWith("/index") ? p.relSrc.slice(0, -"/index".length) : p.relSrc,
243
+ name: nameByRel.get(p.relSrc),
244
+ meta: p.meta ?? {},
245
+ componentPath: p.file
246
+ }));
247
+ const tree = buildRouteTree(blocks, {}, trace);
248
+ const parentByName = /* @__PURE__ */ new Map();
249
+ const walk = (nodes, parent) => {
250
+ for (const n of nodes) {
251
+ if (n.name && parent) parentByName.set(n.name, parent);
252
+ walk(n.children, n.name);
168
253
  }
169
- },
170
- stats() {
171
- return { hits, misses };
254
+ };
255
+ walk(tree);
256
+ for (const r of routes2) {
257
+ const parent = parentByName.get(r.name);
258
+ if (parent && parent !== r.name) r.parent = parent;
172
259
  }
173
- };
260
+ return routes2;
261
+ }
262
+ function deriveName(p) {
263
+ const base = p.relSrc.split("/").pop() ?? "";
264
+ if (base === "index") {
265
+ const dir = p.relSrc.slice(0, p.relSrc.lastIndexOf("/"));
266
+ const stripped = dir.replace(/^(pages|subpackages)(\/|$)/, "").replace(/\/$/, "");
267
+ return stripped ? stripped.replace(/\//g, "-") : "index";
268
+ }
269
+ return p.relSrc.replace(/^(pages|subpackages)\//, "").replace(/\//g, "-");
270
+ }
271
+ function validate(pages2, routes2) {
272
+ const mainCount = pages2.filter((p) => !p.subPackage).length;
273
+ if (mainCount > 32) {
274
+ throw new Error(
275
+ `[gen-routes] \u4E3B\u5305\u9875\u9762\u6570 ${mainCount} \u8D85\u8FC7\u5E73\u53F0\u786C\u8FB9\u754C 32\uFF0C\u8BF7\u5C06\u90E8\u5206\u9875\u9762\u79FB\u5165\u5206\u5305\uFF08platform limitation, cannot exceed\uFF09`
276
+ );
277
+ }
278
+ const dupNames = routes2.filter((r, i) => routes2.findIndex((x) => x.name === r.name) !== i);
279
+ if (dupNames.length) {
280
+ throw new Error(`[gen-routes] \u547D\u540D\u8DEF\u7531\u91CD\u590D\uFF1A${dupNames.map((r) => r.name).join(", ")}`);
281
+ }
282
+ }
283
+ function formatRoute(r) {
284
+ const parts = [
285
+ `name: ${JSON.stringify(r.name)}`,
286
+ `path: ${JSON.stringify(r.path)}`,
287
+ `component: ${JSON.stringify(r.component)}`
288
+ ];
289
+ if (r.parent) parts.push(`parent: ${JSON.stringify(r.parent)}`);
290
+ if (r.subPackage) parts.push(`subPackage: ${JSON.stringify(r.subPackage)}`);
291
+ if (r.meta && Object.keys(r.meta).length) parts.push(`meta: ${JSON.stringify(r.meta)}`);
292
+ if (r.customRouteKeyName) parts.push(`customRouteKeyName: ${JSON.stringify(r.customRouteKeyName)}`);
293
+ if (r.platforms && r.platforms.length) parts.push(`platforms: ${JSON.stringify(r.platforms)}`);
294
+ return ` { ${parts.join(", ")} },`;
295
+ }
296
+ function writeAutoRoutes(routes2) {
297
+ const lines = [
298
+ `// ${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
+ "// \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",
300
+ "import type { RouteRecord } from '@proteus-vue/router/types'",
301
+ "",
302
+ "export const routes: RouteRecord[] = [",
303
+ ...routes2.map(formatRoute),
304
+ "]",
305
+ "",
306
+ "export const tabRoutes: RouteRecord[] = routes.filter(r => r.meta?.isTab)",
307
+ "export const routeMap: Record<string, RouteRecord> = routes.reduce((m, r) => { m[r.name] = r; return m }, {} as Record<string, RouteRecord>)",
308
+ ""
309
+ ];
310
+ lines.push("// \u2605 \u7C7B\u578B\u63D0\u793A\uFF1A\u6309\u8DEF\u7531\u540D\u7D22\u5F15\u7684\u53C2\u6570\u7C7B\u578B\u8868\uFF08\u6765\u6E90\uFF1A<route> \u5757 params \u58F0\u660E\uFF09");
311
+ lines.push("declare module '@proteus-vue/router/types' {");
312
+ lines.push(" interface RouteParamsByName {");
313
+ for (const r of routes2) {
314
+ const params = r.params ?? {};
315
+ const fields = Object.entries(params);
316
+ const body = fields.length ? fields.map(([k, t]) => `${k}?: ${tsType(t)}`).join("; ") : "";
317
+ lines.push(` '${r.name}': { ${body} },`);
318
+ }
319
+ lines.push(" }", "}");
320
+ const outFile = path2.join(ROOT, rc.routesOutput);
321
+ fs2.mkdirSync(path2.dirname(outFile), { recursive: true });
322
+ fs2.writeFileSync(outFile, lines.join("\n"));
323
+ console.log(`[gen-routes] \u5DF2\u751F\u6210 ${path2.relative(ROOT, outFile)}\uFF08${routes2.length} \u6761\u8DEF\u7531 + RouteParamsByName\uFF09`);
324
+ }
325
+ function tsType(t) {
326
+ if (t === "number") return "number";
327
+ if (t === "boolean") return "boolean";
328
+ if (t !== "string") {
329
+ console.warn(`[gen-routes] \u672A\u77E5\u53C2\u6570\u7C7B\u578B ${t}\uFF08\u652F\u6301 string/number/boolean\uFF09\uFF0C\u5DF2\u6309 string \u5904\u7406`);
330
+ }
331
+ return "string";
332
+ }
333
+ function writeAppJson(allPages, routes2) {
334
+ const pages2 = allPages.filter((p) => {
335
+ const pi = p;
336
+ if (pi.webOnly) return false;
337
+ if (pi.platforms && !pi.platforms.includes("mp")) return false;
338
+ return true;
339
+ });
340
+ const mainPages = pages2.filter((p) => !p.subPackage).map((p) => p.mpPath);
341
+ const entryPath = path2.relative(APP_DIR, path2.join(ROOT, config.pagesDir, "index")).replace(/\\/g, "/");
342
+ const entryIdx = mainPages.indexOf(entryPath);
343
+ if (entryIdx > 0) {
344
+ mainPages.splice(entryIdx, 1);
345
+ mainPages.unshift(entryPath);
346
+ }
347
+ const subPackages = rc.subPackages.filter((sp) => pages2.some((p) => p.subPackage === (sp.name ?? path2.basename(sp.root)))).map((sp) => {
348
+ const spName = sp.name ?? path2.basename(sp.root);
349
+ const out = {
350
+ root: path2.relative(APP_DIR, path2.join(ROOT, sp.root)).replace(/\\/g, "/"),
351
+ ...sp.name ? { name: sp.name } : {},
352
+ pages: pages2.filter((p) => p.subPackage === spName && p.relInSub).map((p) => p.relInSub)
353
+ };
354
+ const mod = subPackageModules.get(spName);
355
+ if (mod) {
356
+ const depNames = mod.deps.map(subPackageNameOf).filter((n) => Boolean(n));
357
+ if (depNames.length) out.dependencies = depNames;
358
+ }
359
+ return out;
360
+ });
361
+ const preloadRule = {};
362
+ for (const [spName, mod] of subPackageModules) {
363
+ const targetPackages = mod.preload.map(subPackageNameOf).filter((n) => Boolean(n));
364
+ if (!targetPackages.length) continue;
365
+ const entryPage = pages2.find((p) => p.subPackage === spName && p.relInSub);
366
+ if (!entryPage) continue;
367
+ preloadRule[entryPage.mpPath] = { network: "all", packages: targetPackages };
368
+ }
369
+ const windowConfig = { navigationStyle: "custom" };
370
+ const tabRoutes = routes2.filter((r) => r.meta?.isTab);
371
+ const appJson = { pages: mainPages };
372
+ if (subPackages.length) appJson.subPackages = subPackages;
373
+ if (Object.keys(preloadRule).length) appJson.preloadRule = preloadRule;
374
+ appJson.window = windowConfig;
375
+ if (config.skyline) appJson.lazyCodeLoading = "requiredComponents";
376
+ if (config.skyline) {
377
+ appJson.rendererOptions = { skyline: { defaultDisplayBlock: config.skylineLayout?.defaultDisplayBlock ?? true } };
378
+ }
379
+ const tabBarDecl = rc.tabBar;
380
+ const tabBarListFromDecl = tabBarDecl?.list ?? [];
381
+ const tabBarBase = tabBarDecl ? { color: tabBarDecl.color ?? "#999999", selectedColor: tabBarDecl.selectedColor ?? "#007AFF" } : {};
382
+ if (tabBarListFromDecl.length > 0) {
383
+ if (tabBarListFromDecl.length < 2) {
384
+ console.warn(`[gen-routes] router.tabBar.list \u4EC5\u58F0\u660E ${tabBarListFromDecl.length} \u9879\uFF0C\u5FAE\u4FE1\u8981\u6C42\u81F3\u5C11 2 \u9879\uFF0C\u5DF2\u5FFD\u7565 tabBar \u914D\u7F6E`);
385
+ } else {
386
+ const byName = new Map(routes2.map((r) => [r.name, r]));
387
+ const missing = tabBarListFromDecl.filter((item) => !byName.has(item.name));
388
+ if (missing.length) {
389
+ console.warn(`[gen-routes] router.tabBar.list \u5F15\u7528\u672A\u77E5\u8DEF\u7531\u540D\uFF1A${missing.map((m) => m.name).join(" / ")}\u2014\u2014\u5BF9\u5E94\u9879\u5DF2\u8DF3\u8FC7\uFF08\u8DEF\u7531\u540D\u89C1 ${rc.routesOutput}\uFF09`);
390
+ }
391
+ appJson.tabBar = {
392
+ ...tabBarBase,
393
+ list: tabBarListFromDecl.filter((item) => byName.has(item.name)).map((item) => {
394
+ const r = byName.get(item.name);
395
+ const entry = { pagePath: r.path, text: item.text };
396
+ if (item.icon) entry.iconPath = item.icon;
397
+ return entry;
398
+ })
399
+ };
400
+ }
401
+ } else if (tabRoutes.length >= 2) {
402
+ appJson.tabBar = { ...tabBarBase, list: tabRoutes.map((r) => ({ pagePath: r.path, text: r.meta?.title ?? r.name })) };
403
+ } else if (tabRoutes.length === 1) {
404
+ console.warn(`[gen-routes] tabBar \u4EC5\u58F0\u660E 1 \u9879\uFF08${tabRoutes[0].name}\uFF09\uFF0C\u5FAE\u4FE1\u8981\u6C42\u81F3\u5C11 2 \u9879\uFF0C\u5DF2\u5FFD\u7565 tabBar \u914D\u7F6E\uFF1B\u53EF\u5C06\u66F4\u591A\u9875\u9762\u6807\u8BB0 isTab \u6216\u79FB\u9664\u73B0\u6709 isTab`);
405
+ }
406
+ fs2.mkdirSync(OUT_DIR, { recursive: true });
407
+ fs2.writeFileSync(path2.join(OUT_DIR, "app.json"), JSON.stringify(appJson, null, 2) + "\n");
408
+ console.log(`[gen-routes] \u5DF2\u751F\u6210 dist/mp-weixin/app.json\uFF08\u4E3B\u5305 ${mainPages.length} \u9875\uFF0C\u5206\u5305 ${subPackages.length} \u4E2A\uFF09`);
409
+ }
410
+ const NATIVE_MP_TAGS = /* @__PURE__ */ new Set([
411
+ "view",
412
+ "text",
413
+ "image",
414
+ "button",
415
+ "input",
416
+ "textarea",
417
+ "video",
418
+ "canvas",
419
+ "scroll-view",
420
+ "slot",
421
+ "rich-text",
422
+ "swiper",
423
+ "swiper-item",
424
+ "navigator",
425
+ "icon",
426
+ "progress",
427
+ "checkbox",
428
+ "radio",
429
+ "form",
430
+ "label",
431
+ "picker",
432
+ "slider",
433
+ "switch",
434
+ "map",
435
+ "web-view",
436
+ "cover-view",
437
+ "cover-image",
438
+ "movable-area",
439
+ "movable-view",
440
+ "block",
441
+ "template",
442
+ "wxs",
443
+ "audio",
444
+ "camera",
445
+ "live-player",
446
+ "ad",
447
+ "official-account",
448
+ "open-data",
449
+ "page-container",
450
+ "root-portal",
451
+ "match-media",
452
+ // ★vue-compat-advance Batch 2/5:<transition> 由编译器消费(装饰式,产物不输出该标签)——扫描跳过,非自定义组件
453
+ "transition",
454
+ // ★2026-09-09 G-62 SVG→Skyline P0:SVG 标签由编译器 lowering 为 <image> data-URI(template/svg-to-image),
455
+ // 产物不含这些标签——扫描跳过,否则误报「未找到组件 <svg>」并写入 usingComponents(image-spike 实证)
456
+ "svg",
457
+ "path",
458
+ "circle",
459
+ "rect",
460
+ "line",
461
+ "polyline",
462
+ "polygon",
463
+ "ellipse",
464
+ "g",
465
+ "defs",
466
+ "linearGradient",
467
+ "radialGradient",
468
+ "stop",
469
+ "use",
470
+ "symbol",
471
+ "mask",
472
+ "clipPath",
473
+ "tspan",
474
+ // ★2026-09-09 G-62:SVG 动画标签由编译器消费(转 CSS 或 canvas 场景)——产物无这些标签
475
+ "animate",
476
+ "animateTransform",
477
+ "animateMotion",
478
+ "set",
479
+ // ★2026-09-09 G-62 规范盘点补全:滤镜/图案/标记/内嵌图/文字路径(lowering 保留在 data-URI 内)
480
+ "filter",
481
+ "feGaussianBlur",
482
+ "feColorMatrix",
483
+ "feOffset",
484
+ "feBlend",
485
+ "feComposite",
486
+ "feTurbulence",
487
+ "feDropShadow",
488
+ "feMerge",
489
+ "feMergeNode",
490
+ "feMorphology",
491
+ "feDisplacementMap",
492
+ "feImage",
493
+ "feTile",
494
+ "feDistantLight",
495
+ "fePointLight",
496
+ "feSpotLight",
497
+ "feDiffuseLighting",
498
+ "feSpecularLighting",
499
+ "feComponentTransfer",
500
+ "feFuncA",
501
+ "feFuncB",
502
+ "feFuncG",
503
+ "feFuncR",
504
+ "pattern",
505
+ "marker",
506
+ "view",
507
+ "textPath",
508
+ "title",
509
+ "desc",
510
+ "metadata"
511
+ ]);
512
+ const HTML_TAGS = /* @__PURE__ */ new Set([
513
+ "div",
514
+ "span",
515
+ "p",
516
+ "h1",
517
+ "h2",
518
+ "h3",
519
+ "h4",
520
+ "h5",
521
+ "h6",
522
+ "a",
523
+ "img",
524
+ "br",
525
+ "ul",
526
+ "ol",
527
+ "li",
528
+ "section",
529
+ "header",
530
+ "footer",
531
+ "main",
532
+ "aside",
533
+ "nav",
534
+ "article",
535
+ "strong",
536
+ "em",
537
+ "b",
538
+ "i",
539
+ "small",
540
+ "code",
541
+ "pre",
542
+ "select",
543
+ "option",
544
+ "table",
545
+ "tr",
546
+ "td",
547
+ "th",
548
+ "form",
549
+ "label",
550
+ "tbody",
551
+ "thead",
552
+ "caption",
553
+ "figure",
554
+ "figcaption",
555
+ "details",
556
+ "summary"
557
+ ]);
558
+ function extractTemplateBody2(src) {
559
+ const n = src.length;
560
+ let i = 0;
561
+ while (i < n) {
562
+ const lt = src.indexOf("<", i);
563
+ if (lt < 0) return "";
564
+ if (src.startsWith("<!--", lt)) {
565
+ const e = src.indexOf("-->", lt + 4);
566
+ i = e < 0 ? n : e + 3;
567
+ continue;
568
+ }
569
+ const block = /^<(script|style)\b/i.exec(src.slice(lt, lt + 32));
570
+ if (block) {
571
+ const tag = block[1].toLowerCase();
572
+ const end = src.toLowerCase().indexOf(`</${tag}`, lt + block[0].length);
573
+ if (end < 0) return "";
574
+ const gt = src.indexOf(">", end);
575
+ i = gt < 0 ? n : gt + 1;
576
+ continue;
577
+ }
578
+ if (/^<template[\s>]/i.test(src.slice(lt, lt + 16))) {
579
+ const gt = src.indexOf(">", lt);
580
+ if (gt < 0) return "";
581
+ let depth = 1;
582
+ let j = gt + 1;
583
+ while (j < n) {
584
+ const l2 = src.indexOf("<", j);
585
+ if (l2 < 0) return src.slice(gt + 1);
586
+ if (src.startsWith("<!--", l2)) {
587
+ const e = src.indexOf("-->", l2 + 4);
588
+ j = e < 0 ? n : e + 3;
589
+ continue;
590
+ }
591
+ const seg = src.slice(l2, l2 + 16);
592
+ if (/^<\/template[\s>]/i.test(seg)) {
593
+ depth--;
594
+ if (depth === 0) return src.slice(gt + 1, l2);
595
+ j = l2 + "</template>".length;
596
+ continue;
597
+ }
598
+ if (/^<template[\s>]/i.test(seg)) {
599
+ depth++;
600
+ const g2 = src.indexOf(">", l2);
601
+ j = g2 < 0 ? n : g2 + 1;
602
+ continue;
603
+ }
604
+ j = l2 + 1;
605
+ }
606
+ return src.slice(gt + 1);
607
+ }
608
+ i = lt + 1;
609
+ }
610
+ return "";
611
+ }
612
+ function collectComponents(file, skipSemantic = false) {
613
+ const src = fs2.readFileSync(file, "utf-8");
614
+ const tpl = extractTemplateBody2(src);
615
+ const customTags = new Set(Object.keys(config.rules?.customTags ?? {}));
616
+ const gridRuleDisabled = (config.rules?.disabled ?? []).includes("fluid/semantic-grid");
617
+ const semanticTags = skipSemantic && !gridRuleDisabled ? /* @__PURE__ */ new Set(["p-grid"]) : /* @__PURE__ */ new Set();
618
+ const used = /* @__PURE__ */ new Set();
619
+ let idx = 0;
620
+ while (idx < tpl.length) {
621
+ const lt = tpl.indexOf("<", idx);
622
+ if (lt < 0) break;
623
+ if (tpl.startsWith("<!--", lt)) {
624
+ const e = tpl.indexOf("-->", lt + 4);
625
+ idx = e < 0 ? tpl.length : e + 3;
626
+ continue;
627
+ }
628
+ if (tpl.startsWith("</", lt)) {
629
+ idx = lt + 2;
630
+ continue;
631
+ }
632
+ const mm = /^([A-Za-z][\w-]*)/.exec(tpl.slice(lt + 1));
633
+ if (!mm) {
634
+ idx = lt + 1;
635
+ continue;
636
+ }
637
+ const tagRaw = mm[1];
638
+ const tag = /[A-Z]/.test(tagRaw) ? tagRaw.replace(/\B([A-Z])/g, "-$1").toLowerCase() : tagRaw;
639
+ if (!(NATIVE_MP_TAGS.has(tag) || HTML_TAGS.has(tag) || customTags.has(tag) || semanticTags.has(tag))) used.add(tag);
640
+ idx = lt + 1 + mm[0].length;
641
+ }
642
+ if (/<(?:svg|circle|rect|ellipse|path|line|polyline|polygon)[\s>][\s\S]*?<animate\b/i.test(tpl)) {
643
+ const shapeAnim = /<animate\s[^>]*attributeName\s*=\s*["'](cx|cy|r|rx|ry|x|y|width|height|d|points|stroke-dashoffset|stroke-dasharray)["']/i;
644
+ if (shapeAnim.test(tpl)) used.add("p-svg-canvas");
645
+ }
646
+ const out = {};
647
+ for (const tag of used) {
648
+ const appCandidates = [path2.join(APP_DIR, "components", tag, "index.vue"), path2.join(APP_DIR, "components", `${tag}.vue`)];
649
+ const appFound = appCandidates.find((c) => fs2.existsSync(c));
650
+ if (appFound) {
651
+ out[tag] = `/components/${tag}/index`;
652
+ continue;
653
+ }
654
+ const fwCandidates = [path2.join(FW_COMPONENTS, tag, "index.vue"), path2.join(FW_COMPONENTS, `${tag}.vue`)];
655
+ const fwFound = fwCandidates.find((c) => fs2.existsSync(c));
656
+ if (fwFound) {
657
+ out[tag] = `/proteus/${tag}/index`;
658
+ continue;
659
+ }
660
+ console.warn(`[gen-routes] ${file} \u4F7F\u7528\u4E86\u7EC4\u4EF6 <${tag}>\uFF0C\u4F46\u672A\u627E\u5230 ${appCandidates.join(" \u6216 ")} \u6216\u6846\u67B6\u7EC4\u4EF6 ${fwCandidates.join(" \u6216 ")}`);
661
+ }
662
+ return out;
663
+ }
664
+ function writePageJsons(pages2) {
665
+ for (const p of pages2) {
666
+ const pageJson = {};
667
+ if (config.skyline && !matchWebviewPage(config.page?.webviewPages, p.relSrc, p.relInSub, p.mpPath)) {
668
+ pageJson.renderer = "skyline";
669
+ pageJson.componentFramework = "glass-easel";
670
+ }
671
+ if (p.pageJson) Object.assign(pageJson, p.pageJson);
672
+ const components = collectComponents(p.file, true);
673
+ if (Object.keys(components).length) pageJson.usingComponents = components;
674
+ const outFile = path2.join(OUT_DIR, p.mpPath + ".json");
675
+ fs2.mkdirSync(path2.dirname(outFile), { recursive: true });
676
+ fs2.writeFileSync(outFile, JSON.stringify(pageJson, null, 2) + "\n");
677
+ }
678
+ console.log(`[gen-routes] \u5DF2\u751F\u6210 ${pages2.length} \u4E2A\u9875\u9762 page.json`);
679
+ }
680
+ function writeComponentJsons() {
681
+ const roots = [
682
+ { dir: path2.join(APP_DIR, "components"), prefix: "components" },
683
+ { dir: FW_COMPONENTS, prefix: "proteus" }
684
+ ];
685
+ let count = 0;
686
+ for (const { dir, prefix } of roots) {
687
+ if (!fs2.existsSync(dir)) continue;
688
+ for (const f of walkVueFiles2(dir)) {
689
+ const rel = path2.relative(dir, f).replace(/\\/g, "/").replace(/\.vue$/, "");
690
+ const comps = collectComponents(f);
691
+ const outFile = path2.join(OUT_DIR, prefix, `${rel}.json`);
692
+ fs2.mkdirSync(path2.dirname(outFile), { recursive: true });
693
+ const json = { component: true };
694
+ if (config.skyline) json.componentFramework = "glass-easel";
695
+ json.styleIsolation = "apply-shared";
696
+ if (Object.keys(comps).length) json.usingComponents = comps;
697
+ fs2.writeFileSync(outFile, JSON.stringify(json, null, 2) + "\n");
698
+ count++;
699
+ }
700
+ }
701
+ if (count) console.log(`[gen-routes] \u5DF2\u751F\u6210 ${count} \u4E2A\u7EC4\u4EF6 component.json\uFF08component \u58F0\u660E + usingComponents \u5D4C\u5957\uFF09`);
702
+ }
703
+ function writeProjectConfig() {
704
+ const projectName = path2.basename(ROOT).replace(/[^\w.-]/g, "-");
705
+ const projectConfig = {
706
+ compileType: "miniprogram",
707
+ appid: config.appid,
708
+ projectname: projectName,
709
+ // ★2026-09-09 真机复测实证:skyline 项目需 IDE 级 skylineRenderEnable 开关,否则模拟器回落 WebView
710
+ // (getSkylineInfoSync().isSupported=false / reason=a-b test not enabled——产物 json 声明 renderer:skyline 不够)。
711
+ // 写入 project.config.json(非 private——private 每次重建被清,实测开关丢失后复测全是 WebView 假绿/假红)。
712
+ setting: { minifyWXML: true, urlCheck: false, ...config.skyline ? { skylineRenderEnable: true } : {} }
713
+ };
714
+ fs2.mkdirSync(OUT_DIR, { recursive: true });
715
+ fs2.writeFileSync(path2.join(OUT_DIR, "project.config.json"), JSON.stringify(projectConfig, null, 2) + "\n");
716
+ console.log(`[gen-routes] \u5DF2\u751F\u6210 dist/mp-weixin/project.config.json\uFF08appid=${config.appid}\uFF0Cprojectname=${projectName}\uFF09`);
717
+ }
718
+ fs2.rmSync(OUT_DIR, { recursive: true, force: true });
719
+ const pages = scanPages();
720
+ const routes = buildRoutes(pages);
721
+ validate(pages, routes);
722
+ writeAutoRoutes(routes);
723
+ writeAppJson(pages, routes);
724
+ writePageJsons(pages);
725
+ writeComponentJsons();
726
+ writeProjectConfig();
727
+ console.log(`[gen-routes] \u5B8C\u6210\uFF1A\u5171 ${pages.length} \u4E2A\u9875\u9762`);
728
+ }
729
+
730
+ // src/appSkeleton.ts
731
+ var APP_LAUNCH_SKELETON = `App({
732
+ onLaunch() {
733
+ // \u5168\u94FE\u8DEF\u8C03\u8BD5\u5F00\u5173\uFF08PROTEUS_DEBUG=1 \u6784\u5EFA\u65F6\u7531\u63D2\u4EF6\u66FF\u6362\u4E3A true\uFF09
734
+ const debug = typeof __PROTEUS_DEBUG__ !== 'undefined' && __PROTEUS_DEBUG__
735
+ if (debug) console.log('[proteus][app] \u542F\u52A8', Date.now())
736
+ // \u5168\u5C40\u9519\u8BEF\u6355\u83B7\uFF08debug \u6784\u5EFA\u8F93\u51FA\uFF0C\u6B63\u5F0F\u6784\u5EFA\u5E38\u91CF\u6298\u53E0\u96F6\u6B8B\u7559\uFF09
737
+ if (typeof wx !== 'undefined' && wx.onError) {
738
+ wx.onError(function (err) {
739
+ if (debug) console.error('[proteus][error]', err, Date.now())
740
+ })
741
+ }
742
+ // \u2605Pinia \u5B89\u88C5\uFF08\u4EC5\u9875\u9762\u4F7F\u7528 store \u65F6\u6CE8\u5165\uFF0C\u5426\u5219\u6B64\u884C\u4E3A\u6CE8\u91CA\uFF09\uFF1A
743
+ // \u5C0F\u7A0B\u5E8F\u65E0 createApp \u5B9E\u4F8B \u2192 createMpPinia() \u5185\u90E8 setActivePinia\uFF0C\u9875\u9762 useStore() \u624D\u80FD\u89E3\u6790\uFF1B
744
+ // \u65F6\u5E8F\uFF1AonLaunch \u65E9\u4E8E\u4EFB\u4F55\u9875\u9762 onLoad \u2713
745
+ __PINIA_INSTALL__
746
+ // \u5185\u7F6E\u9884\u8BBE\u6CE8\u518C\uFF08\u540C\u6587\u4EF6\u9759\u6001\u53EF\u5206\u6790\uFF1A\u51FD\u6570\u5B9A\u4E49\u5728\u524D\u3001\u6CE8\u518C\u5728\u540E\uFF0C\u63D2\u4EF6\u5DF2\u4FDD\u8BC1\u987A\u5E8F\uFF09
747
+ if (typeof wx !== 'undefined' && wx.router) {
748
+ __PRESET_REGISTRATION__
749
+ }
750
+ },
751
+ // \u2605lifecycle-plan B4\uFF1AApp \u7EA7 onShow/onHide \u94A9\u5B50\uFF08\u8C03\u8BD5\u65E5\u5FD7\uFF1BWeb \u7AEF\u5BF9\u5E94 visibilitychange\uFF09
752
+ onShow() {
753
+ const debug = typeof __PROTEUS_DEBUG__ !== 'undefined' && __PROTEUS_DEBUG__
754
+ if (debug) console.log('[proteus][app] onShow', Date.now())
755
+ },
756
+ onHide() {
757
+ const debug = typeof __PROTEUS_DEBUG__ !== 'undefined' && __PROTEUS_DEBUG__
758
+ if (debug) console.log('[proteus][app] onHide', Date.now())
759
+ },
760
+ })
761
+ `;
762
+
763
+ // src/cache.ts
764
+ import fs3 from "node:fs";
765
+ import path3 from "node:path";
766
+ import crypto from "node:crypto";
767
+ import { createRequire as createRequire2 } from "node:module";
768
+ function getCompilerVersion(projectRoot) {
769
+ try {
770
+ const require3 = createRequire2(path3.join(projectRoot, "package.json"));
771
+ const pkgJsonPath = require3.resolve("@proteus-vue/compiler/package.json");
772
+ const pkg = JSON.parse(fs3.readFileSync(pkgJsonPath, "utf-8"));
773
+ const dist = path3.join(path3.dirname(pkgJsonPath), pkg.main ?? "dist/index.js");
774
+ const h = crypto.createHash("sha1");
775
+ h.update(fs3.readFileSync(dist, "utf-8"));
776
+ return `${pkg.version}-${h.digest("hex").slice(0, 8)}`;
777
+ } catch {
778
+ return "unknown";
779
+ }
780
+ }
781
+ function getEsbuildVersion(projectRoot) {
782
+ try {
783
+ const require3 = createRequire2(path3.join(projectRoot, "package.json"));
784
+ const pkg = JSON.parse(fs3.readFileSync(require3.resolve("esbuild/package.json"), "utf-8"));
785
+ return pkg.version;
786
+ } catch {
787
+ return "unknown";
788
+ }
789
+ }
790
+ function compileCacheKey(source, options, projectRoot) {
791
+ const h = crypto.createHash("sha1");
792
+ h.update(source);
793
+ h.update("|");
794
+ h.update(JSON.stringify({ ...options, compilerVersion: getCompilerVersion(projectRoot) }));
795
+ return h.digest("hex");
796
+ }
797
+ function createCompileCache(cacheDir) {
798
+ fs3.mkdirSync(cacheDir, { recursive: true });
799
+ const memory = /* @__PURE__ */ new Map();
800
+ let hits = 0;
801
+ let misses = 0;
802
+ return {
803
+ get(key) {
804
+ const mem = memory.get(key);
805
+ if (mem) {
806
+ hits++;
807
+ return mem;
808
+ }
809
+ const file = path3.join(cacheDir, `${key}.json`);
810
+ if (fs3.existsSync(file)) {
811
+ try {
812
+ const entry = JSON.parse(fs3.readFileSync(file, "utf-8"));
813
+ memory.set(key, entry);
814
+ hits++;
815
+ return entry;
816
+ } catch {
817
+ }
818
+ }
819
+ misses++;
820
+ return null;
821
+ },
822
+ set(key, entry) {
823
+ memory.set(key, entry);
824
+ try {
825
+ fs3.writeFileSync(path3.join(cacheDir, `${key}.json`), JSON.stringify(entry));
826
+ } catch {
827
+ }
828
+ },
829
+ stats() {
830
+ return { hits, misses };
831
+ }
832
+ };
833
+ }
834
+ function bundleCacheKey(entryFile, projectRoot) {
835
+ const h = crypto.createHash("sha1");
836
+ h.update(entryFile);
837
+ h.update("|");
838
+ h.update(
839
+ JSON.stringify({
840
+ esbuild: getEsbuildVersion(projectRoot),
841
+ target: "es2018",
842
+ format: "cjs",
843
+ charset: "utf8",
844
+ minify: true,
845
+ external: ["@proteus-vue/*"]
846
+ })
847
+ );
848
+ return h.digest("hex");
849
+ }
850
+ function inputsValid(inputs) {
851
+ for (let i = 0; i < inputs.length; i++) {
852
+ try {
853
+ const st = fs3.statSync(inputs[i].file);
854
+ if (st.mtimeMs !== inputs[i].mtimeMs || st.size !== inputs[i].size) return false;
855
+ } catch {
856
+ return false;
857
+ }
858
+ }
859
+ return true;
860
+ }
861
+ function createBundleCache(cacheDir) {
862
+ fs3.mkdirSync(cacheDir, { recursive: true });
863
+ const memory = /* @__PURE__ */ new Map();
864
+ let hits = 0;
865
+ let misses = 0;
866
+ return {
867
+ get(key) {
868
+ const mem = memory.get(key);
869
+ if (mem && inputsValid(mem.inputs)) {
870
+ hits++;
871
+ return mem;
872
+ }
873
+ const file = path3.join(cacheDir, `${key}.json`);
874
+ if (fs3.existsSync(file)) {
875
+ try {
876
+ const entry = JSON.parse(fs3.readFileSync(file, "utf-8"));
877
+ if (inputsValid(entry.inputs)) {
878
+ memory.set(key, entry);
879
+ hits++;
880
+ return entry;
881
+ }
882
+ } catch {
883
+ }
884
+ }
885
+ misses++;
886
+ return null;
887
+ },
888
+ set(key, entry) {
889
+ memory.set(key, entry);
890
+ try {
891
+ fs3.writeFileSync(path3.join(cacheDir, `${key}.json`), JSON.stringify(entry));
892
+ } catch {
893
+ }
894
+ },
895
+ stats() {
896
+ return { hits, misses };
897
+ }
898
+ };
899
+ }
900
+
901
+ // src/tag-scan.ts
902
+ import fs4 from "node:fs";
903
+ import path4 from "node:path";
904
+ function extractTemplateBody(src) {
905
+ const n = src.length;
906
+ let i = 0;
907
+ while (i < n) {
908
+ const lt = src.indexOf("<", i);
909
+ if (lt < 0) return "";
910
+ if (src.startsWith("<!--", lt)) {
911
+ const e = src.indexOf("-->", lt + 4);
912
+ i = e < 0 ? n : e + 3;
913
+ continue;
914
+ }
915
+ const block = /^<(script|style)\b/i.exec(src.slice(lt, lt + 32));
916
+ if (block) {
917
+ const tag = block[1].toLowerCase();
918
+ const end = src.toLowerCase().indexOf(`</${tag}`, lt + block[0].length);
919
+ if (end < 0) return "";
920
+ const gt = src.indexOf(">", end);
921
+ i = gt < 0 ? n : gt + 1;
922
+ continue;
923
+ }
924
+ if (/^<template[\s>]/i.test(src.slice(lt, lt + 16))) {
925
+ const gt = src.indexOf(">", lt);
926
+ if (gt < 0) return "";
927
+ let depth = 1;
928
+ let j = gt + 1;
929
+ while (j < n) {
930
+ const l2 = src.indexOf("<", j);
931
+ if (l2 < 0) return src.slice(gt + 1);
932
+ if (src.startsWith("<!--", l2)) {
933
+ const e = src.indexOf("-->", l2 + 4);
934
+ j = e < 0 ? n : e + 3;
935
+ continue;
936
+ }
937
+ const seg = src.slice(l2, l2 + 16);
938
+ if (/^<\/template[\s>]/i.test(seg)) {
939
+ depth--;
940
+ if (depth === 0) return src.slice(gt + 1, l2);
941
+ j = l2 + "</template>".length;
942
+ continue;
943
+ }
944
+ if (/^<template[\s>]/i.test(seg)) {
945
+ depth++;
946
+ const g2 = src.indexOf(">", l2);
947
+ j = g2 < 0 ? n : g2 + 1;
948
+ continue;
949
+ }
950
+ j = l2 + 1;
951
+ }
952
+ return src.slice(gt + 1);
953
+ }
954
+ i = lt + 1;
955
+ }
956
+ return "";
957
+ }
958
+ function extractTags(tpl) {
959
+ const out = /* @__PURE__ */ new Set();
960
+ let idx = 0;
961
+ while (idx < tpl.length) {
962
+ const lt = tpl.indexOf("<", idx);
963
+ if (lt < 0) break;
964
+ if (tpl.startsWith("<!--", lt)) {
965
+ const e = tpl.indexOf("-->", lt + 4);
966
+ idx = e < 0 ? tpl.length : e + 3;
967
+ continue;
968
+ }
969
+ if (tpl.startsWith("</", lt)) {
970
+ idx = lt + 2;
971
+ continue;
972
+ }
973
+ const mm = /^([A-Za-z][\w-]*)/.exec(tpl.slice(lt + 1));
974
+ if (!mm) {
975
+ idx = lt + 1;
976
+ continue;
977
+ }
978
+ const raw = mm[1];
979
+ out.add(/[A-Z]/.test(raw) ? raw.replace(/\B([A-Z])/g, "-$1").toLowerCase() : raw);
980
+ idx = lt + 1 + mm[0].length;
981
+ }
982
+ return out;
983
+ }
984
+ function collectCompilerEmittedTags(templateBody) {
985
+ const out = /* @__PURE__ */ new Set();
986
+ if (/<(?:svg|circle|rect|ellipse|path|line|polyline|polygon)[\s>][\s\S]*?<animate\b/i.test(templateBody)) {
987
+ const shapeAnim = /<animate\s[^>]*attributeName\s*=\s*["'](cx|cy|r|rx|ry|x|y|width|height|d|points|stroke-dashoffset|stroke-dasharray)["']/i;
988
+ if (shapeAnim.test(templateBody)) out.add("p-svg-canvas");
989
+ }
990
+ return out;
991
+ }
992
+ function resolveComponentFile(componentsDir, tag) {
993
+ const dirIndex = path4.join(componentsDir, tag, "index.vue");
994
+ if (fs4.existsSync(dirIndex)) return dirIndex;
995
+ const flat = path4.join(componentsDir, `${tag}.vue`);
996
+ if (fs4.existsSync(flat)) return flat;
997
+ return null;
998
+ }
999
+ function collectUsedFrameworkComponents(pageFiles, componentsDir) {
1000
+ const used = /* @__PURE__ */ new Set();
1001
+ const visitedFiles = /* @__PURE__ */ new Set();
1002
+ const queue = [...pageFiles];
1003
+ while (queue.length) {
1004
+ const file = queue.pop();
1005
+ if (visitedFiles.has(file)) continue;
1006
+ visitedFiles.add(file);
1007
+ if (!fs4.existsSync(file)) continue;
1008
+ let tags;
1009
+ const body = (() => {
1010
+ try {
1011
+ return extractTemplateBody(fs4.readFileSync(file, "utf-8"));
1012
+ } catch {
1013
+ return "";
1014
+ }
1015
+ })();
1016
+ try {
1017
+ tags = extractTags(body);
1018
+ for (const t of collectCompilerEmittedTags(body)) tags.add(t);
1019
+ } catch {
1020
+ continue;
1021
+ }
1022
+ for (const tag of tags) {
1023
+ if (used.has(tag)) continue;
1024
+ const compFile = resolveComponentFile(componentsDir, tag);
1025
+ if (!compFile) continue;
1026
+ used.add(tag);
1027
+ queue.push(compFile);
1028
+ }
1029
+ }
1030
+ return used;
174
1031
  }
175
1032
 
176
1033
  // src/plugin.ts
@@ -189,6 +1046,27 @@ var MP_TAG_MAP = {
189
1046
  navigator: "proteus-navigator",
190
1047
  picker: "proteus-picker"
191
1048
  };
1049
+ var MP_ONLY_TAGS = /* @__PURE__ */ new Set([
1050
+ "picker-view",
1051
+ "picker-view-column",
1052
+ "movable-view",
1053
+ "movable-area",
1054
+ "match-media",
1055
+ "root-portal",
1056
+ "page-container",
1057
+ "share-element",
1058
+ "keyboard-accessory",
1059
+ "cover-view",
1060
+ "cover-image",
1061
+ // ★端对齐批次3(2026-09-16):宿主能力组件的 MP 原生标签——其模板用 v-if 双分支
1062
+ // (MP 原生 / Web 降级),Web 端死分支不渲染但会被 resolveComponent 提升解析 → 声明为自定义元素消除告警。
1063
+ "rich-text",
1064
+ "map",
1065
+ "camera",
1066
+ "canvas",
1067
+ "ad",
1068
+ "web-view"
1069
+ ]);
192
1070
  function defaultScopedPlugin() {
193
1071
  return {
194
1072
  name: "proteus-default-scoped",
@@ -209,22 +1087,26 @@ function defaultScopedPlugin() {
209
1087
  out = out.replace(/<script([^>]*)>/, (m) => `${m}${importLine}`);
210
1088
  out = out.replace("</script>", `${hookCode}</script>`);
211
1089
  }
1090
+ out = out.replace(/\sp-fluid=("[^"]*"|'[^']*')/g, (_m, q) => {
1091
+ const inner = q.slice(1, -1).replace(/'/g, "\\'");
1092
+ return ` v-p-fluid="'${inner}'"`;
1093
+ });
212
1094
  return out === code ? null : { code: out, map: null };
213
1095
  }
214
1096
  };
215
1097
  }
216
- var require2 = createRequire2(import.meta.url);
1098
+ var require2 = createRequire3(import.meta.url);
217
1099
  function resolvePkgPath(projectRoot, modPath) {
218
1100
  const m = modPath.match(/^node_modules\/((?:@[^/]+\/)?[^/]+)\/([\s\S]+)$/);
219
1101
  if (m) {
220
1102
  try {
221
- const pkgRequire = createRequire2(path2.join(projectRoot, "package.json"));
222
- const pkgRoot = path2.dirname(pkgRequire.resolve(`${m[1]}/package.json`));
223
- return path2.join(pkgRoot, m[2]);
1103
+ const pkgRequire = createRequire3(path5.join(projectRoot, "package.json"));
1104
+ const pkgRoot = path5.dirname(pkgRequire.resolve(`${m[1]}/package.json`));
1105
+ return path5.join(pkgRoot, m[2]);
224
1106
  } catch {
225
1107
  }
226
1108
  }
227
- return path2.join(projectRoot, modPath);
1109
+ return path5.join(projectRoot, modPath);
228
1110
  }
229
1111
  function preprocessStyle(lang, content) {
230
1112
  if (lang === "scss" || lang === "sass") {
@@ -241,42 +1123,98 @@ function preprocessStyle(lang, content) {
241
1123
  }
242
1124
  return content;
243
1125
  }
244
- function resolveSharedModule(appDir, absFrom, source, frameworkDir) {
1126
+ function loadStyleSrcWithVariant(src, fromFilename, platform = "mp") {
1127
+ const base = path5.resolve(path5.dirname(fromFilename), src);
1128
+ const hit = resolvePlatformVariant(base, platform, fs5.existsSync);
1129
+ if (!hit) return null;
1130
+ try {
1131
+ return fs5.readFileSync(hit, "utf-8");
1132
+ } catch {
1133
+ return null;
1134
+ }
1135
+ }
1136
+ var VENDOR_SINGLETONS = ["pinia", "vue", "@vue/devtools-api"];
1137
+ function resolveSharedModule(appDir, absFrom, source, frameworkDir, resolveFrom, platform = "mp") {
245
1138
  if (source.startsWith("@proteus-vue/")) {
246
1139
  try {
247
- const pkgRoot = path2.dirname(require2.resolve(`${source}/package.json`));
248
- const entry = path2.join(pkgRoot, "dist", "index.js");
249
- if (!fs2.existsSync(entry)) return null;
1140
+ const resolver = resolveFrom ? createRequire3(path5.join(resolveFrom, "package.json")) : require2;
1141
+ const pkgRoot = path5.dirname(resolver.resolve(`${source}/package.json`));
1142
+ const entry = path5.join(pkgRoot, "dist", "index.js");
1143
+ if (!fs5.existsSync(entry)) return null;
250
1144
  return { file: entry, relNoExt: `_proteus/${source.replace("@proteus-vue/", "")}` };
251
1145
  } catch {
252
1146
  return null;
253
1147
  }
254
1148
  }
1149
+ if (VENDOR_SINGLETONS.includes(source)) {
1150
+ const tryResolve = (base2) => {
1151
+ try {
1152
+ return createRequire3(base2).resolve(source);
1153
+ } catch {
1154
+ return null;
1155
+ }
1156
+ };
1157
+ const entry = tryResolve(path5.join(resolveFrom ?? appDir, "package.json")) ?? tryResolve(absFrom);
1158
+ if (!entry || !fs5.existsSync(entry)) return null;
1159
+ return { file: entry, relNoExt: `_proteus/${source}` };
1160
+ }
255
1161
  if (!source.startsWith(".")) return null;
256
- const base = path2.resolve(path2.dirname(absFrom), source);
257
- for (const cand of [base, `${base}.ts`, `${base}.js`, path2.join(base, "index.ts"), path2.join(base, "index.js")]) {
1162
+ const base = path5.resolve(path5.dirname(absFrom), source);
1163
+ const JS_EXTS = /* @__PURE__ */ new Set([".ts", ".js", ".mjs", ".cjs"]);
1164
+ const variantHit = resolvePlatformVariantWithExts(base, [...JS_EXTS], platform, (p) => {
1165
+ try {
1166
+ return fs5.statSync(p).isFile();
1167
+ } catch {
1168
+ return false;
1169
+ }
1170
+ });
1171
+ const candidates = variantHit ? [variantHit] : [base, `${base}.ts`, `${base}.js`, path5.join(base, "index.ts"), path5.join(base, "index.js")];
1172
+ for (const cand of candidates) {
258
1173
  if (cand.endsWith(".vue")) continue;
1174
+ if (!JS_EXTS.has(path5.extname(cand).toLowerCase())) continue;
259
1175
  let isFile = false;
260
1176
  try {
261
- isFile = fs2.statSync(cand).isFile();
1177
+ isFile = fs5.statSync(cand).isFile();
262
1178
  } catch {
263
1179
  isFile = false;
264
1180
  }
265
1181
  if (isFile) {
266
- let relNoExt = path2.relative(appDir, cand).replace(/\\/g, "/").replace(/\.(ts|js)$/, "");
267
- if (relNoExt.startsWith("../") && frameworkDir && !path2.relative(frameworkDir, cand).startsWith("..")) {
268
- relNoExt = `proteus/${path2.relative(frameworkDir, cand).replace(/\\/g, "/").replace(/\.(ts|js)$/, "")}`;
1182
+ let relNoExt = path5.relative(appDir, cand).replace(/\\/g, "/").replace(/\.(ts|js)$/, "");
1183
+ if (relNoExt.startsWith("../") && frameworkDir && !path5.relative(frameworkDir, cand).startsWith("..")) {
1184
+ relNoExt = `proteus/${path5.relative(frameworkDir, cand).replace(/\\/g, "/").replace(/\.(ts|js)$/, "")}`;
269
1185
  }
270
1186
  return { file: cand, relNoExt };
271
1187
  }
272
1188
  }
273
1189
  return null;
274
1190
  }
1191
+ function rewriteRootToPage(css) {
1192
+ return css.replace(/(^|[},\s]):root\b/g, "$1page");
1193
+ }
1194
+ function rewriteFrameworkRequires(js, relOutput) {
1195
+ if (!js.includes("require('@proteus-vue/")) return js;
1196
+ const pageDir = path5.posix.dirname(relOutput);
1197
+ return js.replace(/require\('@proteus-vue\/([A-Za-z0-9_-]+)'\)/g, (m, name) => {
1198
+ const pkgRel = `_proteus/${name}.js`;
1199
+ let rel = path5.posix.relative(pageDir, pkgRel);
1200
+ if (!rel.startsWith(".")) rel = `./${rel}`;
1201
+ return `require('${rel}')`;
1202
+ });
1203
+ }
1204
+ function scanSourceImports(source) {
1205
+ const out = [];
1206
+ const re = /import\s+(?:type\s+)?(?:[\s\S]*?)\s+from\s+['"]([^'"]+)['"]|import\s+['"]([^'"]+)['"]/g;
1207
+ for (const m of source.matchAll(re)) {
1208
+ const s = m[1] || m[2];
1209
+ if (s) out.push({ source: s, typeOnly: /import\s+type\s+/.test(m[0]) });
1210
+ }
1211
+ return out;
1212
+ }
275
1213
  function extractBuilderFnName(code) {
276
1214
  const m = code.match(/function\s+([A-Za-z_$][\w$]*)\s*\(/);
277
1215
  return m ? m[1] : null;
278
1216
  }
279
- function assembleAppJs(mainCode, presets) {
1217
+ function assembleAppJs(mainCode, presets, piniaInstall = "") {
280
1218
  const presetCode = presets.map((p) => p.source.trim()).join("\n\n");
281
1219
  const custom = mainCode.trim();
282
1220
  const registerLines = presets.map((p) => ` wx.router.addRouteBuilder('${p.name}', ${p.fnName})`);
@@ -291,7 +1229,7 @@ ${registerLines.join("\n")}
291
1229
  ${presetCode}${register}`;
292
1230
  }
293
1231
  const skeletonReg = presets.map((p) => ` wx.router.addRouteBuilder('${p.name}', ${p.fnName})`);
294
- const skeleton = APP_LAUNCH_SKELETON.replace("__PRESET_REGISTRATION__", skeletonReg.join("\n") || " // \u65E0\u5185\u7F6E\u9884\u8BBE");
1232
+ const skeleton = APP_LAUNCH_SKELETON.replace("__PRESET_REGISTRATION__", skeletonReg.join("\n") || " // \u65E0\u5185\u7F6E\u9884\u8BBE").replace("__PINIA_INSTALL__", piniaInstall);
295
1233
  return `${custom ? `${custom}
296
1234
 
297
1235
  ` : ""}${presetCode ? `${presetCode}
@@ -303,13 +1241,15 @@ function filterOverriddenPresets(mainCode, presets) {
303
1241
  }
304
1242
  async function loadPresetBuilders(projectRoot, cfg) {
305
1243
  const presets = [];
306
- for (const [name, modPath] of Object.entries(cfg.customRoute.builders)) {
1244
+ const { router: rc, duplicates } = resolveRouterConfig(cfg);
1245
+ 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`);
1246
+ for (const [name, modPath] of Object.entries(rc.customRoute.builders)) {
307
1247
  const abs = resolvePkgPath(projectRoot, modPath);
308
- if (!fs2.existsSync(abs)) {
1248
+ if (!fs5.existsSync(abs)) {
309
1249
  console.warn(`[mp-transform] \u9884\u8BBE builder ${name} \u4E0D\u5B58\u5728\uFF1A${modPath}`);
310
1250
  continue;
311
1251
  }
312
- const { code } = await esbuildTransform(fs2.readFileSync(abs, "utf-8"), { loader: "ts", charset: "utf8" });
1252
+ const { code } = await esbuildTransform(fs5.readFileSync(abs, "utf-8"), { loader: "ts", charset: "utf8" });
313
1253
  const fnName = extractBuilderFnName(code);
314
1254
  if (!fnName) {
315
1255
  console.warn(`[mp-transform] \u9884\u8BBE builder ${name} \u672A\u627E\u5230\u51FD\u6570\u58F0\u660E\uFF0C\u5DF2\u8DF3\u8FC7`);
@@ -320,24 +1260,70 @@ async function loadPresetBuilders(projectRoot, cfg) {
320
1260
  return presets;
321
1261
  }
322
1262
  function walkVueFiles(dir, acc = []) {
323
- if (!fs2.existsSync(dir)) return acc;
324
- for (const entry of fs2.readdirSync(dir, { withFileTypes: true })) {
1263
+ if (!fs5.existsSync(dir)) return acc;
1264
+ for (const entry of fs5.readdirSync(dir, { withFileTypes: true })) {
325
1265
  if (entry.name.startsWith(".")) continue;
326
- const full = path2.join(dir, entry.name);
1266
+ const full = path5.join(dir, entry.name);
327
1267
  if (entry.isDirectory()) walkVueFiles(full, acc);
328
1268
  else if (entry.name.endsWith(".vue")) acc.push(full);
329
1269
  }
330
1270
  return acc;
331
1271
  }
1272
+ function collectMpEntries(opts) {
1273
+ const { projectRoot, appDir, pagesDir, subPackages, componentsDir, webOnlyPages, onSkipWebOnly } = opts;
1274
+ const platform = opts.platform ?? "mp";
1275
+ const out = [];
1276
+ const pushRel = (dir, isComponent) => {
1277
+ for (const f of effectiveVariants2(walkVueFiles(dir), platform)) {
1278
+ if (webOnlyPages?.has(f)) {
1279
+ onSkipWebOnly?.(f);
1280
+ continue;
1281
+ }
1282
+ const relBase = path5.relative(appDir, splitVariant2(f).base).replace(/\\/g, "/");
1283
+ out.push({ file: f, rel: relBase.replace(/\.vue$/, ""), isComponent });
1284
+ }
1285
+ };
1286
+ pushRel(path5.join(projectRoot, pagesDir), false);
1287
+ for (const sp of subPackages) pushRel(path5.join(projectRoot, sp.root), false);
1288
+ pushRel(path5.join(appDir, "components"), true);
1289
+ const pageFiles = out.filter((t) => !t.isComponent).map((t) => t.file);
1290
+ const emitAll = (opts.componentEmit ?? "used") === "all";
1291
+ const usedComponents = emitAll ? null : collectUsedFrameworkComponents(pageFiles, componentsDir);
1292
+ for (const f of effectiveVariants2(walkVueFiles(componentsDir), platform)) {
1293
+ if (webOnlyPages?.has(f)) {
1294
+ onSkipWebOnly?.(f);
1295
+ continue;
1296
+ }
1297
+ if (usedComponents) {
1298
+ const compName = path5.relative(componentsDir, splitVariant2(f).base).replace(/\\/g, "/").split("/")[0];
1299
+ if (!usedComponents.has(compName)) continue;
1300
+ }
1301
+ const relIn = path5.relative(componentsDir, splitVariant2(f).base).replace(/\\/g, "/").replace(/\.vue$/, "");
1302
+ out.push({ file: f, rel: `proteus/${relIn}`, isComponent: true });
1303
+ }
1304
+ return out;
1305
+ }
1306
+ function resolveEffectiveSubPackages(cfg) {
1307
+ const { router: rc } = resolveRouterConfig(cfg);
1308
+ return rc.subPackages;
1309
+ }
332
1310
  function mpTransform(opts) {
333
1311
  const cfg = opts.config;
1312
+ const effectiveSubPackages = resolveEffectiveSubPackages(cfg);
334
1313
  const px2rpx = opts.px2rpx ?? cfg.style.px2rpx;
335
1314
  const rpxRatio = opts.rpxRatio ?? cfg.style.rpxRatio;
336
1315
  const rules = opts.rules ?? cfg.rules;
337
1316
  const autoScrollContainer = cfg.page?.autoScrollContainer ?? true;
1317
+ const renderer = cfg.skyline ? "skyline" : "webview";
1318
+ const fluidLayout = cfg.layout ? { designWidth: cfg.layout.designWidth, viewport: cfg.layout.fluidViewport } : void 0;
338
1319
  const isDebug = process.env.PROTEUS_DEBUG === "1";
339
1320
  let projectRoot = process.cwd();
340
1321
  const warningReport = [];
1322
+ const rustCompiler = process.env.PROTEUS_COMPILER === "rust" || cfg.compiler?.backend === "rust";
1323
+ const rustCliBin = rustCompiler ? resolveRustCliBin(projectRoot) : null;
1324
+ let dualOk = 0;
1325
+ let dualSkipped = 0;
1326
+ let dualSkippedReason = "";
341
1327
  return {
342
1328
  name: "vite-plugin-mp-transform",
343
1329
  enforce: "pre",
@@ -345,50 +1331,90 @@ function mpTransform(opts) {
345
1331
  projectRoot = resolved.root;
346
1332
  },
347
1333
  async buildStart() {
348
- const appDir = path2.join(projectRoot, path2.dirname(cfg.pagesDir));
349
- const compileCache = createCompileCache(path2.join(projectRoot, "node_modules", ".cache", "proteus", "compile"));
350
- const bundleCache = createBundleCache(path2.join(projectRoot, "node_modules", ".cache", "proteus", "bundle"));
351
- const files = [];
352
- const pushRel = (dir) => {
353
- for (const f of walkVueFiles(dir)) {
354
- files.push({ file: f, rel: path2.relative(appDir, f).replace(/\\/g, "/").replace(/\.vue$/, "") });
1334
+ const appDir = path5.join(projectRoot, path5.dirname(cfg.pagesDir));
1335
+ const compileCache = createCompileCache(path5.join(projectRoot, "node_modules", ".cache", "proteus", "compile"));
1336
+ const bundleCache = createBundleCache(path5.join(projectRoot, "node_modules", ".cache", "proteus", "bundle"));
1337
+ const webOnlyPages = /* @__PURE__ */ new Set();
1338
+ const detectWebOnly = (file) => {
1339
+ try {
1340
+ const src = fs5.readFileSync(file, "utf-8");
1341
+ const m = src.match(/<route>\s*([\s\S]*?)<\/route>/);
1342
+ if (!m) return;
1343
+ if (/"?webOnly"?\s*:\s*true/.test(m[1])) {
1344
+ webOnlyPages.add(file);
1345
+ return;
1346
+ }
1347
+ const pm = m[1].match(/"?platforms"?\s*:\s*(\[[^\]]*\])/);
1348
+ if (pm) {
1349
+ try {
1350
+ const arr = JSON.parse(pm[1]);
1351
+ if (Array.isArray(arr) && !arr.some((p) => typeof p === "string" && ["mp", "mp-weixin", "skyline"].includes(p))) {
1352
+ webOnlyPages.add(file);
1353
+ }
1354
+ } catch {
1355
+ }
1356
+ }
1357
+ } catch {
355
1358
  }
356
1359
  };
357
- pushRel(path2.join(projectRoot, cfg.pagesDir));
358
- for (const sp of cfg.subPackages ?? []) {
359
- pushRel(path2.join(projectRoot, sp.root));
1360
+ for (const pagesRoot of [path5.join(projectRoot, cfg.pagesDir), ...(effectiveSubPackages ?? []).map((sp) => path5.join(projectRoot, sp.root))]) {
1361
+ for (const f of walkVueFiles(pagesRoot)) detectWebOnly(f);
360
1362
  }
361
- const appComponents = path2.join(appDir, "components");
362
- if (fs2.existsSync(appComponents)) pushRel(appComponents);
363
- const frameworkComponents = opts.frameworkComponentsDir ?? path2.join(projectRoot, "src", "components");
364
- if (fs2.existsSync(frameworkComponents)) {
365
- for (const f of walkVueFiles(frameworkComponents)) {
366
- const relIn = path2.relative(frameworkComponents, f).replace(/\\/g, "/").replace(/\.vue$/, "");
367
- files.push({ file: f, rel: `proteus/${relIn}` });
1363
+ const frameworkComponents = opts.componentsDir ? path5.resolve(projectRoot, opts.componentsDir) : resolveComponentsRoot(projectRoot);
1364
+ const files = collectMpEntries({
1365
+ projectRoot,
1366
+ appDir,
1367
+ pagesDir: cfg.pagesDir,
1368
+ subPackages: effectiveSubPackages ?? [],
1369
+ componentsDir: frameworkComponents,
1370
+ webOnlyPages,
1371
+ onSkipWebOnly: (f) => console.log(`[mp-transform] \u8DF3\u8FC7 webOnly \u9875\u9762\uFF1A${path5.relative(projectRoot, f).replace(/\\/g, "/")}`),
1372
+ // ★框架组件按引用输出(2026-09-18);PROTEUS_COMPONENTS_EMIT=all 回退全量(非常规用法逃生舱)
1373
+ componentEmit: process.env.PROTEUS_COMPONENTS_EMIT === "all" ? "all" : "used"
1374
+ });
1375
+ const appUsesStore = files.some((f) => {
1376
+ try {
1377
+ const s = fs5.readFileSync(f.file, "utf-8");
1378
+ const script = s.includes("<script") ? s.match(/<script[^>]*>([\s\S]*?)<\/script>/i)?.[1] ?? "" : s;
1379
+ return /\buse[A-Z]\w*Store\s*\(/.test(script);
1380
+ } catch {
1381
+ return false;
368
1382
  }
369
- }
370
- const mpEntry = path2.join(appDir, "main.mp.ts");
371
- if (fs2.existsSync(mpEntry)) {
372
- const src = fs2.readFileSync(mpEntry, "utf-8");
1383
+ });
1384
+ const mpEntry = path5.join(appDir, "main.mp.ts");
1385
+ if (fs5.existsSync(mpEntry)) {
1386
+ const src = fs5.readFileSync(mpEntry, "utf-8");
373
1387
  const { code } = await esbuildTransform(src, { loader: "ts", charset: "utf8" });
374
1388
  const presets = filterOverriddenPresets(code, await loadPresetBuilders(projectRoot, cfg));
375
- const appJs = assembleAppJs(code, presets).replace(/__PROTEUS_DEBUG__/g, isDebug ? "true" : "false").replace(/"worklet"/g, "'worklet'");
1389
+ 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";
1390
+ const appJs = applyPlatformMacros(assembleAppJs(code, presets, piniaInstall).replace(/__PROTEUS_DEBUG__/g, isDebug ? "true" : "false").replace(/"worklet"/g, "'worklet'"), "mp", "code");
376
1391
  this.emitFile({ type: "asset", fileName: "app.js", source: appJs });
377
- 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"}`);
1392
+ 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" : ""}`);
1393
+ }
1394
+ {
1395
+ const explicit = cfg.globalStyle ? path5.resolve(projectRoot, cfg.globalStyle) : void 0;
1396
+ const candidates = [
1397
+ explicit,
1398
+ path5.join(appDir, "app.wxss"),
1399
+ path5.join(projectRoot, "app.wxss")
1400
+ ].filter((p) => Boolean(p));
1401
+ const globalStylePath = candidates.find((p) => fs5.existsSync(p));
1402
+ if (globalStylePath) {
1403
+ const raw = fs5.readFileSync(globalStylePath, "utf-8");
1404
+ const normalized = rewriteRootToPage(raw);
1405
+ const wxss = transformStyleToWxss(normalized, { px2rpx: cfg.style?.px2rpx ?? true, rpxRatio: cfg.style?.rpxRatio ?? 2, rules: cfg.rules });
1406
+ this.emitFile({ type: "asset", fileName: "app.wxss", source: wxss });
1407
+ 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`);
1408
+ }
378
1409
  }
379
1410
  const moduleImportsByFile = /* @__PURE__ */ new Map();
380
1411
  const sharedModules = /* @__PURE__ */ new Set();
381
1412
  const sharedRelNoExt = /* @__PURE__ */ new Map();
382
- const resolveShared = (absFrom, source) => resolveSharedModule(appDir, absFrom, source, frameworkComponents);
1413
+ const resolveShared = (absFrom, source) => resolveSharedModule(appDir, absFrom, source, frameworkComponents, projectRoot, "mp");
383
1414
  const scanImports = (absFile) => {
384
- const src = fs2.readFileSync(absFile, "utf-8");
1415
+ const src = fs5.readFileSync(absFile, "utf-8");
385
1416
  const script = src.includes("<script") ? src.match(/<script[^>]*>([\s\S]*?)<\/script>/i)?.[1] ?? "" : src;
386
- const out = [];
387
- for (const m of script.matchAll(/import\s+(?:type\s+)?.*?from\s+['"]([^'"]+)['"]|import\s+['"]([^'"]+)['"]/gm)) {
388
- const s = m[1] || m[2];
389
- if (s) out.push({ source: s, typeOnly: m[0].includes("import type") });
390
- }
391
- return out;
1417
+ return scanSourceImports(script);
392
1418
  };
393
1419
  for (const { file } of files) {
394
1420
  const list = [];
@@ -402,6 +1428,13 @@ function mpTransform(opts) {
402
1428
  }
403
1429
  if (list.length) moduleImportsByFile.set(file, list);
404
1430
  }
1431
+ if (appUsesStore) {
1432
+ const rt = resolveShared(mpEntry, "@proteus-vue/runtime");
1433
+ if (rt) {
1434
+ sharedModules.add(rt.file);
1435
+ sharedRelNoExt.set(rt.file, rt.relNoExt);
1436
+ }
1437
+ }
405
1438
  const pending = [...sharedModules];
406
1439
  while (pending.length) {
407
1440
  const cur = pending.pop();
@@ -414,7 +1447,7 @@ function mpTransform(opts) {
414
1447
  pending.push(resolved.file);
415
1448
  }
416
1449
  }
417
- const THIRD_PARTY_ALLOW = /* @__PURE__ */ new Set(["pinia", "vue", "vue-demi", "@vue/reactivity", "@vue/shared", "@vue/runtime-core"]);
1450
+ const THIRD_PARTY_ALLOW = /* @__PURE__ */ new Set([...VENDOR_SINGLETONS, "nostics", "vue-demi", "@vue/reactivity", "@vue/shared", "@vue/runtime-core"]);
418
1451
  const hasThirdParty = /* @__PURE__ */ new Set();
419
1452
  for (const sharedFile of sharedModules) {
420
1453
  for (const imp of scanImports(sharedFile)) {
@@ -439,6 +1472,30 @@ function mpTransform(opts) {
439
1472
  moduleImportsByFile.set(file, list.filter((item) => !skipShared.has(resolveShared(file, item.source)?.file ?? "")));
440
1473
  }
441
1474
  }
1475
+ const externalResolvePlugin = (relNoExt) => ({
1476
+ name: "proteus-pkg-require-path",
1477
+ setup(b) {
1478
+ const mapExternal = (target) => {
1479
+ const dir = path5.posix.dirname(relNoExt);
1480
+ let rel = path5.posix.relative(dir, target);
1481
+ if (!rel.startsWith(".")) rel = `./${rel}`;
1482
+ return { path: rel, external: true };
1483
+ };
1484
+ b.onResolve({ filter: /^@proteus-vue\// }, (args) => mapExternal(`_proteus/${args.path.replace("@proteus-vue/", "")}.js`));
1485
+ b.onResolve({ filter: new RegExp(`^(${VENDOR_SINGLETONS.join("|")})$`) }, (args) => mapExternal(`_proteus/${args.path}.js`));
1486
+ b.onLoad({ filter: /\.(md|txt|json)$/ }, (args) => ({
1487
+ contents: `export default ${JSON.stringify(fs5.readFileSync(args.path, "utf-8"))}`,
1488
+ loader: "js"
1489
+ }));
1490
+ b.onLoad({ filter: /\.(png|jpe?g|gif|webp|svg|ico|woff2?|ttf|eot|mp3|mp4|wav|zip)$/ }, (args) => ({
1491
+ errors: [
1492
+ {
1493
+ 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`
1494
+ }
1495
+ ]
1496
+ }));
1497
+ }
1498
+ });
442
1499
  const bundleCacheEnabled = !process.env.PROTEUS_NO_CACHE && !isDebug;
443
1500
  for (const sharedFile of sharedModules) {
444
1501
  if (skipShared.has(sharedFile)) continue;
@@ -446,7 +1503,7 @@ function mpTransform(opts) {
446
1503
  let code = "";
447
1504
  let bundleHit = false;
448
1505
  if (bundleCacheEnabled) {
449
- const bKey = bundleCacheKey(sharedFile, projectRoot);
1506
+ const bKey = bundleCacheKey(sharedFile, projectRoot) + `-sky${cfg.skyline ? 1 : 0}`;
450
1507
  const cachedBundle = bundleCache.get(bKey);
451
1508
  if (cachedBundle) {
452
1509
  code = cachedBundle.output;
@@ -465,24 +1522,25 @@ function mpTransform(opts) {
465
1522
  logLevel: "silent",
466
1523
  minify: true,
467
1524
  metafile: true,
468
- // ★@proteus-vue/* external:运行时 require 产物 _proteus/<name>.js(微信 require 缓存同路径同实例)
469
- external: ["@proteus-vue/*"],
470
- plugins: [
471
- {
472
- name: "proteus-pkg-require-path",
473
- setup(b) {
474
- b.onResolve({ filter: /^@proteus-vue\// }, (args) => {
475
- const pkgRel = `_proteus/${args.path.replace("@proteus-vue/", "")}.js`;
476
- const dir = path2.posix.dirname(relNoExt);
477
- let rel = path2.posix.relative(dir, pkgRel);
478
- if (!rel.startsWith(".")) rel = `./${rel}`;
479
- return { path: rel, external: true };
480
- });
481
- }
482
- }
483
- ]
1525
+ // ★#495c define 注入:esbuild 直出资产不经 vite define——宏在此替换(config.skyline → __PROTEUS_SKYLINE__)
1526
+ // ★vendor 单例化(2026-09-12):pinia/vue 的 CJS 入口有 `process.env.NODE_ENV` 分支,小程序无 process
1527
+ // → 必须 define 掉(否则运行时崩/带 dev 分支体积);Vue flag 一并显式声明消除警告
1528
+ define: {
1529
+ __PROTEUS_DEBUG__: isDebug ? "true" : "false",
1530
+ __PROTEUS_SKYLINE__: cfg.skyline ? "true" : "false",
1531
+ // ★平台编译期宏(条件显隐):MP 共享 .ts 模块脚本内的 __MP__/__WEB__/__TARGET__ 在此替换
1532
+ ...platformDefines("mp"),
1533
+ "process.env.NODE_ENV": isDebug ? '"development"' : '"production"',
1534
+ __VUE_OPTIONS_API__: "true",
1535
+ __VUE_PROD_DEVTOOLS__: "false",
1536
+ __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: "false"
1537
+ },
1538
+ // ★external:@proteus-vue/* 与 vendor 单例(pinia/vue)→ 产物 _proteus/<name>.js
1539
+ // (微信 require 缓存同路径同实例 → 全产物共享同一份,杜绝重复内联导致的实例分裂)
1540
+ external: ["@proteus-vue/*", ...VENDOR_SINGLETONS],
1541
+ plugins: [externalResolvePlugin(relNoExt)]
484
1542
  });
485
- code = build.outputFiles[0]?.text ?? "";
1543
+ code = build.outputFiles?.[0]?.text ?? "";
486
1544
  if (!code) {
487
1545
  console.warn(`[mp-transform] \u5171\u4EAB\u6A21\u5757\u7F16\u8BD1\u5931\u8D25\uFF1A${relNoExt}`);
488
1546
  continue;
@@ -491,7 +1549,7 @@ function mpTransform(opts) {
491
1549
  const inputFiles = Object.keys(build.metafile.inputs);
492
1550
  const inputs = inputFiles.map((f) => {
493
1551
  try {
494
- const st = fs2.statSync(f);
1552
+ const st = fs5.statSync(f);
495
1553
  return { file: f, mtimeMs: st.mtimeMs, size: st.size };
496
1554
  } catch {
497
1555
  return null;
@@ -506,19 +1564,31 @@ function mpTransform(opts) {
506
1564
  for (const [file, list] of moduleImportsByFile) {
507
1565
  const entry = files.find((f) => f.file === file);
508
1566
  if (!entry) continue;
509
- const pageDir = path2.posix.dirname(entry.rel);
1567
+ const pageDir = path5.posix.dirname(entry.rel);
510
1568
  for (const item of list) {
511
1569
  const shared = resolveShared(file, item.source);
512
1570
  if (!shared) continue;
513
1571
  const sharedRel = `${shared.relNoExt}.js`;
514
- let rel = path2.posix.relative(pageDir, sharedRel);
1572
+ let rel = path5.posix.relative(pageDir, sharedRel);
515
1573
  if (!rel.startsWith(".")) rel = `./${rel}`;
516
1574
  item.requirePath = rel;
517
1575
  }
518
1576
  }
519
- for (const { file, rel } of files) {
520
- const source = fs2.readFileSync(file, "utf-8");
521
- const isComponent = file.includes(`${path2.sep}components${path2.sep}`);
1577
+ for (const { file, rel, isComponent } of files) {
1578
+ const source = fs5.readFileSync(file, "utf-8");
1579
+ if (rustCompiler) {
1580
+ const v = verifyDualCompilerEquivalence(source, { rustBin: rustCliBin, filename: file });
1581
+ if (v.status === "ok") {
1582
+ dualOk++;
1583
+ } else if (v.status === "skipped") {
1584
+ dualSkipped++;
1585
+ if (!dualSkippedReason) dualSkippedReason = v.reason ?? "";
1586
+ } else {
1587
+ throw new Error(`[mp-transform] G-29.1 \u53CC\u7F16\u8BD1\u8BED\u4E49\u4E0D\u7B49\u4EF7\uFF1A${rel}
1588
+ ${v.details.join("\n ")}\uFF08${v.reason}\uFF09\u2014\u2014\u4EA7\u7269\u672A\u751F\u6210\uFF1Bconfig.compiler.backend \u6539\u56DE 'node' \u53EF\u964D\u7EA7`);
1589
+ }
1590
+ }
1591
+ const pageRenderer = !isComponent && matchWebviewPage(cfg.page?.webviewPages, rel) ? "webview" : renderer;
522
1592
  const cacheEnabled = !process.env.PROTEUS_NO_CACHE && !isDebug;
523
1593
  let wxml;
524
1594
  let js;
@@ -539,7 +1609,10 @@ function mpTransform(opts) {
539
1609
  moduleImports: moduleImportsByFile.get(file),
540
1610
  annotateLines: isDebug,
541
1611
  debug: isDebug,
542
- autoScrollContainer
1612
+ autoScrollContainer,
1613
+ fluidLayout,
1614
+ renderer: pageRenderer,
1615
+ platform: "mp"
543
1616
  },
544
1617
  projectRoot
545
1618
  );
@@ -561,7 +1634,11 @@ function mpTransform(opts) {
561
1634
  annotateLines: isDebug,
562
1635
  debug: isDebug,
563
1636
  preprocessStyle,
564
- autoScrollContainer
1637
+ loadStyleSrc: (src) => loadStyleSrcWithVariant(src, file, "mp"),
1638
+ autoScrollContainer,
1639
+ fluidLayout,
1640
+ renderer: pageRenderer,
1641
+ platform: "mp"
565
1642
  });
566
1643
  wxml = result.wxml;
567
1644
  js = result.js;
@@ -582,7 +1659,11 @@ function mpTransform(opts) {
582
1659
  annotateLines: isDebug,
583
1660
  debug: isDebug,
584
1661
  preprocessStyle,
585
- autoScrollContainer
1662
+ loadStyleSrc: (src) => loadStyleSrcWithVariant(src, file, "mp"),
1663
+ autoScrollContainer,
1664
+ fluidLayout,
1665
+ renderer,
1666
+ platform: "mp"
586
1667
  });
587
1668
  wxml = result.wxml;
588
1669
  js = result.js;
@@ -594,8 +1675,9 @@ function mpTransform(opts) {
594
1675
  if (cached) {
595
1676
  console.log(`[mp-transform] \u7F16\u8BD1\u7F13\u5B58\u547D\u4E2D\uFF1A${rel}`);
596
1677
  }
597
- const jsWithMap = sourcemap && isDebug ? `${js}//# sourceMappingURL=${rel}.js.map
598
- ` : js;
1678
+ const jsFinal = rewriteFrameworkRequires(js, rel);
1679
+ const jsWithMap = sourcemap && isDebug ? `${jsFinal}//# sourceMappingURL=${rel}.js.map
1680
+ ` : jsFinal;
599
1681
  this.emitFile({ type: "asset", fileName: `${rel}.wxml`, source: wxml });
600
1682
  this.emitFile({ type: "asset", fileName: `${rel}.js`, source: jsWithMap });
601
1683
  this.emitFile({ type: "asset", fileName: `${rel}.wxss`, source: wxss });
@@ -612,6 +1694,31 @@ function mpTransform(opts) {
612
1694
  if (warnings.length) warningReport.push({ file: rel, warnings });
613
1695
  console.log(`[mp-transform] ${rel} \u2192 wxml/js/wxss \u5DF2\u8F93\u51FA`);
614
1696
  }
1697
+ {
1698
+ const publicDir = path5.join(projectRoot, "public");
1699
+ if (fs5.existsSync(publicDir)) {
1700
+ const rels = [];
1701
+ const walk = (dir) => {
1702
+ for (const e of fs5.readdirSync(dir, { withFileTypes: true })) {
1703
+ if (e.name.startsWith(".")) continue;
1704
+ const full = path5.join(dir, e.name);
1705
+ if (e.isDirectory()) walk(full);
1706
+ else rels.push(path5.relative(publicDir, full).replace(/\\/g, "/"));
1707
+ }
1708
+ };
1709
+ walk(publicDir);
1710
+ let assetN = 0;
1711
+ for (const { from, to } of mapPublicAssetVariants(rels, "mp")) {
1712
+ this.emitFile({ type: "asset", fileName: to, source: fs5.readFileSync(path5.join(publicDir, from)) });
1713
+ assetN++;
1714
+ }
1715
+ 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`);
1716
+ }
1717
+ }
1718
+ if (rustCompiler) {
1719
+ if (dualOk) console.log(`[mp-transform] compiler=rust\uFF1A${dualOk} \u4E2A\u6587\u4EF6 Node/Rust \u53CC\u7F16\u8BD1\u8BED\u4E49\u7B49\u4EF7\uFF08G-29.1\uFF09\u2705`);
1720
+ if (dualSkipped) console.warn(`[mp-transform] compiler=rust\uFF1A${dualSkipped} \u4E2A\u6587\u4EF6\u8DF3\u8FC7\u53CC\u7F16\u8BD1\u6821\u9A8C\uFF08${dualSkippedReason || "\u672A\u77E5"}\uFF09`);
1721
+ }
615
1722
  if (!process.env.PROTEUS_NO_CACHE && !isDebug) {
616
1723
  const st = compileCache.stats();
617
1724
  const bs = bundleCache.stats();
@@ -633,450 +1740,397 @@ function mpTransform(opts) {
633
1740
  };
634
1741
  }
635
1742
 
636
- // src/gen-routes.ts
637
- import fs3 from "node:fs";
638
- import path3 from "node:path";
639
- import { mergeMeta } from "@proteus-vue/router/merge";
640
- import { scanRoutes } from "@proteus-vue/router/scan";
641
- import { buildRouteTree } from "@proteus-vue/router/tree";
642
- function runGenRoutes(options) {
643
- const config = options.config;
644
- const ROOT = options.root ?? process.cwd();
645
- const trace = options.trace ?? (() => {
646
- });
647
- const APP_DIR = path3.resolve(ROOT, path3.dirname(config.pagesDir));
648
- const OUT_DIR = path3.join(ROOT, "dist", "mp-weixin");
649
- const FW_COMPONENTS = options.frameworkComponentsDir ?? path3.join(ROOT, "src", "components");
650
- const moduleChunks = /* @__PURE__ */ new Map();
651
- for (const mc of options.moduleConfigs ?? []) moduleChunks.set(mc.name, mc.chunk ?? mc.name);
652
- const subPackageModules = /* @__PURE__ */ new Map();
653
- for (const mc of options.moduleConfigs ?? []) {
654
- const chunk = mc.chunk ?? mc.name;
655
- const matched = (config.subPackages ?? []).some((sp) => (sp.name ?? path3.basename(sp.root)) === chunk);
656
- if (matched) subPackageModules.set(chunk, { deps: Object.keys(mc.dependencies ?? {}), preload: mc.preload ?? [] });
657
- for (const dep of Object.keys(mc.dependencies ?? {})) {
658
- if (!moduleChunks.has(dep)) console.warn(`[gen-routes] \u6A21\u5757 ${mc.name} \u4F9D\u8D56 "${dep}" \u672A\u627E\u5230\u5BF9\u5E94\u6A21\u5757\u5951\u7EA6\uFF08proteus-module.config.ts\uFF09\u2014\u2014\u4F9D\u8D56\u5C06\u4E0D\u751F\u6548`);
659
- }
660
- }
661
- const subPackageNameOf = (moduleName) => {
662
- const chunk = moduleChunks.get(moduleName);
663
- return (config.subPackages ?? []).some((sp) => (sp.name ?? path3.basename(sp.root)) === chunk) ? chunk : void 0;
664
- };
665
- function walkVueFiles2(dir, acc = []) {
666
- if (!fs3.existsSync(dir)) return acc;
667
- for (const entry of fs3.readdirSync(dir, { withFileTypes: true })) {
668
- if (entry.name.startsWith(".")) continue;
669
- const full = path3.join(dir, entry.name);
670
- if (entry.isDirectory()) walkVueFiles2(full, acc);
671
- else if (entry.name.endsWith(".vue")) acc.push(full);
672
- }
673
- return acc;
674
- }
675
- function resolveConfigMeta(configMeta, pageRel) {
676
- if (!configMeta) return void 0;
677
- let dirMeta;
678
- const segs = pageRel.split("/");
679
- for (let i = segs.length - 1; i >= 1; i--) {
680
- const prefix = segs.slice(0, i).join("/");
681
- if (configMeta[prefix]) {
682
- dirMeta = configMeta[prefix];
683
- break;
684
- }
685
- }
686
- const exact = configMeta[pageRel];
687
- if (dirMeta && exact) return mergeMeta(dirMeta, exact);
688
- return dirMeta ?? exact;
689
- }
690
- function scanPages() {
691
- const pages2 = [];
692
- const configMeta = config.router?.meta;
693
- const mainBlocks = scanRoutes(path3.join(ROOT, config.pagesDir), { derivePath: true, verbose: true, includeNoRoute: true });
694
- for (const b of mainBlocks) {
695
- const relSrc = path3.relative(APP_DIR, b.componentPath).replace(/\\/g, "/").replace(/\.vue$/, "");
696
- const pageRel = relSrc.replace(/^pages\//, "");
697
- trace(`[route] ${relSrc} \u6765\u6E90\u767B\u8BB0\uFF08${b.loc.file}:${b.loc.line}\uFF0Croute/scan\uFF09`);
698
- pages2.push({
699
- file: b.componentPath,
700
- relSrc,
701
- mpPath: relSrc,
702
- // ★集中 meta:config(精确/目录前缀)→ 页面 <route> 覆盖(mergeMeta 页面胜)
703
- meta: mergeMeta(resolveConfigMeta(configMeta, pageRel), b.meta),
704
- params: b.params,
705
- pageJson: b.pageJson,
706
- customRouteKeyName: b.customRouteKeyName
707
- });
708
- }
709
- for (const sp of config.subPackages ?? []) {
710
- const spRootAbs = path3.join(ROOT, sp.root);
711
- const spName = sp.name ?? path3.basename(sp.root);
712
- const spBlocks = scanRoutes(spRootAbs, { derivePath: true, verbose: true, includeNoRoute: true });
713
- for (const b of spBlocks) {
714
- const relSrc = path3.relative(APP_DIR, b.componentPath).replace(/\\/g, "/").replace(/\.vue$/, "");
715
- const relInSub = path3.relative(spRootAbs, b.componentPath).replace(/\\/g, "/").replace(/\.vue$/, "");
716
- const pageRel = relInSub.replace(/^pages\//, "");
717
- pages2.push({
718
- file: b.componentPath,
719
- relSrc,
720
- mpPath: relSrc,
721
- subPackage: spName,
722
- relInSub,
723
- meta: mergeMeta(resolveConfigMeta(configMeta, pageRel), b.meta),
724
- params: b.params,
725
- pageJson: b.pageJson,
726
- customRouteKeyName: b.customRouteKeyName,
727
- chunk: b.chunk
728
- });
729
- if (b.chunk && b.chunk !== spName) {
730
- console.warn(`[gen-routes] \u5206\u5305\u9875\u9762 ${relInSub} \u58F0\u660E chunk="${b.chunk}" \u4E0E\u5206\u5305\u540D "${spName}" \u4E0D\u4E00\u81F4\uFF08Router M7.1\uFF1A\u9875\u9762 chunk \u5E94\u5BF9\u9F50\u6A21\u5757\u5206\u5305\u540D\uFF0C\u89C1 module-plan 05\uFF09`);
731
- }
732
- }
1743
+ // src/devtools-plugin.ts
1744
+ import fs6 from "node:fs";
1745
+ import path6 from "node:path";
1746
+ import { createRequire as createRequire4 } from "node:module";
1747
+ import { WebSocketServer } from "ws";
1748
+
1749
+ // src/devtools-relay.ts
1750
+ function createProteusRelay() {
1751
+ const sources = /* @__PURE__ */ new Set();
1752
+ const panels = /* @__PURE__ */ new Set();
1753
+ const pending = /* @__PURE__ */ new Map();
1754
+ function onMessage(role, socket, raw) {
1755
+ let msg = null;
1756
+ try {
1757
+ msg = JSON.parse(raw);
1758
+ } catch {
1759
+ return;
733
1760
  }
734
- return pages2;
735
- }
736
- function buildRoutes(pages2) {
737
- const routes2 = pages2.map((p) => {
738
- const r = {
739
- // ★统一后 name 由 scan 推导(derivePath 模式:index 归并目录名,与旧 toRouteName 一致)
740
- name: deriveName(p),
741
- path: p.mpPath,
742
- // 相对 RouterView 所在目录({appDir}/router)的路径,Web 端 import.meta.glob 按此匹配
743
- component: path3.relative(path3.join(APP_DIR, "router"), p.file).replace(/\\/g, "/")
744
- };
745
- if (p.subPackage) r.subPackage = p.subPackage;
746
- if (p.meta && Object.keys(p.meta).length > 0) r.meta = p.meta;
747
- if (p.customRouteKeyName) r.customRouteKeyName = p.customRouteKeyName;
748
- if (p.params && Object.keys(p.params).length > 0) r.params = p.params;
749
- return r;
750
- });
751
- const nameByRel = new Map(pages2.map((p) => [p.relSrc, deriveName(p)]));
752
- const blocks = pages2.map((p) => ({
753
- loc: { file: p.file, line: 1, column: 1 },
754
- path: p.relSrc.endsWith("/index") ? p.relSrc.slice(0, -"/index".length) : p.relSrc,
755
- name: nameByRel.get(p.relSrc),
756
- meta: p.meta ?? {},
757
- componentPath: p.file
758
- }));
759
- const tree = buildRouteTree(blocks, {}, trace);
760
- const parentByName = /* @__PURE__ */ new Map();
761
- const walk = (nodes, parent) => {
762
- for (const n of nodes) {
763
- if (n.name && parent) parentByName.set(n.name, parent);
764
- walk(n.children, n.name);
1761
+ if (!msg || typeof msg !== "object") return;
1762
+ if (role === "source") {
1763
+ if (typeof msg.id === "number" && pending.has(msg.id)) {
1764
+ const target = pending.get(msg.id);
1765
+ pending.delete(msg.id);
1766
+ target.send(raw);
1767
+ return;
765
1768
  }
766
- };
767
- walk(tree);
768
- for (const r of routes2) {
769
- const parent = parentByName.get(r.name);
770
- if (parent && parent !== r.name) r.parent = parent;
771
- }
772
- return routes2;
773
- }
774
- function deriveName(p) {
775
- const base = p.relSrc.split("/").pop() ?? "";
776
- if (base === "index") {
777
- const dir = p.relSrc.slice(0, p.relSrc.lastIndexOf("/"));
778
- const stripped = dir.replace(/^(pages|subpackages)(\/|$)/, "").replace(/\/$/, "");
779
- return stripped ? stripped.replace(/\//g, "-") : "index";
780
- }
781
- return p.relSrc.replace(/^(pages|subpackages)\//, "").replace(/\//g, "-");
782
- }
783
- function validate(pages2, routes2) {
784
- const mainCount = pages2.filter((p) => !p.subPackage).length;
785
- if (mainCount > 32) {
786
- throw new Error(
787
- `[gen-routes] \u4E3B\u5305\u9875\u9762\u6570 ${mainCount} \u8D85\u8FC7\u5E73\u53F0\u786C\u8FB9\u754C 32\uFF0C\u8BF7\u5C06\u90E8\u5206\u9875\u9762\u79FB\u5165\u5206\u5305\uFF08platform limitation, cannot exceed\uFF09`
788
- );
789
- }
790
- const dupNames = routes2.filter((r, i) => routes2.findIndex((x) => x.name === r.name) !== i);
791
- if (dupNames.length) {
792
- throw new Error(`[gen-routes] \u547D\u540D\u8DEF\u7531\u91CD\u590D\uFF1A${dupNames.map((r) => r.name).join(", ")}`);
793
- }
794
- }
795
- function formatRoute(r) {
796
- const parts = [
797
- `name: ${JSON.stringify(r.name)}`,
798
- `path: ${JSON.stringify(r.path)}`,
799
- `component: ${JSON.stringify(r.component)}`
800
- ];
801
- if (r.parent) parts.push(`parent: ${JSON.stringify(r.parent)}`);
802
- if (r.subPackage) parts.push(`subPackage: ${JSON.stringify(r.subPackage)}`);
803
- if (r.meta && Object.keys(r.meta).length) parts.push(`meta: ${JSON.stringify(r.meta)}`);
804
- if (r.customRouteKeyName) parts.push(`customRouteKeyName: ${JSON.stringify(r.customRouteKeyName)}`);
805
- return ` { ${parts.join(", ")} },`;
806
- }
807
- function writeAutoRoutes(routes2) {
808
- const lines = [
809
- `// ${config.routesOutput} \u2014\u2014 \u5E94\u7528\u4FA7\u8DEF\u7531\u8868\uFF08AUTO-GENERATED by scripts/gen-routes.ts\uFF0C\u52FF\u624B\u52A8\u7F16\u8F91\uFF09`,
810
- "// \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",
811
- "import type { RouteRecord } from '@proteus-vue/router/types'",
812
- "",
813
- "export const routes: RouteRecord[] = [",
814
- ...routes2.map(formatRoute),
815
- "]",
816
- "",
817
- "export const tabRoutes: RouteRecord[] = routes.filter(r => r.meta?.isTab)",
818
- "export const routeMap: Record<string, RouteRecord> = routes.reduce((m, r) => { m[r.name] = r; return m }, {} as Record<string, RouteRecord>)",
819
- ""
820
- ];
821
- lines.push("// \u2605 \u7C7B\u578B\u63D0\u793A\uFF1A\u6309\u8DEF\u7531\u540D\u7D22\u5F15\u7684\u53C2\u6570\u7C7B\u578B\u8868\uFF08\u6765\u6E90\uFF1A<route> \u5757 params \u58F0\u660E\uFF09");
822
- lines.push("declare module '@proteus-vue/router/types' {");
823
- lines.push(" interface RouteParamsByName {");
824
- for (const r of routes2) {
825
- const params = r.params ?? {};
826
- const fields = Object.entries(params);
827
- const body = fields.length ? fields.map(([k, t]) => `${k}?: ${tsType(t)}`).join("; ") : "";
828
- lines.push(` '${r.name}': { ${body} },`);
1769
+ for (const p of panels) p.send(raw);
1770
+ return;
829
1771
  }
830
- lines.push(" }", "}");
831
- const outFile = path3.join(ROOT, config.routesOutput);
832
- fs3.mkdirSync(path3.dirname(outFile), { recursive: true });
833
- fs3.writeFileSync(outFile, lines.join("\n"));
834
- console.log(`[gen-routes] \u5DF2\u751F\u6210 ${path3.relative(ROOT, outFile)}\uFF08${routes2.length} \u6761\u8DEF\u7531 + RouteParamsByName\uFF09`);
835
- }
836
- function tsType(t) {
837
- if (t === "number") return "number";
838
- if (t === "boolean") return "boolean";
839
- if (t !== "string") {
840
- console.warn(`[gen-routes] \u672A\u77E5\u53C2\u6570\u7C7B\u578B ${t}\uFF08\u652F\u6301 string/number/boolean\uFF09\uFF0C\u5DF2\u6309 string \u5904\u7406`);
1772
+ if (typeof msg.id === "number" && sources.size > 0) {
1773
+ pending.set(msg.id, socket);
1774
+ const first = sources.values().next().value;
1775
+ first.send(raw);
841
1776
  }
842
- return "string";
843
1777
  }
844
- function writeAppJson(pages2, routes2) {
845
- const mainPages = pages2.filter((p) => !p.subPackage).map((p) => p.mpPath);
846
- const entryPath = path3.relative(APP_DIR, path3.join(ROOT, config.pagesDir, "index")).replace(/\\/g, "/");
847
- const entryIdx = mainPages.indexOf(entryPath);
848
- if (entryIdx > 0) {
849
- mainPages.splice(entryIdx, 1);
850
- mainPages.unshift(entryPath);
1778
+ function onClose(socket) {
1779
+ sources.delete(socket);
1780
+ panels.delete(socket);
1781
+ for (const entry of pending) {
1782
+ if (entry[1] === socket) pending.delete(entry[0]);
851
1783
  }
852
- const subPackages = (config.subPackages ?? []).filter((sp) => pages2.some((p) => p.subPackage === (sp.name ?? path3.basename(sp.root)))).map((sp) => {
853
- const spName = sp.name ?? path3.basename(sp.root);
854
- const out = {
855
- root: path3.relative(APP_DIR, path3.join(ROOT, sp.root)).replace(/\\/g, "/"),
856
- ...sp.name ? { name: sp.name } : {},
857
- pages: pages2.filter((p) => p.subPackage === spName && p.relInSub).map((p) => p.relInSub)
858
- };
859
- const mod = subPackageModules.get(spName);
860
- if (mod) {
861
- const depNames = mod.deps.map(subPackageNameOf).filter((n) => Boolean(n));
862
- if (depNames.length) out.dependencies = depNames;
1784
+ }
1785
+ return {
1786
+ handleConnection(role, socket) {
1787
+ ;
1788
+ (role === "source" ? sources : panels).add(socket);
1789
+ const withOn = socket;
1790
+ if (typeof withOn.on === "function") {
1791
+ withOn.on("message", (data) => onMessage(role, socket, String(data)));
1792
+ withOn.on("close", () => onClose(socket));
863
1793
  }
864
- return out;
865
- });
866
- const preloadRule = {};
867
- for (const [spName, mod] of subPackageModules) {
868
- const targetPackages = mod.preload.map(subPackageNameOf).filter((n) => Boolean(n));
869
- if (!targetPackages.length) continue;
870
- const entryPage = pages2.find((p) => p.subPackage === spName && p.relInSub);
871
- if (!entryPage) continue;
872
- preloadRule[entryPage.mpPath] = { network: "all", packages: targetPackages };
1794
+ },
1795
+ counts() {
1796
+ return { source: sources.size, panel: panels.size };
1797
+ },
1798
+ close() {
1799
+ for (const s of sources) s.close();
1800
+ for (const p of panels) p.close();
1801
+ sources.clear();
1802
+ panels.clear();
1803
+ pending.clear();
873
1804
  }
874
- const windowConfig = { navigationStyle: "custom" };
875
- const tabRoutes = routes2.filter((r) => r.meta?.isTab);
876
- const appJson = { pages: mainPages };
877
- if (subPackages.length) appJson.subPackages = subPackages;
878
- if (Object.keys(preloadRule).length) appJson.preloadRule = preloadRule;
879
- appJson.window = windowConfig;
880
- if (config.skyline) appJson.lazyCodeLoading = "requiredComponents";
881
- if (config.skyline) {
882
- appJson.rendererOptions = { skyline: { defaultDisplayBlock: config.skylineLayout?.defaultDisplayBlock ?? true } };
1805
+ };
1806
+ }
1807
+
1808
+ // src/devtools-plugin.ts
1809
+ var require_ = createRequire4(import.meta.url);
1810
+ function isOriginAllowed(origin, allowFrom) {
1811
+ if (!allowFrom || allowFrom.length === 0) return true;
1812
+ if (!origin) return false;
1813
+ return allowFrom.indexOf(origin) >= 0;
1814
+ }
1815
+ function resolveDevtoolsDir() {
1816
+ return path6.dirname(require_.resolve("@proteus-vue/devtools/package.json"));
1817
+ }
1818
+ function createPanelPageHandler(devtoolsDir) {
1819
+ return (req, res) => {
1820
+ const pathname = (req.url ?? "/").split("?")[0];
1821
+ const base = "/proteus-devtools";
1822
+ if (pathname === base || pathname === base + "/") {
1823
+ const host = req.headers?.host ?? "localhost";
1824
+ const proto = req.headers?.["x-forwarded-proto"] === "https" ? "wss" : "ws";
1825
+ 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");
1826
+ res.setHeader("content-type", "text/html; charset=utf-8");
1827
+ res.end(html);
1828
+ return true;
883
1829
  }
884
- if (tabRoutes.length >= 2) {
885
- appJson.tabBar = {
886
- list: tabRoutes.map((r) => ({ pagePath: r.path, text: r.meta?.title ?? r.name }))
887
- };
888
- } else if (tabRoutes.length === 1) {
889
- console.warn(`[gen-routes] tabBar \u4EC5\u58F0\u660E 1 \u9879\uFF08${tabRoutes[0].name}\uFF09\uFF0C\u5FAE\u4FE1\u8981\u6C42\u81F3\u5C11 2 \u9879\uFF0C\u5DF2\u5FFD\u7565 tabBar \u914D\u7F6E\uFF1B\u53EF\u5C06\u66F4\u591A\u9875\u9762\u6807\u8BB0 isTab \u6216\u79FB\u9664\u73B0\u6709 isTab`);
1830
+ if (pathname === base + "/style.css") {
1831
+ res.setHeader("content-type", "text/css; charset=utf-8");
1832
+ res.end(fs6.readFileSync(path6.join(devtoolsDir, "style.css")));
1833
+ return true;
890
1834
  }
891
- fs3.mkdirSync(OUT_DIR, { recursive: true });
892
- fs3.writeFileSync(path3.join(OUT_DIR, "app.json"), JSON.stringify(appJson, null, 2) + "\n");
893
- console.log(`[gen-routes] \u5DF2\u751F\u6210 dist/mp-weixin/app.json\uFF08\u4E3B\u5305 ${mainPages.length} \u9875\uFF0C\u5206\u5305 ${subPackages.length} \u4E2A\uFF09`);
1835
+ if (pathname === base + "/panel.js") {
1836
+ res.setHeader("content-type", "application/javascript; charset=utf-8");
1837
+ res.end(fs6.readFileSync(path6.join(devtoolsDir, "dist", "panel.js")));
1838
+ return true;
1839
+ }
1840
+ return false;
1841
+ };
1842
+ }
1843
+ function printPanelUrl(httpServer, logger, defaultPort = 5173) {
1844
+ if (!httpServer || typeof httpServer.once !== "function") return;
1845
+ httpServer.once("listening", () => {
1846
+ const addr = httpServer.address?.();
1847
+ const port = typeof addr === "object" && addr !== null ? addr.port : void 0;
1848
+ logger?.info(` \u279C Proteus DevTools: http://localhost:${port ?? defaultPort}/proteus-devtools`);
1849
+ });
1850
+ }
1851
+ function devtoolsRelayPlugin(opts = {}) {
1852
+ let wss = null;
1853
+ let relay = null;
1854
+ let pageHandler = null;
1855
+ const allowFrom = opts.allowFrom ?? [];
1856
+ function setup(server) {
1857
+ const httpServer = server.httpServer;
1858
+ if (!httpServer) return;
1859
+ relay = createProteusRelay();
1860
+ wss = new WebSocketServer({ noServer: true });
1861
+ httpServer.on("upgrade", (req, socket, head) => {
1862
+ const url = String(req.url ?? "").split("?")[0];
1863
+ const role = url === "/proteus-source" ? "source" : url === "/proteus-panel" ? "panel" : null;
1864
+ if (!role || !relay) return;
1865
+ const origin = req.headers?.origin;
1866
+ if (!isOriginAllowed(origin, allowFrom)) {
1867
+ console.warn(`[proteus-devtools] \u62D2\u7EDD ${role} \u8FDE\u63A5\uFF1AOrigin ${origin ?? "(\u65E0)"} \u4E0D\u5728\u767D\u540D\u5355 ${allowFrom.join(", ")}`);
1868
+ socket.destroy?.();
1869
+ return;
1870
+ }
1871
+ wss?.handleUpgrade(req, socket, head, (ws) => {
1872
+ wss?.emit("connection", ws, req);
1873
+ relay?.handleConnection(role, ws);
1874
+ });
1875
+ });
1876
+ pageHandler = createPanelPageHandler(resolveDevtoolsDir());
1877
+ const middlewares = server.middlewares;
1878
+ middlewares?.use?.((req, res, next) => {
1879
+ if (pageHandler && pageHandler(req, res)) return;
1880
+ next();
1881
+ });
894
1882
  }
895
- const NATIVE_MP_TAGS = /* @__PURE__ */ new Set([
896
- "view",
897
- "text",
898
- "image",
899
- "button",
900
- "input",
901
- "textarea",
902
- "video",
903
- "canvas",
904
- "scroll-view",
905
- "slot",
906
- "rich-text",
907
- "swiper",
908
- "swiper-item",
909
- "navigator",
910
- "icon",
911
- "progress",
912
- "checkbox",
913
- "radio",
914
- "form",
915
- "label",
916
- "picker",
917
- "slider",
918
- "switch",
919
- "map",
920
- "web-view",
921
- "cover-view",
922
- "cover-image",
923
- "movable-area",
924
- "movable-view",
925
- "block",
926
- "template",
927
- "wxs",
928
- "audio",
929
- "camera",
930
- "live-player",
931
- "ad",
932
- "official-account",
933
- "open-data",
934
- "page-container",
935
- "root-portal",
936
- "match-media",
937
- // ★vue-compat-advance Batch 2/5:<transition> 由编译器消费(装饰式,产物不输出该标签)——扫描跳过,非自定义组件
938
- "transition"
939
- ]);
940
- const HTML_TAGS = /* @__PURE__ */ new Set([
941
- "div",
942
- "span",
943
- "p",
944
- "h1",
945
- "h2",
946
- "h3",
947
- "h4",
948
- "h5",
949
- "h6",
950
- "a",
951
- "img",
952
- "br",
953
- "ul",
954
- "ol",
955
- "li",
956
- "section",
957
- "header",
958
- "footer",
959
- "main",
960
- "aside",
961
- "nav",
962
- "article",
963
- "strong",
964
- "em",
965
- "b",
966
- "i",
967
- "small",
968
- "code",
969
- "pre",
970
- "select",
971
- "option",
972
- "table",
973
- "tr",
974
- "td",
975
- "th",
976
- "form",
977
- "label",
978
- "tbody",
979
- "thead",
980
- "caption",
981
- "figure",
982
- "figcaption",
983
- "details",
984
- "summary"
985
- ]);
986
- function collectComponents(file) {
987
- const src = fs3.readFileSync(file, "utf-8");
988
- const tpl = src.match(/<template[^>]*>([\s\S]*?)<\/template>/i)?.[1] ?? "";
989
- const customTags = new Set(Object.keys(config.rules?.customTags ?? {}));
990
- const used = /* @__PURE__ */ new Set();
991
- const tagRe = /<([a-z][\w-]*)/g;
992
- let m;
993
- while (m = tagRe.exec(tpl)) {
994
- const tag = m[1];
995
- if (tag.startsWith("!")) continue;
996
- if (NATIVE_MP_TAGS.has(tag) || HTML_TAGS.has(tag) || customTags.has(tag)) continue;
997
- used.add(tag);
1883
+ return {
1884
+ name: "proteus-devtools-relay",
1885
+ apply: "serve",
1886
+ configureServer(server) {
1887
+ if (opts.enabled === false || relay) return;
1888
+ setup(server);
1889
+ const httpServer = server.httpServer;
1890
+ const logger = server.config?.logger;
1891
+ printPanelUrl(httpServer, logger);
1892
+ },
1893
+ configurePreviewServer(server) {
1894
+ if (opts.enabled === false || relay) return;
1895
+ setup(server);
1896
+ const httpServer = server.httpServer;
1897
+ const logger = server.config?.logger;
1898
+ printPanelUrl(httpServer, logger);
1899
+ },
1900
+ closeBundle() {
1901
+ wss?.close();
1902
+ relay?.close();
1903
+ wss = null;
1904
+ relay = null;
1905
+ pageHandler = null;
998
1906
  }
999
- const out = {};
1000
- for (const tag of used) {
1001
- const appCandidates = [path3.join(APP_DIR, "components", tag, "index.vue"), path3.join(APP_DIR, "components", `${tag}.vue`)];
1002
- const appFound = appCandidates.find((c) => fs3.existsSync(c));
1003
- if (appFound) {
1004
- out[tag] = `/components/${tag}/index`;
1005
- continue;
1907
+ };
1908
+ }
1909
+
1910
+ // src/vite-config.ts
1911
+ import path7 from "node:path";
1912
+ import fs7 from "node:fs";
1913
+ import { pathToFileURL } from "node:url";
1914
+ import { createRequire as createRequire5 } from "node:module";
1915
+ import {
1916
+ platformDefines as platformDefines2,
1917
+ applyPlatformMacrosInSfc,
1918
+ resolvePlatformVariant as resolvePlatformVariant2,
1919
+ resolvePlatformVariantWithExts as resolvePlatformVariantWithExts2,
1920
+ splitVariant as splitVariant3,
1921
+ mapPublicAssetVariants as mapPublicAssetVariants2
1922
+ } from "@proteus-vue/compiler";
1923
+ function platformMacroPlugin(platform) {
1924
+ return {
1925
+ name: "proteus-platform-macros",
1926
+ enforce: "pre",
1927
+ transform(code, id) {
1928
+ if (!id.endsWith(".vue")) return null;
1929
+ const out = applyPlatformMacrosInSfc(code, platform);
1930
+ return out === code ? null : { code: out, map: null };
1931
+ }
1932
+ };
1933
+ }
1934
+ function platformVariantPlugin(root, platform) {
1935
+ const CODE_EXTS = [".ts", ".js", ".mjs", ".cjs", ".vue", ".json", ".css"];
1936
+ return {
1937
+ name: "proteus-platform-variant",
1938
+ enforce: "pre",
1939
+ resolveId(id, importer) {
1940
+ if (!importer || !id) return null;
1941
+ const qIdx = id.indexOf("?");
1942
+ const query = qIdx >= 0 ? id.slice(qIdx) : "";
1943
+ const bare = qIdx >= 0 ? id.slice(0, qIdx) : id;
1944
+ if (!bare) return null;
1945
+ let base;
1946
+ if (bare.startsWith(".")) base = path7.resolve(path7.dirname(importer), bare);
1947
+ else if (bare.startsWith("@/")) base = path7.resolve(root, "src", bare.slice(2));
1948
+ else return null;
1949
+ const hasExt = path7.extname(base) !== "";
1950
+ if (hasExt) {
1951
+ const r2 = resolvePlatformVariant2(base, platform, fs7.existsSync);
1952
+ return r2 && r2 !== base ? r2 + query : null;
1006
1953
  }
1007
- const fwCandidates = [path3.join(FW_COMPONENTS, tag, "index.vue"), path3.join(FW_COMPONENTS, `${tag}.vue`)];
1008
- const fwFound = fwCandidates.find((c) => fs3.existsSync(c));
1009
- if (fwFound) {
1010
- out[tag] = `/proteus/${tag}/index`;
1011
- continue;
1954
+ const r = resolvePlatformVariantWithExts2(base, CODE_EXTS, platform, fs7.existsSync);
1955
+ return r ? r + query : null;
1956
+ }
1957
+ };
1958
+ }
1959
+ function hasPublicVariants(publicDir) {
1960
+ if (!fs7.existsSync(publicDir)) return false;
1961
+ const walk = (dir) => {
1962
+ for (const e of fs7.readdirSync(dir, { withFileTypes: true })) {
1963
+ if (e.name.startsWith(".")) continue;
1964
+ const full = path7.join(dir, e.name);
1965
+ if (e.isDirectory()) {
1966
+ if (walk(full)) return true;
1967
+ } else if (splitVariant3(e.name).platform !== void 0) {
1968
+ return true;
1012
1969
  }
1013
- console.warn(`[gen-routes] ${file} \u4F7F\u7528\u4E86\u7EC4\u4EF6 <${tag}>\uFF0C\u4F46\u672A\u627E\u5230 ${appCandidates.join(" \u6216 ")} \u6216\u6846\u67B6\u7EC4\u4EF6 ${fwCandidates.join(" \u6216 ")}`);
1014
1970
  }
1015
- return out;
1016
- }
1017
- function writePageJsons(pages2) {
1018
- for (const p of pages2) {
1019
- const pageJson = {};
1020
- if (config.skyline) {
1021
- pageJson.renderer = "skyline";
1022
- pageJson.componentFramework = "glass-easel";
1971
+ return false;
1972
+ };
1973
+ return walk(publicDir);
1974
+ }
1975
+ function platformPublicAssetsPlugin(root, platform) {
1976
+ return {
1977
+ name: "proteus-platform-public-assets",
1978
+ apply: "build",
1979
+ generateBundle() {
1980
+ const publicDir = path7.join(root, "public");
1981
+ if (!fs7.existsSync(publicDir)) return;
1982
+ const rels = [];
1983
+ const walk = (dir) => {
1984
+ for (const e of fs7.readdirSync(dir, { withFileTypes: true })) {
1985
+ if (e.name.startsWith(".")) continue;
1986
+ const full = path7.join(dir, e.name);
1987
+ if (e.isDirectory()) walk(full);
1988
+ else rels.push(path7.relative(publicDir, full).replace(/\\/g, "/"));
1989
+ }
1990
+ };
1991
+ walk(publicDir);
1992
+ for (const { from, to } of mapPublicAssetVariants2(rels, platform)) {
1993
+ this.emitFile({ type: "asset", fileName: to, source: fs7.readFileSync(path7.join(publicDir, from)) });
1023
1994
  }
1024
- if (p.pageJson) Object.assign(pageJson, p.pageJson);
1025
- const components = collectComponents(p.file);
1026
- if (Object.keys(components).length) pageJson.usingComponents = components;
1027
- const outFile = path3.join(OUT_DIR, p.mpPath + ".json");
1028
- fs3.mkdirSync(path3.dirname(outFile), { recursive: true });
1029
- fs3.writeFileSync(outFile, JSON.stringify(pageJson, null, 2) + "\n");
1030
1995
  }
1031
- console.log(`[gen-routes] \u5DF2\u751F\u6210 ${pages2.length} \u4E2A\u9875\u9762 page.json`);
1996
+ };
1997
+ }
1998
+ function routeBlocksPlugin() {
1999
+ return {
2000
+ name: "proteus-route-blocks",
2001
+ enforce: "pre",
2002
+ transform(code, id) {
2003
+ if (id.includes("?vue&type=route")) return { code: `export default ${code}`, map: null };
2004
+ return null;
2005
+ }
2006
+ };
2007
+ }
2008
+ function virtualMpEntryPlugin() {
2009
+ const VIRTUAL_ID = "\0proteus:mp-entry";
2010
+ return {
2011
+ name: "proteus-mp-entry",
2012
+ resolveId(id) {
2013
+ return id === "proteus:mp-entry" ? VIRTUAL_ID : null;
2014
+ },
2015
+ load(id) {
2016
+ return id === VIRTUAL_ID ? "export {}" : null;
2017
+ }
2018
+ };
2019
+ }
2020
+ async function importFromRoot(root, spec) {
2021
+ const req = createRequire5(path7.join(root, "package.json"));
2022
+ const resolved = req.resolve(spec);
2023
+ return import(pathToFileURL(resolved).href);
2024
+ }
2025
+ async function resolveProteusViteConfig(ctx, config) {
2026
+ const { root, command, mode } = ctx;
2027
+ const platform = mode === "mp-weixin" || mode === "web" ? mode : config.platform;
2028
+ const isMp = platform === "mp-weixin";
2029
+ const isDebug = process.env.PROTEUS_DEBUG === "1";
2030
+ let plugins;
2031
+ if (isMp) {
2032
+ plugins = [virtualMpEntryPlugin(), mpTransform({ config })];
2033
+ } else {
2034
+ const vueMod = await importFromRoot(root, "@vitejs/plugin-vue");
2035
+ const vue = vueMod.default({
2036
+ template: { compilerOptions: { isCustomElement: (tag) => MP_ONLY_TAGS.has(tag) } }
2037
+ });
2038
+ plugins = [platformVariantPlugin(root, "web"), vue, platformMacroPlugin("web"), platformPublicAssetsPlugin(root, "web"), routeBlocksPlugin()];
1032
2039
  }
1033
- function writeComponentJsons() {
1034
- const roots = [
1035
- { dir: path3.join(APP_DIR, "components"), prefix: "components" },
1036
- { dir: FW_COMPONENTS, prefix: "proteus" }
1037
- ];
1038
- let count = 0;
1039
- for (const { dir, prefix } of roots) {
1040
- if (!fs3.existsSync(dir)) continue;
1041
- for (const f of walkVueFiles2(dir)) {
1042
- const rel = path3.relative(dir, f).replace(/\\/g, "/").replace(/\.vue$/, "");
1043
- const comps = collectComponents(f);
1044
- const outFile = path3.join(OUT_DIR, prefix, `${rel}.json`);
1045
- fs3.mkdirSync(path3.dirname(outFile), { recursive: true });
1046
- const json = { component: true };
1047
- json.styleIsolation = "apply-shared";
1048
- if (Object.keys(comps).length) json.usingComponents = comps;
1049
- fs3.writeFileSync(outFile, JSON.stringify(json, null, 2) + "\n");
1050
- count++;
1051
- }
2040
+ const frameworkConfig = {
2041
+ configFile: false,
2042
+ // ★#418:vite 配置由本函数组装——不读 vite.config.ts(CLI 是唯一驱动)
2043
+ root,
2044
+ define: {
2045
+ // devtools 打通:dev serve 默认开启可观测;build 默认关闭零开销;PROTEUS_DEBUG=1 强制生产调试
2046
+ __PROTEUS_DEBUG__: command === "serve" || isDebug,
2047
+ // Skyline 开关注入:mp 构建时 __PROTEUS_SKYLINE__ = config.skyline
2048
+ __PROTEUS_SKYLINE__: isMp && config.skyline,
2049
+ // ★平台编译期宏(条件显隐)——Web .vue 走标准 @vitejs/plugin-vue:**vite define 对 .vue 不生效**
2050
+ // (实测:模板表达式/script __MP__ 残留),故 Web 端由 platformMacroPlugin(enforce:'pre'
2051
+ // 源码替换)处理,见 plugins。这里仍保留 define 供**非 .vue .ts/.js 模块**使用(同源取值)。
2052
+ ...platformDefines2(isMp ? "mp" : "web")
2053
+ },
2054
+ plugins,
2055
+ // ★平台变体·静态资源 Web 通道(第 3 层):public/ 含平台变体(logo.web.png)时,
2056
+ // 关掉 Vite 默认逐字拷贝(会把他端变体也拷进产物),改由 platformPublicAssetsPlugin web 解析;
2057
+ // 无变体时保持默认(零侵入,避免改变既有工程行为)。
2058
+ publicDir: hasPublicVariants(path7.join(root, "public")) ? false : void 0,
2059
+ resolve: {
2060
+ alias: [{ find: "@", replacement: path7.join(root, "src") }]
2061
+ },
2062
+ build: {
2063
+ target: "es2018",
2064
+ cssCodeSplit: false,
2065
+ minify: isMp ? false : void 0,
2066
+ outDir: path7.join(root, "dist", platform),
2067
+ emptyOutDir: !isMp,
2068
+ rollupOptions: isMp ? { input: "proteus:mp-entry", output: { entryFileNames: "mp-entry.js" } } : void 0
1052
2069
  }
1053
- if (count) console.log(`[gen-routes] \u5DF2\u751F\u6210 ${count} \u4E2A\u7EC4\u4EF6 component.json\uFF08component \u58F0\u660E + usingComponents \u5D4C\u5957\uFF09`);
2070
+ };
2071
+ const userVite = config.vite;
2072
+ let user;
2073
+ if (typeof userVite === "function") {
2074
+ user = await userVite({ command, mode });
2075
+ } else if (userVite && typeof userVite === "object") {
2076
+ user = userVite;
1054
2077
  }
1055
- function writeProjectConfig() {
1056
- const projectName = path3.basename(ROOT).replace(/[^\w.-]/g, "-");
1057
- const projectConfig = {
1058
- compileType: "miniprogram",
1059
- appid: config.appid,
1060
- projectname: projectName,
1061
- setting: { minifyWXML: true, urlCheck: false }
1062
- };
1063
- fs3.mkdirSync(OUT_DIR, { recursive: true });
1064
- fs3.writeFileSync(path3.join(OUT_DIR, "project.config.json"), JSON.stringify(projectConfig, null, 2) + "\n");
1065
- console.log(`[gen-routes] \u5DF2\u751F\u6210 dist/mp-weixin/project.config.json\uFF08appid=${config.appid}\uFF0Cprojectname=${projectName}\uFF09`);
2078
+ if (user) {
2079
+ const { plugins: userPlugins, resolve: userResolve, define: userDefine, build: userBuild, ...rest } = user;
2080
+ Object.assign(frameworkConfig, rest);
2081
+ if (userBuild) {
2082
+ const fwBuild = frameworkConfig.build ?? {};
2083
+ const merged = { ...fwBuild };
2084
+ const ub = userBuild;
2085
+ for (const k of Object.keys(ub)) {
2086
+ const uv = ub[k];
2087
+ if (k === "rollupOptions" && uv && typeof uv === "object") {
2088
+ const fwRo = fwBuild.rollupOptions ?? {};
2089
+ const uRo = uv;
2090
+ const ro = { ...fwRo };
2091
+ for (const rk of Object.keys(uRo)) {
2092
+ const rv = uRo[rk];
2093
+ if (rk === "output" && rv && typeof rv === "object" && fwRo.output && typeof fwRo.output === "object") {
2094
+ ro.output = { ...fwRo.output, ...rv };
2095
+ } else {
2096
+ ro[rk] = rv;
2097
+ }
2098
+ }
2099
+ merged.rollupOptions = ro;
2100
+ } else {
2101
+ merged[k] = uv;
2102
+ }
2103
+ }
2104
+ frameworkConfig.build = merged;
2105
+ }
2106
+ if (userResolve) {
2107
+ const baseAlias = frameworkConfig.resolve?.alias;
2108
+ frameworkConfig.resolve = {
2109
+ ...userResolve,
2110
+ alias: [...Array.isArray(baseAlias) ? baseAlias : [], ...Array.isArray(userResolve.alias) ? userResolve.alias : []]
2111
+ };
2112
+ }
2113
+ if (userDefine) {
2114
+ frameworkConfig.define = { ...frameworkConfig.define, ...userDefine };
2115
+ }
2116
+ if (userPlugins?.length) frameworkConfig.plugins = [...frameworkConfig.plugins ?? [], ...userPlugins];
1066
2117
  }
1067
- fs3.rmSync(OUT_DIR, { recursive: true, force: true });
1068
- const pages = scanPages();
1069
- const routes = buildRoutes(pages);
1070
- validate(pages, routes);
1071
- writeAutoRoutes(routes);
1072
- writeAppJson(pages, routes);
1073
- writePageJsons(pages);
1074
- writeComponentJsons();
1075
- writeProjectConfig();
1076
- console.log(`[gen-routes] \u5B8C\u6210\uFF1A\u5171 ${pages.length} \u4E2A\u9875\u9762`);
2118
+ return { config: frameworkConfig, needsGenRoutes: isMp, platform };
1077
2119
  }
1078
2120
  export {
2121
+ COMPONENTS_PKG,
2122
+ componentsRootExists,
2123
+ createPanelPageHandler,
2124
+ createProteusRelay,
1079
2125
  defaultScopedPlugin,
2126
+ devtoolsRelayPlugin,
2127
+ isOriginAllowed,
1080
2128
  mpTransform,
2129
+ printPanelUrl,
2130
+ resolveComponentsRoot,
2131
+ resolveDevtoolsDir,
2132
+ resolveProteusViteConfig,
2133
+ resolveSharedModule,
2134
+ rewriteRootToPage,
1081
2135
  runGenRoutes
1082
2136
  };