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

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,901 @@
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 fs4 from "node:fs";
3
+ import path4 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 extractTemplateBody(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 = extractTemplateBody(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
+ };
174
899
  }
175
900
 
176
901
  // src/plugin.ts
@@ -189,6 +914,19 @@ var MP_TAG_MAP = {
189
914
  navigator: "proteus-navigator",
190
915
  picker: "proteus-picker"
191
916
  };
917
+ var MP_ONLY_TAGS = /* @__PURE__ */ new Set([
918
+ "picker-view",
919
+ "picker-view-column",
920
+ "movable-view",
921
+ "movable-area",
922
+ "match-media",
923
+ "root-portal",
924
+ "page-container",
925
+ "share-element",
926
+ "keyboard-accessory",
927
+ "cover-view",
928
+ "cover-image"
929
+ ]);
192
930
  function defaultScopedPlugin() {
193
931
  return {
194
932
  name: "proteus-default-scoped",
@@ -209,22 +947,26 @@ function defaultScopedPlugin() {
209
947
  out = out.replace(/<script([^>]*)>/, (m) => `${m}${importLine}`);
210
948
  out = out.replace("</script>", `${hookCode}</script>`);
211
949
  }
950
+ out = out.replace(/\sp-fluid=("[^"]*"|'[^']*')/g, (_m, q) => {
951
+ const inner = q.slice(1, -1).replace(/'/g, "\\'");
952
+ return ` v-p-fluid="'${inner}'"`;
953
+ });
212
954
  return out === code ? null : { code: out, map: null };
213
955
  }
214
956
  };
215
957
  }
216
- var require2 = createRequire2(import.meta.url);
958
+ var require2 = createRequire3(import.meta.url);
217
959
  function resolvePkgPath(projectRoot, modPath) {
218
960
  const m = modPath.match(/^node_modules\/((?:@[^/]+\/)?[^/]+)\/([\s\S]+)$/);
219
961
  if (m) {
220
962
  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]);
963
+ const pkgRequire = createRequire3(path4.join(projectRoot, "package.json"));
964
+ const pkgRoot = path4.dirname(pkgRequire.resolve(`${m[1]}/package.json`));
965
+ return path4.join(pkgRoot, m[2]);
224
966
  } catch {
225
967
  }
226
968
  }
227
- return path2.join(projectRoot, modPath);
969
+ return path4.join(projectRoot, modPath);
228
970
  }
229
971
  function preprocessStyle(lang, content) {
230
972
  if (lang === "scss" || lang === "sass") {
@@ -241,42 +983,98 @@ function preprocessStyle(lang, content) {
241
983
  }
242
984
  return content;
243
985
  }
244
- function resolveSharedModule(appDir, absFrom, source, frameworkDir) {
986
+ function loadStyleSrcWithVariant(src, fromFilename, platform = "mp") {
987
+ const base = path4.resolve(path4.dirname(fromFilename), src);
988
+ const hit = resolvePlatformVariant(base, platform, fs4.existsSync);
989
+ if (!hit) return null;
990
+ try {
991
+ return fs4.readFileSync(hit, "utf-8");
992
+ } catch {
993
+ return null;
994
+ }
995
+ }
996
+ var VENDOR_SINGLETONS = ["pinia", "vue", "@vue/devtools-api"];
997
+ function resolveSharedModule(appDir, absFrom, source, frameworkDir, resolveFrom, platform = "mp") {
245
998
  if (source.startsWith("@proteus-vue/")) {
246
999
  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;
1000
+ const resolver = resolveFrom ? createRequire3(path4.join(resolveFrom, "package.json")) : require2;
1001
+ const pkgRoot = path4.dirname(resolver.resolve(`${source}/package.json`));
1002
+ const entry = path4.join(pkgRoot, "dist", "index.js");
1003
+ if (!fs4.existsSync(entry)) return null;
250
1004
  return { file: entry, relNoExt: `_proteus/${source.replace("@proteus-vue/", "")}` };
251
1005
  } catch {
252
1006
  return null;
253
1007
  }
254
1008
  }
1009
+ if (VENDOR_SINGLETONS.includes(source)) {
1010
+ const tryResolve = (base2) => {
1011
+ try {
1012
+ return createRequire3(base2).resolve(source);
1013
+ } catch {
1014
+ return null;
1015
+ }
1016
+ };
1017
+ const entry = tryResolve(path4.join(resolveFrom ?? appDir, "package.json")) ?? tryResolve(absFrom);
1018
+ if (!entry || !fs4.existsSync(entry)) return null;
1019
+ return { file: entry, relNoExt: `_proteus/${source}` };
1020
+ }
255
1021
  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")]) {
1022
+ const base = path4.resolve(path4.dirname(absFrom), source);
1023
+ const JS_EXTS = /* @__PURE__ */ new Set([".ts", ".js", ".mjs", ".cjs"]);
1024
+ const variantHit = resolvePlatformVariantWithExts(base, [...JS_EXTS], platform, (p) => {
1025
+ try {
1026
+ return fs4.statSync(p).isFile();
1027
+ } catch {
1028
+ return false;
1029
+ }
1030
+ });
1031
+ const candidates = variantHit ? [variantHit] : [base, `${base}.ts`, `${base}.js`, path4.join(base, "index.ts"), path4.join(base, "index.js")];
1032
+ for (const cand of candidates) {
258
1033
  if (cand.endsWith(".vue")) continue;
1034
+ if (!JS_EXTS.has(path4.extname(cand).toLowerCase())) continue;
259
1035
  let isFile = false;
260
1036
  try {
261
- isFile = fs2.statSync(cand).isFile();
1037
+ isFile = fs4.statSync(cand).isFile();
262
1038
  } catch {
263
1039
  isFile = false;
264
1040
  }
265
1041
  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)$/, "")}`;
1042
+ let relNoExt = path4.relative(appDir, cand).replace(/\\/g, "/").replace(/\.(ts|js)$/, "");
1043
+ if (relNoExt.startsWith("../") && frameworkDir && !path4.relative(frameworkDir, cand).startsWith("..")) {
1044
+ relNoExt = `proteus/${path4.relative(frameworkDir, cand).replace(/\\/g, "/").replace(/\.(ts|js)$/, "")}`;
269
1045
  }
270
1046
  return { file: cand, relNoExt };
271
1047
  }
272
1048
  }
273
1049
  return null;
274
1050
  }
1051
+ function rewriteRootToPage(css) {
1052
+ return css.replace(/(^|[},\s]):root\b/g, "$1page");
1053
+ }
1054
+ function rewriteFrameworkRequires(js, relOutput) {
1055
+ if (!js.includes("require('@proteus-vue/")) return js;
1056
+ const pageDir = path4.posix.dirname(relOutput);
1057
+ return js.replace(/require\('@proteus-vue\/([A-Za-z0-9_-]+)'\)/g, (m, name) => {
1058
+ const pkgRel = `_proteus/${name}.js`;
1059
+ let rel = path4.posix.relative(pageDir, pkgRel);
1060
+ if (!rel.startsWith(".")) rel = `./${rel}`;
1061
+ return `require('${rel}')`;
1062
+ });
1063
+ }
1064
+ function scanSourceImports(source) {
1065
+ const out = [];
1066
+ const re = /import\s+(?:type\s+)?(?:[\s\S]*?)\s+from\s+['"]([^'"]+)['"]|import\s+['"]([^'"]+)['"]/g;
1067
+ for (const m of source.matchAll(re)) {
1068
+ const s = m[1] || m[2];
1069
+ if (s) out.push({ source: s, typeOnly: /import\s+type\s+/.test(m[0]) });
1070
+ }
1071
+ return out;
1072
+ }
275
1073
  function extractBuilderFnName(code) {
276
1074
  const m = code.match(/function\s+([A-Za-z_$][\w$]*)\s*\(/);
277
1075
  return m ? m[1] : null;
278
1076
  }
279
- function assembleAppJs(mainCode, presets) {
1077
+ function assembleAppJs(mainCode, presets, piniaInstall = "") {
280
1078
  const presetCode = presets.map((p) => p.source.trim()).join("\n\n");
281
1079
  const custom = mainCode.trim();
282
1080
  const registerLines = presets.map((p) => ` wx.router.addRouteBuilder('${p.name}', ${p.fnName})`);
@@ -291,7 +1089,7 @@ ${registerLines.join("\n")}
291
1089
  ${presetCode}${register}`;
292
1090
  }
293
1091
  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");
1092
+ const skeleton = APP_LAUNCH_SKELETON.replace("__PRESET_REGISTRATION__", skeletonReg.join("\n") || " // \u65E0\u5185\u7F6E\u9884\u8BBE").replace("__PINIA_INSTALL__", piniaInstall);
295
1093
  return `${custom ? `${custom}
296
1094
 
297
1095
  ` : ""}${presetCode ? `${presetCode}
@@ -303,13 +1101,15 @@ function filterOverriddenPresets(mainCode, presets) {
303
1101
  }
304
1102
  async function loadPresetBuilders(projectRoot, cfg) {
305
1103
  const presets = [];
306
- for (const [name, modPath] of Object.entries(cfg.customRoute.builders)) {
1104
+ const { router: rc, duplicates } = resolveRouterConfig(cfg);
1105
+ for (const d of duplicates) console.warn(`[mp-transform] \u8DEF\u7531\u5B57\u6BB5 "${d}" \u5728\u9876\u5C42\u4E0E router \u6BB5\u540C\u65F6\u58F0\u660E\u2014\u2014\u5DF2\u53D6 router.${d}\uFF08#492 \u7EDF\u4E00\u8DEF\u7531\u7BA1\u7406\uFF1A\u5EFA\u8BAE\u5220\u9664\u9876\u5C42\u9057\u7559\u5199\u6CD5\uFF09`);
1106
+ for (const [name, modPath] of Object.entries(rc.customRoute.builders)) {
307
1107
  const abs = resolvePkgPath(projectRoot, modPath);
308
- if (!fs2.existsSync(abs)) {
1108
+ if (!fs4.existsSync(abs)) {
309
1109
  console.warn(`[mp-transform] \u9884\u8BBE builder ${name} \u4E0D\u5B58\u5728\uFF1A${modPath}`);
310
1110
  continue;
311
1111
  }
312
- const { code } = await esbuildTransform(fs2.readFileSync(abs, "utf-8"), { loader: "ts", charset: "utf8" });
1112
+ const { code } = await esbuildTransform(fs4.readFileSync(abs, "utf-8"), { loader: "ts", charset: "utf8" });
313
1113
  const fnName = extractBuilderFnName(code);
314
1114
  if (!fnName) {
315
1115
  console.warn(`[mp-transform] \u9884\u8BBE builder ${name} \u672A\u627E\u5230\u51FD\u6570\u58F0\u660E\uFF0C\u5DF2\u8DF3\u8FC7`);
@@ -320,24 +1120,63 @@ async function loadPresetBuilders(projectRoot, cfg) {
320
1120
  return presets;
321
1121
  }
322
1122
  function walkVueFiles(dir, acc = []) {
323
- if (!fs2.existsSync(dir)) return acc;
324
- for (const entry of fs2.readdirSync(dir, { withFileTypes: true })) {
1123
+ if (!fs4.existsSync(dir)) return acc;
1124
+ for (const entry of fs4.readdirSync(dir, { withFileTypes: true })) {
325
1125
  if (entry.name.startsWith(".")) continue;
326
- const full = path2.join(dir, entry.name);
1126
+ const full = path4.join(dir, entry.name);
327
1127
  if (entry.isDirectory()) walkVueFiles(full, acc);
328
1128
  else if (entry.name.endsWith(".vue")) acc.push(full);
329
1129
  }
330
1130
  return acc;
331
1131
  }
1132
+ function collectMpEntries(opts) {
1133
+ const { projectRoot, appDir, pagesDir, subPackages, componentsDir, webOnlyPages, onSkipWebOnly } = opts;
1134
+ const platform = opts.platform ?? "mp";
1135
+ const out = [];
1136
+ const pushRel = (dir, isComponent) => {
1137
+ for (const f of effectiveVariants2(walkVueFiles(dir), platform)) {
1138
+ if (webOnlyPages?.has(f)) {
1139
+ onSkipWebOnly?.(f);
1140
+ continue;
1141
+ }
1142
+ const relBase = path4.relative(appDir, splitVariant2(f).base).replace(/\\/g, "/");
1143
+ out.push({ file: f, rel: relBase.replace(/\.vue$/, ""), isComponent });
1144
+ }
1145
+ };
1146
+ pushRel(path4.join(projectRoot, pagesDir), false);
1147
+ for (const sp of subPackages) pushRel(path4.join(projectRoot, sp.root), false);
1148
+ pushRel(path4.join(appDir, "components"), true);
1149
+ for (const f of effectiveVariants2(walkVueFiles(componentsDir), platform)) {
1150
+ if (webOnlyPages?.has(f)) {
1151
+ onSkipWebOnly?.(f);
1152
+ continue;
1153
+ }
1154
+ const relIn = path4.relative(componentsDir, splitVariant2(f).base).replace(/\\/g, "/").replace(/\.vue$/, "");
1155
+ out.push({ file: f, rel: `proteus/${relIn}`, isComponent: true });
1156
+ }
1157
+ return out;
1158
+ }
1159
+ function resolveEffectiveSubPackages(cfg) {
1160
+ const { router: rc } = resolveRouterConfig(cfg);
1161
+ return rc.subPackages;
1162
+ }
332
1163
  function mpTransform(opts) {
333
1164
  const cfg = opts.config;
1165
+ const effectiveSubPackages = resolveEffectiveSubPackages(cfg);
334
1166
  const px2rpx = opts.px2rpx ?? cfg.style.px2rpx;
335
1167
  const rpxRatio = opts.rpxRatio ?? cfg.style.rpxRatio;
336
1168
  const rules = opts.rules ?? cfg.rules;
337
1169
  const autoScrollContainer = cfg.page?.autoScrollContainer ?? true;
1170
+ const renderer = cfg.skyline ? "skyline" : "webview";
1171
+ const fluidLayout = cfg.layout ? { designWidth: cfg.layout.designWidth, viewport: cfg.layout.fluidViewport } : void 0;
338
1172
  const isDebug = process.env.PROTEUS_DEBUG === "1";
339
1173
  let projectRoot = process.cwd();
340
1174
  const warningReport = [];
1175
+ const rustCompiler = process.env.PROTEUS_COMPILER === "rust" || cfg.compiler?.backend === "rust";
1176
+ const rustCliBin = rustCompiler ? resolveRustCliBin(projectRoot) : null;
1177
+ let dualOk = 0;
1178
+ let dualSkipped = 0;
1179
+ let dualSkippedReason = "";
341
1180
  return {
342
1181
  name: "vite-plugin-mp-transform",
343
1182
  enforce: "pre",
@@ -345,50 +1184,88 @@ function mpTransform(opts) {
345
1184
  projectRoot = resolved.root;
346
1185
  },
347
1186
  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$/, "") });
1187
+ const appDir = path4.join(projectRoot, path4.dirname(cfg.pagesDir));
1188
+ const compileCache = createCompileCache(path4.join(projectRoot, "node_modules", ".cache", "proteus", "compile"));
1189
+ const bundleCache = createBundleCache(path4.join(projectRoot, "node_modules", ".cache", "proteus", "bundle"));
1190
+ const webOnlyPages = /* @__PURE__ */ new Set();
1191
+ const detectWebOnly = (file) => {
1192
+ try {
1193
+ const src = fs4.readFileSync(file, "utf-8");
1194
+ const m = src.match(/<route>\s*([\s\S]*?)<\/route>/);
1195
+ if (!m) return;
1196
+ if (/"?webOnly"?\s*:\s*true/.test(m[1])) {
1197
+ webOnlyPages.add(file);
1198
+ return;
1199
+ }
1200
+ const pm = m[1].match(/"?platforms"?\s*:\s*(\[[^\]]*\])/);
1201
+ if (pm) {
1202
+ try {
1203
+ const arr = JSON.parse(pm[1]);
1204
+ if (Array.isArray(arr) && !arr.some((p) => typeof p === "string" && ["mp", "mp-weixin", "skyline"].includes(p))) {
1205
+ webOnlyPages.add(file);
1206
+ }
1207
+ } catch {
1208
+ }
1209
+ }
1210
+ } catch {
355
1211
  }
356
1212
  };
357
- pushRel(path2.join(projectRoot, cfg.pagesDir));
358
- for (const sp of cfg.subPackages ?? []) {
359
- pushRel(path2.join(projectRoot, sp.root));
1213
+ for (const pagesRoot of [path4.join(projectRoot, cfg.pagesDir), ...(effectiveSubPackages ?? []).map((sp) => path4.join(projectRoot, sp.root))]) {
1214
+ for (const f of walkVueFiles(pagesRoot)) detectWebOnly(f);
360
1215
  }
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}` });
1216
+ const frameworkComponents = opts.componentsDir ? path4.resolve(projectRoot, opts.componentsDir) : resolveComponentsRoot(projectRoot);
1217
+ const files = collectMpEntries({
1218
+ projectRoot,
1219
+ appDir,
1220
+ pagesDir: cfg.pagesDir,
1221
+ subPackages: effectiveSubPackages ?? [],
1222
+ componentsDir: frameworkComponents,
1223
+ webOnlyPages,
1224
+ onSkipWebOnly: (f) => console.log(`[mp-transform] \u8DF3\u8FC7 webOnly \u9875\u9762\uFF1A${path4.relative(projectRoot, f).replace(/\\/g, "/")}`)
1225
+ });
1226
+ const appUsesStore = files.some((f) => {
1227
+ try {
1228
+ const s = fs4.readFileSync(f.file, "utf-8");
1229
+ const script = s.includes("<script") ? s.match(/<script[^>]*>([\s\S]*?)<\/script>/i)?.[1] ?? "" : s;
1230
+ return /\buse[A-Z]\w*Store\s*\(/.test(script);
1231
+ } catch {
1232
+ return false;
368
1233
  }
369
- }
370
- const mpEntry = path2.join(appDir, "main.mp.ts");
371
- if (fs2.existsSync(mpEntry)) {
372
- const src = fs2.readFileSync(mpEntry, "utf-8");
1234
+ });
1235
+ const mpEntry = path4.join(appDir, "main.mp.ts");
1236
+ if (fs4.existsSync(mpEntry)) {
1237
+ const src = fs4.readFileSync(mpEntry, "utf-8");
373
1238
  const { code } = await esbuildTransform(src, { loader: "ts", charset: "utf8" });
374
1239
  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'");
1240
+ 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";
1241
+ const appJs = applyPlatformMacros(assembleAppJs(code, presets, piniaInstall).replace(/__PROTEUS_DEBUG__/g, isDebug ? "true" : "false").replace(/"worklet"/g, "'worklet'"), "mp", "code");
376
1242
  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"}`);
1243
+ console.log(`[mp-transform] app.js \u5DF2\u76F4\u51FA\uFF08${isDebug ? "debug" : "\u6B63\u5F0F"}\uFF09\uFF0C\u5185\u7F6E\u9884\u8BBE\uFF1A${presets.map((p) => p.name).join("/") || "\u65E0"}${appUsesStore ? "\uFF0CPinia \u5DF2\u5B89\u88C5" : ""}`);
1244
+ }
1245
+ {
1246
+ const explicit = cfg.globalStyle ? path4.resolve(projectRoot, cfg.globalStyle) : void 0;
1247
+ const candidates = [
1248
+ explicit,
1249
+ path4.join(appDir, "app.wxss"),
1250
+ path4.join(projectRoot, "app.wxss")
1251
+ ].filter((p) => Boolean(p));
1252
+ const globalStylePath = candidates.find((p) => fs4.existsSync(p));
1253
+ if (globalStylePath) {
1254
+ const raw = fs4.readFileSync(globalStylePath, "utf-8");
1255
+ const normalized = rewriteRootToPage(raw);
1256
+ const wxss = transformStyleToWxss(normalized, { px2rpx: cfg.style?.px2rpx ?? true, rpxRatio: cfg.style?.rpxRatio ?? 2, rules: cfg.rules });
1257
+ this.emitFile({ type: "asset", fileName: "app.wxss", source: wxss });
1258
+ console.log(`[mp-transform] app.wxss \u5DF2\u4EA7\u51FA\uFF08${path4.relative(projectRoot, globalStylePath).replace(/\\/g, "/")}\u2014\u2014\u5168\u5C40\u8BBE\u8BA1 token/\u91CD\u7F6E\uFF0C:root\u2192page\uFF09`);
1259
+ }
378
1260
  }
379
1261
  const moduleImportsByFile = /* @__PURE__ */ new Map();
380
1262
  const sharedModules = /* @__PURE__ */ new Set();
381
1263
  const sharedRelNoExt = /* @__PURE__ */ new Map();
382
- const resolveShared = (absFrom, source) => resolveSharedModule(appDir, absFrom, source, frameworkComponents);
1264
+ const resolveShared = (absFrom, source) => resolveSharedModule(appDir, absFrom, source, frameworkComponents, projectRoot, "mp");
383
1265
  const scanImports = (absFile) => {
384
- const src = fs2.readFileSync(absFile, "utf-8");
1266
+ const src = fs4.readFileSync(absFile, "utf-8");
385
1267
  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;
1268
+ return scanSourceImports(script);
392
1269
  };
393
1270
  for (const { file } of files) {
394
1271
  const list = [];
@@ -402,6 +1279,13 @@ function mpTransform(opts) {
402
1279
  }
403
1280
  if (list.length) moduleImportsByFile.set(file, list);
404
1281
  }
1282
+ if (appUsesStore) {
1283
+ const rt = resolveShared(mpEntry, "@proteus-vue/runtime");
1284
+ if (rt) {
1285
+ sharedModules.add(rt.file);
1286
+ sharedRelNoExt.set(rt.file, rt.relNoExt);
1287
+ }
1288
+ }
405
1289
  const pending = [...sharedModules];
406
1290
  while (pending.length) {
407
1291
  const cur = pending.pop();
@@ -414,7 +1298,7 @@ function mpTransform(opts) {
414
1298
  pending.push(resolved.file);
415
1299
  }
416
1300
  }
417
- const THIRD_PARTY_ALLOW = /* @__PURE__ */ new Set(["pinia", "vue", "vue-demi", "@vue/reactivity", "@vue/shared", "@vue/runtime-core"]);
1301
+ const THIRD_PARTY_ALLOW = /* @__PURE__ */ new Set([...VENDOR_SINGLETONS, "nostics", "vue-demi", "@vue/reactivity", "@vue/shared", "@vue/runtime-core"]);
418
1302
  const hasThirdParty = /* @__PURE__ */ new Set();
419
1303
  for (const sharedFile of sharedModules) {
420
1304
  for (const imp of scanImports(sharedFile)) {
@@ -439,6 +1323,30 @@ function mpTransform(opts) {
439
1323
  moduleImportsByFile.set(file, list.filter((item) => !skipShared.has(resolveShared(file, item.source)?.file ?? "")));
440
1324
  }
441
1325
  }
1326
+ const externalResolvePlugin = (relNoExt) => ({
1327
+ name: "proteus-pkg-require-path",
1328
+ setup(b) {
1329
+ const mapExternal = (target) => {
1330
+ const dir = path4.posix.dirname(relNoExt);
1331
+ let rel = path4.posix.relative(dir, target);
1332
+ if (!rel.startsWith(".")) rel = `./${rel}`;
1333
+ return { path: rel, external: true };
1334
+ };
1335
+ b.onResolve({ filter: /^@proteus-vue\// }, (args) => mapExternal(`_proteus/${args.path.replace("@proteus-vue/", "")}.js`));
1336
+ b.onResolve({ filter: new RegExp(`^(${VENDOR_SINGLETONS.join("|")})$`) }, (args) => mapExternal(`_proteus/${args.path}.js`));
1337
+ b.onLoad({ filter: /\.(md|txt|json)$/ }, (args) => ({
1338
+ contents: `export default ${JSON.stringify(fs4.readFileSync(args.path, "utf-8"))}`,
1339
+ loader: "js"
1340
+ }));
1341
+ b.onLoad({ filter: /\.(png|jpe?g|gif|webp|svg|ico|woff2?|ttf|eot|mp3|mp4|wav|zip)$/ }, (args) => ({
1342
+ errors: [
1343
+ {
1344
+ text: `MP \u4EA7\u7269\u4E0D\u652F\u6301\u4E8C\u8FDB\u5236\u8D44\u6E90 import\uFF1A${path4.relative(projectRoot, args.path)}\u2014\u2014\u8BF7\u6539\u7528\u7F51\u7EDC URL \u6216 base64 \u5185\u8054`
1345
+ }
1346
+ ]
1347
+ }));
1348
+ }
1349
+ });
442
1350
  const bundleCacheEnabled = !process.env.PROTEUS_NO_CACHE && !isDebug;
443
1351
  for (const sharedFile of sharedModules) {
444
1352
  if (skipShared.has(sharedFile)) continue;
@@ -446,7 +1354,7 @@ function mpTransform(opts) {
446
1354
  let code = "";
447
1355
  let bundleHit = false;
448
1356
  if (bundleCacheEnabled) {
449
- const bKey = bundleCacheKey(sharedFile, projectRoot);
1357
+ const bKey = bundleCacheKey(sharedFile, projectRoot) + `-sky${cfg.skyline ? 1 : 0}`;
450
1358
  const cachedBundle = bundleCache.get(bKey);
451
1359
  if (cachedBundle) {
452
1360
  code = cachedBundle.output;
@@ -465,24 +1373,25 @@ function mpTransform(opts) {
465
1373
  logLevel: "silent",
466
1374
  minify: true,
467
1375
  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
- ]
1376
+ // ★#495c define 注入:esbuild 直出资产不经 vite define——宏在此替换(config.skyline → __PROTEUS_SKYLINE__)
1377
+ // ★vendor 单例化(2026-09-12):pinia/vue 的 CJS 入口有 `process.env.NODE_ENV` 分支,小程序无 process
1378
+ // → 必须 define 掉(否则运行时崩/带 dev 分支体积);Vue flag 一并显式声明消除警告
1379
+ define: {
1380
+ __PROTEUS_DEBUG__: isDebug ? "true" : "false",
1381
+ __PROTEUS_SKYLINE__: cfg.skyline ? "true" : "false",
1382
+ // ★平台编译期宏(条件显隐):MP 共享 .ts 模块脚本内的 __MP__/__WEB__/__TARGET__ 在此替换
1383
+ ...platformDefines("mp"),
1384
+ "process.env.NODE_ENV": isDebug ? '"development"' : '"production"',
1385
+ __VUE_OPTIONS_API__: "true",
1386
+ __VUE_PROD_DEVTOOLS__: "false",
1387
+ __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: "false"
1388
+ },
1389
+ // ★external:@proteus-vue/* 与 vendor 单例(pinia/vue)→ 产物 _proteus/<name>.js
1390
+ // (微信 require 缓存同路径同实例 → 全产物共享同一份,杜绝重复内联导致的实例分裂)
1391
+ external: ["@proteus-vue/*", ...VENDOR_SINGLETONS],
1392
+ plugins: [externalResolvePlugin(relNoExt)]
484
1393
  });
485
- code = build.outputFiles[0]?.text ?? "";
1394
+ code = build.outputFiles?.[0]?.text ?? "";
486
1395
  if (!code) {
487
1396
  console.warn(`[mp-transform] \u5171\u4EAB\u6A21\u5757\u7F16\u8BD1\u5931\u8D25\uFF1A${relNoExt}`);
488
1397
  continue;
@@ -491,7 +1400,7 @@ function mpTransform(opts) {
491
1400
  const inputFiles = Object.keys(build.metafile.inputs);
492
1401
  const inputs = inputFiles.map((f) => {
493
1402
  try {
494
- const st = fs2.statSync(f);
1403
+ const st = fs4.statSync(f);
495
1404
  return { file: f, mtimeMs: st.mtimeMs, size: st.size };
496
1405
  } catch {
497
1406
  return null;
@@ -506,19 +1415,31 @@ function mpTransform(opts) {
506
1415
  for (const [file, list] of moduleImportsByFile) {
507
1416
  const entry = files.find((f) => f.file === file);
508
1417
  if (!entry) continue;
509
- const pageDir = path2.posix.dirname(entry.rel);
1418
+ const pageDir = path4.posix.dirname(entry.rel);
510
1419
  for (const item of list) {
511
1420
  const shared = resolveShared(file, item.source);
512
1421
  if (!shared) continue;
513
1422
  const sharedRel = `${shared.relNoExt}.js`;
514
- let rel = path2.posix.relative(pageDir, sharedRel);
1423
+ let rel = path4.posix.relative(pageDir, sharedRel);
515
1424
  if (!rel.startsWith(".")) rel = `./${rel}`;
516
1425
  item.requirePath = rel;
517
1426
  }
518
1427
  }
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}`);
1428
+ for (const { file, rel, isComponent } of files) {
1429
+ const source = fs4.readFileSync(file, "utf-8");
1430
+ if (rustCompiler) {
1431
+ const v = verifyDualCompilerEquivalence(source, { rustBin: rustCliBin, filename: file });
1432
+ if (v.status === "ok") {
1433
+ dualOk++;
1434
+ } else if (v.status === "skipped") {
1435
+ dualSkipped++;
1436
+ if (!dualSkippedReason) dualSkippedReason = v.reason ?? "";
1437
+ } else {
1438
+ throw new Error(`[mp-transform] G-29.1 \u53CC\u7F16\u8BD1\u8BED\u4E49\u4E0D\u7B49\u4EF7\uFF1A${rel}
1439
+ ${v.details.join("\n ")}\uFF08${v.reason}\uFF09\u2014\u2014\u4EA7\u7269\u672A\u751F\u6210\uFF1Bconfig.compiler.backend \u6539\u56DE 'node' \u53EF\u964D\u7EA7`);
1440
+ }
1441
+ }
1442
+ const pageRenderer = !isComponent && matchWebviewPage(cfg.page?.webviewPages, rel) ? "webview" : renderer;
522
1443
  const cacheEnabled = !process.env.PROTEUS_NO_CACHE && !isDebug;
523
1444
  let wxml;
524
1445
  let js;
@@ -539,7 +1460,10 @@ function mpTransform(opts) {
539
1460
  moduleImports: moduleImportsByFile.get(file),
540
1461
  annotateLines: isDebug,
541
1462
  debug: isDebug,
542
- autoScrollContainer
1463
+ autoScrollContainer,
1464
+ fluidLayout,
1465
+ renderer: pageRenderer,
1466
+ platform: "mp"
543
1467
  },
544
1468
  projectRoot
545
1469
  );
@@ -561,7 +1485,11 @@ function mpTransform(opts) {
561
1485
  annotateLines: isDebug,
562
1486
  debug: isDebug,
563
1487
  preprocessStyle,
564
- autoScrollContainer
1488
+ loadStyleSrc: (src) => loadStyleSrcWithVariant(src, file, "mp"),
1489
+ autoScrollContainer,
1490
+ fluidLayout,
1491
+ renderer: pageRenderer,
1492
+ platform: "mp"
565
1493
  });
566
1494
  wxml = result.wxml;
567
1495
  js = result.js;
@@ -582,7 +1510,11 @@ function mpTransform(opts) {
582
1510
  annotateLines: isDebug,
583
1511
  debug: isDebug,
584
1512
  preprocessStyle,
585
- autoScrollContainer
1513
+ loadStyleSrc: (src) => loadStyleSrcWithVariant(src, file, "mp"),
1514
+ autoScrollContainer,
1515
+ fluidLayout,
1516
+ renderer,
1517
+ platform: "mp"
586
1518
  });
587
1519
  wxml = result.wxml;
588
1520
  js = result.js;
@@ -594,8 +1526,9 @@ function mpTransform(opts) {
594
1526
  if (cached) {
595
1527
  console.log(`[mp-transform] \u7F16\u8BD1\u7F13\u5B58\u547D\u4E2D\uFF1A${rel}`);
596
1528
  }
597
- const jsWithMap = sourcemap && isDebug ? `${js}//# sourceMappingURL=${rel}.js.map
598
- ` : js;
1529
+ const jsFinal = rewriteFrameworkRequires(js, rel);
1530
+ const jsWithMap = sourcemap && isDebug ? `${jsFinal}//# sourceMappingURL=${rel}.js.map
1531
+ ` : jsFinal;
599
1532
  this.emitFile({ type: "asset", fileName: `${rel}.wxml`, source: wxml });
600
1533
  this.emitFile({ type: "asset", fileName: `${rel}.js`, source: jsWithMap });
601
1534
  this.emitFile({ type: "asset", fileName: `${rel}.wxss`, source: wxss });
@@ -612,6 +1545,31 @@ function mpTransform(opts) {
612
1545
  if (warnings.length) warningReport.push({ file: rel, warnings });
613
1546
  console.log(`[mp-transform] ${rel} \u2192 wxml/js/wxss \u5DF2\u8F93\u51FA`);
614
1547
  }
1548
+ {
1549
+ const publicDir = path4.join(projectRoot, "public");
1550
+ if (fs4.existsSync(publicDir)) {
1551
+ const rels = [];
1552
+ const walk = (dir) => {
1553
+ for (const e of fs4.readdirSync(dir, { withFileTypes: true })) {
1554
+ if (e.name.startsWith(".")) continue;
1555
+ const full = path4.join(dir, e.name);
1556
+ if (e.isDirectory()) walk(full);
1557
+ else rels.push(path4.relative(publicDir, full).replace(/\\/g, "/"));
1558
+ }
1559
+ };
1560
+ walk(publicDir);
1561
+ let assetN = 0;
1562
+ for (const { from, to } of mapPublicAssetVariants(rels, "mp")) {
1563
+ this.emitFile({ type: "asset", fileName: to, source: fs4.readFileSync(path4.join(publicDir, from)) });
1564
+ assetN++;
1565
+ }
1566
+ 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`);
1567
+ }
1568
+ }
1569
+ if (rustCompiler) {
1570
+ 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`);
1571
+ 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`);
1572
+ }
615
1573
  if (!process.env.PROTEUS_NO_CACHE && !isDebug) {
616
1574
  const st = compileCache.stats();
617
1575
  const bs = bundleCache.stats();
@@ -633,450 +1591,397 @@ function mpTransform(opts) {
633
1591
  };
634
1592
  }
635
1593
 
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
- }
733
- }
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);
765
- }
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(", ")}`);
1594
+ // src/devtools-plugin.ts
1595
+ import fs5 from "node:fs";
1596
+ import path5 from "node:path";
1597
+ import { createRequire as createRequire4 } from "node:module";
1598
+ import { WebSocketServer } from "ws";
1599
+
1600
+ // src/devtools-relay.ts
1601
+ function createProteusRelay() {
1602
+ const sources = /* @__PURE__ */ new Set();
1603
+ const panels = /* @__PURE__ */ new Set();
1604
+ const pending = /* @__PURE__ */ new Map();
1605
+ function onMessage(role, socket, raw) {
1606
+ let msg = null;
1607
+ try {
1608
+ msg = JSON.parse(raw);
1609
+ } catch {
1610
+ return;
793
1611
  }
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} },`);
1612
+ if (!msg || typeof msg !== "object") return;
1613
+ if (role === "source") {
1614
+ if (typeof msg.id === "number" && pending.has(msg.id)) {
1615
+ const target = pending.get(msg.id);
1616
+ pending.delete(msg.id);
1617
+ target.send(raw);
1618
+ return;
1619
+ }
1620
+ for (const p of panels) p.send(raw);
1621
+ return;
829
1622
  }
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`);
1623
+ if (typeof msg.id === "number" && sources.size > 0) {
1624
+ pending.set(msg.id, socket);
1625
+ const first = sources.values().next().value;
1626
+ first.send(raw);
841
1627
  }
842
- return "string";
843
1628
  }
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);
1629
+ function onClose(socket) {
1630
+ sources.delete(socket);
1631
+ panels.delete(socket);
1632
+ for (const entry of pending) {
1633
+ if (entry[1] === socket) pending.delete(entry[0]);
851
1634
  }
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;
1635
+ }
1636
+ return {
1637
+ handleConnection(role, socket) {
1638
+ ;
1639
+ (role === "source" ? sources : panels).add(socket);
1640
+ const withOn = socket;
1641
+ if (typeof withOn.on === "function") {
1642
+ withOn.on("message", (data) => onMessage(role, socket, String(data)));
1643
+ withOn.on("close", () => onClose(socket));
863
1644
  }
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 };
1645
+ },
1646
+ counts() {
1647
+ return { source: sources.size, panel: panels.size };
1648
+ },
1649
+ close() {
1650
+ for (const s of sources) s.close();
1651
+ for (const p of panels) p.close();
1652
+ sources.clear();
1653
+ panels.clear();
1654
+ pending.clear();
873
1655
  }
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 } };
1656
+ };
1657
+ }
1658
+
1659
+ // src/devtools-plugin.ts
1660
+ var require_ = createRequire4(import.meta.url);
1661
+ function isOriginAllowed(origin, allowFrom) {
1662
+ if (!allowFrom || allowFrom.length === 0) return true;
1663
+ if (!origin) return false;
1664
+ return allowFrom.indexOf(origin) >= 0;
1665
+ }
1666
+ function resolveDevtoolsDir() {
1667
+ return path5.dirname(require_.resolve("@proteus-vue/devtools/package.json"));
1668
+ }
1669
+ function createPanelPageHandler(devtoolsDir) {
1670
+ return (req, res) => {
1671
+ const pathname = (req.url ?? "/").split("?")[0];
1672
+ const base = "/proteus-devtools";
1673
+ if (pathname === base || pathname === base + "/") {
1674
+ const host = req.headers?.host ?? "localhost";
1675
+ const proto = req.headers?.["x-forwarded-proto"] === "https" ? "wss" : "ws";
1676
+ const html = fs5.readFileSync(path5.join(devtoolsDir, "panel.html"), "utf8").replace(/__PROTEUS_DEFAULT_WS__/g, `'${proto}://${host}/proteus-panel'`).replace("./style.css", base + "/style.css").replace("./dist/panel.js", base + "/panel.js");
1677
+ res.setHeader("content-type", "text/html; charset=utf-8");
1678
+ res.end(html);
1679
+ return true;
883
1680
  }
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`);
1681
+ if (pathname === base + "/style.css") {
1682
+ res.setHeader("content-type", "text/css; charset=utf-8");
1683
+ res.end(fs5.readFileSync(path5.join(devtoolsDir, "style.css")));
1684
+ return true;
890
1685
  }
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`);
1686
+ if (pathname === base + "/panel.js") {
1687
+ res.setHeader("content-type", "application/javascript; charset=utf-8");
1688
+ res.end(fs5.readFileSync(path5.join(devtoolsDir, "dist", "panel.js")));
1689
+ return true;
1690
+ }
1691
+ return false;
1692
+ };
1693
+ }
1694
+ function printPanelUrl(httpServer, logger, defaultPort = 5173) {
1695
+ if (!httpServer || typeof httpServer.once !== "function") return;
1696
+ httpServer.once("listening", () => {
1697
+ const addr = httpServer.address?.();
1698
+ const port = typeof addr === "object" && addr !== null ? addr.port : void 0;
1699
+ logger?.info(` \u279C Proteus DevTools: http://localhost:${port ?? defaultPort}/proteus-devtools`);
1700
+ });
1701
+ }
1702
+ function devtoolsRelayPlugin(opts = {}) {
1703
+ let wss = null;
1704
+ let relay = null;
1705
+ let pageHandler = null;
1706
+ const allowFrom = opts.allowFrom ?? [];
1707
+ function setup(server) {
1708
+ const httpServer = server.httpServer;
1709
+ if (!httpServer) return;
1710
+ relay = createProteusRelay();
1711
+ wss = new WebSocketServer({ noServer: true });
1712
+ httpServer.on("upgrade", (req, socket, head) => {
1713
+ const url = String(req.url ?? "").split("?")[0];
1714
+ const role = url === "/proteus-source" ? "source" : url === "/proteus-panel" ? "panel" : null;
1715
+ if (!role || !relay) return;
1716
+ const origin = req.headers?.origin;
1717
+ if (!isOriginAllowed(origin, allowFrom)) {
1718
+ console.warn(`[proteus-devtools] \u62D2\u7EDD ${role} \u8FDE\u63A5\uFF1AOrigin ${origin ?? "(\u65E0)"} \u4E0D\u5728\u767D\u540D\u5355 ${allowFrom.join(", ")}`);
1719
+ socket.destroy?.();
1720
+ return;
1721
+ }
1722
+ wss?.handleUpgrade(req, socket, head, (ws) => {
1723
+ wss?.emit("connection", ws, req);
1724
+ relay?.handleConnection(role, ws);
1725
+ });
1726
+ });
1727
+ pageHandler = createPanelPageHandler(resolveDevtoolsDir());
1728
+ const middlewares = server.middlewares;
1729
+ middlewares?.use?.((req, res, next) => {
1730
+ if (pageHandler && pageHandler(req, res)) return;
1731
+ next();
1732
+ });
894
1733
  }
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);
1734
+ return {
1735
+ name: "proteus-devtools-relay",
1736
+ apply: "serve",
1737
+ configureServer(server) {
1738
+ if (opts.enabled === false || relay) return;
1739
+ setup(server);
1740
+ const httpServer = server.httpServer;
1741
+ const logger = server.config?.logger;
1742
+ printPanelUrl(httpServer, logger);
1743
+ },
1744
+ configurePreviewServer(server) {
1745
+ if (opts.enabled === false || relay) return;
1746
+ setup(server);
1747
+ const httpServer = server.httpServer;
1748
+ const logger = server.config?.logger;
1749
+ printPanelUrl(httpServer, logger);
1750
+ },
1751
+ closeBundle() {
1752
+ wss?.close();
1753
+ relay?.close();
1754
+ wss = null;
1755
+ relay = null;
1756
+ pageHandler = null;
998
1757
  }
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;
1758
+ };
1759
+ }
1760
+
1761
+ // src/vite-config.ts
1762
+ import path6 from "node:path";
1763
+ import fs6 from "node:fs";
1764
+ import { pathToFileURL } from "node:url";
1765
+ import { createRequire as createRequire5 } from "node:module";
1766
+ import {
1767
+ platformDefines as platformDefines2,
1768
+ applyPlatformMacrosInSfc,
1769
+ resolvePlatformVariant as resolvePlatformVariant2,
1770
+ resolvePlatformVariantWithExts as resolvePlatformVariantWithExts2,
1771
+ splitVariant as splitVariant3,
1772
+ mapPublicAssetVariants as mapPublicAssetVariants2
1773
+ } from "@proteus-vue/compiler";
1774
+ function platformMacroPlugin(platform) {
1775
+ return {
1776
+ name: "proteus-platform-macros",
1777
+ enforce: "pre",
1778
+ transform(code, id) {
1779
+ if (!id.endsWith(".vue")) return null;
1780
+ const out = applyPlatformMacrosInSfc(code, platform);
1781
+ return out === code ? null : { code: out, map: null };
1782
+ }
1783
+ };
1784
+ }
1785
+ function platformVariantPlugin(root, platform) {
1786
+ const CODE_EXTS = [".ts", ".js", ".mjs", ".cjs", ".vue", ".json", ".css"];
1787
+ return {
1788
+ name: "proteus-platform-variant",
1789
+ enforce: "pre",
1790
+ resolveId(id, importer) {
1791
+ if (!importer || !id) return null;
1792
+ const qIdx = id.indexOf("?");
1793
+ const query = qIdx >= 0 ? id.slice(qIdx) : "";
1794
+ const bare = qIdx >= 0 ? id.slice(0, qIdx) : id;
1795
+ if (!bare) return null;
1796
+ let base;
1797
+ if (bare.startsWith(".")) base = path6.resolve(path6.dirname(importer), bare);
1798
+ else if (bare.startsWith("@/")) base = path6.resolve(root, "src", bare.slice(2));
1799
+ else return null;
1800
+ const hasExt = path6.extname(base) !== "";
1801
+ if (hasExt) {
1802
+ const r2 = resolvePlatformVariant2(base, platform, fs6.existsSync);
1803
+ return r2 && r2 !== base ? r2 + query : null;
1006
1804
  }
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;
1805
+ const r = resolvePlatformVariantWithExts2(base, CODE_EXTS, platform, fs6.existsSync);
1806
+ return r ? r + query : null;
1807
+ }
1808
+ };
1809
+ }
1810
+ function hasPublicVariants(publicDir) {
1811
+ if (!fs6.existsSync(publicDir)) return false;
1812
+ const walk = (dir) => {
1813
+ for (const e of fs6.readdirSync(dir, { withFileTypes: true })) {
1814
+ if (e.name.startsWith(".")) continue;
1815
+ const full = path6.join(dir, e.name);
1816
+ if (e.isDirectory()) {
1817
+ if (walk(full)) return true;
1818
+ } else if (splitVariant3(e.name).platform !== void 0) {
1819
+ return true;
1012
1820
  }
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
1821
  }
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";
1822
+ return false;
1823
+ };
1824
+ return walk(publicDir);
1825
+ }
1826
+ function platformPublicAssetsPlugin(root, platform) {
1827
+ return {
1828
+ name: "proteus-platform-public-assets",
1829
+ apply: "build",
1830
+ generateBundle() {
1831
+ const publicDir = path6.join(root, "public");
1832
+ if (!fs6.existsSync(publicDir)) return;
1833
+ const rels = [];
1834
+ const walk = (dir) => {
1835
+ for (const e of fs6.readdirSync(dir, { withFileTypes: true })) {
1836
+ if (e.name.startsWith(".")) continue;
1837
+ const full = path6.join(dir, e.name);
1838
+ if (e.isDirectory()) walk(full);
1839
+ else rels.push(path6.relative(publicDir, full).replace(/\\/g, "/"));
1840
+ }
1841
+ };
1842
+ walk(publicDir);
1843
+ for (const { from, to } of mapPublicAssetVariants2(rels, platform)) {
1844
+ this.emitFile({ type: "asset", fileName: to, source: fs6.readFileSync(path6.join(publicDir, from)) });
1023
1845
  }
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
1846
  }
1031
- console.log(`[gen-routes] \u5DF2\u751F\u6210 ${pages2.length} \u4E2A\u9875\u9762 page.json`);
1847
+ };
1848
+ }
1849
+ function routeBlocksPlugin() {
1850
+ return {
1851
+ name: "proteus-route-blocks",
1852
+ enforce: "pre",
1853
+ transform(code, id) {
1854
+ if (id.includes("?vue&type=route")) return { code: `export default ${code}`, map: null };
1855
+ return null;
1856
+ }
1857
+ };
1858
+ }
1859
+ function virtualMpEntryPlugin() {
1860
+ const VIRTUAL_ID = "\0proteus:mp-entry";
1861
+ return {
1862
+ name: "proteus-mp-entry",
1863
+ resolveId(id) {
1864
+ return id === "proteus:mp-entry" ? VIRTUAL_ID : null;
1865
+ },
1866
+ load(id) {
1867
+ return id === VIRTUAL_ID ? "export {}" : null;
1868
+ }
1869
+ };
1870
+ }
1871
+ async function importFromRoot(root, spec) {
1872
+ const req = createRequire5(path6.join(root, "package.json"));
1873
+ const resolved = req.resolve(spec);
1874
+ return import(pathToFileURL(resolved).href);
1875
+ }
1876
+ async function resolveProteusViteConfig(ctx, config) {
1877
+ const { root, command, mode } = ctx;
1878
+ const platform = mode === "mp-weixin" || mode === "web" ? mode : config.platform;
1879
+ const isMp = platform === "mp-weixin";
1880
+ const isDebug = process.env.PROTEUS_DEBUG === "1";
1881
+ let plugins;
1882
+ if (isMp) {
1883
+ plugins = [virtualMpEntryPlugin(), mpTransform({ config })];
1884
+ } else {
1885
+ const vueMod = await importFromRoot(root, "@vitejs/plugin-vue");
1886
+ const vue = vueMod.default({
1887
+ template: { compilerOptions: { isCustomElement: (tag) => MP_ONLY_TAGS.has(tag) } }
1888
+ });
1889
+ plugins = [platformVariantPlugin(root, "web"), vue, platformMacroPlugin("web"), platformPublicAssetsPlugin(root, "web"), routeBlocksPlugin()];
1032
1890
  }
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
- }
1891
+ const frameworkConfig = {
1892
+ configFile: false,
1893
+ // ★#418:vite 配置由本函数组装——不读 vite.config.ts(CLI 是唯一驱动)
1894
+ root,
1895
+ define: {
1896
+ // devtools 打通:dev serve 默认开启可观测;build 默认关闭零开销;PROTEUS_DEBUG=1 强制生产调试
1897
+ __PROTEUS_DEBUG__: command === "serve" || isDebug,
1898
+ // Skyline 开关注入:mp 构建时 __PROTEUS_SKYLINE__ = config.skyline
1899
+ __PROTEUS_SKYLINE__: isMp && config.skyline,
1900
+ // ★平台编译期宏(条件显隐)——Web .vue 走标准 @vitejs/plugin-vue:**vite define 对 .vue 不生效**
1901
+ // (实测:模板表达式/script __MP__ 残留),故 Web 端由 platformMacroPlugin(enforce:'pre'
1902
+ // 源码替换)处理,见 plugins。这里仍保留 define 供**非 .vue .ts/.js 模块**使用(同源取值)。
1903
+ ...platformDefines2(isMp ? "mp" : "web")
1904
+ },
1905
+ plugins,
1906
+ // ★平台变体·静态资源 Web 通道(第 3 层):public/ 含平台变体(logo.web.png)时,
1907
+ // 关掉 Vite 默认逐字拷贝(会把他端变体也拷进产物),改由 platformPublicAssetsPlugin web 解析;
1908
+ // 无变体时保持默认(零侵入,避免改变既有工程行为)。
1909
+ publicDir: hasPublicVariants(path6.join(root, "public")) ? false : void 0,
1910
+ resolve: {
1911
+ alias: [{ find: "@", replacement: path6.join(root, "src") }]
1912
+ },
1913
+ build: {
1914
+ target: "es2018",
1915
+ cssCodeSplit: false,
1916
+ minify: isMp ? false : void 0,
1917
+ outDir: path6.join(root, "dist", platform),
1918
+ emptyOutDir: !isMp,
1919
+ rollupOptions: isMp ? { input: "proteus:mp-entry", output: { entryFileNames: "mp-entry.js" } } : void 0
1052
1920
  }
1053
- if (count) console.log(`[gen-routes] \u5DF2\u751F\u6210 ${count} \u4E2A\u7EC4\u4EF6 component.json\uFF08component \u58F0\u660E + usingComponents \u5D4C\u5957\uFF09`);
1921
+ };
1922
+ const userVite = config.vite;
1923
+ let user;
1924
+ if (typeof userVite === "function") {
1925
+ user = await userVite({ command, mode });
1926
+ } else if (userVite && typeof userVite === "object") {
1927
+ user = userVite;
1054
1928
  }
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`);
1929
+ if (user) {
1930
+ const { plugins: userPlugins, resolve: userResolve, define: userDefine, build: userBuild, ...rest } = user;
1931
+ Object.assign(frameworkConfig, rest);
1932
+ if (userBuild) {
1933
+ const fwBuild = frameworkConfig.build ?? {};
1934
+ const merged = { ...fwBuild };
1935
+ const ub = userBuild;
1936
+ for (const k of Object.keys(ub)) {
1937
+ const uv = ub[k];
1938
+ if (k === "rollupOptions" && uv && typeof uv === "object") {
1939
+ const fwRo = fwBuild.rollupOptions ?? {};
1940
+ const uRo = uv;
1941
+ const ro = { ...fwRo };
1942
+ for (const rk of Object.keys(uRo)) {
1943
+ const rv = uRo[rk];
1944
+ if (rk === "output" && rv && typeof rv === "object" && fwRo.output && typeof fwRo.output === "object") {
1945
+ ro.output = { ...fwRo.output, ...rv };
1946
+ } else {
1947
+ ro[rk] = rv;
1948
+ }
1949
+ }
1950
+ merged.rollupOptions = ro;
1951
+ } else {
1952
+ merged[k] = uv;
1953
+ }
1954
+ }
1955
+ frameworkConfig.build = merged;
1956
+ }
1957
+ if (userResolve) {
1958
+ const baseAlias = frameworkConfig.resolve?.alias;
1959
+ frameworkConfig.resolve = {
1960
+ ...userResolve,
1961
+ alias: [...Array.isArray(baseAlias) ? baseAlias : [], ...Array.isArray(userResolve.alias) ? userResolve.alias : []]
1962
+ };
1963
+ }
1964
+ if (userDefine) {
1965
+ frameworkConfig.define = { ...frameworkConfig.define, ...userDefine };
1966
+ }
1967
+ if (userPlugins?.length) frameworkConfig.plugins = [...frameworkConfig.plugins ?? [], ...userPlugins];
1066
1968
  }
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`);
1969
+ return { config: frameworkConfig, needsGenRoutes: isMp, platform };
1077
1970
  }
1078
1971
  export {
1972
+ COMPONENTS_PKG,
1973
+ componentsRootExists,
1974
+ createPanelPageHandler,
1975
+ createProteusRelay,
1079
1976
  defaultScopedPlugin,
1977
+ devtoolsRelayPlugin,
1978
+ isOriginAllowed,
1080
1979
  mpTransform,
1980
+ printPanelUrl,
1981
+ resolveComponentsRoot,
1982
+ resolveDevtoolsDir,
1983
+ resolveProteusViteConfig,
1984
+ resolveSharedModule,
1985
+ rewriteRootToPage,
1081
1986
  runGenRoutes
1082
1987
  };