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