@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 CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ResolvedBaseLib } from '@open-wxa/wxa-runner-extractor';
2
- import { D as DeviceProfile } from './setup-Cv340Lvj.mjs';
3
- export { A as ApiContext, a as ApiHandler, b as ApiResult, c as AppInfo, M as MockConfig, d as MockRequest, e as MockResponse, f as MockValue, N as NetworkConfig, R as RouteHandler, S as SetupApi, g as SetupConfig, h as SetupContext, i as SetupDefinition } from './setup-Cv340Lvj.mjs';
4
- export { F as FrameError, P as PageSnapshot, R as RecordedRequest, W as WxaTestApi } from './testApi-DO2FyR78.mjs';
2
+ import { D as DeviceProfile } from './setup-DZ5FeKay.mjs';
3
+ export { A as ApiContext, a as ApiHandler, b as ApiResult, c as AppInfo, M as MockConfig, d as MockRequest, e as MockResponse, f as MockValue, N as NetworkConfig, R as RouteHandler, S as SetupApi, g as SetupConfig, h as SetupContext, i as SetupDefinition } from './setup-DZ5FeKay.mjs';
4
+ export { F as FrameError, P as PageSnapshot, R as RecordedRequest, W as WxaTestApi } from './testApi-CnZOLyWw.mjs';
5
5
 
6
6
  /**
7
7
  * 小程序项目结构解析
@@ -170,7 +170,7 @@ declare class AppBuilder {
170
170
  * /__wxa__/proxy?url= 网络代理
171
171
  * /__wxa__/tmp 临时文件:POST ?type=png 保存,返回可直接访问的地址(GET 支持分段请求);对该地址 HEAD 查大小、DELETE 删除
172
172
  * /__wxa__/region/<kind> 地区选择器用的行政区划数据(districts / streets),取自本机开发者工具
173
- * /__wxa__/api/* 真实登录(--real-login):wx.login 的 code、手机号,只允许本机访问
173
+ * /__wxa__/api/* --devtools-login:wx.login 的 code、手机号,服务市场、插件的请求转发,只允许本机访问
174
174
  * /__wxa__/ws 文件变化通知:热更新(hot)或整页刷新(reload)
175
175
  * 其余路径 小程序目录下的静态资源
176
176
  */
@@ -192,7 +192,12 @@ interface DevServerOptions {
192
192
  setup?: string | false;
193
193
  /** 浏览器端运行时(host.js / frame.js)所在目录,默认取构建产物 */
194
194
  runtimeDir?: string;
195
- /** 使用本机微信开发者工具登录态获取真实 code 和手机号 */
195
+ /**
196
+ * 借本机微信开发者工具的登录态访问微信后台:真实的 wx.login code 和手机号、服务市场、插件。
197
+ * 要先登录开发者工具,会真实请求微信
198
+ */
199
+ devtoolsLogin?: boolean;
200
+ /** @deprecated 改名为 devtoolsLogin */
196
201
  realLogin?: boolean;
197
202
  logger?: Pick<Console, 'log' | 'warn' | 'error'>;
198
203
  }
@@ -214,8 +219,10 @@ declare class DevServer {
214
219
  private address;
215
220
  /** 运行时生成的临时文件(canvas 导出、拍照等),随服务器进程存在 */
216
221
  private readonly tempFiles;
217
- /** 开启 --real-login 时才有 */
222
+ private readonly devtoolsLogin;
223
+ /** 开启 --devtools-login 时才有 */
218
224
  private readonly ticketSource;
225
+ private readonly plugins;
219
226
  constructor(options: DevServerOptions);
220
227
  get url(): string;
221
228
  start(): Promise<string>;
@@ -223,6 +230,8 @@ declare class DevServer {
223
230
  private createApp;
224
231
  /** 先编译一遍 setup:编译错误随 app-info 交给宿主显示,而不是让浏览器 import 失败 */
225
232
  private setupInfo;
233
+ /** 构建结果并入这次打开小程序时决定用的插件 */
234
+ private currentBuild;
226
235
  /** 需要构建结果的路由;构建失败时返回错误文本 */
227
236
  private withBuild;
228
237
  private runtimeDir;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ResolvedBaseLib } from '@open-wxa/wxa-runner-extractor';
2
- import { D as DeviceProfile } from './setup-Cv340Lvj.js';
3
- export { A as ApiContext, a as ApiHandler, b as ApiResult, c as AppInfo, M as MockConfig, d as MockRequest, e as MockResponse, f as MockValue, N as NetworkConfig, R as RouteHandler, S as SetupApi, g as SetupConfig, h as SetupContext, i as SetupDefinition } from './setup-Cv340Lvj.js';
4
- export { F as FrameError, P as PageSnapshot, R as RecordedRequest, W as WxaTestApi } from './testApi-nKsjjEhz.js';
2
+ import { D as DeviceProfile } from './setup-DZ5FeKay.js';
3
+ export { A as ApiContext, a as ApiHandler, b as ApiResult, c as AppInfo, M as MockConfig, d as MockRequest, e as MockResponse, f as MockValue, N as NetworkConfig, R as RouteHandler, S as SetupApi, g as SetupConfig, h as SetupContext, i as SetupDefinition } from './setup-DZ5FeKay.js';
4
+ export { F as FrameError, P as PageSnapshot, R as RecordedRequest, W as WxaTestApi } from './testApi-BLozOgCt.js';
5
5
 
6
6
  /**
7
7
  * 小程序项目结构解析
@@ -170,7 +170,7 @@ declare class AppBuilder {
170
170
  * /__wxa__/proxy?url= 网络代理
171
171
  * /__wxa__/tmp 临时文件:POST ?type=png 保存,返回可直接访问的地址(GET 支持分段请求);对该地址 HEAD 查大小、DELETE 删除
172
172
  * /__wxa__/region/<kind> 地区选择器用的行政区划数据(districts / streets),取自本机开发者工具
173
- * /__wxa__/api/* 真实登录(--real-login):wx.login 的 code、手机号,只允许本机访问
173
+ * /__wxa__/api/* --devtools-login:wx.login 的 code、手机号,服务市场、插件的请求转发,只允许本机访问
174
174
  * /__wxa__/ws 文件变化通知:热更新(hot)或整页刷新(reload)
175
175
  * 其余路径 小程序目录下的静态资源
176
176
  */
@@ -192,7 +192,12 @@ interface DevServerOptions {
192
192
  setup?: string | false;
193
193
  /** 浏览器端运行时(host.js / frame.js)所在目录,默认取构建产物 */
194
194
  runtimeDir?: string;
195
- /** 使用本机微信开发者工具登录态获取真实 code 和手机号 */
195
+ /**
196
+ * 借本机微信开发者工具的登录态访问微信后台:真实的 wx.login code 和手机号、服务市场、插件。
197
+ * 要先登录开发者工具,会真实请求微信
198
+ */
199
+ devtoolsLogin?: boolean;
200
+ /** @deprecated 改名为 devtoolsLogin */
196
201
  realLogin?: boolean;
197
202
  logger?: Pick<Console, 'log' | 'warn' | 'error'>;
198
203
  }
@@ -214,8 +219,10 @@ declare class DevServer {
214
219
  private address;
215
220
  /** 运行时生成的临时文件(canvas 导出、拍照等),随服务器进程存在 */
216
221
  private readonly tempFiles;
217
- /** 开启 --real-login 时才有 */
222
+ private readonly devtoolsLogin;
223
+ /** 开启 --devtools-login 时才有 */
218
224
  private readonly ticketSource;
225
+ private readonly plugins;
219
226
  constructor(options: DevServerOptions);
220
227
  get url(): string;
221
228
  start(): Promise<string>;
@@ -223,6 +230,8 @@ declare class DevServer {
223
230
  private createApp;
224
231
  /** 先编译一遍 setup:编译错误随 app-info 交给宿主显示,而不是让浏览器 import 失败 */
225
232
  private setupInfo;
233
+ /** 构建结果并入这次打开小程序时决定用的插件 */
234
+ private currentBuild;
226
235
  /** 需要构建结果的路由;构建失败时返回错误文本 */
227
236
  private withBuild;
228
237
  private runtimeDir;
package/dist/index.js CHANGED
@@ -45,7 +45,7 @@ var import_http = __toESM(require("http"));
45
45
  var import_path4 = __toESM(require("path"));
46
46
  var import_express2 = __toESM(require("express"));
47
47
  var import_ws2 = require("ws");
48
- var import_wxa_runner_extractor3 = require("@open-wxa/wxa-runner-extractor");
48
+ var import_wxa_runner_extractor5 = require("@open-wxa/wxa-runner-extractor");
49
49
 
50
50
  // src/project/AppBuilder.ts
51
51
  var import_wxa_runner_compiler = require("@open-wxa/wxa-runner-compiler");
@@ -481,6 +481,178 @@ function readJsonFile(file) {
481
481
  }
482
482
  }
483
483
 
484
+ // src/server/plugins.ts
485
+ var import_wxa_runner_extractor2 = require("@open-wxa/wxa-runner-extractor");
486
+
487
+ // src/server/wechatDev.ts
488
+ var import_wxa_runner_extractor = require("@open-wxa/wxa-runner-extractor");
489
+ var BASE_URL = "https://servicewechat.com";
490
+ var CLIENT_VERSION = "1021810240";
491
+ var DEFAULT_TIMEOUT = 1e4;
492
+ var ERROR_HINTS = {
493
+ // 40001:开发者工具刷新过 ticket,旧的随即作废
494
+ 40001: "\uFF08\u767B\u5F55\u6001\u5DF2\u5931\u6548\uFF0C\u8BF7\u5728\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u91CC\u91CD\u65B0\u767B\u5F55\uFF09",
495
+ 40029: "\uFF08\u767B\u5F55\u6001\u5DF2\u5931\u6548\uFF0C\u8BF7\u5728\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u91CC\u91CD\u65B0\u767B\u5F55\uFF09",
496
+ 85023: "\uFF08\u5F53\u524D\u5FAE\u4FE1\u8D26\u53F7\u4E0D\u662F\u8BE5\u5C0F\u7A0B\u5E8F\u7684\u5F00\u53D1\u8005\uFF09"
497
+ };
498
+ function requireUsableTicket(ticket) {
499
+ 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 ${import_wxa_runner_extractor.TICKET_ENV}`);
500
+ if (ticket.expiredAt !== void 0 && ticket.expiredAt <= Date.now()) {
501
+ 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");
502
+ }
503
+ return ticket.newticket;
504
+ }
505
+ function explainErrcode(errcode) {
506
+ return errcode !== void 0 && ERROR_HINTS[errcode] || "";
507
+ }
508
+ function requireOk(body, action) {
509
+ const errcode = body.baseresponse?.errcode;
510
+ if (errcode !== 0) throw new Error(`${action} ${errcode}: ${body.baseresponse?.errmsg || "\u672A\u77E5\u9519\u8BEF"}${explainErrcode(errcode)}`);
511
+ return body;
512
+ }
513
+ var WechatDevClient = class {
514
+ /**
515
+ * @param findTicket 每次请求都重新取:开发者工具重新登录、切换账号后不用重启服务器
516
+ */
517
+ constructor(appid, findTicket) {
518
+ this.appid = appid;
519
+ this.findTicket = findTicket;
520
+ }
521
+ /**
522
+ * POST JSON 到 path(如 wxa-dev-logic/jslogin),带上 appid 和开发者 ticket
523
+ *
524
+ * @throws 没有可用的登录态、超时、连不上、HTTP 状态不是 2xx 时,消息是给用户看的原因
525
+ */
526
+ async post(path5, body, timeout = DEFAULT_TIMEOUT) {
527
+ const query = new URLSearchParams({ appid: this.appid, newticket: requireUsableTicket(await this.findTicket()), clientversion: CLIENT_VERSION });
528
+ const response = await fetch(`${BASE_URL}/${path5}?${query}`, {
529
+ method: "POST",
530
+ headers: { "Content-Type": "application/json" },
531
+ body: JSON.stringify(body),
532
+ signal: AbortSignal.timeout(timeout)
533
+ }).catch((error) => {
534
+ 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}`);
535
+ });
536
+ if (!response.ok) throw new Error(`\u5FAE\u4FE1\u670D\u52A1\u5668\u8FD4\u56DE HTTP ${response.status}`);
537
+ return response;
538
+ }
539
+ /** 同 post,返回解析后的 JSON */
540
+ async postJson(path5, body) {
541
+ return (await this.post(path5, body)).json();
542
+ }
543
+ };
544
+
545
+ // src/server/plugins.ts
546
+ var DOWNLOAD_TIMEOUT = 3e4;
547
+ var PACKAGE_MAGIC = 190;
548
+ var PluginStore = class {
549
+ /**
550
+ * @param wechat 开启 --devtools-login 时才有,没有时不下载插件
551
+ */
552
+ constructor(wechat, logger) {
553
+ this.wechat = wechat;
554
+ this.logger = logger;
555
+ /** provider@version → 下载结果;失败的不留,下次打开小程序时重试 */
556
+ this.downloads = /* @__PURE__ */ new Map();
557
+ /**
558
+ * 这次打开小程序时决定用哪些插件。同一次打开的逻辑层、业务模块、渲染层等请求都按它处理:
559
+ * 各自去下载的话,下载失败时每个请求都会重试,结果还可能前后不一,页面列表和逻辑层对不上
560
+ */
561
+ this.selection = null;
562
+ /** 没开 --devtools-login 的提示每个插件只说一次 */
563
+ this.warned = /* @__PURE__ */ new Set();
564
+ }
565
+ /** 打开小程序时(宿主取 app-info)调用:重新决定用哪些插件,下载过的直接用,上次失败的重试 */
566
+ async refresh(build2) {
567
+ this.selection = this.select(build2);
568
+ return withPlugins(build2, await this.selection);
569
+ }
570
+ /** 其余请求沿用这次打开时的决定,不触发下载;还没决定过时(如直接请求逻辑层)先决定一次 */
571
+ async current(build2) {
572
+ this.selection ??= this.select(build2);
573
+ return withPlugins(build2, await this.selection);
574
+ }
575
+ /** app.json 声明的插件;下载不了的跳过,在终端说明原因,小程序自己的页面照常可用 */
576
+ async select(build2) {
577
+ const declared = build2.wxConfig.plugins || {};
578
+ const results = await Promise.all(Object.entries(declared).map(
579
+ ([alias, { provider, version }]) => provider && version ? this.load(alias, provider, version) : null
580
+ ));
581
+ return results.filter((plugin) => !!plugin);
582
+ }
583
+ async load(alias, provider, version) {
584
+ const label = `\u63D2\u4EF6 ${alias}\uFF08${provider}@${version}\uFF09`;
585
+ if (!this.wechat) {
586
+ 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`);
587
+ return null;
588
+ }
589
+ const key = `${provider}@${version}`;
590
+ let download = this.downloads.get(key);
591
+ if (!download) {
592
+ download = this.download(this.wechat, alias, provider, version);
593
+ this.downloads.set(key, download);
594
+ download.catch(() => this.downloads.delete(key));
595
+ }
596
+ try {
597
+ return await download;
598
+ } catch (error) {
599
+ this.logger.warn(`[wxa-runner] ${label} \u4E0B\u8F7D\u5931\u8D25\uFF1A${error.message}`);
600
+ return null;
601
+ }
602
+ }
603
+ /** @throws 没有登录态、连不上、微信拒绝或返回的不是插件代码包时,消息是原因 */
604
+ async download(wechat, alias, provider, version) {
605
+ const response = await wechat.post("wxa-dev/getplugincode", { plugin_id: provider, plugin_version: version }, DOWNLOAD_TIMEOUT);
606
+ const data = Buffer.from(await response.arrayBuffer());
607
+ if (data[0] !== PACKAGE_MAGIC) {
608
+ let body = {};
609
+ try {
610
+ body = JSON.parse(data.toString("utf8"));
611
+ } catch {
612
+ }
613
+ if (body.baseresponse?.errcode) requireOk(body, "\u5FAE\u4FE1\u62D2\u7EDD\u4E0B\u8F7D");
614
+ throw new Error("\u5FAE\u4FE1\u6CA1\u6709\u8FD4\u56DE\u63D2\u4EF6\u4EE3\u7801");
615
+ }
616
+ const files = new Map((0, import_wxa_runner_extractor2.readWxvpkgIndex)(data).map((entry) => [entry.name, Buffer.from((0, import_wxa_runner_extractor2.readWxvpkgFile)(data, entry))]));
617
+ const pluginJson = JSON.parse(files.get("/plugin.json")?.toString("utf8") || "{}");
618
+ return {
619
+ alias,
620
+ provider,
621
+ version,
622
+ files,
623
+ pages: Object.values(pluginJson.pages || {}),
624
+ pageConfigs: pluginJson.page || {}
625
+ };
626
+ }
627
+ warnOnce(key, message) {
628
+ if (this.warned.has(key)) return;
629
+ this.warned.add(key);
630
+ this.logger.warn(message);
631
+ }
632
+ };
633
+ function withPlugins(build2, plugins) {
634
+ if (!plugins.length) return { ...build2, plugins };
635
+ return {
636
+ ...build2,
637
+ plugins,
638
+ pages: [...build2.pages, ...plugins.flatMap((plugin) => plugin.pages)],
639
+ wxConfig: {
640
+ ...build2.wxConfig,
641
+ page: { ...build2.wxConfig.page, ...Object.assign({}, ...plugins.map((plugin) => plugin.pageConfigs)) }
642
+ }
643
+ };
644
+ }
645
+ function findPluginPage(build2, pagePath) {
646
+ for (const plugin of build2.plugins) {
647
+ const prefix = `__plugin__/${plugin.provider}/`;
648
+ if (plugin.pages.includes(pagePath)) return { plugin, innerPath: pagePath.slice(prefix.length) };
649
+ }
650
+ return null;
651
+ }
652
+ function pluginScriptUrl(plugin, file) {
653
+ return `/__wxa__/plugin/${encodeURIComponent(plugin.provider)}/${encodeURIComponent(plugin.version)}/${file}`;
654
+ }
655
+
484
656
  // src/server/html.ts
485
657
  function hostHtml() {
486
658
  return `<!DOCTYPE html>
@@ -507,10 +679,11 @@ function appserviceHtml(build2, launch, options) {
507
679
  ...options.isolated ? { isIsolateContext: true } : {}
508
680
  };
509
681
  const v = build2.version;
682
+ const pluginUrls = build2.plugins.map((plugin) => pluginScriptUrl(plugin, "appservice.js"));
510
683
  const moduleUrls = build2.modules.map((id) => `/__wxa__/module/${encodeURI(id)}?v=${v}`);
511
684
  const bootstrapUrl = `/__wxa__/service-bootstrap.js?v=${v}`;
512
685
  if (options.isolated) {
513
- const scripts = [...moduleUrls, bootstrapUrl];
686
+ const scripts = [...pluginUrls, ...moduleUrls, bootstrapUrl];
514
687
  return `<!DOCTYPE html>
515
688
  <html>
516
689
  <head>
@@ -523,7 +696,7 @@ function appserviceHtml(build2, launch, options) {
523
696
  <body></body>
524
697
  </html>`;
525
698
  }
526
- const modules = moduleUrls.map((src) => `<script src="${src}"></script>`).join("\n");
699
+ const modules = [...pluginUrls, ...moduleUrls].map((src) => `<script src="${src}"></script>`).join("\n");
527
700
  return `<!DOCTYPE html>
528
701
  <html>
529
702
  <head>
@@ -557,29 +730,13 @@ ${empty ? "" : '<script src="/__baselib__/WASubContext.js"></script>\n'}<script>
557
730
  </html>`;
558
731
  }
559
732
  function webviewHtml(build2, pagePath, webviewId, options) {
560
- const pageConfig = build2.wxConfig.page?.[pagePath + ".html"];
561
- const window = { ...build2.wxConfig.global?.window, ...pageConfig?.window };
562
- const wxConfig = { ...build2.wxConfig, window };
563
733
  const id = Number(webviewId) || 0;
564
734
  return `<!DOCTYPE html>
565
735
  <html>
566
736
  <head>
567
- <meta charset="utf-8">
568
- <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
569
- <title>${escapeHtml(pagePath)}</title>
570
- <!-- \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 -->
571
- <style>html{font-size:16px}</style>
572
- <!-- \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
573
- scrollbar-width \u4F18\u5148\u4E8E ::-webkit-scrollbar\uFF0C\u9875\u9762\u6837\u5F0F\u4E0D\u4F1A\u518D\u628A\u6EDA\u52A8\u6761\u52A0\u56DE\u6765 -->
574
- <style>*{scrollbar-width:none}::-webkit-scrollbar{display:none}</style>
575
- <!-- \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
576
- \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 -->
577
- <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>
578
- <script>var __wxConfig = ${toJs(wxConfig)};</script>
579
- <script src="/__wxa__/runtime/frame.js" data-kind="webview" data-webview-id="${id}"></script>
580
- <script src="/__baselib__/WAWebview.js"></script>
737
+ ${webviewHead(build2, pagePath, id)}
581
738
  ${enableHotUpdate(options.hot)}<script src="/__wxa__/page-frame.js?v=${build2.version}"></script>
582
- ${window.disableScroll ? "<style>body{overflow-y:hidden}</style>" : ""}
739
+ ${pageWindow(build2, pagePath).disableScroll ? "<style>body{overflow-y:hidden}</style>" : ""}
583
740
  </head>
584
741
  <body>
585
742
  <script>
@@ -615,6 +772,42 @@ self.console = __wxaConsole;
615
772
  importScripts(${modules.map((url) => toJs(url)).join(", ")});
616
773
  `;
617
774
  }
775
+ function pluginWebviewHtml(build2, plugin, innerPath, pagePath, webviewId) {
776
+ const id = Number(webviewId) || 0;
777
+ 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>`;
778
+ return `<!DOCTYPE html>
779
+ <html>
780
+ <head>
781
+ ${webviewHead(build2, pagePath, id)}
782
+ <script src="${pluginScriptUrl(plugin, "pageframe.js")}"></script>
783
+ ${pageWindow(build2, pagePath).disableScroll ? "<style>body{overflow-y:hidden}</style>" : ""}
784
+ </head>
785
+ <body>
786
+ ${page}
787
+ <script>parent.__wxaHost.frameLoaded('webview', ${id});</script>
788
+ </body>
789
+ </html>`;
790
+ }
791
+ function pageWindow(build2, pagePath) {
792
+ return { ...build2.wxConfig.global?.window, ...build2.wxConfig.page?.[pagePath + ".html"]?.window };
793
+ }
794
+ function webviewHead(build2, pagePath, id) {
795
+ const wxConfig = { ...build2.wxConfig, window: pageWindow(build2, pagePath) };
796
+ return `<meta charset="utf-8">
797
+ <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
798
+ <title>${escapeHtml(pagePath)}</title>
799
+ <!-- \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 -->
800
+ <style>html{font-size:16px}</style>
801
+ <!-- \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
802
+ scrollbar-width \u4F18\u5148\u4E8E ::-webkit-scrollbar\uFF0C\u9875\u9762\u6837\u5F0F\u4E0D\u4F1A\u518D\u628A\u6EDA\u52A8\u6761\u52A0\u56DE\u6765 -->
803
+ <style>*{scrollbar-width:none}::-webkit-scrollbar{display:none}</style>
804
+ <!-- \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
805
+ \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 -->
806
+ <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>
807
+ <script>var __wxConfig = ${toJs(wxConfig)};</script>
808
+ <script src="/__wxa__/runtime/frame.js" data-kind="webview" data-webview-id="${id}"></script>
809
+ <script src="/__baselib__/WAWebview.js"></script>`;
810
+ }
618
811
  function escapeHtml(text) {
619
812
  return text.replace(/[&<>"]/g, (ch) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[ch]);
620
813
  }
@@ -720,20 +913,20 @@ var import_child_process = require("child_process");
720
913
  var import_crypto = require("crypto");
721
914
  var import_promises = require("timers/promises");
722
915
  var import_ws = require("ws");
723
- var import_wxa_runner_extractor = require("@open-wxa/wxa-runner-extractor");
916
+ var import_wxa_runner_extractor3 = require("@open-wxa/wxa-runner-extractor");
724
917
  var AUTO_PORT = 9420;
725
918
  var CLI_TIMEOUT = 6e4;
726
919
  var AUTOMATOR_TIMEOUT = 5e3;
727
920
  var WINDOW_TIMEOUT = 1e4;
728
921
  var WINDOW_RETRY_INTERVAL = 500;
729
922
  function developerTicketSource(projectPath, logger) {
730
- const cli = (0, import_wxa_runner_extractor.findElectronDevtoolsCli)();
731
- if (!cli) return async () => (0, import_wxa_runner_extractor.findDeveloperTicket)();
923
+ const cli = (0, import_wxa_runner_extractor3.findElectronDevtoolsCli)();
924
+ if (!cli) return async () => (0, import_wxa_runner_extractor3.findDeveloperTicket)();
732
925
  let automatorPort = null;
733
926
  const startOnce = () => {
734
927
  if (!automatorPort) {
735
928
  const starting = startAutomation(cli, projectPath).then((port) => {
736
- logger.log(`[wxa-runner] \u771F\u5B9E\u767B\u5F55\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`);
929
+ 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`);
737
930
  return port;
738
931
  });
739
932
  starting.catch(() => {
@@ -744,7 +937,7 @@ function developerTicketSource(projectPath, logger) {
744
937
  return automatorPort;
745
938
  };
746
939
  return async () => {
747
- if (process.env[import_wxa_runner_extractor.TICKET_ENV]) return { newticket: process.env[import_wxa_runner_extractor.TICKET_ENV] };
940
+ if (process.env[import_wxa_runner_extractor3.TICKET_ENV]) return { newticket: process.env[import_wxa_runner_extractor3.TICKET_ENV] };
748
941
  const port = startOnce();
749
942
  try {
750
943
  return await requestTicketOnceWindowReady(await port, projectPath);
@@ -849,74 +1042,62 @@ function explainAutomatorError(message) {
849
1042
  return new Error(`\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u8FD4\u56DE\u9519\u8BEF\uFF1A${message}`);
850
1043
  }
851
1044
 
852
- // src/server/realLogin.ts
1045
+ // src/server/devtoolsLogin.ts
853
1046
  var import_express = __toESM(require("express"));
854
- var import_wxa_runner_extractor2 = require("@open-wxa/wxa-runner-extractor");
855
- var DEV_LOGIC = "https://servicewechat.com/wxa-dev-logic";
856
- var CLIENT_VERSION = "1021810240";
857
- var UPSTREAM_TIMEOUT = 1e4;
1047
+ var import_wxa_runner_extractor4 = require("@open-wxa/wxa-runner-extractor");
858
1048
  var LOOPBACK_ADDRESSES = ["127.0.0.1", "::1", "::ffff:127.0.0.1"];
859
1049
  var LOOPBACK_HOSTS = ["localhost", "127.0.0.1", "[::1]"];
860
- var ERROR_HINTS = {
861
- // 40001:开发者工具刷新过 ticket,旧的随即作废
862
- 40001: "\uFF08\u767B\u5F55\u6001\u5DF2\u5931\u6548\uFF0C\u8BF7\u5728\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u91CC\u91CD\u65B0\u767B\u5F55\uFF09",
863
- 40029: "\uFF08\u767B\u5F55\u6001\u5DF2\u5931\u6548\uFF0C\u8BF7\u5728\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u91CC\u91CD\u65B0\u767B\u5F55\uFF09",
864
- 85023: "\uFF08\u5F53\u524D\u5FAE\u4FE1\u8D26\u53F7\u4E0D\u662F\u8BE5\u5C0F\u7A0B\u5E8F\u7684\u5F00\u53D1\u8005\uFF09"
865
- };
866
- function requireUsableTicket(ticket) {
867
- 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 ${import_wxa_runner_extractor2.TICKET_ENV}`);
868
- if (ticket.expiredAt !== void 0 && ticket.expiredAt <= Date.now()) {
869
- 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");
870
- }
871
- return ticket.newticket;
872
- }
873
- function realLoginRouter(appid, findTicket = import_wxa_runner_extractor2.findDeveloperTicket) {
874
- const callDevLogic = async (endpoint, body) => {
875
- const query = new URLSearchParams({ appid, newticket: requireUsableTicket(await findTicket()), clientversion: CLIENT_VERSION });
876
- const response = await fetch(`${DEV_LOGIC}/${endpoint}?${query}`, {
877
- method: "POST",
878
- headers: { "Content-Type": "application/json" },
879
- body: JSON.stringify(body),
880
- signal: AbortSignal.timeout(UPSTREAM_TIMEOUT)
881
- }).catch((error) => {
882
- 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}`);
883
- });
884
- if (!response.ok) throw new Error(`\u5FAE\u4FE1\u670D\u52A1\u5668\u8FD4\u56DE HTTP ${response.status}`);
885
- return response.json();
886
- };
1050
+ var FORWARDED_WX_DATA_APIS = ["webapi_servicemarket_jsapi_comm"];
1051
+ function devtoolsLoginRouter(appid, findTicket = import_wxa_runner_extractor4.findDeveloperTicket) {
1052
+ const wechat = new WechatDevClient(appid, findTicket);
887
1053
  const router = import_express.default.Router();
888
1054
  router.use(onlyLoopback);
889
1055
  router.post("/login", respond(async () => {
890
- const body = await callDevLogic("jslogin", { scope: ["snsapi_base"] });
891
- const errcode = body.baseresponse?.errcode;
892
- if (errcode !== 0 || !body.code) {
893
- throw new Error(`\u5FAE\u4FE1\u767B\u5F55\u5931\u8D25 ${errcode}: ${body.baseresponse?.errmsg || "\u672A\u77E5\u9519\u8BEF"}${ERROR_HINTS[errcode] ?? ""}`);
894
- }
1056
+ const body = requireOk(await wechat.postJson("wxa-dev-logic/jslogin", { scope: ["snsapi_base"] }), "\u5FAE\u4FE1\u767B\u5F55\u5931\u8D25");
1057
+ if (!body.code) throw new Error("\u5FAE\u4FE1\u767B\u5F55\u5931\u8D25\uFF1A\u5FAE\u4FE1\u6CA1\u6709\u8FD4\u56DE code");
895
1058
  return { code: body.code };
896
1059
  }));
897
1060
  router.post("/phone-number", respond(async () => {
898
- const body = await callDevLogic("jsgetuserwxphone", { appid });
1061
+ const body = await wechat.postJson("wxa-dev-logic/jsgetuserwxphone", { appid });
899
1062
  const errcode = body.jsapi_baseresponse?.errcode ?? body.BaseResponse?.Ret;
900
1063
  if (errcode !== 0 || typeof body.data !== "string") {
901
1064
  const errmsg = body.jsapi_baseresponse?.errmsg || body.BaseResponse?.ErrMsg?.String || "\u672A\u77E5\u9519\u8BEF";
902
- throw new Error(`\u83B7\u53D6\u624B\u673A\u53F7\u5931\u8D25 ${errcode}: ${errmsg}${ERROR_HINTS[errcode] ?? ""}`);
1065
+ throw new Error(`\u83B7\u53D6\u624B\u673A\u53F7\u5931\u8D25 ${errcode}: ${errmsg}${explainErrcode(errcode)}`);
903
1066
  }
904
1067
  const { encryptedData, iv } = JSON.parse(body.data);
905
1068
  return { data: body.data, encryptedData, iv };
906
1069
  }));
1070
+ router.post("/operate-wx-data", import_express.default.json({ limit: "10mb" }), (req, res, next) => {
1071
+ const apiName = req.body?.data?.api_name;
1072
+ if (FORWARDED_WX_DATA_APIS.includes(apiName)) return next();
1073
+ res.status(403).json({ error: `operateWXData \u53EA\u8F6C\u53D1\u670D\u52A1\u5E02\u573A\u7684\u8BF7\u6C42\uFF0C\u4E0D\u8F6C\u53D1 ${apiName}` });
1074
+ }, respond(async (req) => {
1075
+ const body = requireOk(await wechat.postJson("wxa-dev-logic/jsoperatewxdata", { data: JSON.stringify(req.body.data) }), "\u670D\u52A1\u5E02\u573A\u8C03\u7528\u5931\u8D25");
1076
+ 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");
1077
+ return { data: JSON.parse(body.data) };
1078
+ }));
1079
+ router.post("/verify-plugin", import_express.default.json(), respond(async (req) => {
1080
+ const body = requireOk(await wechat.postJson("wxa-dev-logic/verifyplugin", { ext_info: JSON.stringify(req.body?.data || {}) }), "\u63D2\u4EF6\u6821\u9A8C\u5931\u8D25");
1081
+ return { data: { list: body.list || [] } };
1082
+ }));
1083
+ router.post("/batch-get-contact", import_express.default.json(), respond(async (req) => {
1084
+ const body = requireOk(await wechat.postJson("wxa-dev-logic/batchgetcontact", { appid_list: req.body?.appIds || [] }), "\u83B7\u53D6\u63D2\u4EF6\u4FE1\u606F\u5931\u8D25");
1085
+ const contacts = body.contact_list || [];
1086
+ return { contactList: contacts.filter((contact) => contact.plugin_info).map((contact) => ({ appId: contact.appid, plugin_list: contact.plugin_info.plugin_list })) };
1087
+ }));
907
1088
  return router;
908
1089
  }
909
1090
  function onlyLoopback(req, res, next) {
910
1091
  if (!LOOPBACK_ADDRESSES.includes(req.socket.remoteAddress || "") || !LOOPBACK_HOSTS.includes(req.hostname)) {
911
- res.status(403).json({ error: "\u771F\u5B9E\u767B\u5F55\u63A5\u53E3\u53EA\u5141\u8BB8\u672C\u673A\u8BBF\u95EE" });
1092
+ res.status(403).json({ error: "\u5F00\u53D1\u8005\u767B\u5F55\u6001\u63A5\u53E3\u53EA\u5141\u8BB8\u672C\u673A\u8BBF\u95EE" });
912
1093
  return;
913
1094
  }
914
1095
  next();
915
1096
  }
916
1097
  function respond(action) {
917
- return async (_req, res) => {
1098
+ return async (req, res) => {
918
1099
  try {
919
- res.json(await action());
1100
+ res.json(await action(req));
920
1101
  } catch (error) {
921
1102
  res.status(500).json({ error: error.message });
922
1103
  }
@@ -1047,11 +1228,16 @@ var DevServer = class {
1047
1228
  this.tempFiles = /* @__PURE__ */ new Map();
1048
1229
  this.logger = options.logger || console;
1049
1230
  this.project = new MiniProgram(import_path4.default.resolve(options.projectPath));
1050
- this.baseLib = (0, import_wxa_runner_extractor3.resolveBaseLib)({ version: options.baseLibVersion, cacheDir: options.baseLibCacheDir });
1231
+ this.baseLib = (0, import_wxa_runner_extractor5.resolveBaseLib)({ version: options.baseLibVersion, cacheDir: options.baseLibCacheDir });
1051
1232
  if (!this.baseLib.compatible) {
1052
1233
  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`);
1053
1234
  }
1054
- this.ticketSource = options.realLogin ? developerTicketSource(this.project.projectPath, this.logger) : null;
1235
+ if (options.realLogin !== void 0 && options.devtoolsLogin === void 0) {
1236
+ 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");
1237
+ }
1238
+ this.devtoolsLogin = (options.devtoolsLogin ?? options.realLogin) === true;
1239
+ this.ticketSource = this.devtoolsLogin ? developerTicketSource(this.project.projectPath, this.logger) : null;
1240
+ this.plugins = new PluginStore(this.ticketSource && new WechatDevClient(this.project.appid, this.ticketSource), this.logger);
1055
1241
  this.device = { ...DEFAULT_DEVICE, ...options.device };
1056
1242
  this.builder = new AppBuilder(this.project);
1057
1243
  this.setupFile = resolveSetupFile(options.setup, this.project.projectPath);
@@ -1087,7 +1273,7 @@ ${error.message}`);
1087
1273
  const { port } = server.address();
1088
1274
  this.address = `http://${host}:${port}`;
1089
1275
  if (this.options.watch !== false) this.startWatching();
1090
- this.ticketSource?.().then(requireUsableTicket).catch((error) => this.logger.warn(`[wxa-runner] \u771F\u5B9E\u767B\u5F55\u6682\u4E0D\u53EF\u7528\uFF1A${error.message}`));
1276
+ 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}`));
1091
1277
  return this.address;
1092
1278
  }
1093
1279
  async close() {
@@ -1114,9 +1300,9 @@ ${error.message}`);
1114
1300
  });
1115
1301
  app.get("/__wxa__/app-info.json", async (_req, res) => {
1116
1302
  noStore(res);
1117
- const base = { appid: this.project.appid, baseLibVersion: this.baseLib.version, device: this.device, realLogin: this.options.realLogin === true };
1303
+ const base = { appid: this.project.appid, baseLibVersion: this.baseLib.version, device: this.device, devtoolsLogin: this.devtoolsLogin };
1118
1304
  try {
1119
- const build2 = await this.builder.build();
1305
+ const build2 = await this.plugins.refresh(await this.builder.build());
1120
1306
  const info = { ...base, version: build2.version, wxConfig: build2.wxConfig, pages: build2.pages };
1121
1307
  if (this.setupFile) info.setup = await this.setupInfo(this.setupFile);
1122
1308
  res.json(info);
@@ -1139,7 +1325,7 @@ ${error.message}`);
1139
1325
  app.get("/__wxa__/appservice.html", this.withBuild((build2, req, res) => {
1140
1326
  const launchPath = String(req.query.path || "").replace(/^\//, "");
1141
1327
  const launch = build2.pages.includes(launchPath) ? { path: launchPath, query: Object.fromEntries(new URLSearchParams(String(req.query.query || ""))) } : { path: build2.pages[0], query: {} };
1142
- res.type("html").send(appserviceHtml(build2, launch, { hot: this.hotEnabled, isolated: (0, import_wxa_runner_extractor3.usesIsolatedContext)(this.baseLib.version) }));
1328
+ res.type("html").send(appserviceHtml(build2, launch, { hot: this.hotEnabled, isolated: (0, import_wxa_runner_extractor5.usesIsolatedContext)(this.baseLib.version) }));
1143
1329
  }));
1144
1330
  app.get("/__wxa__/subcontext.html", (req, res) => {
1145
1331
  const id = Number(req.query.id);
@@ -1152,7 +1338,19 @@ ${error.message}`);
1152
1338
  app.get(/^\/__wxa__\/webview\/(.+)\.html$/, this.withBuild((build2, req, res, next) => {
1153
1339
  const pagePath = decodeURIComponent(req.params[0]);
1154
1340
  if (!build2.pages.includes(pagePath)) return next();
1155
- res.type("html").send(webviewHtml(build2, pagePath, Number(req.query.id), { hot: this.hotEnabled }));
1341
+ const pluginPage = findPluginPage(build2, pagePath);
1342
+ 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 }));
1343
+ }));
1344
+ app.get("/__wxa__/plugin/:provider/:version/:file(appservice.js|pageframe.js)", this.withBuild((build2, req, res, next) => {
1345
+ const { provider, version, file } = req.params;
1346
+ const code = build2.plugins.find((plugin) => plugin.provider === provider && plugin.version === version)?.files.get(`/${file}`);
1347
+ if (!code) return next();
1348
+ res.type("js").send(code);
1349
+ }));
1350
+ app.get(/^\/__plugin__\/([^/]+)(\/.+)$/, this.withBuild((build2, req, res, next) => {
1351
+ const file = build2.plugins.find((plugin) => plugin.provider === req.params[0])?.files.get(decodeURIComponent(req.params[1]));
1352
+ if (!file) return next();
1353
+ res.type(import_path4.default.extname(req.params[1]) || "application/octet-stream").send(file);
1156
1354
  }));
1157
1355
  app.get(/^\/__wxa__\/module\/(.+\.js)$/, this.withBuild((build2, req, res) => {
1158
1356
  const id = decodeURIComponent(req.params[0]);
@@ -1208,7 +1406,7 @@ ${error.message}`);
1208
1406
  });
1209
1407
  app.get("/__wxa__/region/:kind", (req, res) => {
1210
1408
  const kind = req.params.kind;
1211
- const data = kind === "districts" || kind === "streets" ? (0, import_wxa_runner_extractor3.readRegionData)(kind) : null;
1409
+ const data = kind === "districts" || kind === "streets" ? (0, import_wxa_runner_extractor5.readRegionData)(kind) : null;
1212
1410
  if (!data) {
1213
1411
  res.status(404).end();
1214
1412
  return;
@@ -1241,7 +1439,7 @@ ${error.message}`);
1241
1439
  app.delete("/__wxa__/tmp/:name", (req, res) => {
1242
1440
  res.status(this.tempFiles.delete(req.params.name) ? 204 : 404).end();
1243
1441
  });
1244
- if (this.ticketSource) app.use("/__wxa__/api", realLoginRouter(this.project.appid, this.ticketSource));
1442
+ if (this.ticketSource) app.use("/__wxa__/api", devtoolsLoginRouter(this.project.appid, this.ticketSource));
1245
1443
  app.use("/__wxa__/webview", import_express2.default.static(root));
1246
1444
  app.use(import_express2.default.static(root));
1247
1445
  app.use((error, _req, res, _next) => {
@@ -1259,13 +1457,17 @@ ${error.message}`);
1259
1457
  return { env: this.setupEnv, error: error.message };
1260
1458
  }
1261
1459
  }
1460
+ /** 构建结果并入这次打开小程序时决定用的插件 */
1461
+ async currentBuild() {
1462
+ return this.plugins.current(await this.builder.build());
1463
+ }
1262
1464
  /** 需要构建结果的路由;构建失败时返回错误文本 */
1263
1465
  withBuild(handler) {
1264
1466
  return async (req, res, next) => {
1265
1467
  res.setHeader("Cache-Control", "no-store");
1266
1468
  let build2;
1267
1469
  try {
1268
- build2 = await this.builder.build();
1470
+ build2 = await this.currentBuild();
1269
1471
  } catch (error) {
1270
1472
  res.status(500).type("text").send(error.message);
1271
1473
  return;