@liujitcn/kratos-taro-app-core 0.0.18 → 0.0.19
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/README.md +1 -1
- package/dist/runner.mjs +226 -54
- package/package.json +1 -1
- package/src/components/KratosTabBar.scss +1 -0
- package/src/rpc/base/v1/config.ts +2 -2
- package/src/rpc/{common → base}/v1/enum.ts +22 -39
- package/src/rpc/base/v1/file.ts +1 -1
- package/src/rpc/base/v1/login.ts +2 -2
- package/src/rpc/base/v1/mcp.ts +1 -1
- package/src/rpc/base/v1/oauth.ts +1 -1
- package/src/rpc/base/v1/sse.ts +1 -1
- package/src/rpc/common/v1/common.ts +1 -1
- package/src/rpc/common/v1/types.ts +1 -1
- package/src/rpc/google/protobuf/empty.ts +1 -1
- package/src/rpc/google/protobuf/wrappers.ts +1 -1
- package/src/rpc/system/app/v1/auth.ts +1 -1
- package/src/rpc/system/app/v1/base_area.ts +1 -1
- package/src/rpc/system/app/v1/base_dict.ts +1 -1
- package/src/rpc/system/app/v1/base_menu.ts +1 -1
- package/src/rpc/system/common/v1/enum.ts +1 -1
- package/src/runner.ts +270 -53
- package/src/stores/setting.ts +1 -1
- package/src/utils/http.ts +4 -4
- package/src/utils/passwordCrypto.ts +1 -1
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
|
|
27
27
|
模块必须同时提供运行时定义和 `./build` 导出。构建入口返回模块名称、包根目录与页面清单;runner 根据宿主的静态 `module-manifest.ts` 解析模块顺序,扫描页面并创建事务。
|
|
28
28
|
|
|
29
|
-
runner 支持命名导入和默认导入的模块,后注册模块可以覆盖相同物理路由。页面私有 `components` 不会成为路由,模块 static 会先合并到宿主再由 Taro 编译。H5 下,runner 根据页面 `navigationStyle` 和 `navigationBar*`
|
|
29
|
+
runner 支持命名导入和默认导入的模块,后注册模块可以覆盖相同物理路由。页面私有 `components` 不会成为路由,模块 static 会先合并到宿主再由 Taro 编译。H5 下,runner 根据页面 `navigationStyle` 和 `navigationBar*` 配置生成默认顶部导航栏;微信小程序继续使用原生导航栏。构建失败、正常退出和收到终止信号时都会执行恢复;传入 `--prepare-only` 时只装配页面供类型检查使用,配合 `--cleanup-only` 在检查结束后恢复,不启动 Taro 编译。
|
|
30
30
|
|
|
31
31
|
## 验证
|
|
32
32
|
|
package/dist/runner.mjs
CHANGED
|
@@ -15,6 +15,9 @@ import { dirname, relative, resolve } from "node:path";
|
|
|
15
15
|
import { pathToFileURL } from "node:url";
|
|
16
16
|
import { spawn } from "node:child_process";
|
|
17
17
|
import ts from "typescript";
|
|
18
|
+
var sourceVersionCounter = 0;
|
|
19
|
+
var PAGE_ASSEMBLY_WAIT_TIMEOUT_MS = 3e4;
|
|
20
|
+
var PAGE_ASSEMBLY_POLL_INTERVAL_MS = 25;
|
|
18
21
|
async function main() {
|
|
19
22
|
const options = parseArguments(process.argv.slice(2));
|
|
20
23
|
const hostRoot = process.cwd();
|
|
@@ -23,6 +26,7 @@ async function main() {
|
|
|
23
26
|
const baseConfigFile = resolve(inputDir, "app.config.base.json");
|
|
24
27
|
const transactionFile = resolve(hostRoot, ".kratos-taro-app-pages-state.json");
|
|
25
28
|
recoverBuildTransaction(transactionFile, inputDir, appConfigFile);
|
|
29
|
+
if (options.cleanupOnly) return;
|
|
26
30
|
const originalAppConfig = readFileSync(appConfigFile, "utf8");
|
|
27
31
|
const manifest = JSON.parse(readFileSync(baseConfigFile, "utf8"));
|
|
28
32
|
const modules = await loadBuildModules(hostRoot, resolve(inputDir, "module-manifest.ts"));
|
|
@@ -38,34 +42,24 @@ async function main() {
|
|
|
38
42
|
});
|
|
39
43
|
});
|
|
40
44
|
});
|
|
41
|
-
const
|
|
42
|
-
const pageFile = resolve(inputDir, `${page.route}.tsx`);
|
|
43
|
-
const configFile = resolve(inputDir, `${page.route}.config.ts`);
|
|
44
|
-
return [pageFile, configFile].filter((file) => !existsSync(file));
|
|
45
|
-
});
|
|
46
|
-
const staticRoot = resolve(inputDir, "static");
|
|
47
|
-
const staticFiles = collectModuleStaticFiles(
|
|
48
|
-
modules.map((module) => resolve(module.root, "src/static")),
|
|
49
|
-
staticRoot
|
|
50
|
-
);
|
|
51
|
-
const generatedStaticFiles = staticFiles.map(({ target }) => target);
|
|
52
|
-
const transaction = {
|
|
53
|
-
inputDir,
|
|
45
|
+
const pageAssembly = preparePageAssembly({
|
|
54
46
|
appConfigFile,
|
|
47
|
+
baseManifest: manifest,
|
|
48
|
+
inputDir,
|
|
49
|
+
moduleStaticRoots: modules.map((module) => resolve(module.root, "src/static")),
|
|
55
50
|
originalAppConfig,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
};
|
|
60
|
-
writeFileSync(transactionFile, `${JSON.stringify(transaction, null, 2)}
|
|
61
|
-
`, { flag: "wx" });
|
|
51
|
+
pageMap: [...pageMap.values()],
|
|
52
|
+
transactionFile
|
|
53
|
+
});
|
|
62
54
|
let cleaned = false;
|
|
63
55
|
const cleanup = () => {
|
|
64
56
|
if (cleaned) return;
|
|
65
57
|
cleaned = true;
|
|
66
|
-
|
|
67
|
-
if (existsSync(transactionFile)) unlinkSync(transactionFile);
|
|
58
|
+
releasePageAssembly(transactionFile, pageAssembly.leaseFile);
|
|
68
59
|
};
|
|
60
|
+
if (options.prepareOnly) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
69
63
|
const handleSignal = (signal) => {
|
|
70
64
|
child?.kill(signal);
|
|
71
65
|
};
|
|
@@ -73,34 +67,14 @@ async function main() {
|
|
|
73
67
|
process.once("SIGINT", () => handleSignal("SIGINT"));
|
|
74
68
|
process.once("SIGTERM", () => handleSignal("SIGTERM"));
|
|
75
69
|
try {
|
|
76
|
-
pageMap.forEach((page) => {
|
|
77
|
-
appendPage(manifest, page.route);
|
|
78
|
-
const pageFile = resolve(inputDir, `${page.route}.tsx`);
|
|
79
|
-
const configFile = resolve(inputDir, `${page.route}.config.ts`);
|
|
80
|
-
if (!existsSync(pageFile)) {
|
|
81
|
-
mkdirSync(dirname(pageFile), { recursive: true });
|
|
82
|
-
writeFileSync(pageFile, createPageWrapper(page));
|
|
83
|
-
}
|
|
84
|
-
if (!existsSync(configFile)) {
|
|
85
|
-
writeFileSync(configFile, createPageConfig(page));
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
staticFiles.forEach(({ source, target }) => {
|
|
89
|
-
mkdirSync(dirname(target), { recursive: true });
|
|
90
|
-
copyFileSync(source, target);
|
|
91
|
-
});
|
|
92
|
-
writeFileSync(
|
|
93
|
-
appConfigFile,
|
|
94
|
-
`export default defineAppConfig(${JSON.stringify(manifest, null, 2)})
|
|
95
|
-
`
|
|
96
|
-
);
|
|
97
70
|
const args = ["exec", "taro", "build", "--type", options.type, "--mode", options.mode];
|
|
98
71
|
if (options.watch) args.push("--watch");
|
|
99
72
|
child = spawn(process.platform === "win32" ? "pnpm.cmd" : "pnpm", args, {
|
|
100
73
|
cwd: hostRoot,
|
|
101
74
|
env: {
|
|
102
75
|
...process.env,
|
|
103
|
-
NODE_ENV: options.mode === "development" ? "development" : "production"
|
|
76
|
+
NODE_ENV: options.mode === "development" ? "development" : "production",
|
|
77
|
+
KRATOS_TARO_OUTPUT_ROOT: process.env.KRATOS_TARO_OUTPUT_ROOT || (options.type === "h5" ? "dist/h5" : "dist/mp-weixin")
|
|
104
78
|
},
|
|
105
79
|
stdio: "inherit"
|
|
106
80
|
});
|
|
@@ -123,6 +97,8 @@ function parseArguments(args) {
|
|
|
123
97
|
let type;
|
|
124
98
|
let mode = "production";
|
|
125
99
|
let watch = false;
|
|
100
|
+
let prepareOnly = false;
|
|
101
|
+
let cleanupOnly = false;
|
|
126
102
|
for (let index = 0; index < args.length; index += 1) {
|
|
127
103
|
const argument = args[index];
|
|
128
104
|
if (argument === "--type") {
|
|
@@ -133,12 +109,16 @@ function parseArguments(args) {
|
|
|
133
109
|
mode = args[++index] || mode;
|
|
134
110
|
} else if (argument === "--watch") {
|
|
135
111
|
watch = true;
|
|
112
|
+
} else if (argument === "--prepare-only") {
|
|
113
|
+
prepareOnly = true;
|
|
114
|
+
} else if (argument === "--cleanup-only") {
|
|
115
|
+
cleanupOnly = true;
|
|
136
116
|
} else {
|
|
137
117
|
throw new Error(`\u672A\u77E5\u53C2\u6570\uFF1A${argument}`);
|
|
138
118
|
}
|
|
139
119
|
}
|
|
140
120
|
if (!type) throw new Error("\u7F3A\u5C11 --type h5|weapp");
|
|
141
|
-
return { type, mode, watch };
|
|
121
|
+
return { type, mode, watch, prepareOnly, cleanupOnly };
|
|
142
122
|
}
|
|
143
123
|
async function loadBuildModules(hostRoot, manifestFile) {
|
|
144
124
|
const source = readFileSync(manifestFile, "utf8");
|
|
@@ -185,26 +165,212 @@ async function loadBuildModules(hostRoot, manifestFile) {
|
|
|
185
165
|
);
|
|
186
166
|
}
|
|
187
167
|
function recoverBuildTransaction(transactionFile, inputDir, appConfigFile) {
|
|
168
|
+
const lockFile = `${transactionFile}.lock`;
|
|
169
|
+
acquirePageAssemblyLock(lockFile);
|
|
170
|
+
try {
|
|
171
|
+
const transaction = readBuildTransaction(transactionFile, inputDir, appConfigFile);
|
|
172
|
+
if (!transaction || isPageAssemblyActive(transaction, transactionFile)) return;
|
|
173
|
+
restoreBuildTransaction(transaction, transactionFile);
|
|
174
|
+
} finally {
|
|
175
|
+
releasePageAssemblyLock(lockFile);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function preparePageAssembly(options) {
|
|
179
|
+
const lockFile = `${options.transactionFile}.lock`;
|
|
180
|
+
acquirePageAssemblyLock(lockFile);
|
|
181
|
+
try {
|
|
182
|
+
let transaction = readBuildTransaction(
|
|
183
|
+
options.transactionFile,
|
|
184
|
+
options.inputDir,
|
|
185
|
+
options.appConfigFile
|
|
186
|
+
);
|
|
187
|
+
if (transaction && isPageAssemblyActive(transaction, options.transactionFile)) {
|
|
188
|
+
return {
|
|
189
|
+
transaction,
|
|
190
|
+
leaseFile: createPageAssemblyLease(options.transactionFile)
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
let originalAppConfig = options.originalAppConfig;
|
|
194
|
+
if (transaction) {
|
|
195
|
+
originalAppConfig = transaction.originalAppConfig;
|
|
196
|
+
restoreBuildTransaction(transaction, options.transactionFile);
|
|
197
|
+
}
|
|
198
|
+
const generatedFiles = options.pageMap.flatMap((page) => {
|
|
199
|
+
const pageFile = resolve(options.inputDir, `${page.route}.tsx`);
|
|
200
|
+
const configFile = resolve(options.inputDir, `${page.route}.config.ts`);
|
|
201
|
+
return [pageFile, configFile].filter(
|
|
202
|
+
(file) => !existsSync(file) || isGeneratedPageFile(file) || isGeneratedPageConfig(file)
|
|
203
|
+
);
|
|
204
|
+
});
|
|
205
|
+
const staticFiles = collectModuleStaticFiles(
|
|
206
|
+
options.moduleStaticRoots,
|
|
207
|
+
resolve(options.inputDir, "static")
|
|
208
|
+
);
|
|
209
|
+
transaction = {
|
|
210
|
+
inputDir: options.inputDir,
|
|
211
|
+
appConfigFile: options.appConfigFile,
|
|
212
|
+
originalAppConfig,
|
|
213
|
+
generatedFiles,
|
|
214
|
+
generatedStaticFiles: staticFiles.map(({ target }) => target),
|
|
215
|
+
ownerPid: process.pid,
|
|
216
|
+
ready: false
|
|
217
|
+
};
|
|
218
|
+
writeBuildTransaction(options.transactionFile, transaction, "wx");
|
|
219
|
+
const leaseFile = createPageAssemblyLease(options.transactionFile);
|
|
220
|
+
try {
|
|
221
|
+
options.pageMap.forEach((page) => {
|
|
222
|
+
appendPage(options.baseManifest, page.route);
|
|
223
|
+
const pageFile = resolve(options.inputDir, `${page.route}.tsx`);
|
|
224
|
+
const configFile = resolve(options.inputDir, `${page.route}.config.ts`);
|
|
225
|
+
if (!existsSync(pageFile)) {
|
|
226
|
+
mkdirSync(dirname(pageFile), { recursive: true });
|
|
227
|
+
writeFileSync(pageFile, createPageWrapper(page));
|
|
228
|
+
}
|
|
229
|
+
if (!existsSync(configFile)) writeFileSync(configFile, createPageConfig(page));
|
|
230
|
+
});
|
|
231
|
+
staticFiles.forEach(({ source, target }) => {
|
|
232
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
233
|
+
copyFileSync(source, target);
|
|
234
|
+
});
|
|
235
|
+
writeFileSync(
|
|
236
|
+
options.appConfigFile,
|
|
237
|
+
`export default defineAppConfig(${JSON.stringify(options.baseManifest, null, 2)})
|
|
238
|
+
`
|
|
239
|
+
);
|
|
240
|
+
transaction.ready = true;
|
|
241
|
+
writeBuildTransaction(options.transactionFile, transaction);
|
|
242
|
+
return { transaction, leaseFile };
|
|
243
|
+
} catch (error) {
|
|
244
|
+
releasePageAssemblyLease(leaseFile);
|
|
245
|
+
restoreBuildTransaction(transaction, options.transactionFile);
|
|
246
|
+
throw error;
|
|
247
|
+
}
|
|
248
|
+
} finally {
|
|
249
|
+
releasePageAssemblyLock(lockFile);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
function readBuildTransaction(transactionFile, inputDir, appConfigFile) {
|
|
188
253
|
if (!existsSync(transactionFile)) return;
|
|
189
254
|
const transaction = JSON.parse(readFileSync(transactionFile, "utf8"));
|
|
190
255
|
if (transaction.inputDir !== inputDir || transaction.appConfigFile !== appConfigFile) {
|
|
191
256
|
throw new Error(`Taro \u6784\u5EFA\u4E8B\u52A1\u76EE\u5F55\u4E0D\u5339\u914D\uFF1A${transactionFile}`);
|
|
192
257
|
}
|
|
193
|
-
|
|
194
|
-
throw new Error(
|
|
195
|
-
`\u9875\u9762\u88C5\u914D\u5DF2\u7531\u8FDB\u7A0B ${transaction.ownerPid} \u4F7F\u7528\uFF0C\u8BF7\u5148\u505C\u6B62\u53E6\u4E00\u4E2A H5 \u6216\u5C0F\u7A0B\u5E8F\u5F00\u53D1\u8FDB\u7A0B`
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
cleanupTransaction(transaction);
|
|
199
|
-
unlinkSync(transactionFile);
|
|
258
|
+
return transaction;
|
|
200
259
|
}
|
|
201
|
-
function
|
|
260
|
+
function writeBuildTransaction(transactionFile, transaction, flag) {
|
|
261
|
+
writeFileSync(
|
|
262
|
+
transactionFile,
|
|
263
|
+
`${JSON.stringify(transaction, null, 2)}
|
|
264
|
+
`,
|
|
265
|
+
flag ? { flag } : void 0
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
function restoreBuildTransaction(transaction, transactionFile) {
|
|
202
269
|
for (const file of [...transaction.generatedFiles].reverse()) {
|
|
203
270
|
if (existsSync(file)) unlinkSync(file);
|
|
204
271
|
removeEmptyParents(dirname(file), transaction.inputDir);
|
|
205
272
|
}
|
|
206
|
-
removeGeneratedStaticFiles(
|
|
273
|
+
removeGeneratedStaticFiles(
|
|
274
|
+
transaction.generatedStaticFiles ?? [],
|
|
275
|
+
resolve(transaction.inputDir, "static")
|
|
276
|
+
);
|
|
207
277
|
writeFileSync(transaction.appConfigFile, transaction.originalAppConfig);
|
|
278
|
+
if (existsSync(transactionFile)) unlinkSync(transactionFile);
|
|
279
|
+
}
|
|
280
|
+
function releasePageAssembly(transactionFile, leaseFile) {
|
|
281
|
+
releasePageAssemblyLease(leaseFile);
|
|
282
|
+
const lockFile = `${transactionFile}.lock`;
|
|
283
|
+
acquirePageAssemblyLock(lockFile);
|
|
284
|
+
try {
|
|
285
|
+
const transaction = readBuildTransactionFromFile(transactionFile);
|
|
286
|
+
if (!transaction || isPageAssemblyActive(transaction, transactionFile)) return;
|
|
287
|
+
restoreBuildTransaction(transaction, transactionFile);
|
|
288
|
+
} finally {
|
|
289
|
+
releasePageAssemblyLock(lockFile);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
function readBuildTransactionFromFile(transactionFile) {
|
|
293
|
+
if (!existsSync(transactionFile)) return;
|
|
294
|
+
return JSON.parse(readFileSync(transactionFile, "utf8"));
|
|
295
|
+
}
|
|
296
|
+
function createPageAssemblyLease(transactionFile) {
|
|
297
|
+
const leaseDirectory = `${transactionFile}.leases`;
|
|
298
|
+
mkdirSync(leaseDirectory, { recursive: true });
|
|
299
|
+
const leaseFile = resolve(
|
|
300
|
+
leaseDirectory,
|
|
301
|
+
`${process.pid}-${Date.now()}-${++sourceVersionCounter}.lease`
|
|
302
|
+
);
|
|
303
|
+
writeFileSync(leaseFile, `${JSON.stringify({ ownerPid: process.pid })}
|
|
304
|
+
`, { flag: "wx" });
|
|
305
|
+
return leaseFile;
|
|
306
|
+
}
|
|
307
|
+
function releasePageAssemblyLease(leaseFile) {
|
|
308
|
+
if (existsSync(leaseFile)) unlinkSync(leaseFile);
|
|
309
|
+
const leaseDirectory = dirname(leaseFile);
|
|
310
|
+
if (existsSync(leaseDirectory) && !readdirSync(leaseDirectory).length) {
|
|
311
|
+
try {
|
|
312
|
+
rmdirSync(leaseDirectory);
|
|
313
|
+
} catch (error) {
|
|
314
|
+
const code = error.code;
|
|
315
|
+
if (code !== "ENOTEMPTY" && code !== "EEXIST") throw error;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
function isPageAssemblyActive(transaction, transactionFile) {
|
|
320
|
+
if (listActivePageLeases(`${transactionFile}.leases`).length) return true;
|
|
321
|
+
return transaction.ownerPid !== void 0 && transaction.ownerPid !== process.pid && isProcessRunning(transaction.ownerPid);
|
|
322
|
+
}
|
|
323
|
+
function listActivePageLeases(leaseDirectory) {
|
|
324
|
+
if (!existsSync(leaseDirectory)) return [];
|
|
325
|
+
const activePids = [];
|
|
326
|
+
for (const entry of readdirSync(leaseDirectory)) {
|
|
327
|
+
const leaseFile = resolve(leaseDirectory, entry);
|
|
328
|
+
try {
|
|
329
|
+
const lease = JSON.parse(readFileSync(leaseFile, "utf8"));
|
|
330
|
+
if (lease.ownerPid && isProcessRunning(lease.ownerPid)) {
|
|
331
|
+
activePids.push(lease.ownerPid);
|
|
332
|
+
} else {
|
|
333
|
+
unlinkSync(leaseFile);
|
|
334
|
+
}
|
|
335
|
+
} catch {
|
|
336
|
+
unlinkSync(leaseFile);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return activePids;
|
|
340
|
+
}
|
|
341
|
+
function acquirePageAssemblyLock(lockFile) {
|
|
342
|
+
const deadline = Date.now() + PAGE_ASSEMBLY_WAIT_TIMEOUT_MS;
|
|
343
|
+
while (true) {
|
|
344
|
+
try {
|
|
345
|
+
writeFileSync(lockFile, `${JSON.stringify({ ownerPid: process.pid })}
|
|
346
|
+
`, { flag: "wx" });
|
|
347
|
+
return;
|
|
348
|
+
} catch (error) {
|
|
349
|
+
if (error.code !== "EEXIST") throw error;
|
|
350
|
+
const ownerPid = readPageAssemblyLockOwner(lockFile);
|
|
351
|
+
if (ownerPid !== void 0 && !isProcessRunning(ownerPid)) {
|
|
352
|
+
unlinkSync(lockFile);
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
if (Date.now() >= deadline) throw new Error(`\u7B49\u5F85 Taro \u9875\u9762\u88C5\u914D\u9501\u8D85\u65F6\uFF1A${lockFile}`);
|
|
356
|
+
waitForPageAssembly(PAGE_ASSEMBLY_POLL_INTERVAL_MS);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
function releasePageAssemblyLock(lockFile) {
|
|
361
|
+
if (!existsSync(lockFile)) return;
|
|
362
|
+
if (readPageAssemblyLockOwner(lockFile) === process.pid) unlinkSync(lockFile);
|
|
363
|
+
}
|
|
364
|
+
function readPageAssemblyLockOwner(lockFile) {
|
|
365
|
+
try {
|
|
366
|
+
return JSON.parse(readFileSync(lockFile, "utf8")).ownerPid;
|
|
367
|
+
} catch {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
function waitForPageAssembly(milliseconds) {
|
|
372
|
+
const buffer = new SharedArrayBuffer(4);
|
|
373
|
+
Atomics.wait(new Int32Array(buffer), 0, 0, milliseconds);
|
|
208
374
|
}
|
|
209
375
|
function isProcessRunning(pid) {
|
|
210
376
|
try {
|
|
@@ -272,7 +438,7 @@ function appendPage(manifest, route) {
|
|
|
272
438
|
if (!subpackage.pages.includes(subRoute)) subpackage.pages.push(subRoute);
|
|
273
439
|
}
|
|
274
440
|
function createPageWrapper(page) {
|
|
275
|
-
return `import KratosPage from ${JSON.stringify(page.source)}
|
|
441
|
+
return `import KratosPage from ${JSON.stringify(`${page.source}.tsx`)}
|
|
276
442
|
import { KratosPageFrame } from '@liujitcn/kratos-taro-app-core/components/KratosPageFrame'
|
|
277
443
|
import { KratosTabBar } from '@liujitcn/kratos-taro-app-core/components/KratosTabBar'
|
|
278
444
|
|
|
@@ -298,6 +464,12 @@ function createPageConfig(page) {
|
|
|
298
464
|
return `export default definePageConfig(${JSON.stringify(page.style ?? {}, null, 2)})
|
|
299
465
|
`;
|
|
300
466
|
}
|
|
467
|
+
function isGeneratedPageFile(file) {
|
|
468
|
+
return existsSync(file) && readFileSync(file, "utf8").includes("\u81EA\u52A8\u751F\u6210\u7684\u6A21\u5757\u9875\u9762\u5305\u88C5\u5668");
|
|
469
|
+
}
|
|
470
|
+
function isGeneratedPageConfig(file) {
|
|
471
|
+
return existsSync(file) && readFileSync(file, "utf8").includes("definePageConfig(");
|
|
472
|
+
}
|
|
301
473
|
function normalizeRoute(route) {
|
|
302
474
|
return route.replace(/\\/g, "/").replace(/^\/+/, "").replace(/\.tsx$/, "");
|
|
303
475
|
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
|
|
2
2
|
// versions:
|
|
3
|
-
// protoc-gen-ts_proto v2.
|
|
3
|
+
// protoc-gen-ts_proto v2.11.8
|
|
4
4
|
// protoc unknown
|
|
5
5
|
// source: base/v1/config.proto
|
|
6
6
|
|
|
7
7
|
/* eslint-disable */
|
|
8
|
-
import type { BaseConfigSite } from "
|
|
8
|
+
import type { BaseConfigSite } from "./enum";
|
|
9
9
|
|
|
10
10
|
/** 获取系统配置条件 */
|
|
11
11
|
export interface GetConfigRequest {
|
|
@@ -1,18 +1,31 @@
|
|
|
1
1
|
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
|
|
2
2
|
// versions:
|
|
3
|
-
// protoc-gen-ts_proto v2.
|
|
3
|
+
// protoc-gen-ts_proto v2.11.8
|
|
4
4
|
// protoc unknown
|
|
5
|
-
// source:
|
|
5
|
+
// source: base/v1/enum.proto
|
|
6
6
|
|
|
7
7
|
/* eslint-disable */
|
|
8
8
|
|
|
9
|
-
/**
|
|
10
|
-
export enum
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
/** AI 助手消息状态 */
|
|
10
|
+
export enum AiMessageStatus {
|
|
11
|
+
/** UNKNOWN_AMS - 未知AI助手消息状态 */
|
|
12
|
+
UNKNOWN_AMS = 0,
|
|
13
|
+
/** GENERATING_AMS - 生成中 */
|
|
14
|
+
GENERATING_AMS = 1,
|
|
15
|
+
/** SUCCESS_AMS - 成功 */
|
|
16
|
+
SUCCESS_AMS = 2,
|
|
17
|
+
/** FAILED_AMS - 失败 */
|
|
18
|
+
FAILED_AMS = 3,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** 终端类型 */
|
|
22
|
+
export enum Terminal {
|
|
23
|
+
/** UNKNOWN_TERMINAL - 未知终端 */
|
|
24
|
+
UNKNOWN_TERMINAL = 0,
|
|
25
|
+
/** TERMINAL_APP - 移动端 */
|
|
26
|
+
TERMINAL_APP = 1,
|
|
27
|
+
/** TERMINAL_ADMIN - 管理端 */
|
|
28
|
+
TERMINAL_ADMIN = 2,
|
|
16
29
|
}
|
|
17
30
|
|
|
18
31
|
/** 系统配置位置 */
|
|
@@ -39,33 +52,3 @@ export enum PasswordCryptoScene {
|
|
|
39
52
|
/** UPDATE_USER_PASSWORD - 个人修改密码加密场景 */
|
|
40
53
|
UPDATE_USER_PASSWORD = 4,
|
|
41
54
|
}
|
|
42
|
-
|
|
43
|
-
/** AI 助手消息状态 */
|
|
44
|
-
export enum AiMessageStatus {
|
|
45
|
-
/** UNKNOWN_AAMS - 未知AI助手消息状态 */
|
|
46
|
-
UNKNOWN_AAMS = 0,
|
|
47
|
-
/** GENERATING_AAMS - 生成中 */
|
|
48
|
-
GENERATING_AAMS = 1,
|
|
49
|
-
/** SUCCESS_AAMS - 成功 */
|
|
50
|
-
SUCCESS_AAMS = 2,
|
|
51
|
-
/** FAILED_AAMS - 失败 */
|
|
52
|
-
FAILED_AAMS = 3,
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/** 支付通知资源类型 */
|
|
56
|
-
export enum ResourceType {
|
|
57
|
-
/** TRANSACTION - 支付 */
|
|
58
|
-
TRANSACTION = 0,
|
|
59
|
-
/** REFUND - 退款 */
|
|
60
|
-
REFUND = 1,
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/** 终端类型 */
|
|
64
|
-
export enum Terminal {
|
|
65
|
-
/** TERMINAL_UNSPECIFIED - 未知终端 */
|
|
66
|
-
TERMINAL_UNSPECIFIED = 0,
|
|
67
|
-
/** TERMINAL_APP - 移动端 */
|
|
68
|
-
TERMINAL_APP = 1,
|
|
69
|
-
/** TERMINAL_ADMIN - 管理端 */
|
|
70
|
-
TERMINAL_ADMIN = 2,
|
|
71
|
-
}
|
package/src/rpc/base/v1/file.ts
CHANGED
package/src/rpc/base/v1/login.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
|
|
2
2
|
// versions:
|
|
3
|
-
// protoc-gen-ts_proto v2.
|
|
3
|
+
// protoc-gen-ts_proto v2.11.8
|
|
4
4
|
// protoc unknown
|
|
5
5
|
// source: base/v1/login.proto
|
|
6
6
|
|
|
7
7
|
/* eslint-disable */
|
|
8
|
-
import type { PasswordCryptoScene } from "../../common/v1/enum";
|
|
9
8
|
import type { PasswordCrypto } from "../../common/v1/types";
|
|
10
9
|
import type { Empty } from "../../google/protobuf/empty";
|
|
10
|
+
import type { PasswordCryptoScene } from "./enum";
|
|
11
11
|
|
|
12
12
|
/** 验证码获取条件 */
|
|
13
13
|
export interface CaptchaRequest {
|
package/src/rpc/base/v1/mcp.ts
CHANGED
package/src/rpc/base/v1/oauth.ts
CHANGED
package/src/rpc/base/v1/sse.ts
CHANGED
package/src/runner.ts
CHANGED
|
@@ -34,13 +34,25 @@ type BuildTransaction = {
|
|
|
34
34
|
originalAppConfig: string
|
|
35
35
|
generatedFiles: string[]
|
|
36
36
|
generatedStaticFiles: string[]
|
|
37
|
-
ownerPid
|
|
37
|
+
ownerPid?: number
|
|
38
|
+
ready?: boolean
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
type PageAssembly = {
|
|
42
|
+
transaction: BuildTransaction
|
|
43
|
+
leaseFile: string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let sourceVersionCounter = 0
|
|
47
|
+
const PAGE_ASSEMBLY_WAIT_TIMEOUT_MS = 30_000
|
|
48
|
+
const PAGE_ASSEMBLY_POLL_INTERVAL_MS = 25
|
|
49
|
+
|
|
40
50
|
type CliOptions = {
|
|
41
51
|
type: 'h5' | 'weapp'
|
|
42
52
|
mode: string
|
|
43
53
|
watch: boolean
|
|
54
|
+
prepareOnly: boolean
|
|
55
|
+
cleanupOnly: boolean
|
|
44
56
|
}
|
|
45
57
|
|
|
46
58
|
/** 运行 Taro 构建并在进程结束后恢复临时页面。 */
|
|
@@ -52,6 +64,7 @@ async function main(): Promise<void> {
|
|
|
52
64
|
const baseConfigFile = resolve(inputDir, 'app.config.base.json')
|
|
53
65
|
const transactionFile = resolve(hostRoot, '.kratos-taro-app-pages-state.json')
|
|
54
66
|
recoverBuildTransaction(transactionFile, inputDir, appConfigFile)
|
|
67
|
+
if (options.cleanupOnly) return
|
|
55
68
|
|
|
56
69
|
const originalAppConfig = readFileSync(appConfigFile, 'utf8')
|
|
57
70
|
const manifest = JSON.parse(readFileSync(baseConfigFile, 'utf8')) as AppManifest
|
|
@@ -69,33 +82,24 @@ async function main(): Promise<void> {
|
|
|
69
82
|
})
|
|
70
83
|
})
|
|
71
84
|
|
|
72
|
-
const
|
|
73
|
-
const pageFile = resolve(inputDir, `${page.route}.tsx`)
|
|
74
|
-
const configFile = resolve(inputDir, `${page.route}.config.ts`)
|
|
75
|
-
return [pageFile, configFile].filter((file) => !existsSync(file))
|
|
76
|
-
})
|
|
77
|
-
const staticRoot = resolve(inputDir, 'static')
|
|
78
|
-
const staticFiles = collectModuleStaticFiles(
|
|
79
|
-
modules.map((module) => resolve(module.root, 'src/static')),
|
|
80
|
-
staticRoot,
|
|
81
|
-
)
|
|
82
|
-
const generatedStaticFiles = staticFiles.map(({ target }) => target)
|
|
83
|
-
const transaction: BuildTransaction = {
|
|
84
|
-
inputDir,
|
|
85
|
+
const pageAssembly = preparePageAssembly({
|
|
85
86
|
appConfigFile,
|
|
87
|
+
baseManifest: manifest,
|
|
88
|
+
inputDir,
|
|
89
|
+
moduleStaticRoots: modules.map((module) => resolve(module.root, 'src/static')),
|
|
86
90
|
originalAppConfig,
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
writeFileSync(transactionFile, `${JSON.stringify(transaction, null, 2)}\n`, { flag: 'wx' })
|
|
91
|
+
pageMap: [...pageMap.values()],
|
|
92
|
+
transactionFile,
|
|
93
|
+
})
|
|
92
94
|
|
|
93
95
|
let cleaned = false
|
|
94
96
|
const cleanup = () => {
|
|
95
97
|
if (cleaned) return
|
|
96
98
|
cleaned = true
|
|
97
|
-
|
|
98
|
-
|
|
99
|
+
releasePageAssembly(transactionFile, pageAssembly.leaseFile)
|
|
100
|
+
}
|
|
101
|
+
if (options.prepareOnly) {
|
|
102
|
+
return
|
|
99
103
|
}
|
|
100
104
|
const handleSignal = (signal: NodeJS.Signals) => {
|
|
101
105
|
child?.kill(signal)
|
|
@@ -105,27 +109,6 @@ async function main(): Promise<void> {
|
|
|
105
109
|
process.once('SIGTERM', () => handleSignal('SIGTERM'))
|
|
106
110
|
|
|
107
111
|
try {
|
|
108
|
-
pageMap.forEach((page) => {
|
|
109
|
-
appendPage(manifest, page.route)
|
|
110
|
-
const pageFile = resolve(inputDir, `${page.route}.tsx`)
|
|
111
|
-
const configFile = resolve(inputDir, `${page.route}.config.ts`)
|
|
112
|
-
if (!existsSync(pageFile)) {
|
|
113
|
-
mkdirSync(dirname(pageFile), { recursive: true })
|
|
114
|
-
writeFileSync(pageFile, createPageWrapper(page))
|
|
115
|
-
}
|
|
116
|
-
if (!existsSync(configFile)) {
|
|
117
|
-
writeFileSync(configFile, createPageConfig(page))
|
|
118
|
-
}
|
|
119
|
-
})
|
|
120
|
-
staticFiles.forEach(({ source, target }) => {
|
|
121
|
-
mkdirSync(dirname(target), { recursive: true })
|
|
122
|
-
copyFileSync(source, target)
|
|
123
|
-
})
|
|
124
|
-
writeFileSync(
|
|
125
|
-
appConfigFile,
|
|
126
|
-
`export default defineAppConfig(${JSON.stringify(manifest, null, 2)})\n`,
|
|
127
|
-
)
|
|
128
|
-
|
|
129
112
|
const args = ['exec', 'taro', 'build', '--type', options.type, '--mode', options.mode]
|
|
130
113
|
if (options.watch) args.push('--watch')
|
|
131
114
|
child = spawn(process.platform === 'win32' ? 'pnpm.cmd' : 'pnpm', args, {
|
|
@@ -133,6 +116,9 @@ async function main(): Promise<void> {
|
|
|
133
116
|
env: {
|
|
134
117
|
...process.env,
|
|
135
118
|
NODE_ENV: options.mode === 'development' ? 'development' : 'production',
|
|
119
|
+
KRATOS_TARO_OUTPUT_ROOT:
|
|
120
|
+
process.env.KRATOS_TARO_OUTPUT_ROOT ||
|
|
121
|
+
(options.type === 'h5' ? 'dist/h5' : 'dist/mp-weixin'),
|
|
136
122
|
},
|
|
137
123
|
stdio: 'inherit',
|
|
138
124
|
})
|
|
@@ -156,6 +142,8 @@ function parseArguments(args: string[]): CliOptions {
|
|
|
156
142
|
let type: CliOptions['type'] | undefined
|
|
157
143
|
let mode = 'production'
|
|
158
144
|
let watch = false
|
|
145
|
+
let prepareOnly = false
|
|
146
|
+
let cleanupOnly = false
|
|
159
147
|
for (let index = 0; index < args.length; index += 1) {
|
|
160
148
|
const argument = args[index]
|
|
161
149
|
if (argument === '--type') {
|
|
@@ -166,12 +154,16 @@ function parseArguments(args: string[]): CliOptions {
|
|
|
166
154
|
mode = args[++index] || mode
|
|
167
155
|
} else if (argument === '--watch') {
|
|
168
156
|
watch = true
|
|
157
|
+
} else if (argument === '--prepare-only') {
|
|
158
|
+
prepareOnly = true
|
|
159
|
+
} else if (argument === '--cleanup-only') {
|
|
160
|
+
cleanupOnly = true
|
|
169
161
|
} else {
|
|
170
162
|
throw new Error(`未知参数:${argument}`)
|
|
171
163
|
}
|
|
172
164
|
}
|
|
173
165
|
if (!type) throw new Error('缺少 --type h5|weapp')
|
|
174
|
-
return { type, mode, watch }
|
|
166
|
+
return { type, mode, watch, prepareOnly, cleanupOnly }
|
|
175
167
|
}
|
|
176
168
|
|
|
177
169
|
async function loadBuildModules(
|
|
@@ -237,27 +229,244 @@ function recoverBuildTransaction(
|
|
|
237
229
|
inputDir: string,
|
|
238
230
|
appConfigFile: string,
|
|
239
231
|
): void {
|
|
232
|
+
const lockFile = `${transactionFile}.lock`
|
|
233
|
+
acquirePageAssemblyLock(lockFile)
|
|
234
|
+
try {
|
|
235
|
+
const transaction = readBuildTransaction(transactionFile, inputDir, appConfigFile)
|
|
236
|
+
if (!transaction || isPageAssemblyActive(transaction, transactionFile)) return
|
|
237
|
+
restoreBuildTransaction(transaction, transactionFile)
|
|
238
|
+
} finally {
|
|
239
|
+
releasePageAssemblyLock(lockFile)
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function preparePageAssembly(options: {
|
|
244
|
+
appConfigFile: string
|
|
245
|
+
baseManifest: AppManifest
|
|
246
|
+
inputDir: string
|
|
247
|
+
moduleStaticRoots: string[]
|
|
248
|
+
originalAppConfig: string
|
|
249
|
+
pageMap: PageEntry[]
|
|
250
|
+
transactionFile: string
|
|
251
|
+
}): PageAssembly {
|
|
252
|
+
const lockFile = `${options.transactionFile}.lock`
|
|
253
|
+
acquirePageAssemblyLock(lockFile)
|
|
254
|
+
try {
|
|
255
|
+
let transaction = readBuildTransaction(
|
|
256
|
+
options.transactionFile,
|
|
257
|
+
options.inputDir,
|
|
258
|
+
options.appConfigFile,
|
|
259
|
+
)
|
|
260
|
+
if (transaction && isPageAssemblyActive(transaction, options.transactionFile)) {
|
|
261
|
+
return {
|
|
262
|
+
transaction,
|
|
263
|
+
leaseFile: createPageAssemblyLease(options.transactionFile),
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
let originalAppConfig = options.originalAppConfig
|
|
268
|
+
if (transaction) {
|
|
269
|
+
originalAppConfig = transaction.originalAppConfig
|
|
270
|
+
restoreBuildTransaction(transaction, options.transactionFile)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const generatedFiles = options.pageMap.flatMap((page) => {
|
|
274
|
+
const pageFile = resolve(options.inputDir, `${page.route}.tsx`)
|
|
275
|
+
const configFile = resolve(options.inputDir, `${page.route}.config.ts`)
|
|
276
|
+
return [pageFile, configFile].filter(
|
|
277
|
+
(file) => !existsSync(file) || isGeneratedPageFile(file) || isGeneratedPageConfig(file),
|
|
278
|
+
)
|
|
279
|
+
})
|
|
280
|
+
const staticFiles = collectModuleStaticFiles(
|
|
281
|
+
options.moduleStaticRoots,
|
|
282
|
+
resolve(options.inputDir, 'static'),
|
|
283
|
+
)
|
|
284
|
+
transaction = {
|
|
285
|
+
inputDir: options.inputDir,
|
|
286
|
+
appConfigFile: options.appConfigFile,
|
|
287
|
+
originalAppConfig,
|
|
288
|
+
generatedFiles,
|
|
289
|
+
generatedStaticFiles: staticFiles.map(({ target }) => target),
|
|
290
|
+
ownerPid: process.pid,
|
|
291
|
+
ready: false,
|
|
292
|
+
}
|
|
293
|
+
writeBuildTransaction(options.transactionFile, transaction, 'wx')
|
|
294
|
+
const leaseFile = createPageAssemblyLease(options.transactionFile)
|
|
295
|
+
try {
|
|
296
|
+
options.pageMap.forEach((page) => {
|
|
297
|
+
appendPage(options.baseManifest, page.route)
|
|
298
|
+
const pageFile = resolve(options.inputDir, `${page.route}.tsx`)
|
|
299
|
+
const configFile = resolve(options.inputDir, `${page.route}.config.ts`)
|
|
300
|
+
if (!existsSync(pageFile)) {
|
|
301
|
+
mkdirSync(dirname(pageFile), { recursive: true })
|
|
302
|
+
writeFileSync(pageFile, createPageWrapper(page))
|
|
303
|
+
}
|
|
304
|
+
if (!existsSync(configFile)) writeFileSync(configFile, createPageConfig(page))
|
|
305
|
+
})
|
|
306
|
+
staticFiles.forEach(({ source, target }) => {
|
|
307
|
+
mkdirSync(dirname(target), { recursive: true })
|
|
308
|
+
copyFileSync(source, target)
|
|
309
|
+
})
|
|
310
|
+
writeFileSync(
|
|
311
|
+
options.appConfigFile,
|
|
312
|
+
`export default defineAppConfig(${JSON.stringify(options.baseManifest, null, 2)})\n`,
|
|
313
|
+
)
|
|
314
|
+
transaction.ready = true
|
|
315
|
+
writeBuildTransaction(options.transactionFile, transaction)
|
|
316
|
+
return { transaction, leaseFile }
|
|
317
|
+
} catch (error) {
|
|
318
|
+
releasePageAssemblyLease(leaseFile)
|
|
319
|
+
restoreBuildTransaction(transaction, options.transactionFile)
|
|
320
|
+
throw error
|
|
321
|
+
}
|
|
322
|
+
} finally {
|
|
323
|
+
releasePageAssemblyLock(lockFile)
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function readBuildTransaction(
|
|
328
|
+
transactionFile: string,
|
|
329
|
+
inputDir: string,
|
|
330
|
+
appConfigFile: string,
|
|
331
|
+
): BuildTransaction | undefined {
|
|
240
332
|
if (!existsSync(transactionFile)) return
|
|
241
333
|
const transaction = JSON.parse(readFileSync(transactionFile, 'utf8')) as BuildTransaction
|
|
242
334
|
if (transaction.inputDir !== inputDir || transaction.appConfigFile !== appConfigFile) {
|
|
243
335
|
throw new Error(`Taro 构建事务目录不匹配:${transactionFile}`)
|
|
244
336
|
}
|
|
245
|
-
|
|
246
|
-
throw new Error(
|
|
247
|
-
`页面装配已由进程 ${transaction.ownerPid} 使用,请先停止另一个 H5 或小程序开发进程`,
|
|
248
|
-
)
|
|
249
|
-
}
|
|
250
|
-
cleanupTransaction(transaction)
|
|
251
|
-
unlinkSync(transactionFile)
|
|
337
|
+
return transaction
|
|
252
338
|
}
|
|
253
339
|
|
|
254
|
-
function
|
|
340
|
+
function writeBuildTransaction(
|
|
341
|
+
transactionFile: string,
|
|
342
|
+
transaction: BuildTransaction,
|
|
343
|
+
flag?: 'wx',
|
|
344
|
+
): void {
|
|
345
|
+
writeFileSync(
|
|
346
|
+
transactionFile,
|
|
347
|
+
`${JSON.stringify(transaction, null, 2)}\n`,
|
|
348
|
+
flag ? { flag } : undefined,
|
|
349
|
+
)
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function restoreBuildTransaction(transaction: BuildTransaction, transactionFile: string): void {
|
|
255
353
|
for (const file of [...transaction.generatedFiles].reverse()) {
|
|
256
354
|
if (existsSync(file)) unlinkSync(file)
|
|
257
355
|
removeEmptyParents(dirname(file), transaction.inputDir)
|
|
258
356
|
}
|
|
259
|
-
removeGeneratedStaticFiles(
|
|
357
|
+
removeGeneratedStaticFiles(
|
|
358
|
+
transaction.generatedStaticFiles ?? [],
|
|
359
|
+
resolve(transaction.inputDir, 'static'),
|
|
360
|
+
)
|
|
260
361
|
writeFileSync(transaction.appConfigFile, transaction.originalAppConfig)
|
|
362
|
+
if (existsSync(transactionFile)) unlinkSync(transactionFile)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function releasePageAssembly(transactionFile: string, leaseFile: string): void {
|
|
366
|
+
releasePageAssemblyLease(leaseFile)
|
|
367
|
+
const lockFile = `${transactionFile}.lock`
|
|
368
|
+
acquirePageAssemblyLock(lockFile)
|
|
369
|
+
try {
|
|
370
|
+
const transaction = readBuildTransactionFromFile(transactionFile)
|
|
371
|
+
if (!transaction || isPageAssemblyActive(transaction, transactionFile)) return
|
|
372
|
+
restoreBuildTransaction(transaction, transactionFile)
|
|
373
|
+
} finally {
|
|
374
|
+
releasePageAssemblyLock(lockFile)
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function readBuildTransactionFromFile(transactionFile: string): BuildTransaction | undefined {
|
|
379
|
+
if (!existsSync(transactionFile)) return
|
|
380
|
+
return JSON.parse(readFileSync(transactionFile, 'utf8')) as BuildTransaction
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function createPageAssemblyLease(transactionFile: string): string {
|
|
384
|
+
const leaseDirectory = `${transactionFile}.leases`
|
|
385
|
+
mkdirSync(leaseDirectory, { recursive: true })
|
|
386
|
+
const leaseFile = resolve(
|
|
387
|
+
leaseDirectory,
|
|
388
|
+
`${process.pid}-${Date.now()}-${++sourceVersionCounter}.lease`,
|
|
389
|
+
)
|
|
390
|
+
writeFileSync(leaseFile, `${JSON.stringify({ ownerPid: process.pid })}\n`, { flag: 'wx' })
|
|
391
|
+
return leaseFile
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function releasePageAssemblyLease(leaseFile: string): void {
|
|
395
|
+
if (existsSync(leaseFile)) unlinkSync(leaseFile)
|
|
396
|
+
const leaseDirectory = dirname(leaseFile)
|
|
397
|
+
if (existsSync(leaseDirectory) && !readdirSync(leaseDirectory).length) {
|
|
398
|
+
try {
|
|
399
|
+
rmdirSync(leaseDirectory)
|
|
400
|
+
} catch (error) {
|
|
401
|
+
const code = (error as NodeJS.ErrnoException).code
|
|
402
|
+
if (code !== 'ENOTEMPTY' && code !== 'EEXIST') throw error
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function isPageAssemblyActive(transaction: BuildTransaction, transactionFile: string): boolean {
|
|
408
|
+
if (listActivePageLeases(`${transactionFile}.leases`).length) return true
|
|
409
|
+
return (
|
|
410
|
+
transaction.ownerPid !== undefined &&
|
|
411
|
+
transaction.ownerPid !== process.pid &&
|
|
412
|
+
isProcessRunning(transaction.ownerPid)
|
|
413
|
+
)
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function listActivePageLeases(leaseDirectory: string): number[] {
|
|
417
|
+
if (!existsSync(leaseDirectory)) return []
|
|
418
|
+
const activePids: number[] = []
|
|
419
|
+
for (const entry of readdirSync(leaseDirectory)) {
|
|
420
|
+
const leaseFile = resolve(leaseDirectory, entry)
|
|
421
|
+
try {
|
|
422
|
+
const lease = JSON.parse(readFileSync(leaseFile, 'utf8')) as { ownerPid?: number }
|
|
423
|
+
if (lease.ownerPid && isProcessRunning(lease.ownerPid)) {
|
|
424
|
+
activePids.push(lease.ownerPid)
|
|
425
|
+
} else {
|
|
426
|
+
unlinkSync(leaseFile)
|
|
427
|
+
}
|
|
428
|
+
} catch {
|
|
429
|
+
unlinkSync(leaseFile)
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return activePids
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function acquirePageAssemblyLock(lockFile: string): void {
|
|
436
|
+
const deadline = Date.now() + PAGE_ASSEMBLY_WAIT_TIMEOUT_MS
|
|
437
|
+
while (true) {
|
|
438
|
+
try {
|
|
439
|
+
writeFileSync(lockFile, `${JSON.stringify({ ownerPid: process.pid })}\n`, { flag: 'wx' })
|
|
440
|
+
return
|
|
441
|
+
} catch (error) {
|
|
442
|
+
if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error
|
|
443
|
+
const ownerPid = readPageAssemblyLockOwner(lockFile)
|
|
444
|
+
if (ownerPid !== undefined && !isProcessRunning(ownerPid)) {
|
|
445
|
+
unlinkSync(lockFile)
|
|
446
|
+
continue
|
|
447
|
+
}
|
|
448
|
+
if (Date.now() >= deadline) throw new Error(`等待 Taro 页面装配锁超时:${lockFile}`)
|
|
449
|
+
waitForPageAssembly(PAGE_ASSEMBLY_POLL_INTERVAL_MS)
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function releasePageAssemblyLock(lockFile: string): void {
|
|
455
|
+
if (!existsSync(lockFile)) return
|
|
456
|
+
if (readPageAssemblyLockOwner(lockFile) === process.pid) unlinkSync(lockFile)
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function readPageAssemblyLockOwner(lockFile: string): number | undefined {
|
|
460
|
+
try {
|
|
461
|
+
return (JSON.parse(readFileSync(lockFile, 'utf8')) as { ownerPid?: number }).ownerPid
|
|
462
|
+
} catch {
|
|
463
|
+
return
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function waitForPageAssembly(milliseconds: number): void {
|
|
468
|
+
const buffer = new SharedArrayBuffer(4)
|
|
469
|
+
Atomics.wait(new Int32Array(buffer), 0, 0, milliseconds)
|
|
261
470
|
}
|
|
262
471
|
|
|
263
472
|
function isProcessRunning(pid: number): boolean {
|
|
@@ -334,7 +543,7 @@ function appendPage(manifest: AppManifest, route: string): void {
|
|
|
334
543
|
}
|
|
335
544
|
|
|
336
545
|
function createPageWrapper(page: PageEntry): string {
|
|
337
|
-
return `import KratosPage from ${JSON.stringify(page.source)}
|
|
546
|
+
return `import KratosPage from ${JSON.stringify(`${page.source}.tsx`)}
|
|
338
547
|
import { KratosPageFrame } from '@liujitcn/kratos-taro-app-core/components/KratosPageFrame'
|
|
339
548
|
import { KratosTabBar } from '@liujitcn/kratos-taro-app-core/components/KratosTabBar'
|
|
340
549
|
|
|
@@ -361,6 +570,14 @@ function createPageConfig(page: PageEntry): string {
|
|
|
361
570
|
return `export default definePageConfig(${JSON.stringify(page.style ?? {}, null, 2)})\n`
|
|
362
571
|
}
|
|
363
572
|
|
|
573
|
+
function isGeneratedPageFile(file: string): boolean {
|
|
574
|
+
return existsSync(file) && readFileSync(file, 'utf8').includes('自动生成的模块页面包装器')
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function isGeneratedPageConfig(file: string): boolean {
|
|
578
|
+
return existsSync(file) && readFileSync(file, 'utf8').includes('definePageConfig(')
|
|
579
|
+
}
|
|
580
|
+
|
|
364
581
|
function normalizeRoute(route: string): string {
|
|
365
582
|
return route
|
|
366
583
|
.replace(/\\/g, '/')
|
package/src/stores/setting.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { create } from 'zustand'
|
|
2
2
|
import { defConfigService } from '../api/base/config'
|
|
3
|
-
import { BaseConfigSite } from '../rpc/
|
|
3
|
+
import { BaseConfigSite } from '../rpc/base/v1/enum'
|
|
4
4
|
|
|
5
5
|
const REQUIRED_APP_CONFIGS = [
|
|
6
6
|
{ key: 'serviceProtocol', name: '服务条款' },
|
package/src/utils/http.ts
CHANGED
|
@@ -113,7 +113,7 @@ async function sendRequest<T>(
|
|
|
113
113
|
...(accessToken ? { Authorization: accessToken } : {}),
|
|
114
114
|
},
|
|
115
115
|
})
|
|
116
|
-
const responseData = response.data as ErrorData
|
|
116
|
+
const responseData = response.data as ErrorData | null | undefined
|
|
117
117
|
if (response.statusCode >= 200 && response.statusCode < 300 && !isAuthErrorResponse(responseData)) {
|
|
118
118
|
return response.data as T
|
|
119
119
|
}
|
|
@@ -129,7 +129,7 @@ async function sendRequest<T>(
|
|
|
129
129
|
handleAuthExpiredByMode(authMode, requestUrl, responseData)
|
|
130
130
|
throw response
|
|
131
131
|
}
|
|
132
|
-
await Taro.showToast({ icon: 'none', title: responseData
|
|
132
|
+
await Taro.showToast({ icon: 'none', title: responseData?.message || '请求错误' })
|
|
133
133
|
throw response
|
|
134
134
|
} catch (error) {
|
|
135
135
|
if (
|
|
@@ -243,7 +243,7 @@ async function promptRelogin(): Promise<void> {
|
|
|
243
243
|
function handleAuthExpiredByMode(
|
|
244
244
|
authMode: AuthMode,
|
|
245
245
|
url: string,
|
|
246
|
-
responseData: ErrorData,
|
|
246
|
+
responseData: ErrorData | null | undefined,
|
|
247
247
|
): void {
|
|
248
248
|
if (authMode === 'required' && !AUTH_EXPIRED_EXCLUDED_URL_SET.has(url)) {
|
|
249
249
|
void promptRelogin()
|
|
@@ -251,7 +251,7 @@ function handleAuthExpiredByMode(
|
|
|
251
251
|
}
|
|
252
252
|
silentClearAuthData()
|
|
253
253
|
if (authMode !== 'optional') {
|
|
254
|
-
void Taro.showToast({ icon: 'none', title: responseData
|
|
254
|
+
void Taro.showToast({ icon: 'none', title: responseData?.message || '请求错误' })
|
|
255
255
|
}
|
|
256
256
|
}
|
|
257
257
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Taro from '@tarojs/taro'
|
|
2
2
|
import * as miniCrypto from 'asmcrypto.js'
|
|
3
3
|
import { defLoginService } from '../api/base/login'
|
|
4
|
-
import { PasswordCryptoScene } from '../rpc/
|
|
4
|
+
import { PasswordCryptoScene } from '../rpc/base/v1/enum'
|
|
5
5
|
import type { PasswordCrypto } from '../rpc/common/v1/types'
|
|
6
6
|
|
|
7
7
|
type MiniCrypto = typeof import('asmcrypto.js')
|