@open-wxa/wxa-runner-core 0.12.3 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +50 -7
- package/dist/index.d.ts +50 -7
- package/dist/index.js +167 -44
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +163 -43
- package/dist/index.mjs.map +1 -1
- package/dist/runtime/frame.js +28 -4
- package/dist/runtime/frame.js.map +1 -1
- package/dist/runtime/host.js +164 -49
- package/dist/runtime/host.js.map +1 -1
- package/dist/{setup--EIOgQvG.d.mts → setup-DnirrFJk.d.mts} +2 -0
- package/dist/{setup--EIOgQvG.d.ts → setup-DnirrFJk.d.ts} +2 -0
- package/dist/setup.d.mts +1 -1
- package/dist/setup.d.ts +1 -1
- package/dist/test-api.d.mts +3 -3
- package/dist/test-api.d.ts +3 -3
- package/dist/{testApi-CoYw0cPm.d.mts → testApi-BqpWSa3B.d.mts} +3 -3
- package/dist/{testApi-BfzCTOhg.d.ts → testApi-CnKR1f3R.d.ts} +3 -3
- package/dist/web.d.mts +2 -0
- package/dist/web.d.ts +2 -0
- package/package.json +3 -3
package/dist/runtime/host.js
CHANGED
|
@@ -1,11 +1,52 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
(() => {
|
|
3
|
-
// src/runtime/host/
|
|
3
|
+
// src/runtime/host/frameErrors.ts
|
|
4
4
|
var DEDUPE_MS = 1e3;
|
|
5
|
+
var MINI_PROGRAM_ERROR = /^(\(in promise\) )?MiniProgramError\n/;
|
|
6
|
+
var STACK_FRAME = /^\s*at /;
|
|
7
|
+
function isErrorLike(value) {
|
|
8
|
+
return typeof value === "object" && value !== null && typeof value.message === "string";
|
|
9
|
+
}
|
|
10
|
+
function caughtError(value) {
|
|
11
|
+
if (isErrorLike(value) && MINI_PROGRAM_ERROR.test(value.message)) {
|
|
12
|
+
const ownStack = typeof value.stack === "string" && /^[^\n]*MiniProgramError\n/.test(value.stack);
|
|
13
|
+
return { message: value.message.replace(MINI_PROGRAM_ERROR, ""), stack: ownStack ? void 0 : value.stack };
|
|
14
|
+
}
|
|
15
|
+
if (typeof value === "string" && MINI_PROGRAM_ERROR.test(value)) {
|
|
16
|
+
const text = value.replace(MINI_PROGRAM_ERROR, "");
|
|
17
|
+
const firstFrame2 = text.search(/^\s*at /m);
|
|
18
|
+
const head = (firstFrame2 < 0 ? text : text.slice(0, firstFrame2)).replace(/\n$/, "");
|
|
19
|
+
const message = /^([\s\S]*)\n[^\n]*?: \1$/.exec(head)?.[1] ?? head;
|
|
20
|
+
return { message, stack: firstFrame2 < 0 ? void 0 : text.slice(firstFrame2) };
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
var RecentErrors = class {
|
|
25
|
+
constructor() {
|
|
26
|
+
this.recent = /* @__PURE__ */ new Map();
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 比较栈里的各帧而不是整个栈:基础库打印成字符串时,栈开头的 `TypeError: …` 那行拆不出来。
|
|
30
|
+
* 不同位置抛出的同名错误帧不同,各记各的
|
|
31
|
+
*/
|
|
32
|
+
isRepeated(kind, webviewId, message, stack) {
|
|
33
|
+
const frames = (stack || "").split("\n").filter((line) => STACK_FRAME.test(line)).map((line) => line.trim()).join("\n");
|
|
34
|
+
const key = `${kind}:${webviewId}:${message}
|
|
35
|
+
${frames}`;
|
|
36
|
+
const now = Date.now();
|
|
37
|
+
for (const [recent, time] of this.recent) {
|
|
38
|
+
if (now - time >= DEDUPE_MS) this.recent.delete(recent);
|
|
39
|
+
}
|
|
40
|
+
if (this.recent.has(key)) return true;
|
|
41
|
+
this.recent.set(key, now);
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// src/runtime/host/ConsoleForwarder.ts
|
|
5
47
|
var MAX_ARG_LENGTH = 1e3;
|
|
6
48
|
var MAX_TEXT_LENGTH = 8e3;
|
|
7
49
|
var LAYERS = { service: "\u903B\u8F91\u5C42", webview: "\u6E32\u67D3\u5C42" };
|
|
8
|
-
var MINI_PROGRAM_ERROR = /^(\(in promise\) )?MiniProgramError\n/;
|
|
9
50
|
var MODULE_URL = /\bhttps?:\/\/[^/\s]+\/__wxa__\/module\/([^?\s)]+?)(?:\?[^:\s)]*)?:(\d+):(\d+)/;
|
|
10
51
|
var ConsoleForwarder = class {
|
|
11
52
|
/**
|
|
@@ -17,13 +58,11 @@
|
|
|
17
58
|
this.send = send;
|
|
18
59
|
this.pageOf = pageOf;
|
|
19
60
|
this.hintsOf = hintsOf;
|
|
20
|
-
this.recentErrors = /* @__PURE__ */ new Map();
|
|
21
61
|
}
|
|
22
|
-
/** 运行时错误:全局 error、未处理的 Promise
|
|
62
|
+
/** 运行时错误:全局 error、未处理的 Promise 拒绝、基础库捕获的业务代码错误。两路报来的同一个错误由宿主去重(见 frameErrors.ts) */
|
|
23
63
|
error(kind, webviewId, message, stack) {
|
|
64
|
+
if (!this.mode) return;
|
|
24
65
|
const frames = businessFrames(stack);
|
|
25
|
-
if (!this.mode || this.isRepeated(`${kind}:${webviewId}:${message}
|
|
26
|
-
${frames}`)) return;
|
|
27
66
|
const hints = this.hintsOf(kind).map((hint) => `
|
|
28
67
|
\u63D0\u793A\uFF1A${hint}`).join("");
|
|
29
68
|
this.post("error", `${this.origin(kind, webviewId)}\u62A5\u9519\uFF1A${message}${frames ? `
|
|
@@ -33,14 +72,9 @@ ${frames}` : ""}${hints}`);
|
|
|
33
72
|
warn(text) {
|
|
34
73
|
if (this.mode) this.post("warn", text);
|
|
35
74
|
}
|
|
36
|
-
/** iframe 里的 console.warn / console.error
|
|
75
|
+
/** iframe 里的 console.warn / console.error;基础库捕获业务代码错误后打印的不经过这里,按报错转发 */
|
|
37
76
|
console(kind, webviewId, level, args) {
|
|
38
|
-
|
|
39
|
-
if (thrown) {
|
|
40
|
-
this.error(kind, webviewId, thrown.message, thrown.stack);
|
|
41
|
-
} else if (this.mode === "all") {
|
|
42
|
-
this.post(level, `${this.origin(kind, webviewId)} console.${level}\uFF1A${args.map(formatArg).join(" ")}`);
|
|
43
|
-
}
|
|
77
|
+
if (this.mode === "all") this.post(level, `${this.origin(kind, webviewId)} console.${level}\uFF1A${args.map(formatArg).join(" ")}`);
|
|
44
78
|
}
|
|
45
79
|
post(level, text) {
|
|
46
80
|
this.send({ type: "console", level, text: text.length > MAX_TEXT_LENGTH ? `${text.slice(0, MAX_TEXT_LENGTH)}\u2026` : text });
|
|
@@ -49,33 +83,7 @@ ${frames}` : ""}${hints}`);
|
|
|
49
83
|
const page = this.pageOf(kind, webviewId);
|
|
50
84
|
return page ? `${LAYERS[kind]}\uFF08${page}\uFF09` : LAYERS[kind];
|
|
51
85
|
}
|
|
52
|
-
/** @param key 来源、错误信息和业务代码的帧:两路报来的同一个错误相同,不同位置抛出的同名错误不同 */
|
|
53
|
-
isRepeated(key) {
|
|
54
|
-
const now = Date.now();
|
|
55
|
-
for (const [recent, time] of this.recentErrors) {
|
|
56
|
-
if (now - time >= DEDUPE_MS) this.recentErrors.delete(recent);
|
|
57
|
-
}
|
|
58
|
-
if (this.recentErrors.has(key)) return true;
|
|
59
|
-
this.recentErrors.set(key, now);
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
86
|
};
|
|
63
|
-
function isErrorLike(value) {
|
|
64
|
-
return typeof value === "object" && value !== null && typeof value.message === "string";
|
|
65
|
-
}
|
|
66
|
-
function miniProgramError(value) {
|
|
67
|
-
if (isErrorLike(value) && MINI_PROGRAM_ERROR.test(value.message)) {
|
|
68
|
-
return { message: value.message.replace(MINI_PROGRAM_ERROR, ""), stack: value.stack };
|
|
69
|
-
}
|
|
70
|
-
if (typeof value === "string" && MINI_PROGRAM_ERROR.test(value)) {
|
|
71
|
-
const text = value.replace(MINI_PROGRAM_ERROR, "");
|
|
72
|
-
const firstFrame2 = text.search(/^\s*at /m);
|
|
73
|
-
const head = (firstFrame2 < 0 ? text : text.slice(0, firstFrame2)).replace(/\n$/, "");
|
|
74
|
-
const message = /^([\s\S]*)\n[^\n]*?: \1$/.exec(head)?.[1] ?? head;
|
|
75
|
-
return { message, stack: firstFrame2 < 0 ? void 0 : text.slice(firstFrame2) };
|
|
76
|
-
}
|
|
77
|
-
return null;
|
|
78
|
-
}
|
|
79
87
|
function businessFrames(stack) {
|
|
80
88
|
return (stack || "").split("\n").filter((line) => /^\s*at /.test(line) && MODULE_URL.test(line)).map((line) => ` ${line.trim().replace(MODULE_URL, "$1:$2:$3")}`).join("\n");
|
|
81
89
|
}
|
|
@@ -106,7 +114,6 @@ ${frames}` : "");
|
|
|
106
114
|
"systemLog",
|
|
107
115
|
"traceEvent",
|
|
108
116
|
"getPermissionBytes",
|
|
109
|
-
"getABTestConfig",
|
|
110
117
|
"initRenderDone",
|
|
111
118
|
"saveInitialRenderingCache",
|
|
112
119
|
"setKeyValue",
|
|
@@ -627,6 +634,7 @@ ${frames}` : "");
|
|
|
627
634
|
|
|
628
635
|
// src/runtime/host/api/misc.ts
|
|
629
636
|
var SERVICE_MARKET = "webapi_servicemarket_jsapi_comm";
|
|
637
|
+
var CLOUD = "qbase_commapi";
|
|
630
638
|
function createMiscApis(appid, options) {
|
|
631
639
|
const viaDevtools = async (api, url, json) => {
|
|
632
640
|
try {
|
|
@@ -636,6 +644,14 @@ ${frames}` : "");
|
|
|
636
644
|
throw error;
|
|
637
645
|
}
|
|
638
646
|
};
|
|
647
|
+
let cloudWarned = false;
|
|
648
|
+
const cloudUnavailable = (api, operation) => {
|
|
649
|
+
if (!cloudWarned) {
|
|
650
|
+
cloudWarned = true;
|
|
651
|
+
console.warn("[wxa-runner] \u4E91\u5F00\u53D1\uFF08wx.cloud\uFF09\u5728\u6D4F\u89C8\u5668\u8FD0\u884C\u65F6\u4E0D\u53EF\u7528\uFF0C\u8C03\u7528\u4F1A\u5931\u8D25");
|
|
652
|
+
}
|
|
653
|
+
return fail(api, `wxa-runner \u4E0D\u652F\u6301\u4E91\u5F00\u53D1\uFF08${operation ?? "unknown"}\uFF09`);
|
|
654
|
+
};
|
|
639
655
|
return {
|
|
640
656
|
// canvas 导出图片、拍照、录像的结果都经过这里
|
|
641
657
|
base64ToTempFilePath: async (args) => {
|
|
@@ -652,11 +668,14 @@ ${frames}` : "");
|
|
|
652
668
|
(error) => serviceMarketFail(error.message)
|
|
653
669
|
);
|
|
654
670
|
}
|
|
671
|
+
if (args.data?.api_name === CLOUD) return cloudUnavailable(api, args.data.data?.qbase_api_name);
|
|
655
672
|
if (args.data?.api_name === "webapi_getwxanetwork") return bizDomainUnavailable(api);
|
|
656
673
|
if (args.data?.api_name === "webapi_getwxauserprivacyauthinfo") return ok(api, { data: { data: JSON.stringify(PRIVACY_AUTH_INFO) } });
|
|
657
|
-
return ok(api);
|
|
674
|
+
return ok(api, { data: {} });
|
|
658
675
|
},
|
|
659
676
|
getAppContact: (args, { api }) => args.keyPath === "WxaAppInfo.Network.BizDomain" ? bizDomainUnavailable(api) : ok(api),
|
|
677
|
+
// 基础库内部的实验开关。不在实验里时原生返回空字符串;没有 testConfig 时 3.3.4 读它的 length 报错
|
|
678
|
+
getABTestConfig: (_args, { api }) => ok(api, { testConfig: "" }),
|
|
660
679
|
// 插件:基础库启动时校验插件、取插件信息,插件里的网络请求要等拿到这两样才发出。插件代码也只在 --devtools-login 时下载
|
|
661
680
|
...options?.devtoolsLogin ? {
|
|
662
681
|
verifyPlugin: (async (args, { api }) => ok(api, await viaDevtools(api, "/__wxa__/api/verify-plugin", { data: args.data }))),
|
|
@@ -1958,7 +1977,7 @@ ${frames}` : "");
|
|
|
1958
1977
|
if (warnedOrigins.has(url.origin)) return;
|
|
1959
1978
|
warnedOrigins.add(url.origin);
|
|
1960
1979
|
console.warn(
|
|
1961
|
-
`[wxa-runner] web-view \u52A0\u8F7D\u7684 ${url.origin} \u4E0E\u6A21\u62DF\u5668\uFF08${location.origin}\uFF09\u4E0D\u662F\u540C\u4E00\u4E2A\u7AD9\u70B9\uFF0C\u6D4F\u89C8\u5668\u6309\u8DE8\u7AD9\u5D4C\u5165\u5904\u7406\uFF0C\u7F51\u9875\u7684 Cookie \u53EF\u80FD\u4E0D\u751F\u6548\uFF08\u5FAE\u4FE1\u91CC\u7684\u5C0F\u7A0B\u5E8F\u6CA1\u6709\u8FD9\u4E2A\u95EE\u9898\uFF09\u3002\u9700\u8981 Cookie \u65F6\u8BA9\u7F51\u9875\u5728\u65B0\u7A97\u53E3\u6253\u5F00\uFF1A\
|
|
1980
|
+
`[wxa-runner] web-view \u52A0\u8F7D\u7684 ${url.origin} \u4E0E\u6A21\u62DF\u5668\uFF08${location.origin}\uFF09\u4E0D\u662F\u540C\u4E00\u4E2A\u7AD9\u70B9\uFF0C\u6D4F\u89C8\u5668\u6309\u8DE8\u7AD9\u5D4C\u5165\u5904\u7406\uFF0C\u7F51\u9875\u7684 Cookie \u53EF\u80FD\u4E0D\u751F\u6548\uFF08\u5FAE\u4FE1\u91CC\u7684\u5C0F\u7A0B\u5E8F\u6CA1\u6709\u8FD9\u4E2A\u95EE\u9898\uFF09\u3002\u9700\u8981 Cookie \u65F6\u8BA9\u7F51\u9875\u5728\u65B0\u7A97\u53E3\u6253\u5F00\uFF1A\u914D\u7F6E\u6587\u4EF6 wxa.config.ts \u91CC\u5199 webViewWindow: true\uFF0CPlaywright \u7528 test.use({ wxaWebView: 'window' })`
|
|
1962
1981
|
);
|
|
1963
1982
|
}
|
|
1964
1983
|
function readableUrl(frame) {
|
|
@@ -2138,6 +2157,49 @@ ${frames}` : "");
|
|
|
2138
2157
|
return navigator.userActivation?.isActive ?? true;
|
|
2139
2158
|
}
|
|
2140
2159
|
|
|
2160
|
+
// src/runtime/host/ui/pageTransition.ts
|
|
2161
|
+
var DURATION = 300;
|
|
2162
|
+
function canAnimatePageTransition(frame) {
|
|
2163
|
+
return frame.isConnected && !document.hidden && !window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
2164
|
+
}
|
|
2165
|
+
async function animatePageTransition(previous, current, direction) {
|
|
2166
|
+
const viewport = current.parentElement;
|
|
2167
|
+
const body = current.contentDocument?.body;
|
|
2168
|
+
if (!viewport || previous.parentElement !== viewport || !body) return;
|
|
2169
|
+
const { visibility, zIndex } = previous.style;
|
|
2170
|
+
const outgoing = previous.contentDocument?.documentElement ?? previous;
|
|
2171
|
+
const wasInert = outgoing.inert;
|
|
2172
|
+
const shield = body.ownerDocument.createElement("div");
|
|
2173
|
+
shield.className = "wxa-page-transition-shield";
|
|
2174
|
+
shield.setAttribute("aria-hidden", "true");
|
|
2175
|
+
shield.style.cssText = "position: fixed; inset: 0; z-index: 2147483647; pointer-events: auto; background: transparent;";
|
|
2176
|
+
const sliding = direction === "push" ? current : previous;
|
|
2177
|
+
const transform = direction === "push" ? ["translateX(100%)", "translateX(0)"] : ["translateX(0)", "translateX(100%)"];
|
|
2178
|
+
let animation;
|
|
2179
|
+
let timer;
|
|
2180
|
+
try {
|
|
2181
|
+
outgoing.inert = true;
|
|
2182
|
+
body.append(shield);
|
|
2183
|
+
previous.style.visibility = "visible";
|
|
2184
|
+
if (direction === "pop") previous.style.zIndex = "2";
|
|
2185
|
+
animation = sliding.animate({ transform }, { duration: DURATION, easing: "ease", fill: "both" });
|
|
2186
|
+
await Promise.race([
|
|
2187
|
+
animation.finished.catch(() => void 0),
|
|
2188
|
+
// 切到后台后,浏览器可能暂停动画,路由队列仍需继续。
|
|
2189
|
+
new Promise((resolve) => {
|
|
2190
|
+
timer = setTimeout(resolve, DURATION + 60);
|
|
2191
|
+
})
|
|
2192
|
+
]);
|
|
2193
|
+
} finally {
|
|
2194
|
+
clearTimeout(timer);
|
|
2195
|
+
animation?.cancel();
|
|
2196
|
+
previous.style.visibility = visibility;
|
|
2197
|
+
previous.style.zIndex = zIndex;
|
|
2198
|
+
outgoing.inert = wasInert;
|
|
2199
|
+
shield.remove();
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
|
|
2141
2203
|
// src/runtime/host/ui/dom.ts
|
|
2142
2204
|
function element(tag, className, text) {
|
|
2143
2205
|
const el = document.createElement(tag);
|
|
@@ -2456,6 +2518,7 @@ html, body { margin: 0; height: 100%; background: radial-gradient(120% 80% at 50
|
|
|
2456
2518
|
this.pages = root.querySelector(".wxa-pages");
|
|
2457
2519
|
this.tabBar = root.querySelector(".wxa-tabbar");
|
|
2458
2520
|
this.overlay = root.querySelector(".wxa-overlay");
|
|
2521
|
+
guardOverlayTouchClicks(this.overlay);
|
|
2459
2522
|
this.routeLabel = root.querySelector(".wxa-route");
|
|
2460
2523
|
root.querySelector(".wxa-reload").addEventListener("click", () => location.reload());
|
|
2461
2524
|
this.scaleButton.addEventListener("click", () => {
|
|
@@ -2494,6 +2557,18 @@ html, body { margin: 0; height: 100%; background: radial-gradient(120% 80% at 50
|
|
|
2494
2557
|
onBack(handler) {
|
|
2495
2558
|
this.backButton.addEventListener("click", handler);
|
|
2496
2559
|
}
|
|
2560
|
+
async transitionPages(previous, current, direction) {
|
|
2561
|
+
const navWasInert = this.navBar.inert;
|
|
2562
|
+
const tabsWereInert = this.tabBar.inert;
|
|
2563
|
+
this.navBar.inert = true;
|
|
2564
|
+
this.tabBar.inert = true;
|
|
2565
|
+
try {
|
|
2566
|
+
await animatePageTransition(previous, current, direction);
|
|
2567
|
+
} finally {
|
|
2568
|
+
this.navBar.inert = navWasInert;
|
|
2569
|
+
this.tabBar.inert = tabsWereInert;
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2497
2572
|
setTabBar(config, onTap) {
|
|
2498
2573
|
this.tabBarConfig = config && config.list?.length && !config.custom ? config : null;
|
|
2499
2574
|
this.onTabTap = onTap;
|
|
@@ -2738,6 +2813,24 @@ html, body { margin: 0; height: 100%; background: radial-gradient(120% 80% at 50
|
|
|
2738
2813
|
function maskElement(dim) {
|
|
2739
2814
|
return element("div", "wxa-mask" + (dim ? " is-dim" : ""));
|
|
2740
2815
|
}
|
|
2816
|
+
function guardOverlayTouchClicks(overlay) {
|
|
2817
|
+
let press;
|
|
2818
|
+
overlay.addEventListener("pointerdown", (event) => {
|
|
2819
|
+
press = event.pointerType === "touch" && event.target instanceof Node ? { id: event.pointerId, target: event.target } : void 0;
|
|
2820
|
+
}, true);
|
|
2821
|
+
overlay.addEventListener("pointercancel", (event) => {
|
|
2822
|
+
if (press?.id === event.pointerId) press = void 0;
|
|
2823
|
+
}, true);
|
|
2824
|
+
overlay.addEventListener("click", (event) => {
|
|
2825
|
+
if (event.pointerType !== "touch") return;
|
|
2826
|
+
if (press?.id === event.pointerId) {
|
|
2827
|
+
const start = press;
|
|
2828
|
+
press = void 0;
|
|
2829
|
+
if (event.target instanceof Node && event.target.contains(start.target)) return;
|
|
2830
|
+
}
|
|
2831
|
+
event.stopPropagation();
|
|
2832
|
+
}, true);
|
|
2833
|
+
}
|
|
2741
2834
|
var BACK_ICON = '<svg width="12" height="20" viewBox="0 0 12 20"><path d="M10 2 2 10l8 8" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
|
|
2742
2835
|
var MORE_ICON = '<svg width="22" height="8" viewBox="0 0 22 8" fill="currentColor"><circle cx="3.5" cy="4" r="2.3"/><circle cx="11" cy="4" r="3.3"/><circle cx="18.5" cy="4" r="2.3"/></svg>';
|
|
2743
2836
|
var CLOSE_ICON = '<svg width="20" height="20" viewBox="0 0 20 20"><circle cx="10" cy="10" r="8.2" fill="none" stroke="currentColor" stroke-width="1.8"/><circle cx="10" cy="10" r="3.4" fill="currentColor"/></svg>';
|
|
@@ -2760,6 +2853,7 @@ html, body { margin: 0; height: 100%; background: radial-gradient(120% 80% at 50
|
|
|
2760
2853
|
constructor(info, root, sendReport = () => void 0) {
|
|
2761
2854
|
this.info = info;
|
|
2762
2855
|
this.errors = [];
|
|
2856
|
+
this.recentErrors = new RecentErrors();
|
|
2763
2857
|
/** 小程序发出的 wx.request */
|
|
2764
2858
|
this.requests = new RequestLog();
|
|
2765
2859
|
this.api = new ApiRegistry((context) => this.reportUnimplemented(context));
|
|
@@ -2917,14 +3011,20 @@ html, body { margin: 0; height: 100%; background: radial-gradient(120% 80% at 50
|
|
|
2917
3011
|
message: err ? err.message : String(error),
|
|
2918
3012
|
stack: typeof err?.stack === "string" ? err.stack : void 0
|
|
2919
3013
|
};
|
|
2920
|
-
this.
|
|
2921
|
-
this.forwarder.error(kind, webviewId, report.message, report.stack);
|
|
3014
|
+
this.recordError(report);
|
|
2922
3015
|
}
|
|
2923
3016
|
get forwardConsole() {
|
|
2924
3017
|
return this.forwarder.mode;
|
|
2925
3018
|
}
|
|
2926
3019
|
reportConsole(kind, webviewId, level, args) {
|
|
2927
|
-
|
|
3020
|
+
const caught = level === "error" ? caughtError(args[0]) : null;
|
|
3021
|
+
if (caught) this.recordError({ kind, webviewId, ...caught });
|
|
3022
|
+
else this.forwarder.console(kind, webviewId, level, args);
|
|
3023
|
+
}
|
|
3024
|
+
recordError(report) {
|
|
3025
|
+
if (this.recentErrors.isRepeated(report.kind, report.webviewId, report.message, report.stack)) return;
|
|
3026
|
+
this.errors.push(report);
|
|
3027
|
+
this.forwarder.error(report.kind, report.webviewId, report.message, report.stack);
|
|
2928
3028
|
}
|
|
2929
3029
|
/**
|
|
2930
3030
|
* 只记录逻辑层的公开 wx API 和 FileSystemManager 方法,每个原生接口一次。
|
|
@@ -2987,20 +3087,35 @@ html, body { margin: 0; height: 100%; background: radial-gradient(120% 80% at 50
|
|
|
2987
3087
|
this.stack.reset(page);
|
|
2988
3088
|
this.activate(page, "appLaunch");
|
|
2989
3089
|
}
|
|
2990
|
-
async navigateTo(url) {
|
|
3090
|
+
async navigateTo(url, { animate = true } = {}) {
|
|
2991
3091
|
if (!this.stack.canPush()) return fail("navigateTo", "webview count limit exceed");
|
|
2992
3092
|
const { path, query } = this.resolveUrl(url);
|
|
2993
3093
|
if (this.tabPaths.has(path)) return fail("navigateTo", "can not navigateTo a tabbar page");
|
|
2994
3094
|
if (!this.hasPage(path)) return fail("navigateTo", `page "${path}" is not found`);
|
|
2995
3095
|
const page = await this.openPage(path, query);
|
|
3096
|
+
const previous = this.currentPage();
|
|
2996
3097
|
this.stack.push(page);
|
|
2997
3098
|
this.activate(page, "navigateTo");
|
|
3099
|
+
if (animate && this.info.pageTransition && previous && canAnimatePageTransition(previous.frame)) {
|
|
3100
|
+
await this.shell.transitionPages(previous.frame, page.frame, "push");
|
|
3101
|
+
}
|
|
2998
3102
|
return ok("navigateTo", { webviewId: page.id });
|
|
2999
3103
|
}
|
|
3000
3104
|
async navigateBack(delta) {
|
|
3001
3105
|
if (this.stack.size <= 1) return fail("navigateBack", "cannot navigate back at first page.");
|
|
3002
|
-
this.stack.pop(delta)
|
|
3003
|
-
|
|
3106
|
+
const popped = this.stack.pop(delta);
|
|
3107
|
+
const outgoing = popped[0];
|
|
3108
|
+
const animate = this.info.pageTransition && outgoing && canAnimatePageTransition(outgoing.frame);
|
|
3109
|
+
for (const page of popped) {
|
|
3110
|
+
if (!animate || page !== outgoing) this.closePage(page);
|
|
3111
|
+
}
|
|
3112
|
+
const target = this.currentPage();
|
|
3113
|
+
try {
|
|
3114
|
+
this.activate(target, "navigateBack");
|
|
3115
|
+
if (animate) await this.shell.transitionPages(outgoing.frame, target.frame, "pop");
|
|
3116
|
+
} finally {
|
|
3117
|
+
if (animate) this.closePage(outgoing);
|
|
3118
|
+
}
|
|
3004
3119
|
return ok("navigateBack");
|
|
3005
3120
|
}
|
|
3006
3121
|
async redirectTo(url) {
|
|
@@ -3432,7 +3547,7 @@ ${script}
|
|
|
3432
3547
|
await host.enqueueRoute(async () => {
|
|
3433
3548
|
for (const { path, query } of this.saved.slice(1)) {
|
|
3434
3549
|
const search = new URLSearchParams(query).toString();
|
|
3435
|
-
const result = await host.navigateTo(search ? `${path}?${search}` : path);
|
|
3550
|
+
const result = await host.navigateTo(search ? `${path}?${search}` : path, { animate: false });
|
|
3436
3551
|
if (!String(result.errMsg).endsWith(":ok")) {
|
|
3437
3552
|
console.warn(`[wxa-runner] \u6062\u590D\u9875\u9762\u6808\u65F6\u6253\u4E0D\u5F00 ${path}\uFF1A${result.errMsg}`);
|
|
3438
3553
|
return;
|