@proteus-vue/plugin-vite 0.2.0-beta.0 → 0.2.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +1560 -655
- package/dist/plugin.d.ts +73 -7
- package/dist/resolve-components.d.ts +10 -0
- package/dist/vite-config.d.ts +20 -0
- package/package.json +13 -11
package/dist/index.js
CHANGED
|
@@ -1,176 +1,901 @@
|
|
|
1
1
|
// src/plugin.ts
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import { createRequire as
|
|
2
|
+
import fs4 from "node:fs";
|
|
3
|
+
import path4 from "node:path";
|
|
4
|
+
import { createRequire as createRequire3 } from "node:module";
|
|
5
|
+
|
|
6
|
+
// ../types/src/router-config.ts
|
|
7
|
+
var DEFAULT_ROUTES_OUTPUT = "src/router/auto-routes.ts";
|
|
8
|
+
function resolveRouterConfig(config) {
|
|
9
|
+
const section = isObj(config) && isObj(config.router) ? config.router : {};
|
|
10
|
+
const cfg = isObj(config) ? config : {};
|
|
11
|
+
const duplicates = [];
|
|
12
|
+
let routesOutput;
|
|
13
|
+
if (section.routesOutput !== void 0) {
|
|
14
|
+
routesOutput = section.routesOutput;
|
|
15
|
+
if (cfg.routesOutput !== void 0) duplicates.push("routesOutput");
|
|
16
|
+
} else if (cfg.routesOutput !== void 0) {
|
|
17
|
+
routesOutput = cfg.routesOutput;
|
|
18
|
+
} else {
|
|
19
|
+
routesOutput = DEFAULT_ROUTES_OUTPUT;
|
|
20
|
+
}
|
|
21
|
+
let subPackages;
|
|
22
|
+
if (section.subPackages !== void 0) {
|
|
23
|
+
subPackages = section.subPackages;
|
|
24
|
+
if (cfg.subPackages !== void 0) duplicates.push("subPackages");
|
|
25
|
+
} else if (cfg.subPackages !== void 0) {
|
|
26
|
+
subPackages = cfg.subPackages;
|
|
27
|
+
} else {
|
|
28
|
+
subPackages = [];
|
|
29
|
+
}
|
|
30
|
+
let customRoute;
|
|
31
|
+
if (section.customRoute !== void 0) {
|
|
32
|
+
const cr = isObj(section.customRoute) ? section.customRoute : {};
|
|
33
|
+
customRoute = {
|
|
34
|
+
registerPresets: cr.registerPresets !== false,
|
|
35
|
+
builders: cr.builders ?? {}
|
|
36
|
+
};
|
|
37
|
+
if (cfg.customRoute !== void 0) duplicates.push("customRoute");
|
|
38
|
+
} else if (cfg.customRoute !== void 0) {
|
|
39
|
+
const cr = isObj(cfg.customRoute) ? cfg.customRoute : {};
|
|
40
|
+
customRoute = {
|
|
41
|
+
registerPresets: cr.registerPresets !== false,
|
|
42
|
+
builders: cr.builders ?? {}
|
|
43
|
+
};
|
|
44
|
+
} else {
|
|
45
|
+
customRoute = { registerPresets: true, builders: {} };
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
router: {
|
|
49
|
+
routesOutput,
|
|
50
|
+
subPackages,
|
|
51
|
+
customRoute,
|
|
52
|
+
tabBar: section.tabBar,
|
|
53
|
+
meta: section.meta
|
|
54
|
+
},
|
|
55
|
+
duplicates
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function isObj(v) {
|
|
59
|
+
return typeof v === "object" && v !== null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/plugin.ts
|
|
5
63
|
import { transform as esbuildTransform, build as esbuildBuild } from "esbuild";
|
|
6
64
|
import * as sass from "sass";
|
|
7
|
-
import {
|
|
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 extractTemplateBody(src) {
|
|
559
|
+
const n = src.length;
|
|
560
|
+
let i = 0;
|
|
561
|
+
while (i < n) {
|
|
562
|
+
const lt = src.indexOf("<", i);
|
|
563
|
+
if (lt < 0) return "";
|
|
564
|
+
if (src.startsWith("<!--", lt)) {
|
|
565
|
+
const e = src.indexOf("-->", lt + 4);
|
|
566
|
+
i = e < 0 ? n : e + 3;
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
const block = /^<(script|style)\b/i.exec(src.slice(lt, lt + 32));
|
|
570
|
+
if (block) {
|
|
571
|
+
const tag = block[1].toLowerCase();
|
|
572
|
+
const end = src.toLowerCase().indexOf(`</${tag}`, lt + block[0].length);
|
|
573
|
+
if (end < 0) return "";
|
|
574
|
+
const gt = src.indexOf(">", end);
|
|
575
|
+
i = gt < 0 ? n : gt + 1;
|
|
576
|
+
continue;
|
|
577
|
+
}
|
|
578
|
+
if (/^<template[\s>]/i.test(src.slice(lt, lt + 16))) {
|
|
579
|
+
const gt = src.indexOf(">", lt);
|
|
580
|
+
if (gt < 0) return "";
|
|
581
|
+
let depth = 1;
|
|
582
|
+
let j = gt + 1;
|
|
583
|
+
while (j < n) {
|
|
584
|
+
const l2 = src.indexOf("<", j);
|
|
585
|
+
if (l2 < 0) return src.slice(gt + 1);
|
|
586
|
+
if (src.startsWith("<!--", l2)) {
|
|
587
|
+
const e = src.indexOf("-->", l2 + 4);
|
|
588
|
+
j = e < 0 ? n : e + 3;
|
|
589
|
+
continue;
|
|
590
|
+
}
|
|
591
|
+
const seg = src.slice(l2, l2 + 16);
|
|
592
|
+
if (/^<\/template[\s>]/i.test(seg)) {
|
|
593
|
+
depth--;
|
|
594
|
+
if (depth === 0) return src.slice(gt + 1, l2);
|
|
595
|
+
j = l2 + "</template>".length;
|
|
596
|
+
continue;
|
|
597
|
+
}
|
|
598
|
+
if (/^<template[\s>]/i.test(seg)) {
|
|
599
|
+
depth++;
|
|
600
|
+
const g2 = src.indexOf(">", l2);
|
|
601
|
+
j = g2 < 0 ? n : g2 + 1;
|
|
602
|
+
continue;
|
|
603
|
+
}
|
|
604
|
+
j = l2 + 1;
|
|
605
|
+
}
|
|
606
|
+
return src.slice(gt + 1);
|
|
607
|
+
}
|
|
608
|
+
i = lt + 1;
|
|
609
|
+
}
|
|
610
|
+
return "";
|
|
611
|
+
}
|
|
612
|
+
function collectComponents(file, skipSemantic = false) {
|
|
613
|
+
const src = fs2.readFileSync(file, "utf-8");
|
|
614
|
+
const tpl = extractTemplateBody(src);
|
|
615
|
+
const customTags = new Set(Object.keys(config.rules?.customTags ?? {}));
|
|
616
|
+
const gridRuleDisabled = (config.rules?.disabled ?? []).includes("fluid/semantic-grid");
|
|
617
|
+
const semanticTags = skipSemantic && !gridRuleDisabled ? /* @__PURE__ */ new Set(["p-grid"]) : /* @__PURE__ */ new Set();
|
|
618
|
+
const used = /* @__PURE__ */ new Set();
|
|
619
|
+
let idx = 0;
|
|
620
|
+
while (idx < tpl.length) {
|
|
621
|
+
const lt = tpl.indexOf("<", idx);
|
|
622
|
+
if (lt < 0) break;
|
|
623
|
+
if (tpl.startsWith("<!--", lt)) {
|
|
624
|
+
const e = tpl.indexOf("-->", lt + 4);
|
|
625
|
+
idx = e < 0 ? tpl.length : e + 3;
|
|
626
|
+
continue;
|
|
627
|
+
}
|
|
628
|
+
if (tpl.startsWith("</", lt)) {
|
|
629
|
+
idx = lt + 2;
|
|
630
|
+
continue;
|
|
631
|
+
}
|
|
632
|
+
const mm = /^([A-Za-z][\w-]*)/.exec(tpl.slice(lt + 1));
|
|
633
|
+
if (!mm) {
|
|
634
|
+
idx = lt + 1;
|
|
635
|
+
continue;
|
|
636
|
+
}
|
|
637
|
+
const tagRaw = mm[1];
|
|
638
|
+
const tag = /[A-Z]/.test(tagRaw) ? tagRaw.replace(/\B([A-Z])/g, "-$1").toLowerCase() : tagRaw;
|
|
639
|
+
if (!(NATIVE_MP_TAGS.has(tag) || HTML_TAGS.has(tag) || customTags.has(tag) || semanticTags.has(tag))) used.add(tag);
|
|
640
|
+
idx = lt + 1 + mm[0].length;
|
|
641
|
+
}
|
|
642
|
+
if (/<(?:svg|circle|rect|ellipse|path|line|polyline|polygon)[\s>][\s\S]*?<animate\b/i.test(tpl)) {
|
|
643
|
+
const shapeAnim = /<animate\s[^>]*attributeName\s*=\s*["'](cx|cy|r|rx|ry|x|y|width|height|d|points|stroke-dashoffset|stroke-dasharray)["']/i;
|
|
644
|
+
if (shapeAnim.test(tpl)) used.add("p-svg-canvas");
|
|
645
|
+
}
|
|
646
|
+
const out = {};
|
|
647
|
+
for (const tag of used) {
|
|
648
|
+
const appCandidates = [path2.join(APP_DIR, "components", tag, "index.vue"), path2.join(APP_DIR, "components", `${tag}.vue`)];
|
|
649
|
+
const appFound = appCandidates.find((c) => fs2.existsSync(c));
|
|
650
|
+
if (appFound) {
|
|
651
|
+
out[tag] = `/components/${tag}/index`;
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
654
|
+
const fwCandidates = [path2.join(FW_COMPONENTS, tag, "index.vue"), path2.join(FW_COMPONENTS, `${tag}.vue`)];
|
|
655
|
+
const fwFound = fwCandidates.find((c) => fs2.existsSync(c));
|
|
656
|
+
if (fwFound) {
|
|
657
|
+
out[tag] = `/proteus/${tag}/index`;
|
|
658
|
+
continue;
|
|
659
|
+
}
|
|
660
|
+
console.warn(`[gen-routes] ${file} \u4F7F\u7528\u4E86\u7EC4\u4EF6 <${tag}>\uFF0C\u4F46\u672A\u627E\u5230 ${appCandidates.join(" \u6216 ")} \u6216\u6846\u67B6\u7EC4\u4EF6 ${fwCandidates.join(" \u6216 ")}`);
|
|
661
|
+
}
|
|
662
|
+
return out;
|
|
663
|
+
}
|
|
664
|
+
function writePageJsons(pages2) {
|
|
665
|
+
for (const p of pages2) {
|
|
666
|
+
const pageJson = {};
|
|
667
|
+
if (config.skyline && !matchWebviewPage(config.page?.webviewPages, p.relSrc, p.relInSub, p.mpPath)) {
|
|
668
|
+
pageJson.renderer = "skyline";
|
|
669
|
+
pageJson.componentFramework = "glass-easel";
|
|
670
|
+
}
|
|
671
|
+
if (p.pageJson) Object.assign(pageJson, p.pageJson);
|
|
672
|
+
const components = collectComponents(p.file, true);
|
|
673
|
+
if (Object.keys(components).length) pageJson.usingComponents = components;
|
|
674
|
+
const outFile = path2.join(OUT_DIR, p.mpPath + ".json");
|
|
675
|
+
fs2.mkdirSync(path2.dirname(outFile), { recursive: true });
|
|
676
|
+
fs2.writeFileSync(outFile, JSON.stringify(pageJson, null, 2) + "\n");
|
|
677
|
+
}
|
|
678
|
+
console.log(`[gen-routes] \u5DF2\u751F\u6210 ${pages2.length} \u4E2A\u9875\u9762 page.json`);
|
|
679
|
+
}
|
|
680
|
+
function writeComponentJsons() {
|
|
681
|
+
const roots = [
|
|
682
|
+
{ dir: path2.join(APP_DIR, "components"), prefix: "components" },
|
|
683
|
+
{ dir: FW_COMPONENTS, prefix: "proteus" }
|
|
684
|
+
];
|
|
685
|
+
let count = 0;
|
|
686
|
+
for (const { dir, prefix } of roots) {
|
|
687
|
+
if (!fs2.existsSync(dir)) continue;
|
|
688
|
+
for (const f of walkVueFiles2(dir)) {
|
|
689
|
+
const rel = path2.relative(dir, f).replace(/\\/g, "/").replace(/\.vue$/, "");
|
|
690
|
+
const comps = collectComponents(f);
|
|
691
|
+
const outFile = path2.join(OUT_DIR, prefix, `${rel}.json`);
|
|
692
|
+
fs2.mkdirSync(path2.dirname(outFile), { recursive: true });
|
|
693
|
+
const json = { component: true };
|
|
694
|
+
if (config.skyline) json.componentFramework = "glass-easel";
|
|
695
|
+
json.styleIsolation = "apply-shared";
|
|
696
|
+
if (Object.keys(comps).length) json.usingComponents = comps;
|
|
697
|
+
fs2.writeFileSync(outFile, JSON.stringify(json, null, 2) + "\n");
|
|
698
|
+
count++;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
if (count) console.log(`[gen-routes] \u5DF2\u751F\u6210 ${count} \u4E2A\u7EC4\u4EF6 component.json\uFF08component \u58F0\u660E + usingComponents \u5D4C\u5957\uFF09`);
|
|
702
|
+
}
|
|
703
|
+
function writeProjectConfig() {
|
|
704
|
+
const projectName = path2.basename(ROOT).replace(/[^\w.-]/g, "-");
|
|
705
|
+
const projectConfig = {
|
|
706
|
+
compileType: "miniprogram",
|
|
707
|
+
appid: config.appid,
|
|
708
|
+
projectname: projectName,
|
|
709
|
+
// ★2026-09-09 真机复测实证:skyline 项目需 IDE 级 skylineRenderEnable 开关,否则模拟器回落 WebView
|
|
710
|
+
// (getSkylineInfoSync().isSupported=false / reason=a-b test not enabled——产物 json 声明 renderer:skyline 不够)。
|
|
711
|
+
// 写入 project.config.json(非 private——private 每次重建被清,实测开关丢失后复测全是 WebView 假绿/假红)。
|
|
712
|
+
setting: { minifyWXML: true, urlCheck: false, ...config.skyline ? { skylineRenderEnable: true } : {} }
|
|
713
|
+
};
|
|
714
|
+
fs2.mkdirSync(OUT_DIR, { recursive: true });
|
|
715
|
+
fs2.writeFileSync(path2.join(OUT_DIR, "project.config.json"), JSON.stringify(projectConfig, null, 2) + "\n");
|
|
716
|
+
console.log(`[gen-routes] \u5DF2\u751F\u6210 dist/mp-weixin/project.config.json\uFF08appid=${config.appid}\uFF0Cprojectname=${projectName}\uFF09`);
|
|
717
|
+
}
|
|
718
|
+
fs2.rmSync(OUT_DIR, { recursive: true, force: true });
|
|
719
|
+
const pages = scanPages();
|
|
720
|
+
const routes = buildRoutes(pages);
|
|
721
|
+
validate(pages, routes);
|
|
722
|
+
writeAutoRoutes(routes);
|
|
723
|
+
writeAppJson(pages, routes);
|
|
724
|
+
writePageJsons(pages);
|
|
725
|
+
writeComponentJsons();
|
|
726
|
+
writeProjectConfig();
|
|
727
|
+
console.log(`[gen-routes] \u5B8C\u6210\uFF1A\u5171 ${pages.length} \u4E2A\u9875\u9762`);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// src/appSkeleton.ts
|
|
731
|
+
var APP_LAUNCH_SKELETON = `App({
|
|
732
|
+
onLaunch() {
|
|
733
|
+
// \u5168\u94FE\u8DEF\u8C03\u8BD5\u5F00\u5173\uFF08PROTEUS_DEBUG=1 \u6784\u5EFA\u65F6\u7531\u63D2\u4EF6\u66FF\u6362\u4E3A true\uFF09
|
|
734
|
+
const debug = typeof __PROTEUS_DEBUG__ !== 'undefined' && __PROTEUS_DEBUG__
|
|
735
|
+
if (debug) console.log('[proteus][app] \u542F\u52A8', Date.now())
|
|
736
|
+
// \u5168\u5C40\u9519\u8BEF\u6355\u83B7\uFF08debug \u6784\u5EFA\u8F93\u51FA\uFF0C\u6B63\u5F0F\u6784\u5EFA\u5E38\u91CF\u6298\u53E0\u96F6\u6B8B\u7559\uFF09
|
|
737
|
+
if (typeof wx !== 'undefined' && wx.onError) {
|
|
738
|
+
wx.onError(function (err) {
|
|
739
|
+
if (debug) console.error('[proteus][error]', err, Date.now())
|
|
740
|
+
})
|
|
741
|
+
}
|
|
742
|
+
// \u2605Pinia \u5B89\u88C5\uFF08\u4EC5\u9875\u9762\u4F7F\u7528 store \u65F6\u6CE8\u5165\uFF0C\u5426\u5219\u6B64\u884C\u4E3A\u6CE8\u91CA\uFF09\uFF1A
|
|
743
|
+
// \u5C0F\u7A0B\u5E8F\u65E0 createApp \u5B9E\u4F8B \u2192 createMpPinia() \u5185\u90E8 setActivePinia\uFF0C\u9875\u9762 useStore() \u624D\u80FD\u89E3\u6790\uFF1B
|
|
744
|
+
// \u65F6\u5E8F\uFF1AonLaunch \u65E9\u4E8E\u4EFB\u4F55\u9875\u9762 onLoad \u2713
|
|
745
|
+
__PINIA_INSTALL__
|
|
746
|
+
// \u5185\u7F6E\u9884\u8BBE\u6CE8\u518C\uFF08\u540C\u6587\u4EF6\u9759\u6001\u53EF\u5206\u6790\uFF1A\u51FD\u6570\u5B9A\u4E49\u5728\u524D\u3001\u6CE8\u518C\u5728\u540E\uFF0C\u63D2\u4EF6\u5DF2\u4FDD\u8BC1\u987A\u5E8F\uFF09
|
|
747
|
+
if (typeof wx !== 'undefined' && wx.router) {
|
|
748
|
+
__PRESET_REGISTRATION__
|
|
749
|
+
}
|
|
750
|
+
},
|
|
751
|
+
// \u2605lifecycle-plan B4\uFF1AApp \u7EA7 onShow/onHide \u94A9\u5B50\uFF08\u8C03\u8BD5\u65E5\u5FD7\uFF1BWeb \u7AEF\u5BF9\u5E94 visibilitychange\uFF09
|
|
752
|
+
onShow() {
|
|
753
|
+
const debug = typeof __PROTEUS_DEBUG__ !== 'undefined' && __PROTEUS_DEBUG__
|
|
754
|
+
if (debug) console.log('[proteus][app] onShow', Date.now())
|
|
755
|
+
},
|
|
756
|
+
onHide() {
|
|
757
|
+
const debug = typeof __PROTEUS_DEBUG__ !== 'undefined' && __PROTEUS_DEBUG__
|
|
758
|
+
if (debug) console.log('[proteus][app] onHide', Date.now())
|
|
759
|
+
},
|
|
760
|
+
})
|
|
761
|
+
`;
|
|
762
|
+
|
|
763
|
+
// src/cache.ts
|
|
764
|
+
import fs3 from "node:fs";
|
|
765
|
+
import path3 from "node:path";
|
|
766
|
+
import crypto from "node:crypto";
|
|
767
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
768
|
+
function getCompilerVersion(projectRoot) {
|
|
769
|
+
try {
|
|
770
|
+
const require3 = createRequire2(path3.join(projectRoot, "package.json"));
|
|
771
|
+
const pkgJsonPath = require3.resolve("@proteus-vue/compiler/package.json");
|
|
772
|
+
const pkg = JSON.parse(fs3.readFileSync(pkgJsonPath, "utf-8"));
|
|
773
|
+
const dist = path3.join(path3.dirname(pkgJsonPath), pkg.main ?? "dist/index.js");
|
|
774
|
+
const h = crypto.createHash("sha1");
|
|
775
|
+
h.update(fs3.readFileSync(dist, "utf-8"));
|
|
776
|
+
return `${pkg.version}-${h.digest("hex").slice(0, 8)}`;
|
|
777
|
+
} catch {
|
|
778
|
+
return "unknown";
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
function getEsbuildVersion(projectRoot) {
|
|
782
|
+
try {
|
|
783
|
+
const require3 = createRequire2(path3.join(projectRoot, "package.json"));
|
|
784
|
+
const pkg = JSON.parse(fs3.readFileSync(require3.resolve("esbuild/package.json"), "utf-8"));
|
|
785
|
+
return pkg.version;
|
|
786
|
+
} catch {
|
|
787
|
+
return "unknown";
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
function compileCacheKey(source, options, projectRoot) {
|
|
791
|
+
const h = crypto.createHash("sha1");
|
|
792
|
+
h.update(source);
|
|
793
|
+
h.update("|");
|
|
794
|
+
h.update(JSON.stringify({ ...options, compilerVersion: getCompilerVersion(projectRoot) }));
|
|
795
|
+
return h.digest("hex");
|
|
796
|
+
}
|
|
797
|
+
function createCompileCache(cacheDir) {
|
|
798
|
+
fs3.mkdirSync(cacheDir, { recursive: true });
|
|
799
|
+
const memory = /* @__PURE__ */ new Map();
|
|
800
|
+
let hits = 0;
|
|
801
|
+
let misses = 0;
|
|
802
|
+
return {
|
|
803
|
+
get(key) {
|
|
804
|
+
const mem = memory.get(key);
|
|
805
|
+
if (mem) {
|
|
806
|
+
hits++;
|
|
807
|
+
return mem;
|
|
808
|
+
}
|
|
809
|
+
const file = path3.join(cacheDir, `${key}.json`);
|
|
810
|
+
if (fs3.existsSync(file)) {
|
|
811
|
+
try {
|
|
812
|
+
const entry = JSON.parse(fs3.readFileSync(file, "utf-8"));
|
|
813
|
+
memory.set(key, entry);
|
|
814
|
+
hits++;
|
|
815
|
+
return entry;
|
|
816
|
+
} catch {
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
misses++;
|
|
820
|
+
return null;
|
|
821
|
+
},
|
|
822
|
+
set(key, entry) {
|
|
823
|
+
memory.set(key, entry);
|
|
824
|
+
try {
|
|
825
|
+
fs3.writeFileSync(path3.join(cacheDir, `${key}.json`), JSON.stringify(entry));
|
|
826
|
+
} catch {
|
|
827
|
+
}
|
|
828
|
+
},
|
|
829
|
+
stats() {
|
|
830
|
+
return { hits, misses };
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
function bundleCacheKey(entryFile, projectRoot) {
|
|
835
|
+
const h = crypto.createHash("sha1");
|
|
836
|
+
h.update(entryFile);
|
|
837
|
+
h.update("|");
|
|
838
|
+
h.update(
|
|
839
|
+
JSON.stringify({
|
|
840
|
+
esbuild: getEsbuildVersion(projectRoot),
|
|
841
|
+
target: "es2018",
|
|
842
|
+
format: "cjs",
|
|
843
|
+
charset: "utf8",
|
|
844
|
+
minify: true,
|
|
845
|
+
external: ["@proteus-vue/*"]
|
|
846
|
+
})
|
|
847
|
+
);
|
|
848
|
+
return h.digest("hex");
|
|
849
|
+
}
|
|
850
|
+
function inputsValid(inputs) {
|
|
851
|
+
for (let i = 0; i < inputs.length; i++) {
|
|
852
|
+
try {
|
|
853
|
+
const st = fs3.statSync(inputs[i].file);
|
|
854
|
+
if (st.mtimeMs !== inputs[i].mtimeMs || st.size !== inputs[i].size) return false;
|
|
855
|
+
} catch {
|
|
856
|
+
return false;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
return true;
|
|
860
|
+
}
|
|
861
|
+
function createBundleCache(cacheDir) {
|
|
862
|
+
fs3.mkdirSync(cacheDir, { recursive: true });
|
|
863
|
+
const memory = /* @__PURE__ */ new Map();
|
|
864
|
+
let hits = 0;
|
|
865
|
+
let misses = 0;
|
|
866
|
+
return {
|
|
867
|
+
get(key) {
|
|
868
|
+
const mem = memory.get(key);
|
|
869
|
+
if (mem && inputsValid(mem.inputs)) {
|
|
870
|
+
hits++;
|
|
871
|
+
return mem;
|
|
872
|
+
}
|
|
873
|
+
const file = path3.join(cacheDir, `${key}.json`);
|
|
874
|
+
if (fs3.existsSync(file)) {
|
|
875
|
+
try {
|
|
876
|
+
const entry = JSON.parse(fs3.readFileSync(file, "utf-8"));
|
|
877
|
+
if (inputsValid(entry.inputs)) {
|
|
878
|
+
memory.set(key, entry);
|
|
879
|
+
hits++;
|
|
880
|
+
return entry;
|
|
881
|
+
}
|
|
882
|
+
} catch {
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
misses++;
|
|
886
|
+
return null;
|
|
887
|
+
},
|
|
888
|
+
set(key, entry) {
|
|
889
|
+
memory.set(key, entry);
|
|
890
|
+
try {
|
|
891
|
+
fs3.writeFileSync(path3.join(cacheDir, `${key}.json`), JSON.stringify(entry));
|
|
892
|
+
} catch {
|
|
893
|
+
}
|
|
894
|
+
},
|
|
895
|
+
stats() {
|
|
896
|
+
return { hits, misses };
|
|
897
|
+
}
|
|
898
|
+
};
|
|
174
899
|
}
|
|
175
900
|
|
|
176
901
|
// src/plugin.ts
|
|
@@ -189,6 +914,19 @@ var MP_TAG_MAP = {
|
|
|
189
914
|
navigator: "proteus-navigator",
|
|
190
915
|
picker: "proteus-picker"
|
|
191
916
|
};
|
|
917
|
+
var MP_ONLY_TAGS = /* @__PURE__ */ new Set([
|
|
918
|
+
"picker-view",
|
|
919
|
+
"picker-view-column",
|
|
920
|
+
"movable-view",
|
|
921
|
+
"movable-area",
|
|
922
|
+
"match-media",
|
|
923
|
+
"root-portal",
|
|
924
|
+
"page-container",
|
|
925
|
+
"share-element",
|
|
926
|
+
"keyboard-accessory",
|
|
927
|
+
"cover-view",
|
|
928
|
+
"cover-image"
|
|
929
|
+
]);
|
|
192
930
|
function defaultScopedPlugin() {
|
|
193
931
|
return {
|
|
194
932
|
name: "proteus-default-scoped",
|
|
@@ -209,22 +947,26 @@ function defaultScopedPlugin() {
|
|
|
209
947
|
out = out.replace(/<script([^>]*)>/, (m) => `${m}${importLine}`);
|
|
210
948
|
out = out.replace("</script>", `${hookCode}</script>`);
|
|
211
949
|
}
|
|
950
|
+
out = out.replace(/\sp-fluid=("[^"]*"|'[^']*')/g, (_m, q) => {
|
|
951
|
+
const inner = q.slice(1, -1).replace(/'/g, "\\'");
|
|
952
|
+
return ` v-p-fluid="'${inner}'"`;
|
|
953
|
+
});
|
|
212
954
|
return out === code ? null : { code: out, map: null };
|
|
213
955
|
}
|
|
214
956
|
};
|
|
215
957
|
}
|
|
216
|
-
var require2 =
|
|
958
|
+
var require2 = createRequire3(import.meta.url);
|
|
217
959
|
function resolvePkgPath(projectRoot, modPath) {
|
|
218
960
|
const m = modPath.match(/^node_modules\/((?:@[^/]+\/)?[^/]+)\/([\s\S]+)$/);
|
|
219
961
|
if (m) {
|
|
220
962
|
try {
|
|
221
|
-
const pkgRequire =
|
|
222
|
-
const pkgRoot =
|
|
223
|
-
return
|
|
963
|
+
const pkgRequire = createRequire3(path4.join(projectRoot, "package.json"));
|
|
964
|
+
const pkgRoot = path4.dirname(pkgRequire.resolve(`${m[1]}/package.json`));
|
|
965
|
+
return path4.join(pkgRoot, m[2]);
|
|
224
966
|
} catch {
|
|
225
967
|
}
|
|
226
968
|
}
|
|
227
|
-
return
|
|
969
|
+
return path4.join(projectRoot, modPath);
|
|
228
970
|
}
|
|
229
971
|
function preprocessStyle(lang, content) {
|
|
230
972
|
if (lang === "scss" || lang === "sass") {
|
|
@@ -241,42 +983,98 @@ function preprocessStyle(lang, content) {
|
|
|
241
983
|
}
|
|
242
984
|
return content;
|
|
243
985
|
}
|
|
244
|
-
function
|
|
986
|
+
function loadStyleSrcWithVariant(src, fromFilename, platform = "mp") {
|
|
987
|
+
const base = path4.resolve(path4.dirname(fromFilename), src);
|
|
988
|
+
const hit = resolvePlatformVariant(base, platform, fs4.existsSync);
|
|
989
|
+
if (!hit) return null;
|
|
990
|
+
try {
|
|
991
|
+
return fs4.readFileSync(hit, "utf-8");
|
|
992
|
+
} catch {
|
|
993
|
+
return null;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
var VENDOR_SINGLETONS = ["pinia", "vue", "@vue/devtools-api"];
|
|
997
|
+
function resolveSharedModule(appDir, absFrom, source, frameworkDir, resolveFrom, platform = "mp") {
|
|
245
998
|
if (source.startsWith("@proteus-vue/")) {
|
|
246
999
|
try {
|
|
247
|
-
const
|
|
248
|
-
const
|
|
249
|
-
|
|
1000
|
+
const resolver = resolveFrom ? createRequire3(path4.join(resolveFrom, "package.json")) : require2;
|
|
1001
|
+
const pkgRoot = path4.dirname(resolver.resolve(`${source}/package.json`));
|
|
1002
|
+
const entry = path4.join(pkgRoot, "dist", "index.js");
|
|
1003
|
+
if (!fs4.existsSync(entry)) return null;
|
|
250
1004
|
return { file: entry, relNoExt: `_proteus/${source.replace("@proteus-vue/", "")}` };
|
|
251
1005
|
} catch {
|
|
252
1006
|
return null;
|
|
253
1007
|
}
|
|
254
1008
|
}
|
|
1009
|
+
if (VENDOR_SINGLETONS.includes(source)) {
|
|
1010
|
+
const tryResolve = (base2) => {
|
|
1011
|
+
try {
|
|
1012
|
+
return createRequire3(base2).resolve(source);
|
|
1013
|
+
} catch {
|
|
1014
|
+
return null;
|
|
1015
|
+
}
|
|
1016
|
+
};
|
|
1017
|
+
const entry = tryResolve(path4.join(resolveFrom ?? appDir, "package.json")) ?? tryResolve(absFrom);
|
|
1018
|
+
if (!entry || !fs4.existsSync(entry)) return null;
|
|
1019
|
+
return { file: entry, relNoExt: `_proteus/${source}` };
|
|
1020
|
+
}
|
|
255
1021
|
if (!source.startsWith(".")) return null;
|
|
256
|
-
const base =
|
|
257
|
-
|
|
1022
|
+
const base = path4.resolve(path4.dirname(absFrom), source);
|
|
1023
|
+
const JS_EXTS = /* @__PURE__ */ new Set([".ts", ".js", ".mjs", ".cjs"]);
|
|
1024
|
+
const variantHit = resolvePlatformVariantWithExts(base, [...JS_EXTS], platform, (p) => {
|
|
1025
|
+
try {
|
|
1026
|
+
return fs4.statSync(p).isFile();
|
|
1027
|
+
} catch {
|
|
1028
|
+
return false;
|
|
1029
|
+
}
|
|
1030
|
+
});
|
|
1031
|
+
const candidates = variantHit ? [variantHit] : [base, `${base}.ts`, `${base}.js`, path4.join(base, "index.ts"), path4.join(base, "index.js")];
|
|
1032
|
+
for (const cand of candidates) {
|
|
258
1033
|
if (cand.endsWith(".vue")) continue;
|
|
1034
|
+
if (!JS_EXTS.has(path4.extname(cand).toLowerCase())) continue;
|
|
259
1035
|
let isFile = false;
|
|
260
1036
|
try {
|
|
261
|
-
isFile =
|
|
1037
|
+
isFile = fs4.statSync(cand).isFile();
|
|
262
1038
|
} catch {
|
|
263
1039
|
isFile = false;
|
|
264
1040
|
}
|
|
265
1041
|
if (isFile) {
|
|
266
|
-
let relNoExt =
|
|
267
|
-
if (relNoExt.startsWith("../") && frameworkDir && !
|
|
268
|
-
relNoExt = `proteus/${
|
|
1042
|
+
let relNoExt = path4.relative(appDir, cand).replace(/\\/g, "/").replace(/\.(ts|js)$/, "");
|
|
1043
|
+
if (relNoExt.startsWith("../") && frameworkDir && !path4.relative(frameworkDir, cand).startsWith("..")) {
|
|
1044
|
+
relNoExt = `proteus/${path4.relative(frameworkDir, cand).replace(/\\/g, "/").replace(/\.(ts|js)$/, "")}`;
|
|
269
1045
|
}
|
|
270
1046
|
return { file: cand, relNoExt };
|
|
271
1047
|
}
|
|
272
1048
|
}
|
|
273
1049
|
return null;
|
|
274
1050
|
}
|
|
1051
|
+
function rewriteRootToPage(css) {
|
|
1052
|
+
return css.replace(/(^|[},\s]):root\b/g, "$1page");
|
|
1053
|
+
}
|
|
1054
|
+
function rewriteFrameworkRequires(js, relOutput) {
|
|
1055
|
+
if (!js.includes("require('@proteus-vue/")) return js;
|
|
1056
|
+
const pageDir = path4.posix.dirname(relOutput);
|
|
1057
|
+
return js.replace(/require\('@proteus-vue\/([A-Za-z0-9_-]+)'\)/g, (m, name) => {
|
|
1058
|
+
const pkgRel = `_proteus/${name}.js`;
|
|
1059
|
+
let rel = path4.posix.relative(pageDir, pkgRel);
|
|
1060
|
+
if (!rel.startsWith(".")) rel = `./${rel}`;
|
|
1061
|
+
return `require('${rel}')`;
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
function scanSourceImports(source) {
|
|
1065
|
+
const out = [];
|
|
1066
|
+
const re = /import\s+(?:type\s+)?(?:[\s\S]*?)\s+from\s+['"]([^'"]+)['"]|import\s+['"]([^'"]+)['"]/g;
|
|
1067
|
+
for (const m of source.matchAll(re)) {
|
|
1068
|
+
const s = m[1] || m[2];
|
|
1069
|
+
if (s) out.push({ source: s, typeOnly: /import\s+type\s+/.test(m[0]) });
|
|
1070
|
+
}
|
|
1071
|
+
return out;
|
|
1072
|
+
}
|
|
275
1073
|
function extractBuilderFnName(code) {
|
|
276
1074
|
const m = code.match(/function\s+([A-Za-z_$][\w$]*)\s*\(/);
|
|
277
1075
|
return m ? m[1] : null;
|
|
278
1076
|
}
|
|
279
|
-
function assembleAppJs(mainCode, presets) {
|
|
1077
|
+
function assembleAppJs(mainCode, presets, piniaInstall = "") {
|
|
280
1078
|
const presetCode = presets.map((p) => p.source.trim()).join("\n\n");
|
|
281
1079
|
const custom = mainCode.trim();
|
|
282
1080
|
const registerLines = presets.map((p) => ` wx.router.addRouteBuilder('${p.name}', ${p.fnName})`);
|
|
@@ -291,7 +1089,7 @@ ${registerLines.join("\n")}
|
|
|
291
1089
|
${presetCode}${register}`;
|
|
292
1090
|
}
|
|
293
1091
|
const skeletonReg = presets.map((p) => ` wx.router.addRouteBuilder('${p.name}', ${p.fnName})`);
|
|
294
|
-
const skeleton = APP_LAUNCH_SKELETON.replace("__PRESET_REGISTRATION__", skeletonReg.join("\n") || " // \u65E0\u5185\u7F6E\u9884\u8BBE");
|
|
1092
|
+
const skeleton = APP_LAUNCH_SKELETON.replace("__PRESET_REGISTRATION__", skeletonReg.join("\n") || " // \u65E0\u5185\u7F6E\u9884\u8BBE").replace("__PINIA_INSTALL__", piniaInstall);
|
|
295
1093
|
return `${custom ? `${custom}
|
|
296
1094
|
|
|
297
1095
|
` : ""}${presetCode ? `${presetCode}
|
|
@@ -303,13 +1101,15 @@ function filterOverriddenPresets(mainCode, presets) {
|
|
|
303
1101
|
}
|
|
304
1102
|
async function loadPresetBuilders(projectRoot, cfg) {
|
|
305
1103
|
const presets = [];
|
|
306
|
-
|
|
1104
|
+
const { router: rc, duplicates } = resolveRouterConfig(cfg);
|
|
1105
|
+
for (const d of duplicates) console.warn(`[mp-transform] \u8DEF\u7531\u5B57\u6BB5 "${d}" \u5728\u9876\u5C42\u4E0E router \u6BB5\u540C\u65F6\u58F0\u660E\u2014\u2014\u5DF2\u53D6 router.${d}\uFF08#492 \u7EDF\u4E00\u8DEF\u7531\u7BA1\u7406\uFF1A\u5EFA\u8BAE\u5220\u9664\u9876\u5C42\u9057\u7559\u5199\u6CD5\uFF09`);
|
|
1106
|
+
for (const [name, modPath] of Object.entries(rc.customRoute.builders)) {
|
|
307
1107
|
const abs = resolvePkgPath(projectRoot, modPath);
|
|
308
|
-
if (!
|
|
1108
|
+
if (!fs4.existsSync(abs)) {
|
|
309
1109
|
console.warn(`[mp-transform] \u9884\u8BBE builder ${name} \u4E0D\u5B58\u5728\uFF1A${modPath}`);
|
|
310
1110
|
continue;
|
|
311
1111
|
}
|
|
312
|
-
const { code } = await esbuildTransform(
|
|
1112
|
+
const { code } = await esbuildTransform(fs4.readFileSync(abs, "utf-8"), { loader: "ts", charset: "utf8" });
|
|
313
1113
|
const fnName = extractBuilderFnName(code);
|
|
314
1114
|
if (!fnName) {
|
|
315
1115
|
console.warn(`[mp-transform] \u9884\u8BBE builder ${name} \u672A\u627E\u5230\u51FD\u6570\u58F0\u660E\uFF0C\u5DF2\u8DF3\u8FC7`);
|
|
@@ -320,24 +1120,63 @@ async function loadPresetBuilders(projectRoot, cfg) {
|
|
|
320
1120
|
return presets;
|
|
321
1121
|
}
|
|
322
1122
|
function walkVueFiles(dir, acc = []) {
|
|
323
|
-
if (!
|
|
324
|
-
for (const entry of
|
|
1123
|
+
if (!fs4.existsSync(dir)) return acc;
|
|
1124
|
+
for (const entry of fs4.readdirSync(dir, { withFileTypes: true })) {
|
|
325
1125
|
if (entry.name.startsWith(".")) continue;
|
|
326
|
-
const full =
|
|
1126
|
+
const full = path4.join(dir, entry.name);
|
|
327
1127
|
if (entry.isDirectory()) walkVueFiles(full, acc);
|
|
328
1128
|
else if (entry.name.endsWith(".vue")) acc.push(full);
|
|
329
1129
|
}
|
|
330
1130
|
return acc;
|
|
331
1131
|
}
|
|
1132
|
+
function collectMpEntries(opts) {
|
|
1133
|
+
const { projectRoot, appDir, pagesDir, subPackages, componentsDir, webOnlyPages, onSkipWebOnly } = opts;
|
|
1134
|
+
const platform = opts.platform ?? "mp";
|
|
1135
|
+
const out = [];
|
|
1136
|
+
const pushRel = (dir, isComponent) => {
|
|
1137
|
+
for (const f of effectiveVariants2(walkVueFiles(dir), platform)) {
|
|
1138
|
+
if (webOnlyPages?.has(f)) {
|
|
1139
|
+
onSkipWebOnly?.(f);
|
|
1140
|
+
continue;
|
|
1141
|
+
}
|
|
1142
|
+
const relBase = path4.relative(appDir, splitVariant2(f).base).replace(/\\/g, "/");
|
|
1143
|
+
out.push({ file: f, rel: relBase.replace(/\.vue$/, ""), isComponent });
|
|
1144
|
+
}
|
|
1145
|
+
};
|
|
1146
|
+
pushRel(path4.join(projectRoot, pagesDir), false);
|
|
1147
|
+
for (const sp of subPackages) pushRel(path4.join(projectRoot, sp.root), false);
|
|
1148
|
+
pushRel(path4.join(appDir, "components"), true);
|
|
1149
|
+
for (const f of effectiveVariants2(walkVueFiles(componentsDir), platform)) {
|
|
1150
|
+
if (webOnlyPages?.has(f)) {
|
|
1151
|
+
onSkipWebOnly?.(f);
|
|
1152
|
+
continue;
|
|
1153
|
+
}
|
|
1154
|
+
const relIn = path4.relative(componentsDir, splitVariant2(f).base).replace(/\\/g, "/").replace(/\.vue$/, "");
|
|
1155
|
+
out.push({ file: f, rel: `proteus/${relIn}`, isComponent: true });
|
|
1156
|
+
}
|
|
1157
|
+
return out;
|
|
1158
|
+
}
|
|
1159
|
+
function resolveEffectiveSubPackages(cfg) {
|
|
1160
|
+
const { router: rc } = resolveRouterConfig(cfg);
|
|
1161
|
+
return rc.subPackages;
|
|
1162
|
+
}
|
|
332
1163
|
function mpTransform(opts) {
|
|
333
1164
|
const cfg = opts.config;
|
|
1165
|
+
const effectiveSubPackages = resolveEffectiveSubPackages(cfg);
|
|
334
1166
|
const px2rpx = opts.px2rpx ?? cfg.style.px2rpx;
|
|
335
1167
|
const rpxRatio = opts.rpxRatio ?? cfg.style.rpxRatio;
|
|
336
1168
|
const rules = opts.rules ?? cfg.rules;
|
|
337
1169
|
const autoScrollContainer = cfg.page?.autoScrollContainer ?? true;
|
|
1170
|
+
const renderer = cfg.skyline ? "skyline" : "webview";
|
|
1171
|
+
const fluidLayout = cfg.layout ? { designWidth: cfg.layout.designWidth, viewport: cfg.layout.fluidViewport } : void 0;
|
|
338
1172
|
const isDebug = process.env.PROTEUS_DEBUG === "1";
|
|
339
1173
|
let projectRoot = process.cwd();
|
|
340
1174
|
const warningReport = [];
|
|
1175
|
+
const rustCompiler = process.env.PROTEUS_COMPILER === "rust" || cfg.compiler?.backend === "rust";
|
|
1176
|
+
const rustCliBin = rustCompiler ? resolveRustCliBin(projectRoot) : null;
|
|
1177
|
+
let dualOk = 0;
|
|
1178
|
+
let dualSkipped = 0;
|
|
1179
|
+
let dualSkippedReason = "";
|
|
341
1180
|
return {
|
|
342
1181
|
name: "vite-plugin-mp-transform",
|
|
343
1182
|
enforce: "pre",
|
|
@@ -345,50 +1184,88 @@ function mpTransform(opts) {
|
|
|
345
1184
|
projectRoot = resolved.root;
|
|
346
1185
|
},
|
|
347
1186
|
async buildStart() {
|
|
348
|
-
const appDir =
|
|
349
|
-
const compileCache = createCompileCache(
|
|
350
|
-
const bundleCache = createBundleCache(
|
|
351
|
-
const
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
|
|
1187
|
+
const appDir = path4.join(projectRoot, path4.dirname(cfg.pagesDir));
|
|
1188
|
+
const compileCache = createCompileCache(path4.join(projectRoot, "node_modules", ".cache", "proteus", "compile"));
|
|
1189
|
+
const bundleCache = createBundleCache(path4.join(projectRoot, "node_modules", ".cache", "proteus", "bundle"));
|
|
1190
|
+
const webOnlyPages = /* @__PURE__ */ new Set();
|
|
1191
|
+
const detectWebOnly = (file) => {
|
|
1192
|
+
try {
|
|
1193
|
+
const src = fs4.readFileSync(file, "utf-8");
|
|
1194
|
+
const m = src.match(/<route>\s*([\s\S]*?)<\/route>/);
|
|
1195
|
+
if (!m) return;
|
|
1196
|
+
if (/"?webOnly"?\s*:\s*true/.test(m[1])) {
|
|
1197
|
+
webOnlyPages.add(file);
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
const pm = m[1].match(/"?platforms"?\s*:\s*(\[[^\]]*\])/);
|
|
1201
|
+
if (pm) {
|
|
1202
|
+
try {
|
|
1203
|
+
const arr = JSON.parse(pm[1]);
|
|
1204
|
+
if (Array.isArray(arr) && !arr.some((p) => typeof p === "string" && ["mp", "mp-weixin", "skyline"].includes(p))) {
|
|
1205
|
+
webOnlyPages.add(file);
|
|
1206
|
+
}
|
|
1207
|
+
} catch {
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
} catch {
|
|
355
1211
|
}
|
|
356
1212
|
};
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
pushRel(path2.join(projectRoot, sp.root));
|
|
1213
|
+
for (const pagesRoot of [path4.join(projectRoot, cfg.pagesDir), ...(effectiveSubPackages ?? []).map((sp) => path4.join(projectRoot, sp.root))]) {
|
|
1214
|
+
for (const f of walkVueFiles(pagesRoot)) detectWebOnly(f);
|
|
360
1215
|
}
|
|
361
|
-
const
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
1216
|
+
const frameworkComponents = opts.componentsDir ? path4.resolve(projectRoot, opts.componentsDir) : resolveComponentsRoot(projectRoot);
|
|
1217
|
+
const files = collectMpEntries({
|
|
1218
|
+
projectRoot,
|
|
1219
|
+
appDir,
|
|
1220
|
+
pagesDir: cfg.pagesDir,
|
|
1221
|
+
subPackages: effectiveSubPackages ?? [],
|
|
1222
|
+
componentsDir: frameworkComponents,
|
|
1223
|
+
webOnlyPages,
|
|
1224
|
+
onSkipWebOnly: (f) => console.log(`[mp-transform] \u8DF3\u8FC7 webOnly \u9875\u9762\uFF1A${path4.relative(projectRoot, f).replace(/\\/g, "/")}`)
|
|
1225
|
+
});
|
|
1226
|
+
const appUsesStore = files.some((f) => {
|
|
1227
|
+
try {
|
|
1228
|
+
const s = fs4.readFileSync(f.file, "utf-8");
|
|
1229
|
+
const script = s.includes("<script") ? s.match(/<script[^>]*>([\s\S]*?)<\/script>/i)?.[1] ?? "" : s;
|
|
1230
|
+
return /\buse[A-Z]\w*Store\s*\(/.test(script);
|
|
1231
|
+
} catch {
|
|
1232
|
+
return false;
|
|
368
1233
|
}
|
|
369
|
-
}
|
|
370
|
-
const mpEntry =
|
|
371
|
-
if (
|
|
372
|
-
const src =
|
|
1234
|
+
});
|
|
1235
|
+
const mpEntry = path4.join(appDir, "main.mp.ts");
|
|
1236
|
+
if (fs4.existsSync(mpEntry)) {
|
|
1237
|
+
const src = fs4.readFileSync(mpEntry, "utf-8");
|
|
373
1238
|
const { code } = await esbuildTransform(src, { loader: "ts", charset: "utf8" });
|
|
374
1239
|
const presets = filterOverriddenPresets(code, await loadPresetBuilders(projectRoot, cfg));
|
|
375
|
-
const
|
|
1240
|
+
const piniaInstall = appUsesStore ? " // \u2605\u9875\u9762\u4F7F\u7528 store \u2192 \u5B89\u88C5\u5E76\u6FC0\u6D3B Pinia\uFF08\u5C0F\u7A0B\u5E8F\u65E0 createApp\uFF0C\u5FC5\u987B setActivePinia\uFF09\n var __proteusPiniaMod = require('./_proteus/runtime.js')\n if (__proteusPiniaMod && __proteusPiniaMod.createMpPinia) __proteusPiniaMod.createMpPinia()" : " // \uFF08\u672A\u68C0\u6D4B\u5230 store \u4F7F\u7528\u2014\u2014\u8DF3\u8FC7 Pinia \u5B89\u88C5\uFF09";
|
|
1241
|
+
const appJs = applyPlatformMacros(assembleAppJs(code, presets, piniaInstall).replace(/__PROTEUS_DEBUG__/g, isDebug ? "true" : "false").replace(/"worklet"/g, "'worklet'"), "mp", "code");
|
|
376
1242
|
this.emitFile({ type: "asset", fileName: "app.js", source: appJs });
|
|
377
|
-
console.log(`[mp-transform] app.js \u5DF2\u76F4\u51FA\uFF08${isDebug ? "debug" : "\u6B63\u5F0F"}\uFF09\uFF0C\u5185\u7F6E\u9884\u8BBE\uFF1A${presets.map((p) => p.name).join("/") || "\u65E0"}`);
|
|
1243
|
+
console.log(`[mp-transform] app.js \u5DF2\u76F4\u51FA\uFF08${isDebug ? "debug" : "\u6B63\u5F0F"}\uFF09\uFF0C\u5185\u7F6E\u9884\u8BBE\uFF1A${presets.map((p) => p.name).join("/") || "\u65E0"}${appUsesStore ? "\uFF0CPinia \u5DF2\u5B89\u88C5" : ""}`);
|
|
1244
|
+
}
|
|
1245
|
+
{
|
|
1246
|
+
const explicit = cfg.globalStyle ? path4.resolve(projectRoot, cfg.globalStyle) : void 0;
|
|
1247
|
+
const candidates = [
|
|
1248
|
+
explicit,
|
|
1249
|
+
path4.join(appDir, "app.wxss"),
|
|
1250
|
+
path4.join(projectRoot, "app.wxss")
|
|
1251
|
+
].filter((p) => Boolean(p));
|
|
1252
|
+
const globalStylePath = candidates.find((p) => fs4.existsSync(p));
|
|
1253
|
+
if (globalStylePath) {
|
|
1254
|
+
const raw = fs4.readFileSync(globalStylePath, "utf-8");
|
|
1255
|
+
const normalized = rewriteRootToPage(raw);
|
|
1256
|
+
const wxss = transformStyleToWxss(normalized, { px2rpx: cfg.style?.px2rpx ?? true, rpxRatio: cfg.style?.rpxRatio ?? 2, rules: cfg.rules });
|
|
1257
|
+
this.emitFile({ type: "asset", fileName: "app.wxss", source: wxss });
|
|
1258
|
+
console.log(`[mp-transform] app.wxss \u5DF2\u4EA7\u51FA\uFF08${path4.relative(projectRoot, globalStylePath).replace(/\\/g, "/")}\u2014\u2014\u5168\u5C40\u8BBE\u8BA1 token/\u91CD\u7F6E\uFF0C:root\u2192page\uFF09`);
|
|
1259
|
+
}
|
|
378
1260
|
}
|
|
379
1261
|
const moduleImportsByFile = /* @__PURE__ */ new Map();
|
|
380
1262
|
const sharedModules = /* @__PURE__ */ new Set();
|
|
381
1263
|
const sharedRelNoExt = /* @__PURE__ */ new Map();
|
|
382
|
-
const resolveShared = (absFrom, source) => resolveSharedModule(appDir, absFrom, source, frameworkComponents);
|
|
1264
|
+
const resolveShared = (absFrom, source) => resolveSharedModule(appDir, absFrom, source, frameworkComponents, projectRoot, "mp");
|
|
383
1265
|
const scanImports = (absFile) => {
|
|
384
|
-
const src =
|
|
1266
|
+
const src = fs4.readFileSync(absFile, "utf-8");
|
|
385
1267
|
const script = src.includes("<script") ? src.match(/<script[^>]*>([\s\S]*?)<\/script>/i)?.[1] ?? "" : src;
|
|
386
|
-
|
|
387
|
-
for (const m of script.matchAll(/import\s+(?:type\s+)?.*?from\s+['"]([^'"]+)['"]|import\s+['"]([^'"]+)['"]/gm)) {
|
|
388
|
-
const s = m[1] || m[2];
|
|
389
|
-
if (s) out.push({ source: s, typeOnly: m[0].includes("import type") });
|
|
390
|
-
}
|
|
391
|
-
return out;
|
|
1268
|
+
return scanSourceImports(script);
|
|
392
1269
|
};
|
|
393
1270
|
for (const { file } of files) {
|
|
394
1271
|
const list = [];
|
|
@@ -402,6 +1279,13 @@ function mpTransform(opts) {
|
|
|
402
1279
|
}
|
|
403
1280
|
if (list.length) moduleImportsByFile.set(file, list);
|
|
404
1281
|
}
|
|
1282
|
+
if (appUsesStore) {
|
|
1283
|
+
const rt = resolveShared(mpEntry, "@proteus-vue/runtime");
|
|
1284
|
+
if (rt) {
|
|
1285
|
+
sharedModules.add(rt.file);
|
|
1286
|
+
sharedRelNoExt.set(rt.file, rt.relNoExt);
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
405
1289
|
const pending = [...sharedModules];
|
|
406
1290
|
while (pending.length) {
|
|
407
1291
|
const cur = pending.pop();
|
|
@@ -414,7 +1298,7 @@ function mpTransform(opts) {
|
|
|
414
1298
|
pending.push(resolved.file);
|
|
415
1299
|
}
|
|
416
1300
|
}
|
|
417
|
-
const THIRD_PARTY_ALLOW = /* @__PURE__ */ new Set([
|
|
1301
|
+
const THIRD_PARTY_ALLOW = /* @__PURE__ */ new Set([...VENDOR_SINGLETONS, "nostics", "vue-demi", "@vue/reactivity", "@vue/shared", "@vue/runtime-core"]);
|
|
418
1302
|
const hasThirdParty = /* @__PURE__ */ new Set();
|
|
419
1303
|
for (const sharedFile of sharedModules) {
|
|
420
1304
|
for (const imp of scanImports(sharedFile)) {
|
|
@@ -439,6 +1323,30 @@ function mpTransform(opts) {
|
|
|
439
1323
|
moduleImportsByFile.set(file, list.filter((item) => !skipShared.has(resolveShared(file, item.source)?.file ?? "")));
|
|
440
1324
|
}
|
|
441
1325
|
}
|
|
1326
|
+
const externalResolvePlugin = (relNoExt) => ({
|
|
1327
|
+
name: "proteus-pkg-require-path",
|
|
1328
|
+
setup(b) {
|
|
1329
|
+
const mapExternal = (target) => {
|
|
1330
|
+
const dir = path4.posix.dirname(relNoExt);
|
|
1331
|
+
let rel = path4.posix.relative(dir, target);
|
|
1332
|
+
if (!rel.startsWith(".")) rel = `./${rel}`;
|
|
1333
|
+
return { path: rel, external: true };
|
|
1334
|
+
};
|
|
1335
|
+
b.onResolve({ filter: /^@proteus-vue\// }, (args) => mapExternal(`_proteus/${args.path.replace("@proteus-vue/", "")}.js`));
|
|
1336
|
+
b.onResolve({ filter: new RegExp(`^(${VENDOR_SINGLETONS.join("|")})$`) }, (args) => mapExternal(`_proteus/${args.path}.js`));
|
|
1337
|
+
b.onLoad({ filter: /\.(md|txt|json)$/ }, (args) => ({
|
|
1338
|
+
contents: `export default ${JSON.stringify(fs4.readFileSync(args.path, "utf-8"))}`,
|
|
1339
|
+
loader: "js"
|
|
1340
|
+
}));
|
|
1341
|
+
b.onLoad({ filter: /\.(png|jpe?g|gif|webp|svg|ico|woff2?|ttf|eot|mp3|mp4|wav|zip)$/ }, (args) => ({
|
|
1342
|
+
errors: [
|
|
1343
|
+
{
|
|
1344
|
+
text: `MP \u4EA7\u7269\u4E0D\u652F\u6301\u4E8C\u8FDB\u5236\u8D44\u6E90 import\uFF1A${path4.relative(projectRoot, args.path)}\u2014\u2014\u8BF7\u6539\u7528\u7F51\u7EDC URL \u6216 base64 \u5185\u8054`
|
|
1345
|
+
}
|
|
1346
|
+
]
|
|
1347
|
+
}));
|
|
1348
|
+
}
|
|
1349
|
+
});
|
|
442
1350
|
const bundleCacheEnabled = !process.env.PROTEUS_NO_CACHE && !isDebug;
|
|
443
1351
|
for (const sharedFile of sharedModules) {
|
|
444
1352
|
if (skipShared.has(sharedFile)) continue;
|
|
@@ -446,7 +1354,7 @@ function mpTransform(opts) {
|
|
|
446
1354
|
let code = "";
|
|
447
1355
|
let bundleHit = false;
|
|
448
1356
|
if (bundleCacheEnabled) {
|
|
449
|
-
const bKey = bundleCacheKey(sharedFile, projectRoot)
|
|
1357
|
+
const bKey = bundleCacheKey(sharedFile, projectRoot) + `-sky${cfg.skyline ? 1 : 0}`;
|
|
450
1358
|
const cachedBundle = bundleCache.get(bKey);
|
|
451
1359
|
if (cachedBundle) {
|
|
452
1360
|
code = cachedBundle.output;
|
|
@@ -465,24 +1373,25 @@ function mpTransform(opts) {
|
|
|
465
1373
|
logLevel: "silent",
|
|
466
1374
|
minify: true,
|
|
467
1375
|
metafile: true,
|
|
468
|
-
//
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
]
|
|
1376
|
+
// ★#495c define 注入:esbuild 直出资产不经 vite define——宏在此替换(config.skyline → __PROTEUS_SKYLINE__)
|
|
1377
|
+
// ★vendor 单例化(2026-09-12):pinia/vue 的 CJS 入口有 `process.env.NODE_ENV` 分支,小程序无 process
|
|
1378
|
+
// → 必须 define 掉(否则运行时崩/带 dev 分支体积);Vue flag 一并显式声明消除警告
|
|
1379
|
+
define: {
|
|
1380
|
+
__PROTEUS_DEBUG__: isDebug ? "true" : "false",
|
|
1381
|
+
__PROTEUS_SKYLINE__: cfg.skyline ? "true" : "false",
|
|
1382
|
+
// ★平台编译期宏(条件显隐):MP 共享 .ts 模块脚本内的 __MP__/__WEB__/__TARGET__ 在此替换
|
|
1383
|
+
...platformDefines("mp"),
|
|
1384
|
+
"process.env.NODE_ENV": isDebug ? '"development"' : '"production"',
|
|
1385
|
+
__VUE_OPTIONS_API__: "true",
|
|
1386
|
+
__VUE_PROD_DEVTOOLS__: "false",
|
|
1387
|
+
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: "false"
|
|
1388
|
+
},
|
|
1389
|
+
// ★external:@proteus-vue/* 与 vendor 单例(pinia/vue)→ 产物 _proteus/<name>.js
|
|
1390
|
+
// (微信 require 缓存同路径同实例 → 全产物共享同一份,杜绝重复内联导致的实例分裂)
|
|
1391
|
+
external: ["@proteus-vue/*", ...VENDOR_SINGLETONS],
|
|
1392
|
+
plugins: [externalResolvePlugin(relNoExt)]
|
|
484
1393
|
});
|
|
485
|
-
code = build.outputFiles[0]?.text ?? "";
|
|
1394
|
+
code = build.outputFiles?.[0]?.text ?? "";
|
|
486
1395
|
if (!code) {
|
|
487
1396
|
console.warn(`[mp-transform] \u5171\u4EAB\u6A21\u5757\u7F16\u8BD1\u5931\u8D25\uFF1A${relNoExt}`);
|
|
488
1397
|
continue;
|
|
@@ -491,7 +1400,7 @@ function mpTransform(opts) {
|
|
|
491
1400
|
const inputFiles = Object.keys(build.metafile.inputs);
|
|
492
1401
|
const inputs = inputFiles.map((f) => {
|
|
493
1402
|
try {
|
|
494
|
-
const st =
|
|
1403
|
+
const st = fs4.statSync(f);
|
|
495
1404
|
return { file: f, mtimeMs: st.mtimeMs, size: st.size };
|
|
496
1405
|
} catch {
|
|
497
1406
|
return null;
|
|
@@ -506,19 +1415,31 @@ function mpTransform(opts) {
|
|
|
506
1415
|
for (const [file, list] of moduleImportsByFile) {
|
|
507
1416
|
const entry = files.find((f) => f.file === file);
|
|
508
1417
|
if (!entry) continue;
|
|
509
|
-
const pageDir =
|
|
1418
|
+
const pageDir = path4.posix.dirname(entry.rel);
|
|
510
1419
|
for (const item of list) {
|
|
511
1420
|
const shared = resolveShared(file, item.source);
|
|
512
1421
|
if (!shared) continue;
|
|
513
1422
|
const sharedRel = `${shared.relNoExt}.js`;
|
|
514
|
-
let rel =
|
|
1423
|
+
let rel = path4.posix.relative(pageDir, sharedRel);
|
|
515
1424
|
if (!rel.startsWith(".")) rel = `./${rel}`;
|
|
516
1425
|
item.requirePath = rel;
|
|
517
1426
|
}
|
|
518
1427
|
}
|
|
519
|
-
for (const { file, rel } of files) {
|
|
520
|
-
const source =
|
|
521
|
-
|
|
1428
|
+
for (const { file, rel, isComponent } of files) {
|
|
1429
|
+
const source = fs4.readFileSync(file, "utf-8");
|
|
1430
|
+
if (rustCompiler) {
|
|
1431
|
+
const v = verifyDualCompilerEquivalence(source, { rustBin: rustCliBin, filename: file });
|
|
1432
|
+
if (v.status === "ok") {
|
|
1433
|
+
dualOk++;
|
|
1434
|
+
} else if (v.status === "skipped") {
|
|
1435
|
+
dualSkipped++;
|
|
1436
|
+
if (!dualSkippedReason) dualSkippedReason = v.reason ?? "";
|
|
1437
|
+
} else {
|
|
1438
|
+
throw new Error(`[mp-transform] G-29.1 \u53CC\u7F16\u8BD1\u8BED\u4E49\u4E0D\u7B49\u4EF7\uFF1A${rel}
|
|
1439
|
+
${v.details.join("\n ")}\uFF08${v.reason}\uFF09\u2014\u2014\u4EA7\u7269\u672A\u751F\u6210\uFF1Bconfig.compiler.backend \u6539\u56DE 'node' \u53EF\u964D\u7EA7`);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
const pageRenderer = !isComponent && matchWebviewPage(cfg.page?.webviewPages, rel) ? "webview" : renderer;
|
|
522
1443
|
const cacheEnabled = !process.env.PROTEUS_NO_CACHE && !isDebug;
|
|
523
1444
|
let wxml;
|
|
524
1445
|
let js;
|
|
@@ -539,7 +1460,10 @@ function mpTransform(opts) {
|
|
|
539
1460
|
moduleImports: moduleImportsByFile.get(file),
|
|
540
1461
|
annotateLines: isDebug,
|
|
541
1462
|
debug: isDebug,
|
|
542
|
-
autoScrollContainer
|
|
1463
|
+
autoScrollContainer,
|
|
1464
|
+
fluidLayout,
|
|
1465
|
+
renderer: pageRenderer,
|
|
1466
|
+
platform: "mp"
|
|
543
1467
|
},
|
|
544
1468
|
projectRoot
|
|
545
1469
|
);
|
|
@@ -561,7 +1485,11 @@ function mpTransform(opts) {
|
|
|
561
1485
|
annotateLines: isDebug,
|
|
562
1486
|
debug: isDebug,
|
|
563
1487
|
preprocessStyle,
|
|
564
|
-
|
|
1488
|
+
loadStyleSrc: (src) => loadStyleSrcWithVariant(src, file, "mp"),
|
|
1489
|
+
autoScrollContainer,
|
|
1490
|
+
fluidLayout,
|
|
1491
|
+
renderer: pageRenderer,
|
|
1492
|
+
platform: "mp"
|
|
565
1493
|
});
|
|
566
1494
|
wxml = result.wxml;
|
|
567
1495
|
js = result.js;
|
|
@@ -582,7 +1510,11 @@ function mpTransform(opts) {
|
|
|
582
1510
|
annotateLines: isDebug,
|
|
583
1511
|
debug: isDebug,
|
|
584
1512
|
preprocessStyle,
|
|
585
|
-
|
|
1513
|
+
loadStyleSrc: (src) => loadStyleSrcWithVariant(src, file, "mp"),
|
|
1514
|
+
autoScrollContainer,
|
|
1515
|
+
fluidLayout,
|
|
1516
|
+
renderer,
|
|
1517
|
+
platform: "mp"
|
|
586
1518
|
});
|
|
587
1519
|
wxml = result.wxml;
|
|
588
1520
|
js = result.js;
|
|
@@ -594,8 +1526,9 @@ function mpTransform(opts) {
|
|
|
594
1526
|
if (cached) {
|
|
595
1527
|
console.log(`[mp-transform] \u7F16\u8BD1\u7F13\u5B58\u547D\u4E2D\uFF1A${rel}`);
|
|
596
1528
|
}
|
|
597
|
-
const
|
|
598
|
-
|
|
1529
|
+
const jsFinal = rewriteFrameworkRequires(js, rel);
|
|
1530
|
+
const jsWithMap = sourcemap && isDebug ? `${jsFinal}//# sourceMappingURL=${rel}.js.map
|
|
1531
|
+
` : jsFinal;
|
|
599
1532
|
this.emitFile({ type: "asset", fileName: `${rel}.wxml`, source: wxml });
|
|
600
1533
|
this.emitFile({ type: "asset", fileName: `${rel}.js`, source: jsWithMap });
|
|
601
1534
|
this.emitFile({ type: "asset", fileName: `${rel}.wxss`, source: wxss });
|
|
@@ -612,6 +1545,31 @@ function mpTransform(opts) {
|
|
|
612
1545
|
if (warnings.length) warningReport.push({ file: rel, warnings });
|
|
613
1546
|
console.log(`[mp-transform] ${rel} \u2192 wxml/js/wxss \u5DF2\u8F93\u51FA`);
|
|
614
1547
|
}
|
|
1548
|
+
{
|
|
1549
|
+
const publicDir = path4.join(projectRoot, "public");
|
|
1550
|
+
if (fs4.existsSync(publicDir)) {
|
|
1551
|
+
const rels = [];
|
|
1552
|
+
const walk = (dir) => {
|
|
1553
|
+
for (const e of fs4.readdirSync(dir, { withFileTypes: true })) {
|
|
1554
|
+
if (e.name.startsWith(".")) continue;
|
|
1555
|
+
const full = path4.join(dir, e.name);
|
|
1556
|
+
if (e.isDirectory()) walk(full);
|
|
1557
|
+
else rels.push(path4.relative(publicDir, full).replace(/\\/g, "/"));
|
|
1558
|
+
}
|
|
1559
|
+
};
|
|
1560
|
+
walk(publicDir);
|
|
1561
|
+
let assetN = 0;
|
|
1562
|
+
for (const { from, to } of mapPublicAssetVariants(rels, "mp")) {
|
|
1563
|
+
this.emitFile({ type: "asset", fileName: to, source: fs4.readFileSync(path4.join(publicDir, from)) });
|
|
1564
|
+
assetN++;
|
|
1565
|
+
}
|
|
1566
|
+
if (assetN) console.log(`[mp-transform] public \u9759\u6001\u8D44\u6E90 \u2192 ${assetN} \u4E2A\uFF08\u5E73\u53F0\u53D8\u4F53\u5DF2\u6309 mp \u89E3\u6790\uFF09`);
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
if (rustCompiler) {
|
|
1570
|
+
if (dualOk) console.log(`[mp-transform] compiler=rust\uFF1A${dualOk} \u4E2A\u6587\u4EF6 Node/Rust \u53CC\u7F16\u8BD1\u8BED\u4E49\u7B49\u4EF7\uFF08G-29.1\uFF09\u2705`);
|
|
1571
|
+
if (dualSkipped) console.warn(`[mp-transform] compiler=rust\uFF1A${dualSkipped} \u4E2A\u6587\u4EF6\u8DF3\u8FC7\u53CC\u7F16\u8BD1\u6821\u9A8C\uFF08${dualSkippedReason || "\u672A\u77E5"}\uFF09`);
|
|
1572
|
+
}
|
|
615
1573
|
if (!process.env.PROTEUS_NO_CACHE && !isDebug) {
|
|
616
1574
|
const st = compileCache.stats();
|
|
617
1575
|
const bs = bundleCache.stats();
|
|
@@ -633,450 +1591,397 @@ function mpTransform(opts) {
|
|
|
633
1591
|
};
|
|
634
1592
|
}
|
|
635
1593
|
|
|
636
|
-
// src/
|
|
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
|
-
}
|
|
733
|
-
}
|
|
734
|
-
return pages2;
|
|
735
|
-
}
|
|
736
|
-
function buildRoutes(pages2) {
|
|
737
|
-
const routes2 = pages2.map((p) => {
|
|
738
|
-
const r = {
|
|
739
|
-
// ★统一后 name 由 scan 推导(derivePath 模式:index 归并目录名,与旧 toRouteName 一致)
|
|
740
|
-
name: deriveName(p),
|
|
741
|
-
path: p.mpPath,
|
|
742
|
-
// 相对 RouterView 所在目录({appDir}/router)的路径,Web 端 import.meta.glob 按此匹配
|
|
743
|
-
component: path3.relative(path3.join(APP_DIR, "router"), p.file).replace(/\\/g, "/")
|
|
744
|
-
};
|
|
745
|
-
if (p.subPackage) r.subPackage = p.subPackage;
|
|
746
|
-
if (p.meta && Object.keys(p.meta).length > 0) r.meta = p.meta;
|
|
747
|
-
if (p.customRouteKeyName) r.customRouteKeyName = p.customRouteKeyName;
|
|
748
|
-
if (p.params && Object.keys(p.params).length > 0) r.params = p.params;
|
|
749
|
-
return r;
|
|
750
|
-
});
|
|
751
|
-
const nameByRel = new Map(pages2.map((p) => [p.relSrc, deriveName(p)]));
|
|
752
|
-
const blocks = pages2.map((p) => ({
|
|
753
|
-
loc: { file: p.file, line: 1, column: 1 },
|
|
754
|
-
path: p.relSrc.endsWith("/index") ? p.relSrc.slice(0, -"/index".length) : p.relSrc,
|
|
755
|
-
name: nameByRel.get(p.relSrc),
|
|
756
|
-
meta: p.meta ?? {},
|
|
757
|
-
componentPath: p.file
|
|
758
|
-
}));
|
|
759
|
-
const tree = buildRouteTree(blocks, {}, trace);
|
|
760
|
-
const parentByName = /* @__PURE__ */ new Map();
|
|
761
|
-
const walk = (nodes, parent) => {
|
|
762
|
-
for (const n of nodes) {
|
|
763
|
-
if (n.name && parent) parentByName.set(n.name, parent);
|
|
764
|
-
walk(n.children, n.name);
|
|
765
|
-
}
|
|
766
|
-
};
|
|
767
|
-
walk(tree);
|
|
768
|
-
for (const r of routes2) {
|
|
769
|
-
const parent = parentByName.get(r.name);
|
|
770
|
-
if (parent && parent !== r.name) r.parent = parent;
|
|
771
|
-
}
|
|
772
|
-
return routes2;
|
|
773
|
-
}
|
|
774
|
-
function deriveName(p) {
|
|
775
|
-
const base = p.relSrc.split("/").pop() ?? "";
|
|
776
|
-
if (base === "index") {
|
|
777
|
-
const dir = p.relSrc.slice(0, p.relSrc.lastIndexOf("/"));
|
|
778
|
-
const stripped = dir.replace(/^(pages|subpackages)(\/|$)/, "").replace(/\/$/, "");
|
|
779
|
-
return stripped ? stripped.replace(/\//g, "-") : "index";
|
|
780
|
-
}
|
|
781
|
-
return p.relSrc.replace(/^(pages|subpackages)\//, "").replace(/\//g, "-");
|
|
782
|
-
}
|
|
783
|
-
function validate(pages2, routes2) {
|
|
784
|
-
const mainCount = pages2.filter((p) => !p.subPackage).length;
|
|
785
|
-
if (mainCount > 32) {
|
|
786
|
-
throw new Error(
|
|
787
|
-
`[gen-routes] \u4E3B\u5305\u9875\u9762\u6570 ${mainCount} \u8D85\u8FC7\u5E73\u53F0\u786C\u8FB9\u754C 32\uFF0C\u8BF7\u5C06\u90E8\u5206\u9875\u9762\u79FB\u5165\u5206\u5305\uFF08platform limitation, cannot exceed\uFF09`
|
|
788
|
-
);
|
|
789
|
-
}
|
|
790
|
-
const dupNames = routes2.filter((r, i) => routes2.findIndex((x) => x.name === r.name) !== i);
|
|
791
|
-
if (dupNames.length) {
|
|
792
|
-
throw new Error(`[gen-routes] \u547D\u540D\u8DEF\u7531\u91CD\u590D\uFF1A${dupNames.map((r) => r.name).join(", ")}`);
|
|
1594
|
+
// src/devtools-plugin.ts
|
|
1595
|
+
import fs5 from "node:fs";
|
|
1596
|
+
import path5 from "node:path";
|
|
1597
|
+
import { createRequire as createRequire4 } from "node:module";
|
|
1598
|
+
import { WebSocketServer } from "ws";
|
|
1599
|
+
|
|
1600
|
+
// src/devtools-relay.ts
|
|
1601
|
+
function createProteusRelay() {
|
|
1602
|
+
const sources = /* @__PURE__ */ new Set();
|
|
1603
|
+
const panels = /* @__PURE__ */ new Set();
|
|
1604
|
+
const pending = /* @__PURE__ */ new Map();
|
|
1605
|
+
function onMessage(role, socket, raw) {
|
|
1606
|
+
let msg = null;
|
|
1607
|
+
try {
|
|
1608
|
+
msg = JSON.parse(raw);
|
|
1609
|
+
} catch {
|
|
1610
|
+
return;
|
|
793
1611
|
}
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
if (r.customRouteKeyName) parts.push(`customRouteKeyName: ${JSON.stringify(r.customRouteKeyName)}`);
|
|
805
|
-
return ` { ${parts.join(", ")} },`;
|
|
806
|
-
}
|
|
807
|
-
function writeAutoRoutes(routes2) {
|
|
808
|
-
const lines = [
|
|
809
|
-
`// ${config.routesOutput} \u2014\u2014 \u5E94\u7528\u4FA7\u8DEF\u7531\u8868\uFF08AUTO-GENERATED by scripts/gen-routes.ts\uFF0C\u52FF\u624B\u52A8\u7F16\u8F91\uFF09`,
|
|
810
|
-
"// \u2605\u62C6\u5305\u6B65\u9AA4 4\uFF1Aauto-routes \u968F\u5E94\u7528\u5B58\u653E\uFF08\u5DE5\u5382\u5316\u540E\u8DEF\u7531\u8868\u7531\u5E94\u7528\u6CE8\u5165 createRouter\uFF09\uFF0C\u4E0D\u518D\u5C5E\u4E8E @proteus-vue/router \u5305",
|
|
811
|
-
"import type { RouteRecord } from '@proteus-vue/router/types'",
|
|
812
|
-
"",
|
|
813
|
-
"export const routes: RouteRecord[] = [",
|
|
814
|
-
...routes2.map(formatRoute),
|
|
815
|
-
"]",
|
|
816
|
-
"",
|
|
817
|
-
"export const tabRoutes: RouteRecord[] = routes.filter(r => r.meta?.isTab)",
|
|
818
|
-
"export const routeMap: Record<string, RouteRecord> = routes.reduce((m, r) => { m[r.name] = r; return m }, {} as Record<string, RouteRecord>)",
|
|
819
|
-
""
|
|
820
|
-
];
|
|
821
|
-
lines.push("// \u2605 \u7C7B\u578B\u63D0\u793A\uFF1A\u6309\u8DEF\u7531\u540D\u7D22\u5F15\u7684\u53C2\u6570\u7C7B\u578B\u8868\uFF08\u6765\u6E90\uFF1A<route> \u5757 params \u58F0\u660E\uFF09");
|
|
822
|
-
lines.push("declare module '@proteus-vue/router/types' {");
|
|
823
|
-
lines.push(" interface RouteParamsByName {");
|
|
824
|
-
for (const r of routes2) {
|
|
825
|
-
const params = r.params ?? {};
|
|
826
|
-
const fields = Object.entries(params);
|
|
827
|
-
const body = fields.length ? fields.map(([k, t]) => `${k}?: ${tsType(t)}`).join("; ") : "";
|
|
828
|
-
lines.push(` '${r.name}': { ${body} },`);
|
|
1612
|
+
if (!msg || typeof msg !== "object") return;
|
|
1613
|
+
if (role === "source") {
|
|
1614
|
+
if (typeof msg.id === "number" && pending.has(msg.id)) {
|
|
1615
|
+
const target = pending.get(msg.id);
|
|
1616
|
+
pending.delete(msg.id);
|
|
1617
|
+
target.send(raw);
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1620
|
+
for (const p of panels) p.send(raw);
|
|
1621
|
+
return;
|
|
829
1622
|
}
|
|
830
|
-
|
|
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`);
|
|
1623
|
+
if (typeof msg.id === "number" && sources.size > 0) {
|
|
1624
|
+
pending.set(msg.id, socket);
|
|
1625
|
+
const first = sources.values().next().value;
|
|
1626
|
+
first.send(raw);
|
|
841
1627
|
}
|
|
842
|
-
return "string";
|
|
843
1628
|
}
|
|
844
|
-
function
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
const
|
|
848
|
-
|
|
849
|
-
mainPages.splice(entryIdx, 1);
|
|
850
|
-
mainPages.unshift(entryPath);
|
|
1629
|
+
function onClose(socket) {
|
|
1630
|
+
sources.delete(socket);
|
|
1631
|
+
panels.delete(socket);
|
|
1632
|
+
for (const entry of pending) {
|
|
1633
|
+
if (entry[1] === socket) pending.delete(entry[0]);
|
|
851
1634
|
}
|
|
852
|
-
|
|
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;
|
|
1635
|
+
}
|
|
1636
|
+
return {
|
|
1637
|
+
handleConnection(role, socket) {
|
|
1638
|
+
;
|
|
1639
|
+
(role === "source" ? sources : panels).add(socket);
|
|
1640
|
+
const withOn = socket;
|
|
1641
|
+
if (typeof withOn.on === "function") {
|
|
1642
|
+
withOn.on("message", (data) => onMessage(role, socket, String(data)));
|
|
1643
|
+
withOn.on("close", () => onClose(socket));
|
|
863
1644
|
}
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
1645
|
+
},
|
|
1646
|
+
counts() {
|
|
1647
|
+
return { source: sources.size, panel: panels.size };
|
|
1648
|
+
},
|
|
1649
|
+
close() {
|
|
1650
|
+
for (const s of sources) s.close();
|
|
1651
|
+
for (const p of panels) p.close();
|
|
1652
|
+
sources.clear();
|
|
1653
|
+
panels.clear();
|
|
1654
|
+
pending.clear();
|
|
873
1655
|
}
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
1656
|
+
};
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
// src/devtools-plugin.ts
|
|
1660
|
+
var require_ = createRequire4(import.meta.url);
|
|
1661
|
+
function isOriginAllowed(origin, allowFrom) {
|
|
1662
|
+
if (!allowFrom || allowFrom.length === 0) return true;
|
|
1663
|
+
if (!origin) return false;
|
|
1664
|
+
return allowFrom.indexOf(origin) >= 0;
|
|
1665
|
+
}
|
|
1666
|
+
function resolveDevtoolsDir() {
|
|
1667
|
+
return path5.dirname(require_.resolve("@proteus-vue/devtools/package.json"));
|
|
1668
|
+
}
|
|
1669
|
+
function createPanelPageHandler(devtoolsDir) {
|
|
1670
|
+
return (req, res) => {
|
|
1671
|
+
const pathname = (req.url ?? "/").split("?")[0];
|
|
1672
|
+
const base = "/proteus-devtools";
|
|
1673
|
+
if (pathname === base || pathname === base + "/") {
|
|
1674
|
+
const host = req.headers?.host ?? "localhost";
|
|
1675
|
+
const proto = req.headers?.["x-forwarded-proto"] === "https" ? "wss" : "ws";
|
|
1676
|
+
const html = fs5.readFileSync(path5.join(devtoolsDir, "panel.html"), "utf8").replace(/__PROTEUS_DEFAULT_WS__/g, `'${proto}://${host}/proteus-panel'`).replace("./style.css", base + "/style.css").replace("./dist/panel.js", base + "/panel.js");
|
|
1677
|
+
res.setHeader("content-type", "text/html; charset=utf-8");
|
|
1678
|
+
res.end(html);
|
|
1679
|
+
return true;
|
|
883
1680
|
}
|
|
884
|
-
if (
|
|
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`);
|
|
1681
|
+
if (pathname === base + "/style.css") {
|
|
1682
|
+
res.setHeader("content-type", "text/css; charset=utf-8");
|
|
1683
|
+
res.end(fs5.readFileSync(path5.join(devtoolsDir, "style.css")));
|
|
1684
|
+
return true;
|
|
890
1685
|
}
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
1686
|
+
if (pathname === base + "/panel.js") {
|
|
1687
|
+
res.setHeader("content-type", "application/javascript; charset=utf-8");
|
|
1688
|
+
res.end(fs5.readFileSync(path5.join(devtoolsDir, "dist", "panel.js")));
|
|
1689
|
+
return true;
|
|
1690
|
+
}
|
|
1691
|
+
return false;
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
function printPanelUrl(httpServer, logger, defaultPort = 5173) {
|
|
1695
|
+
if (!httpServer || typeof httpServer.once !== "function") return;
|
|
1696
|
+
httpServer.once("listening", () => {
|
|
1697
|
+
const addr = httpServer.address?.();
|
|
1698
|
+
const port = typeof addr === "object" && addr !== null ? addr.port : void 0;
|
|
1699
|
+
logger?.info(` \u279C Proteus DevTools: http://localhost:${port ?? defaultPort}/proteus-devtools`);
|
|
1700
|
+
});
|
|
1701
|
+
}
|
|
1702
|
+
function devtoolsRelayPlugin(opts = {}) {
|
|
1703
|
+
let wss = null;
|
|
1704
|
+
let relay = null;
|
|
1705
|
+
let pageHandler = null;
|
|
1706
|
+
const allowFrom = opts.allowFrom ?? [];
|
|
1707
|
+
function setup(server) {
|
|
1708
|
+
const httpServer = server.httpServer;
|
|
1709
|
+
if (!httpServer) return;
|
|
1710
|
+
relay = createProteusRelay();
|
|
1711
|
+
wss = new WebSocketServer({ noServer: true });
|
|
1712
|
+
httpServer.on("upgrade", (req, socket, head) => {
|
|
1713
|
+
const url = String(req.url ?? "").split("?")[0];
|
|
1714
|
+
const role = url === "/proteus-source" ? "source" : url === "/proteus-panel" ? "panel" : null;
|
|
1715
|
+
if (!role || !relay) return;
|
|
1716
|
+
const origin = req.headers?.origin;
|
|
1717
|
+
if (!isOriginAllowed(origin, allowFrom)) {
|
|
1718
|
+
console.warn(`[proteus-devtools] \u62D2\u7EDD ${role} \u8FDE\u63A5\uFF1AOrigin ${origin ?? "(\u65E0)"} \u4E0D\u5728\u767D\u540D\u5355 ${allowFrom.join(", ")}`);
|
|
1719
|
+
socket.destroy?.();
|
|
1720
|
+
return;
|
|
1721
|
+
}
|
|
1722
|
+
wss?.handleUpgrade(req, socket, head, (ws) => {
|
|
1723
|
+
wss?.emit("connection", ws, req);
|
|
1724
|
+
relay?.handleConnection(role, ws);
|
|
1725
|
+
});
|
|
1726
|
+
});
|
|
1727
|
+
pageHandler = createPanelPageHandler(resolveDevtoolsDir());
|
|
1728
|
+
const middlewares = server.middlewares;
|
|
1729
|
+
middlewares?.use?.((req, res, next) => {
|
|
1730
|
+
if (pageHandler && pageHandler(req, res)) return;
|
|
1731
|
+
next();
|
|
1732
|
+
});
|
|
894
1733
|
}
|
|
895
|
-
|
|
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);
|
|
1734
|
+
return {
|
|
1735
|
+
name: "proteus-devtools-relay",
|
|
1736
|
+
apply: "serve",
|
|
1737
|
+
configureServer(server) {
|
|
1738
|
+
if (opts.enabled === false || relay) return;
|
|
1739
|
+
setup(server);
|
|
1740
|
+
const httpServer = server.httpServer;
|
|
1741
|
+
const logger = server.config?.logger;
|
|
1742
|
+
printPanelUrl(httpServer, logger);
|
|
1743
|
+
},
|
|
1744
|
+
configurePreviewServer(server) {
|
|
1745
|
+
if (opts.enabled === false || relay) return;
|
|
1746
|
+
setup(server);
|
|
1747
|
+
const httpServer = server.httpServer;
|
|
1748
|
+
const logger = server.config?.logger;
|
|
1749
|
+
printPanelUrl(httpServer, logger);
|
|
1750
|
+
},
|
|
1751
|
+
closeBundle() {
|
|
1752
|
+
wss?.close();
|
|
1753
|
+
relay?.close();
|
|
1754
|
+
wss = null;
|
|
1755
|
+
relay = null;
|
|
1756
|
+
pageHandler = null;
|
|
998
1757
|
}
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1758
|
+
};
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
// src/vite-config.ts
|
|
1762
|
+
import path6 from "node:path";
|
|
1763
|
+
import fs6 from "node:fs";
|
|
1764
|
+
import { pathToFileURL } from "node:url";
|
|
1765
|
+
import { createRequire as createRequire5 } from "node:module";
|
|
1766
|
+
import {
|
|
1767
|
+
platformDefines as platformDefines2,
|
|
1768
|
+
applyPlatformMacrosInSfc,
|
|
1769
|
+
resolvePlatformVariant as resolvePlatformVariant2,
|
|
1770
|
+
resolvePlatformVariantWithExts as resolvePlatformVariantWithExts2,
|
|
1771
|
+
splitVariant as splitVariant3,
|
|
1772
|
+
mapPublicAssetVariants as mapPublicAssetVariants2
|
|
1773
|
+
} from "@proteus-vue/compiler";
|
|
1774
|
+
function platformMacroPlugin(platform) {
|
|
1775
|
+
return {
|
|
1776
|
+
name: "proteus-platform-macros",
|
|
1777
|
+
enforce: "pre",
|
|
1778
|
+
transform(code, id) {
|
|
1779
|
+
if (!id.endsWith(".vue")) return null;
|
|
1780
|
+
const out = applyPlatformMacrosInSfc(code, platform);
|
|
1781
|
+
return out === code ? null : { code: out, map: null };
|
|
1782
|
+
}
|
|
1783
|
+
};
|
|
1784
|
+
}
|
|
1785
|
+
function platformVariantPlugin(root, platform) {
|
|
1786
|
+
const CODE_EXTS = [".ts", ".js", ".mjs", ".cjs", ".vue", ".json", ".css"];
|
|
1787
|
+
return {
|
|
1788
|
+
name: "proteus-platform-variant",
|
|
1789
|
+
enforce: "pre",
|
|
1790
|
+
resolveId(id, importer) {
|
|
1791
|
+
if (!importer || !id) return null;
|
|
1792
|
+
const qIdx = id.indexOf("?");
|
|
1793
|
+
const query = qIdx >= 0 ? id.slice(qIdx) : "";
|
|
1794
|
+
const bare = qIdx >= 0 ? id.slice(0, qIdx) : id;
|
|
1795
|
+
if (!bare) return null;
|
|
1796
|
+
let base;
|
|
1797
|
+
if (bare.startsWith(".")) base = path6.resolve(path6.dirname(importer), bare);
|
|
1798
|
+
else if (bare.startsWith("@/")) base = path6.resolve(root, "src", bare.slice(2));
|
|
1799
|
+
else return null;
|
|
1800
|
+
const hasExt = path6.extname(base) !== "";
|
|
1801
|
+
if (hasExt) {
|
|
1802
|
+
const r2 = resolvePlatformVariant2(base, platform, fs6.existsSync);
|
|
1803
|
+
return r2 && r2 !== base ? r2 + query : null;
|
|
1006
1804
|
}
|
|
1007
|
-
const
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1805
|
+
const r = resolvePlatformVariantWithExts2(base, CODE_EXTS, platform, fs6.existsSync);
|
|
1806
|
+
return r ? r + query : null;
|
|
1807
|
+
}
|
|
1808
|
+
};
|
|
1809
|
+
}
|
|
1810
|
+
function hasPublicVariants(publicDir) {
|
|
1811
|
+
if (!fs6.existsSync(publicDir)) return false;
|
|
1812
|
+
const walk = (dir) => {
|
|
1813
|
+
for (const e of fs6.readdirSync(dir, { withFileTypes: true })) {
|
|
1814
|
+
if (e.name.startsWith(".")) continue;
|
|
1815
|
+
const full = path6.join(dir, e.name);
|
|
1816
|
+
if (e.isDirectory()) {
|
|
1817
|
+
if (walk(full)) return true;
|
|
1818
|
+
} else if (splitVariant3(e.name).platform !== void 0) {
|
|
1819
|
+
return true;
|
|
1012
1820
|
}
|
|
1013
|
-
console.warn(`[gen-routes] ${file} \u4F7F\u7528\u4E86\u7EC4\u4EF6 <${tag}>\uFF0C\u4F46\u672A\u627E\u5230 ${appCandidates.join(" \u6216 ")} \u6216\u6846\u67B6\u7EC4\u4EF6 ${fwCandidates.join(" \u6216 ")}`);
|
|
1014
1821
|
}
|
|
1015
|
-
return
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1822
|
+
return false;
|
|
1823
|
+
};
|
|
1824
|
+
return walk(publicDir);
|
|
1825
|
+
}
|
|
1826
|
+
function platformPublicAssetsPlugin(root, platform) {
|
|
1827
|
+
return {
|
|
1828
|
+
name: "proteus-platform-public-assets",
|
|
1829
|
+
apply: "build",
|
|
1830
|
+
generateBundle() {
|
|
1831
|
+
const publicDir = path6.join(root, "public");
|
|
1832
|
+
if (!fs6.existsSync(publicDir)) return;
|
|
1833
|
+
const rels = [];
|
|
1834
|
+
const walk = (dir) => {
|
|
1835
|
+
for (const e of fs6.readdirSync(dir, { withFileTypes: true })) {
|
|
1836
|
+
if (e.name.startsWith(".")) continue;
|
|
1837
|
+
const full = path6.join(dir, e.name);
|
|
1838
|
+
if (e.isDirectory()) walk(full);
|
|
1839
|
+
else rels.push(path6.relative(publicDir, full).replace(/\\/g, "/"));
|
|
1840
|
+
}
|
|
1841
|
+
};
|
|
1842
|
+
walk(publicDir);
|
|
1843
|
+
for (const { from, to } of mapPublicAssetVariants2(rels, platform)) {
|
|
1844
|
+
this.emitFile({ type: "asset", fileName: to, source: fs6.readFileSync(path6.join(publicDir, from)) });
|
|
1023
1845
|
}
|
|
1024
|
-
if (p.pageJson) Object.assign(pageJson, p.pageJson);
|
|
1025
|
-
const components = collectComponents(p.file);
|
|
1026
|
-
if (Object.keys(components).length) pageJson.usingComponents = components;
|
|
1027
|
-
const outFile = path3.join(OUT_DIR, p.mpPath + ".json");
|
|
1028
|
-
fs3.mkdirSync(path3.dirname(outFile), { recursive: true });
|
|
1029
|
-
fs3.writeFileSync(outFile, JSON.stringify(pageJson, null, 2) + "\n");
|
|
1030
1846
|
}
|
|
1031
|
-
|
|
1847
|
+
};
|
|
1848
|
+
}
|
|
1849
|
+
function routeBlocksPlugin() {
|
|
1850
|
+
return {
|
|
1851
|
+
name: "proteus-route-blocks",
|
|
1852
|
+
enforce: "pre",
|
|
1853
|
+
transform(code, id) {
|
|
1854
|
+
if (id.includes("?vue&type=route")) return { code: `export default ${code}`, map: null };
|
|
1855
|
+
return null;
|
|
1856
|
+
}
|
|
1857
|
+
};
|
|
1858
|
+
}
|
|
1859
|
+
function virtualMpEntryPlugin() {
|
|
1860
|
+
const VIRTUAL_ID = "\0proteus:mp-entry";
|
|
1861
|
+
return {
|
|
1862
|
+
name: "proteus-mp-entry",
|
|
1863
|
+
resolveId(id) {
|
|
1864
|
+
return id === "proteus:mp-entry" ? VIRTUAL_ID : null;
|
|
1865
|
+
},
|
|
1866
|
+
load(id) {
|
|
1867
|
+
return id === VIRTUAL_ID ? "export {}" : null;
|
|
1868
|
+
}
|
|
1869
|
+
};
|
|
1870
|
+
}
|
|
1871
|
+
async function importFromRoot(root, spec) {
|
|
1872
|
+
const req = createRequire5(path6.join(root, "package.json"));
|
|
1873
|
+
const resolved = req.resolve(spec);
|
|
1874
|
+
return import(pathToFileURL(resolved).href);
|
|
1875
|
+
}
|
|
1876
|
+
async function resolveProteusViteConfig(ctx, config) {
|
|
1877
|
+
const { root, command, mode } = ctx;
|
|
1878
|
+
const platform = mode === "mp-weixin" || mode === "web" ? mode : config.platform;
|
|
1879
|
+
const isMp = platform === "mp-weixin";
|
|
1880
|
+
const isDebug = process.env.PROTEUS_DEBUG === "1";
|
|
1881
|
+
let plugins;
|
|
1882
|
+
if (isMp) {
|
|
1883
|
+
plugins = [virtualMpEntryPlugin(), mpTransform({ config })];
|
|
1884
|
+
} else {
|
|
1885
|
+
const vueMod = await importFromRoot(root, "@vitejs/plugin-vue");
|
|
1886
|
+
const vue = vueMod.default({
|
|
1887
|
+
template: { compilerOptions: { isCustomElement: (tag) => MP_ONLY_TAGS.has(tag) } }
|
|
1888
|
+
});
|
|
1889
|
+
plugins = [platformVariantPlugin(root, "web"), vue, platformMacroPlugin("web"), platformPublicAssetsPlugin(root, "web"), routeBlocksPlugin()];
|
|
1032
1890
|
}
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1891
|
+
const frameworkConfig = {
|
|
1892
|
+
configFile: false,
|
|
1893
|
+
// ★#418:vite 配置由本函数组装——不读 vite.config.ts(CLI 是唯一驱动)
|
|
1894
|
+
root,
|
|
1895
|
+
define: {
|
|
1896
|
+
// devtools 打通:dev serve 默认开启可观测;build 默认关闭零开销;PROTEUS_DEBUG=1 强制生产调试
|
|
1897
|
+
__PROTEUS_DEBUG__: command === "serve" || isDebug,
|
|
1898
|
+
// Skyline 开关注入:mp 构建时 __PROTEUS_SKYLINE__ = config.skyline
|
|
1899
|
+
__PROTEUS_SKYLINE__: isMp && config.skyline,
|
|
1900
|
+
// ★平台编译期宏(条件显隐)——Web 端 .vue 走标准 @vitejs/plugin-vue:**vite define 对 .vue 不生效**
|
|
1901
|
+
// (实测:模板表达式/script 内 __MP__ 残留),故 Web 端由 platformMacroPlugin(enforce:'pre'
|
|
1902
|
+
// 源码替换)处理,见 plugins。这里仍保留 define 供**非 .vue 的 .ts/.js 模块**使用(同源取值)。
|
|
1903
|
+
...platformDefines2(isMp ? "mp" : "web")
|
|
1904
|
+
},
|
|
1905
|
+
plugins,
|
|
1906
|
+
// ★平台变体·静态资源 Web 通道(第 3 层):public/ 含平台变体(logo.web.png)时,
|
|
1907
|
+
// 关掉 Vite 默认逐字拷贝(会把他端变体也拷进产物),改由 platformPublicAssetsPlugin 按 web 解析;
|
|
1908
|
+
// 无变体时保持默认(零侵入,避免改变既有工程行为)。
|
|
1909
|
+
publicDir: hasPublicVariants(path6.join(root, "public")) ? false : void 0,
|
|
1910
|
+
resolve: {
|
|
1911
|
+
alias: [{ find: "@", replacement: path6.join(root, "src") }]
|
|
1912
|
+
},
|
|
1913
|
+
build: {
|
|
1914
|
+
target: "es2018",
|
|
1915
|
+
cssCodeSplit: false,
|
|
1916
|
+
minify: isMp ? false : void 0,
|
|
1917
|
+
outDir: path6.join(root, "dist", platform),
|
|
1918
|
+
emptyOutDir: !isMp,
|
|
1919
|
+
rollupOptions: isMp ? { input: "proteus:mp-entry", output: { entryFileNames: "mp-entry.js" } } : void 0
|
|
1052
1920
|
}
|
|
1053
|
-
|
|
1921
|
+
};
|
|
1922
|
+
const userVite = config.vite;
|
|
1923
|
+
let user;
|
|
1924
|
+
if (typeof userVite === "function") {
|
|
1925
|
+
user = await userVite({ command, mode });
|
|
1926
|
+
} else if (userVite && typeof userVite === "object") {
|
|
1927
|
+
user = userVite;
|
|
1054
1928
|
}
|
|
1055
|
-
|
|
1056
|
-
const
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1929
|
+
if (user) {
|
|
1930
|
+
const { plugins: userPlugins, resolve: userResolve, define: userDefine, build: userBuild, ...rest } = user;
|
|
1931
|
+
Object.assign(frameworkConfig, rest);
|
|
1932
|
+
if (userBuild) {
|
|
1933
|
+
const fwBuild = frameworkConfig.build ?? {};
|
|
1934
|
+
const merged = { ...fwBuild };
|
|
1935
|
+
const ub = userBuild;
|
|
1936
|
+
for (const k of Object.keys(ub)) {
|
|
1937
|
+
const uv = ub[k];
|
|
1938
|
+
if (k === "rollupOptions" && uv && typeof uv === "object") {
|
|
1939
|
+
const fwRo = fwBuild.rollupOptions ?? {};
|
|
1940
|
+
const uRo = uv;
|
|
1941
|
+
const ro = { ...fwRo };
|
|
1942
|
+
for (const rk of Object.keys(uRo)) {
|
|
1943
|
+
const rv = uRo[rk];
|
|
1944
|
+
if (rk === "output" && rv && typeof rv === "object" && fwRo.output && typeof fwRo.output === "object") {
|
|
1945
|
+
ro.output = { ...fwRo.output, ...rv };
|
|
1946
|
+
} else {
|
|
1947
|
+
ro[rk] = rv;
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
merged.rollupOptions = ro;
|
|
1951
|
+
} else {
|
|
1952
|
+
merged[k] = uv;
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
frameworkConfig.build = merged;
|
|
1956
|
+
}
|
|
1957
|
+
if (userResolve) {
|
|
1958
|
+
const baseAlias = frameworkConfig.resolve?.alias;
|
|
1959
|
+
frameworkConfig.resolve = {
|
|
1960
|
+
...userResolve,
|
|
1961
|
+
alias: [...Array.isArray(baseAlias) ? baseAlias : [], ...Array.isArray(userResolve.alias) ? userResolve.alias : []]
|
|
1962
|
+
};
|
|
1963
|
+
}
|
|
1964
|
+
if (userDefine) {
|
|
1965
|
+
frameworkConfig.define = { ...frameworkConfig.define, ...userDefine };
|
|
1966
|
+
}
|
|
1967
|
+
if (userPlugins?.length) frameworkConfig.plugins = [...frameworkConfig.plugins ?? [], ...userPlugins];
|
|
1066
1968
|
}
|
|
1067
|
-
|
|
1068
|
-
const pages = scanPages();
|
|
1069
|
-
const routes = buildRoutes(pages);
|
|
1070
|
-
validate(pages, routes);
|
|
1071
|
-
writeAutoRoutes(routes);
|
|
1072
|
-
writeAppJson(pages, routes);
|
|
1073
|
-
writePageJsons(pages);
|
|
1074
|
-
writeComponentJsons();
|
|
1075
|
-
writeProjectConfig();
|
|
1076
|
-
console.log(`[gen-routes] \u5B8C\u6210\uFF1A\u5171 ${pages.length} \u4E2A\u9875\u9762`);
|
|
1969
|
+
return { config: frameworkConfig, needsGenRoutes: isMp, platform };
|
|
1077
1970
|
}
|
|
1078
1971
|
export {
|
|
1972
|
+
COMPONENTS_PKG,
|
|
1973
|
+
componentsRootExists,
|
|
1974
|
+
createPanelPageHandler,
|
|
1975
|
+
createProteusRelay,
|
|
1079
1976
|
defaultScopedPlugin,
|
|
1977
|
+
devtoolsRelayPlugin,
|
|
1978
|
+
isOriginAllowed,
|
|
1080
1979
|
mpTransform,
|
|
1980
|
+
printPanelUrl,
|
|
1981
|
+
resolveComponentsRoot,
|
|
1982
|
+
resolveDevtoolsDir,
|
|
1983
|
+
resolveProteusViteConfig,
|
|
1984
|
+
resolveSharedModule,
|
|
1985
|
+
rewriteRootToPage,
|
|
1081
1986
|
runGenRoutes
|
|
1082
1987
|
};
|