@open-wxa/wxa-runner-core 0.6.0 → 0.7.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 +15 -6
- package/dist/index.d.ts +15 -6
- package/dist/index.js +280 -78
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +274 -72
- package/dist/index.mjs.map +1 -1
- package/dist/runtime/host.js +30 -12
- package/dist/runtime/host.js.map +1 -1
- package/dist/{setup-Cv340Lvj.d.mts → setup-DZ5FeKay.d.mts} +2 -1
- package/dist/{setup-Cv340Lvj.d.ts → setup-DZ5FeKay.d.ts} +2 -1
- package/dist/setup.d.mts +1 -1
- package/dist/setup.d.ts +1 -1
- package/dist/test-api.d.mts +2 -2
- package/dist/test-api.d.ts +2 -2
- package/dist/{testApi-nKsjjEhz.d.ts → testApi-BLozOgCt.d.ts} +1 -1
- package/dist/{testApi-DO2FyR78.d.mts → testApi-CnZOLyWw.d.mts} +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -445,6 +445,178 @@ function readJsonFile(file) {
|
|
|
445
445
|
}
|
|
446
446
|
}
|
|
447
447
|
|
|
448
|
+
// src/server/plugins.ts
|
|
449
|
+
import { readWxvpkgFile, readWxvpkgIndex } from "@open-wxa/wxa-runner-extractor";
|
|
450
|
+
|
|
451
|
+
// src/server/wechatDev.ts
|
|
452
|
+
import { TICKET_ENV } from "@open-wxa/wxa-runner-extractor";
|
|
453
|
+
var BASE_URL = "https://servicewechat.com";
|
|
454
|
+
var CLIENT_VERSION = "1021810240";
|
|
455
|
+
var DEFAULT_TIMEOUT = 1e4;
|
|
456
|
+
var ERROR_HINTS = {
|
|
457
|
+
// 40001:开发者工具刷新过 ticket,旧的随即作废
|
|
458
|
+
40001: "\uFF08\u767B\u5F55\u6001\u5DF2\u5931\u6548\uFF0C\u8BF7\u5728\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u91CC\u91CD\u65B0\u767B\u5F55\uFF09",
|
|
459
|
+
40029: "\uFF08\u767B\u5F55\u6001\u5DF2\u5931\u6548\uFF0C\u8BF7\u5728\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u91CC\u91CD\u65B0\u767B\u5F55\uFF09",
|
|
460
|
+
85023: "\uFF08\u5F53\u524D\u5FAE\u4FE1\u8D26\u53F7\u4E0D\u662F\u8BE5\u5C0F\u7A0B\u5E8F\u7684\u5F00\u53D1\u8005\uFF09"
|
|
461
|
+
};
|
|
462
|
+
function requireUsableTicket(ticket) {
|
|
463
|
+
if (!ticket) throw new Error(`\u672A\u627E\u5230\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u7684\u767B\u5F55\u6001\uFF0C\u8BF7\u767B\u5F55\u5F00\u53D1\u8005\u5DE5\u5177\uFF0C\u6216\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF ${TICKET_ENV}`);
|
|
464
|
+
if (ticket.expiredAt !== void 0 && ticket.expiredAt <= Date.now()) {
|
|
465
|
+
throw new Error("\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u7684\u767B\u5F55\u6001\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u6253\u5F00\u5F00\u53D1\u8005\u5DE5\u5177\u5237\u65B0\u767B\u5F55\u6001");
|
|
466
|
+
}
|
|
467
|
+
return ticket.newticket;
|
|
468
|
+
}
|
|
469
|
+
function explainErrcode(errcode) {
|
|
470
|
+
return errcode !== void 0 && ERROR_HINTS[errcode] || "";
|
|
471
|
+
}
|
|
472
|
+
function requireOk(body, action) {
|
|
473
|
+
const errcode = body.baseresponse?.errcode;
|
|
474
|
+
if (errcode !== 0) throw new Error(`${action} ${errcode}: ${body.baseresponse?.errmsg || "\u672A\u77E5\u9519\u8BEF"}${explainErrcode(errcode)}`);
|
|
475
|
+
return body;
|
|
476
|
+
}
|
|
477
|
+
var WechatDevClient = class {
|
|
478
|
+
/**
|
|
479
|
+
* @param findTicket 每次请求都重新取:开发者工具重新登录、切换账号后不用重启服务器
|
|
480
|
+
*/
|
|
481
|
+
constructor(appid, findTicket) {
|
|
482
|
+
this.appid = appid;
|
|
483
|
+
this.findTicket = findTicket;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* POST JSON 到 path(如 wxa-dev-logic/jslogin),带上 appid 和开发者 ticket
|
|
487
|
+
*
|
|
488
|
+
* @throws 没有可用的登录态、超时、连不上、HTTP 状态不是 2xx 时,消息是给用户看的原因
|
|
489
|
+
*/
|
|
490
|
+
async post(path5, body, timeout = DEFAULT_TIMEOUT) {
|
|
491
|
+
const query = new URLSearchParams({ appid: this.appid, newticket: requireUsableTicket(await this.findTicket()), clientversion: CLIENT_VERSION });
|
|
492
|
+
const response = await fetch(`${BASE_URL}/${path5}?${query}`, {
|
|
493
|
+
method: "POST",
|
|
494
|
+
headers: { "Content-Type": "application/json" },
|
|
495
|
+
body: JSON.stringify(body),
|
|
496
|
+
signal: AbortSignal.timeout(timeout)
|
|
497
|
+
}).catch((error) => {
|
|
498
|
+
throw new Error(error.name === "TimeoutError" ? `\u8BF7\u6C42\u5FAE\u4FE1\u670D\u52A1\u5668\u8D85\u65F6\uFF08${timeout / 1e3}s\uFF09` : `\u8BF7\u6C42\u5FAE\u4FE1\u670D\u52A1\u5668\u5931\u8D25\uFF1A${error.cause?.message ?? error.message}`);
|
|
499
|
+
});
|
|
500
|
+
if (!response.ok) throw new Error(`\u5FAE\u4FE1\u670D\u52A1\u5668\u8FD4\u56DE HTTP ${response.status}`);
|
|
501
|
+
return response;
|
|
502
|
+
}
|
|
503
|
+
/** 同 post,返回解析后的 JSON */
|
|
504
|
+
async postJson(path5, body) {
|
|
505
|
+
return (await this.post(path5, body)).json();
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
// src/server/plugins.ts
|
|
510
|
+
var DOWNLOAD_TIMEOUT = 3e4;
|
|
511
|
+
var PACKAGE_MAGIC = 190;
|
|
512
|
+
var PluginStore = class {
|
|
513
|
+
/**
|
|
514
|
+
* @param wechat 开启 --devtools-login 时才有,没有时不下载插件
|
|
515
|
+
*/
|
|
516
|
+
constructor(wechat, logger) {
|
|
517
|
+
this.wechat = wechat;
|
|
518
|
+
this.logger = logger;
|
|
519
|
+
/** provider@version → 下载结果;失败的不留,下次打开小程序时重试 */
|
|
520
|
+
this.downloads = /* @__PURE__ */ new Map();
|
|
521
|
+
/**
|
|
522
|
+
* 这次打开小程序时决定用哪些插件。同一次打开的逻辑层、业务模块、渲染层等请求都按它处理:
|
|
523
|
+
* 各自去下载的话,下载失败时每个请求都会重试,结果还可能前后不一,页面列表和逻辑层对不上
|
|
524
|
+
*/
|
|
525
|
+
this.selection = null;
|
|
526
|
+
/** 没开 --devtools-login 的提示每个插件只说一次 */
|
|
527
|
+
this.warned = /* @__PURE__ */ new Set();
|
|
528
|
+
}
|
|
529
|
+
/** 打开小程序时(宿主取 app-info)调用:重新决定用哪些插件,下载过的直接用,上次失败的重试 */
|
|
530
|
+
async refresh(build2) {
|
|
531
|
+
this.selection = this.select(build2);
|
|
532
|
+
return withPlugins(build2, await this.selection);
|
|
533
|
+
}
|
|
534
|
+
/** 其余请求沿用这次打开时的决定,不触发下载;还没决定过时(如直接请求逻辑层)先决定一次 */
|
|
535
|
+
async current(build2) {
|
|
536
|
+
this.selection ??= this.select(build2);
|
|
537
|
+
return withPlugins(build2, await this.selection);
|
|
538
|
+
}
|
|
539
|
+
/** app.json 声明的插件;下载不了的跳过,在终端说明原因,小程序自己的页面照常可用 */
|
|
540
|
+
async select(build2) {
|
|
541
|
+
const declared = build2.wxConfig.plugins || {};
|
|
542
|
+
const results = await Promise.all(Object.entries(declared).map(
|
|
543
|
+
([alias, { provider, version }]) => provider && version ? this.load(alias, provider, version) : null
|
|
544
|
+
));
|
|
545
|
+
return results.filter((plugin) => !!plugin);
|
|
546
|
+
}
|
|
547
|
+
async load(alias, provider, version) {
|
|
548
|
+
const label = `\u63D2\u4EF6 ${alias}\uFF08${provider}@${version}\uFF09`;
|
|
549
|
+
if (!this.wechat) {
|
|
550
|
+
this.warnOnce(label, `[wxa-runner] ${label} \u7684\u4EE3\u7801\u8981\u7528\u5F00\u53D1\u8005\u7684\u767B\u5F55\u6001\u5411\u5FAE\u4FE1\u4E0B\u8F7D\uFF0C\u542F\u52A8\u65F6\u52A0 --devtools-login`);
|
|
551
|
+
return null;
|
|
552
|
+
}
|
|
553
|
+
const key = `${provider}@${version}`;
|
|
554
|
+
let download = this.downloads.get(key);
|
|
555
|
+
if (!download) {
|
|
556
|
+
download = this.download(this.wechat, alias, provider, version);
|
|
557
|
+
this.downloads.set(key, download);
|
|
558
|
+
download.catch(() => this.downloads.delete(key));
|
|
559
|
+
}
|
|
560
|
+
try {
|
|
561
|
+
return await download;
|
|
562
|
+
} catch (error) {
|
|
563
|
+
this.logger.warn(`[wxa-runner] ${label} \u4E0B\u8F7D\u5931\u8D25\uFF1A${error.message}`);
|
|
564
|
+
return null;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
/** @throws 没有登录态、连不上、微信拒绝或返回的不是插件代码包时,消息是原因 */
|
|
568
|
+
async download(wechat, alias, provider, version) {
|
|
569
|
+
const response = await wechat.post("wxa-dev/getplugincode", { plugin_id: provider, plugin_version: version }, DOWNLOAD_TIMEOUT);
|
|
570
|
+
const data = Buffer.from(await response.arrayBuffer());
|
|
571
|
+
if (data[0] !== PACKAGE_MAGIC) {
|
|
572
|
+
let body = {};
|
|
573
|
+
try {
|
|
574
|
+
body = JSON.parse(data.toString("utf8"));
|
|
575
|
+
} catch {
|
|
576
|
+
}
|
|
577
|
+
if (body.baseresponse?.errcode) requireOk(body, "\u5FAE\u4FE1\u62D2\u7EDD\u4E0B\u8F7D");
|
|
578
|
+
throw new Error("\u5FAE\u4FE1\u6CA1\u6709\u8FD4\u56DE\u63D2\u4EF6\u4EE3\u7801");
|
|
579
|
+
}
|
|
580
|
+
const files = new Map(readWxvpkgIndex(data).map((entry) => [entry.name, Buffer.from(readWxvpkgFile(data, entry))]));
|
|
581
|
+
const pluginJson = JSON.parse(files.get("/plugin.json")?.toString("utf8") || "{}");
|
|
582
|
+
return {
|
|
583
|
+
alias,
|
|
584
|
+
provider,
|
|
585
|
+
version,
|
|
586
|
+
files,
|
|
587
|
+
pages: Object.values(pluginJson.pages || {}),
|
|
588
|
+
pageConfigs: pluginJson.page || {}
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
warnOnce(key, message) {
|
|
592
|
+
if (this.warned.has(key)) return;
|
|
593
|
+
this.warned.add(key);
|
|
594
|
+
this.logger.warn(message);
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
function withPlugins(build2, plugins) {
|
|
598
|
+
if (!plugins.length) return { ...build2, plugins };
|
|
599
|
+
return {
|
|
600
|
+
...build2,
|
|
601
|
+
plugins,
|
|
602
|
+
pages: [...build2.pages, ...plugins.flatMap((plugin) => plugin.pages)],
|
|
603
|
+
wxConfig: {
|
|
604
|
+
...build2.wxConfig,
|
|
605
|
+
page: { ...build2.wxConfig.page, ...Object.assign({}, ...plugins.map((plugin) => plugin.pageConfigs)) }
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
function findPluginPage(build2, pagePath) {
|
|
610
|
+
for (const plugin of build2.plugins) {
|
|
611
|
+
const prefix = `__plugin__/${plugin.provider}/`;
|
|
612
|
+
if (plugin.pages.includes(pagePath)) return { plugin, innerPath: pagePath.slice(prefix.length) };
|
|
613
|
+
}
|
|
614
|
+
return null;
|
|
615
|
+
}
|
|
616
|
+
function pluginScriptUrl(plugin, file) {
|
|
617
|
+
return `/__wxa__/plugin/${encodeURIComponent(plugin.provider)}/${encodeURIComponent(plugin.version)}/${file}`;
|
|
618
|
+
}
|
|
619
|
+
|
|
448
620
|
// src/server/html.ts
|
|
449
621
|
function hostHtml() {
|
|
450
622
|
return `<!DOCTYPE html>
|
|
@@ -471,10 +643,11 @@ function appserviceHtml(build2, launch, options) {
|
|
|
471
643
|
...options.isolated ? { isIsolateContext: true } : {}
|
|
472
644
|
};
|
|
473
645
|
const v = build2.version;
|
|
646
|
+
const pluginUrls = build2.plugins.map((plugin) => pluginScriptUrl(plugin, "appservice.js"));
|
|
474
647
|
const moduleUrls = build2.modules.map((id) => `/__wxa__/module/${encodeURI(id)}?v=${v}`);
|
|
475
648
|
const bootstrapUrl = `/__wxa__/service-bootstrap.js?v=${v}`;
|
|
476
649
|
if (options.isolated) {
|
|
477
|
-
const scripts = [...moduleUrls, bootstrapUrl];
|
|
650
|
+
const scripts = [...pluginUrls, ...moduleUrls, bootstrapUrl];
|
|
478
651
|
return `<!DOCTYPE html>
|
|
479
652
|
<html>
|
|
480
653
|
<head>
|
|
@@ -487,7 +660,7 @@ function appserviceHtml(build2, launch, options) {
|
|
|
487
660
|
<body></body>
|
|
488
661
|
</html>`;
|
|
489
662
|
}
|
|
490
|
-
const modules = moduleUrls.map((src) => `<script src="${src}"></script>`).join("\n");
|
|
663
|
+
const modules = [...pluginUrls, ...moduleUrls].map((src) => `<script src="${src}"></script>`).join("\n");
|
|
491
664
|
return `<!DOCTYPE html>
|
|
492
665
|
<html>
|
|
493
666
|
<head>
|
|
@@ -521,29 +694,13 @@ ${empty ? "" : '<script src="/__baselib__/WASubContext.js"></script>\n'}<script>
|
|
|
521
694
|
</html>`;
|
|
522
695
|
}
|
|
523
696
|
function webviewHtml(build2, pagePath, webviewId, options) {
|
|
524
|
-
const pageConfig = build2.wxConfig.page?.[pagePath + ".html"];
|
|
525
|
-
const window = { ...build2.wxConfig.global?.window, ...pageConfig?.window };
|
|
526
|
-
const wxConfig = { ...build2.wxConfig, window };
|
|
527
697
|
const id = Number(webviewId) || 0;
|
|
528
698
|
return `<!DOCTYPE html>
|
|
529
699
|
<html>
|
|
530
700
|
<head>
|
|
531
|
-
|
|
532
|
-
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
|
533
|
-
<title>${escapeHtml(pagePath)}</title>
|
|
534
|
-
<!-- \u5C0F\u7A0B\u5E8F\u6E32\u67D3\u5C42\u7684\u9ED8\u8BA4\u5B57\u53F7\u662F 16px\uFF1B\u4E0D\u58F0\u660E\u7684\u8BDD\u4F1A\u7EE7\u627F\u6D4F\u89C8\u5668\u8BBE\u7F6E\u91CC\u7684\u9ED8\u8BA4\u5B57\u53F7\u3002\u653E\u5728\u6700\u524D\u9762\uFF0C\u57FA\u7840\u5E93\u548C\u9875\u9762\u6837\u5F0F\u90FD\u80FD\u8986\u76D6\u5B83 -->
|
|
535
|
-
<style>html{font-size:16px}</style>
|
|
536
|
-
<!-- \u771F\u673A\u7684\u6EDA\u52A8\u6761\u662F\u4E0D\u5360\u5E03\u5C40\u7684\u6D6E\u5C42\uFF1B\u684C\u9762\u6D4F\u89C8\u5668\u7684\u5E38\u89C4\u6EDA\u52A8\u6761\u548C\u81EA\u5B9A\u4E49 ::-webkit-scrollbar \u4F1A\u6324\u7A84\u9875\u9762\uFF0C\u4E00\u5F8B\u9690\u85CF\u3002
|
|
537
|
-
scrollbar-width \u4F18\u5148\u4E8E ::-webkit-scrollbar\uFF0C\u9875\u9762\u6837\u5F0F\u4E0D\u4F1A\u518D\u628A\u6EDA\u52A8\u6761\u52A0\u56DE\u6765 -->
|
|
538
|
-
<style>*{scrollbar-width:none}::-webkit-scrollbar{display:none}</style>
|
|
539
|
-
<!-- \u903B\u8F91\u5C42\u5EFA\u7684 2d\u3001webgl \u753B\u5E03\u642C\u8FDB wx-canvas \u540E\u94FA\u6EE1\u5B83\uFF08\u89C1 frame.ts \u7684 moveCanvasesIntoPages\uFF09\u3002
|
|
540
|
-
\u57FA\u7840\u5E93\u8FD8\u4F1A\u6309\u903B\u8F91\u5C42\u91CC\u7684\u5E03\u5C40\u6539\u5B83\u7684\u4F4D\u7F6E\u548C\u663E\u9690\uFF0C\u8FD9\u91CC\u4E00\u5F8B\u538B\u4F4F\uFF1B\u89E6\u6478\u7167\u5E38\u4EA4\u7ED9 wx-canvas \u81EA\u5DF1\u7684 canvas -->
|
|
541
|
-
<style>.wxa-service-canvas{position:absolute!important;left:0!important;top:0!important;width:100%!important;height:100%!important;visibility:inherit!important;pointer-events:none}</style>
|
|
542
|
-
<script>var __wxConfig = ${toJs(wxConfig)};</script>
|
|
543
|
-
<script src="/__wxa__/runtime/frame.js" data-kind="webview" data-webview-id="${id}"></script>
|
|
544
|
-
<script src="/__baselib__/WAWebview.js"></script>
|
|
701
|
+
${webviewHead(build2, pagePath, id)}
|
|
545
702
|
${enableHotUpdate(options.hot)}<script src="/__wxa__/page-frame.js?v=${build2.version}"></script>
|
|
546
|
-
${
|
|
703
|
+
${pageWindow(build2, pagePath).disableScroll ? "<style>body{overflow-y:hidden}</style>" : ""}
|
|
547
704
|
</head>
|
|
548
705
|
<body>
|
|
549
706
|
<script>
|
|
@@ -579,6 +736,42 @@ self.console = __wxaConsole;
|
|
|
579
736
|
importScripts(${modules.map((url) => toJs(url)).join(", ")});
|
|
580
737
|
`;
|
|
581
738
|
}
|
|
739
|
+
function pluginWebviewHtml(build2, plugin, innerPath, pagePath, webviewId) {
|
|
740
|
+
const id = Number(webviewId) || 0;
|
|
741
|
+
const page = plugin.files.get(`/${innerPath}.html`)?.toString("utf8") ?? `<script>console.error(${JSON.stringify(`[wxa-runner] \u63D2\u4EF6 ${plugin.alias} \u91CC\u6CA1\u6709\u9875\u9762 ${innerPath}`)})</script>`;
|
|
742
|
+
return `<!DOCTYPE html>
|
|
743
|
+
<html>
|
|
744
|
+
<head>
|
|
745
|
+
${webviewHead(build2, pagePath, id)}
|
|
746
|
+
<script src="${pluginScriptUrl(plugin, "pageframe.js")}"></script>
|
|
747
|
+
${pageWindow(build2, pagePath).disableScroll ? "<style>body{overflow-y:hidden}</style>" : ""}
|
|
748
|
+
</head>
|
|
749
|
+
<body>
|
|
750
|
+
${page}
|
|
751
|
+
<script>parent.__wxaHost.frameLoaded('webview', ${id});</script>
|
|
752
|
+
</body>
|
|
753
|
+
</html>`;
|
|
754
|
+
}
|
|
755
|
+
function pageWindow(build2, pagePath) {
|
|
756
|
+
return { ...build2.wxConfig.global?.window, ...build2.wxConfig.page?.[pagePath + ".html"]?.window };
|
|
757
|
+
}
|
|
758
|
+
function webviewHead(build2, pagePath, id) {
|
|
759
|
+
const wxConfig = { ...build2.wxConfig, window: pageWindow(build2, pagePath) };
|
|
760
|
+
return `<meta charset="utf-8">
|
|
761
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
|
762
|
+
<title>${escapeHtml(pagePath)}</title>
|
|
763
|
+
<!-- \u5C0F\u7A0B\u5E8F\u6E32\u67D3\u5C42\u7684\u9ED8\u8BA4\u5B57\u53F7\u662F 16px\uFF1B\u4E0D\u58F0\u660E\u7684\u8BDD\u4F1A\u7EE7\u627F\u6D4F\u89C8\u5668\u8BBE\u7F6E\u91CC\u7684\u9ED8\u8BA4\u5B57\u53F7\u3002\u653E\u5728\u6700\u524D\u9762\uFF0C\u57FA\u7840\u5E93\u548C\u9875\u9762\u6837\u5F0F\u90FD\u80FD\u8986\u76D6\u5B83 -->
|
|
764
|
+
<style>html{font-size:16px}</style>
|
|
765
|
+
<!-- \u771F\u673A\u7684\u6EDA\u52A8\u6761\u662F\u4E0D\u5360\u5E03\u5C40\u7684\u6D6E\u5C42\uFF1B\u684C\u9762\u6D4F\u89C8\u5668\u7684\u5E38\u89C4\u6EDA\u52A8\u6761\u548C\u81EA\u5B9A\u4E49 ::-webkit-scrollbar \u4F1A\u6324\u7A84\u9875\u9762\uFF0C\u4E00\u5F8B\u9690\u85CF\u3002
|
|
766
|
+
scrollbar-width \u4F18\u5148\u4E8E ::-webkit-scrollbar\uFF0C\u9875\u9762\u6837\u5F0F\u4E0D\u4F1A\u518D\u628A\u6EDA\u52A8\u6761\u52A0\u56DE\u6765 -->
|
|
767
|
+
<style>*{scrollbar-width:none}::-webkit-scrollbar{display:none}</style>
|
|
768
|
+
<!-- \u903B\u8F91\u5C42\u5EFA\u7684 2d\u3001webgl \u753B\u5E03\u642C\u8FDB wx-canvas \u540E\u94FA\u6EE1\u5B83\uFF08\u89C1 frame.ts \u7684 moveCanvasesIntoPages\uFF09\u3002
|
|
769
|
+
\u57FA\u7840\u5E93\u8FD8\u4F1A\u6309\u903B\u8F91\u5C42\u91CC\u7684\u5E03\u5C40\u6539\u5B83\u7684\u4F4D\u7F6E\u548C\u663E\u9690\uFF0C\u8FD9\u91CC\u4E00\u5F8B\u538B\u4F4F\uFF1B\u89E6\u6478\u7167\u5E38\u4EA4\u7ED9 wx-canvas \u81EA\u5DF1\u7684 canvas -->
|
|
770
|
+
<style>.wxa-service-canvas{position:absolute!important;left:0!important;top:0!important;width:100%!important;height:100%!important;visibility:inherit!important;pointer-events:none}</style>
|
|
771
|
+
<script>var __wxConfig = ${toJs(wxConfig)};</script>
|
|
772
|
+
<script src="/__wxa__/runtime/frame.js" data-kind="webview" data-webview-id="${id}"></script>
|
|
773
|
+
<script src="/__baselib__/WAWebview.js"></script>`;
|
|
774
|
+
}
|
|
582
775
|
function escapeHtml(text) {
|
|
583
776
|
return text.replace(/[&<>"]/g, (ch) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[ch]);
|
|
584
777
|
}
|
|
@@ -684,7 +877,7 @@ import { execFile } from "child_process";
|
|
|
684
877
|
import { randomUUID } from "crypto";
|
|
685
878
|
import { setTimeout as sleep } from "timers/promises";
|
|
686
879
|
import { WebSocket } from "ws";
|
|
687
|
-
import { findDeveloperTicket, findElectronDevtoolsCli, TICKET_ENV } from "@open-wxa/wxa-runner-extractor";
|
|
880
|
+
import { findDeveloperTicket, findElectronDevtoolsCli, TICKET_ENV as TICKET_ENV2 } from "@open-wxa/wxa-runner-extractor";
|
|
688
881
|
var AUTO_PORT = 9420;
|
|
689
882
|
var CLI_TIMEOUT = 6e4;
|
|
690
883
|
var AUTOMATOR_TIMEOUT = 5e3;
|
|
@@ -697,7 +890,7 @@ function developerTicketSource(projectPath, logger) {
|
|
|
697
890
|
const startOnce = () => {
|
|
698
891
|
if (!automatorPort) {
|
|
699
892
|
const starting = startAutomation(cli, projectPath).then((port) => {
|
|
700
|
-
logger.log(`[wxa-runner] \
|
|
893
|
+
logger.log(`[wxa-runner] --devtools-login\uFF1A\u5DF2\u5728\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u91CC\u4E3A\u9879\u76EE\u5F00\u542F\u81EA\u52A8\u5316\u63A5\u53E3\uFF08\u7AEF\u53E3 ${port}\uFF09\uFF0Cticket \u4ECE\u5F00\u53D1\u8005\u5DE5\u5177\u5B9E\u65F6\u83B7\u53D6`);
|
|
701
894
|
return port;
|
|
702
895
|
});
|
|
703
896
|
starting.catch(() => {
|
|
@@ -708,7 +901,7 @@ function developerTicketSource(projectPath, logger) {
|
|
|
708
901
|
return automatorPort;
|
|
709
902
|
};
|
|
710
903
|
return async () => {
|
|
711
|
-
if (process.env[
|
|
904
|
+
if (process.env[TICKET_ENV2]) return { newticket: process.env[TICKET_ENV2] };
|
|
712
905
|
const port = startOnce();
|
|
713
906
|
try {
|
|
714
907
|
return await requestTicketOnceWindowReady(await port, projectPath);
|
|
@@ -813,74 +1006,62 @@ function explainAutomatorError(message) {
|
|
|
813
1006
|
return new Error(`\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u8FD4\u56DE\u9519\u8BEF\uFF1A${message}`);
|
|
814
1007
|
}
|
|
815
1008
|
|
|
816
|
-
// src/server/
|
|
1009
|
+
// src/server/devtoolsLogin.ts
|
|
817
1010
|
import express from "express";
|
|
818
|
-
import { findDeveloperTicket as findDeveloperTicket2
|
|
819
|
-
var DEV_LOGIC = "https://servicewechat.com/wxa-dev-logic";
|
|
820
|
-
var CLIENT_VERSION = "1021810240";
|
|
821
|
-
var UPSTREAM_TIMEOUT = 1e4;
|
|
1011
|
+
import { findDeveloperTicket as findDeveloperTicket2 } from "@open-wxa/wxa-runner-extractor";
|
|
822
1012
|
var LOOPBACK_ADDRESSES = ["127.0.0.1", "::1", "::ffff:127.0.0.1"];
|
|
823
1013
|
var LOOPBACK_HOSTS = ["localhost", "127.0.0.1", "[::1]"];
|
|
824
|
-
var
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
40029: "\uFF08\u767B\u5F55\u6001\u5DF2\u5931\u6548\uFF0C\u8BF7\u5728\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u91CC\u91CD\u65B0\u767B\u5F55\uFF09",
|
|
828
|
-
85023: "\uFF08\u5F53\u524D\u5FAE\u4FE1\u8D26\u53F7\u4E0D\u662F\u8BE5\u5C0F\u7A0B\u5E8F\u7684\u5F00\u53D1\u8005\uFF09"
|
|
829
|
-
};
|
|
830
|
-
function requireUsableTicket(ticket) {
|
|
831
|
-
if (!ticket) throw new Error(`\u672A\u627E\u5230\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u7684\u767B\u5F55\u6001\uFF0C\u8BF7\u767B\u5F55\u5F00\u53D1\u8005\u5DE5\u5177\uFF0C\u6216\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF ${TICKET_ENV2}`);
|
|
832
|
-
if (ticket.expiredAt !== void 0 && ticket.expiredAt <= Date.now()) {
|
|
833
|
-
throw new Error("\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u7684\u767B\u5F55\u6001\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u6253\u5F00\u5F00\u53D1\u8005\u5DE5\u5177\u5237\u65B0\u767B\u5F55\u6001");
|
|
834
|
-
}
|
|
835
|
-
return ticket.newticket;
|
|
836
|
-
}
|
|
837
|
-
function realLoginRouter(appid, findTicket = findDeveloperTicket2) {
|
|
838
|
-
const callDevLogic = async (endpoint, body) => {
|
|
839
|
-
const query = new URLSearchParams({ appid, newticket: requireUsableTicket(await findTicket()), clientversion: CLIENT_VERSION });
|
|
840
|
-
const response = await fetch(`${DEV_LOGIC}/${endpoint}?${query}`, {
|
|
841
|
-
method: "POST",
|
|
842
|
-
headers: { "Content-Type": "application/json" },
|
|
843
|
-
body: JSON.stringify(body),
|
|
844
|
-
signal: AbortSignal.timeout(UPSTREAM_TIMEOUT)
|
|
845
|
-
}).catch((error) => {
|
|
846
|
-
throw new Error(error.name === "TimeoutError" ? `\u8BF7\u6C42\u5FAE\u4FE1\u670D\u52A1\u5668\u8D85\u65F6\uFF08${UPSTREAM_TIMEOUT / 1e3}s\uFF09` : `\u8BF7\u6C42\u5FAE\u4FE1\u670D\u52A1\u5668\u5931\u8D25\uFF1A${error.cause?.message ?? error.message}`);
|
|
847
|
-
});
|
|
848
|
-
if (!response.ok) throw new Error(`\u5FAE\u4FE1\u670D\u52A1\u5668\u8FD4\u56DE HTTP ${response.status}`);
|
|
849
|
-
return response.json();
|
|
850
|
-
};
|
|
1014
|
+
var FORWARDED_WX_DATA_APIS = ["webapi_servicemarket_jsapi_comm"];
|
|
1015
|
+
function devtoolsLoginRouter(appid, findTicket = findDeveloperTicket2) {
|
|
1016
|
+
const wechat = new WechatDevClient(appid, findTicket);
|
|
851
1017
|
const router = express.Router();
|
|
852
1018
|
router.use(onlyLoopback);
|
|
853
1019
|
router.post("/login", respond(async () => {
|
|
854
|
-
const body = await
|
|
855
|
-
|
|
856
|
-
if (errcode !== 0 || !body.code) {
|
|
857
|
-
throw new Error(`\u5FAE\u4FE1\u767B\u5F55\u5931\u8D25 ${errcode}: ${body.baseresponse?.errmsg || "\u672A\u77E5\u9519\u8BEF"}${ERROR_HINTS[errcode] ?? ""}`);
|
|
858
|
-
}
|
|
1020
|
+
const body = requireOk(await wechat.postJson("wxa-dev-logic/jslogin", { scope: ["snsapi_base"] }), "\u5FAE\u4FE1\u767B\u5F55\u5931\u8D25");
|
|
1021
|
+
if (!body.code) throw new Error("\u5FAE\u4FE1\u767B\u5F55\u5931\u8D25\uFF1A\u5FAE\u4FE1\u6CA1\u6709\u8FD4\u56DE code");
|
|
859
1022
|
return { code: body.code };
|
|
860
1023
|
}));
|
|
861
1024
|
router.post("/phone-number", respond(async () => {
|
|
862
|
-
const body = await
|
|
1025
|
+
const body = await wechat.postJson("wxa-dev-logic/jsgetuserwxphone", { appid });
|
|
863
1026
|
const errcode = body.jsapi_baseresponse?.errcode ?? body.BaseResponse?.Ret;
|
|
864
1027
|
if (errcode !== 0 || typeof body.data !== "string") {
|
|
865
1028
|
const errmsg = body.jsapi_baseresponse?.errmsg || body.BaseResponse?.ErrMsg?.String || "\u672A\u77E5\u9519\u8BEF";
|
|
866
|
-
throw new Error(`\u83B7\u53D6\u624B\u673A\u53F7\u5931\u8D25 ${errcode}: ${errmsg}${
|
|
1029
|
+
throw new Error(`\u83B7\u53D6\u624B\u673A\u53F7\u5931\u8D25 ${errcode}: ${errmsg}${explainErrcode(errcode)}`);
|
|
867
1030
|
}
|
|
868
1031
|
const { encryptedData, iv } = JSON.parse(body.data);
|
|
869
1032
|
return { data: body.data, encryptedData, iv };
|
|
870
1033
|
}));
|
|
1034
|
+
router.post("/operate-wx-data", express.json({ limit: "10mb" }), (req, res, next) => {
|
|
1035
|
+
const apiName = req.body?.data?.api_name;
|
|
1036
|
+
if (FORWARDED_WX_DATA_APIS.includes(apiName)) return next();
|
|
1037
|
+
res.status(403).json({ error: `operateWXData \u53EA\u8F6C\u53D1\u670D\u52A1\u5E02\u573A\u7684\u8BF7\u6C42\uFF0C\u4E0D\u8F6C\u53D1 ${apiName}` });
|
|
1038
|
+
}, respond(async (req) => {
|
|
1039
|
+
const body = requireOk(await wechat.postJson("wxa-dev-logic/jsoperatewxdata", { data: JSON.stringify(req.body.data) }), "\u670D\u52A1\u5E02\u573A\u8C03\u7528\u5931\u8D25");
|
|
1040
|
+
if (typeof body.data !== "string") throw new Error("\u670D\u52A1\u5E02\u573A\u8C03\u7528\u5931\u8D25\uFF1A\u5FAE\u4FE1\u6CA1\u6709\u8FD4\u56DE\u7ED3\u679C");
|
|
1041
|
+
return { data: JSON.parse(body.data) };
|
|
1042
|
+
}));
|
|
1043
|
+
router.post("/verify-plugin", express.json(), respond(async (req) => {
|
|
1044
|
+
const body = requireOk(await wechat.postJson("wxa-dev-logic/verifyplugin", { ext_info: JSON.stringify(req.body?.data || {}) }), "\u63D2\u4EF6\u6821\u9A8C\u5931\u8D25");
|
|
1045
|
+
return { data: { list: body.list || [] } };
|
|
1046
|
+
}));
|
|
1047
|
+
router.post("/batch-get-contact", express.json(), respond(async (req) => {
|
|
1048
|
+
const body = requireOk(await wechat.postJson("wxa-dev-logic/batchgetcontact", { appid_list: req.body?.appIds || [] }), "\u83B7\u53D6\u63D2\u4EF6\u4FE1\u606F\u5931\u8D25");
|
|
1049
|
+
const contacts = body.contact_list || [];
|
|
1050
|
+
return { contactList: contacts.filter((contact) => contact.plugin_info).map((contact) => ({ appId: contact.appid, plugin_list: contact.plugin_info.plugin_list })) };
|
|
1051
|
+
}));
|
|
871
1052
|
return router;
|
|
872
1053
|
}
|
|
873
1054
|
function onlyLoopback(req, res, next) {
|
|
874
1055
|
if (!LOOPBACK_ADDRESSES.includes(req.socket.remoteAddress || "") || !LOOPBACK_HOSTS.includes(req.hostname)) {
|
|
875
|
-
res.status(403).json({ error: "\
|
|
1056
|
+
res.status(403).json({ error: "\u5F00\u53D1\u8005\u767B\u5F55\u6001\u63A5\u53E3\u53EA\u5141\u8BB8\u672C\u673A\u8BBF\u95EE" });
|
|
876
1057
|
return;
|
|
877
1058
|
}
|
|
878
1059
|
next();
|
|
879
1060
|
}
|
|
880
1061
|
function respond(action) {
|
|
881
|
-
return async (
|
|
1062
|
+
return async (req, res) => {
|
|
882
1063
|
try {
|
|
883
|
-
res.json(await action());
|
|
1064
|
+
res.json(await action(req));
|
|
884
1065
|
} catch (error) {
|
|
885
1066
|
res.status(500).json({ error: error.message });
|
|
886
1067
|
}
|
|
@@ -1015,7 +1196,12 @@ var DevServer = class {
|
|
|
1015
1196
|
if (!this.baseLib.compatible) {
|
|
1016
1197
|
this.logger.warn(`[wxa-runner] \u57FA\u7840\u5E93 ${this.baseLib.version} \u9AD8\u4E8E\u5DF2\u9A8C\u8BC1\u7684\u7248\u672C\u8303\u56F4\uFF0C\u53EF\u80FD\u65E0\u6CD5\u6B63\u5E38\u8FD0\u884C`);
|
|
1017
1198
|
}
|
|
1018
|
-
|
|
1199
|
+
if (options.realLogin !== void 0 && options.devtoolsLogin === void 0) {
|
|
1200
|
+
this.logger.warn("[wxa-runner] realLogin\uFF08--real-login\uFF09\u5DF2\u6539\u540D\u4E3A devtoolsLogin\uFF08--devtools-login\uFF09\uFF0C\u65E7\u540D\u4EE5\u540E\u4F1A\u79FB\u9664");
|
|
1201
|
+
}
|
|
1202
|
+
this.devtoolsLogin = (options.devtoolsLogin ?? options.realLogin) === true;
|
|
1203
|
+
this.ticketSource = this.devtoolsLogin ? developerTicketSource(this.project.projectPath, this.logger) : null;
|
|
1204
|
+
this.plugins = new PluginStore(this.ticketSource && new WechatDevClient(this.project.appid, this.ticketSource), this.logger);
|
|
1019
1205
|
this.device = { ...DEFAULT_DEVICE, ...options.device };
|
|
1020
1206
|
this.builder = new AppBuilder(this.project);
|
|
1021
1207
|
this.setupFile = resolveSetupFile(options.setup, this.project.projectPath);
|
|
@@ -1051,7 +1237,7 @@ ${error.message}`);
|
|
|
1051
1237
|
const { port } = server.address();
|
|
1052
1238
|
this.address = `http://${host}:${port}`;
|
|
1053
1239
|
if (this.options.watch !== false) this.startWatching();
|
|
1054
|
-
this.ticketSource?.().then(requireUsableTicket).catch((error) => this.logger.warn(`[wxa-runner] \
|
|
1240
|
+
this.ticketSource?.().then(requireUsableTicket).catch((error) => this.logger.warn(`[wxa-runner] \u5F00\u53D1\u8005\u5DE5\u5177\u7684\u767B\u5F55\u6001\u6682\u4E0D\u53EF\u7528\uFF08--devtools-login\uFF09\uFF1A${error.message}`));
|
|
1055
1241
|
return this.address;
|
|
1056
1242
|
}
|
|
1057
1243
|
async close() {
|
|
@@ -1078,9 +1264,9 @@ ${error.message}`);
|
|
|
1078
1264
|
});
|
|
1079
1265
|
app.get("/__wxa__/app-info.json", async (_req, res) => {
|
|
1080
1266
|
noStore(res);
|
|
1081
|
-
const base = { appid: this.project.appid, baseLibVersion: this.baseLib.version, device: this.device,
|
|
1267
|
+
const base = { appid: this.project.appid, baseLibVersion: this.baseLib.version, device: this.device, devtoolsLogin: this.devtoolsLogin };
|
|
1082
1268
|
try {
|
|
1083
|
-
const build2 = await this.builder.build();
|
|
1269
|
+
const build2 = await this.plugins.refresh(await this.builder.build());
|
|
1084
1270
|
const info = { ...base, version: build2.version, wxConfig: build2.wxConfig, pages: build2.pages };
|
|
1085
1271
|
if (this.setupFile) info.setup = await this.setupInfo(this.setupFile);
|
|
1086
1272
|
res.json(info);
|
|
@@ -1116,7 +1302,19 @@ ${error.message}`);
|
|
|
1116
1302
|
app.get(/^\/__wxa__\/webview\/(.+)\.html$/, this.withBuild((build2, req, res, next) => {
|
|
1117
1303
|
const pagePath = decodeURIComponent(req.params[0]);
|
|
1118
1304
|
if (!build2.pages.includes(pagePath)) return next();
|
|
1119
|
-
|
|
1305
|
+
const pluginPage = findPluginPage(build2, pagePath);
|
|
1306
|
+
res.type("html").send(pluginPage ? pluginWebviewHtml(build2, pluginPage.plugin, pluginPage.innerPath, pagePath, Number(req.query.id)) : webviewHtml(build2, pagePath, Number(req.query.id), { hot: this.hotEnabled }));
|
|
1307
|
+
}));
|
|
1308
|
+
app.get("/__wxa__/plugin/:provider/:version/:file(appservice.js|pageframe.js)", this.withBuild((build2, req, res, next) => {
|
|
1309
|
+
const { provider, version, file } = req.params;
|
|
1310
|
+
const code = build2.plugins.find((plugin) => plugin.provider === provider && plugin.version === version)?.files.get(`/${file}`);
|
|
1311
|
+
if (!code) return next();
|
|
1312
|
+
res.type("js").send(code);
|
|
1313
|
+
}));
|
|
1314
|
+
app.get(/^\/__plugin__\/([^/]+)(\/.+)$/, this.withBuild((build2, req, res, next) => {
|
|
1315
|
+
const file = build2.plugins.find((plugin) => plugin.provider === req.params[0])?.files.get(decodeURIComponent(req.params[1]));
|
|
1316
|
+
if (!file) return next();
|
|
1317
|
+
res.type(path4.extname(req.params[1]) || "application/octet-stream").send(file);
|
|
1120
1318
|
}));
|
|
1121
1319
|
app.get(/^\/__wxa__\/module\/(.+\.js)$/, this.withBuild((build2, req, res) => {
|
|
1122
1320
|
const id = decodeURIComponent(req.params[0]);
|
|
@@ -1205,7 +1403,7 @@ ${error.message}`);
|
|
|
1205
1403
|
app.delete("/__wxa__/tmp/:name", (req, res) => {
|
|
1206
1404
|
res.status(this.tempFiles.delete(req.params.name) ? 204 : 404).end();
|
|
1207
1405
|
});
|
|
1208
|
-
if (this.ticketSource) app.use("/__wxa__/api",
|
|
1406
|
+
if (this.ticketSource) app.use("/__wxa__/api", devtoolsLoginRouter(this.project.appid, this.ticketSource));
|
|
1209
1407
|
app.use("/__wxa__/webview", express2.static(root));
|
|
1210
1408
|
app.use(express2.static(root));
|
|
1211
1409
|
app.use((error, _req, res, _next) => {
|
|
@@ -1223,13 +1421,17 @@ ${error.message}`);
|
|
|
1223
1421
|
return { env: this.setupEnv, error: error.message };
|
|
1224
1422
|
}
|
|
1225
1423
|
}
|
|
1424
|
+
/** 构建结果并入这次打开小程序时决定用的插件 */
|
|
1425
|
+
async currentBuild() {
|
|
1426
|
+
return this.plugins.current(await this.builder.build());
|
|
1427
|
+
}
|
|
1226
1428
|
/** 需要构建结果的路由;构建失败时返回错误文本 */
|
|
1227
1429
|
withBuild(handler) {
|
|
1228
1430
|
return async (req, res, next) => {
|
|
1229
1431
|
res.setHeader("Cache-Control", "no-store");
|
|
1230
1432
|
let build2;
|
|
1231
1433
|
try {
|
|
1232
|
-
build2 = await this.
|
|
1434
|
+
build2 = await this.currentBuild();
|
|
1233
1435
|
} catch (error) {
|
|
1234
1436
|
res.status(500).type("text").send(error.message);
|
|
1235
1437
|
return;
|