@open-wxa/wxa-runner-core 0.12.4 → 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 CHANGED
@@ -1,9 +1,9 @@
1
1
  import http, { IncomingMessage, ServerResponse } from 'http';
2
2
  import { ResolvedBaseLib } from '@open-wxa/wxa-runner-extractor';
3
- import { D as DeviceProfile, F as ForwardConsole } from './setup--EIOgQvG.mjs';
4
- 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--EIOgQvG.mjs';
3
+ import { D as DeviceProfile, F as ForwardConsole } from './setup-DnirrFJk.mjs';
4
+ 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-DnirrFJk.mjs';
5
5
  import { Request, Response } from 'express';
6
- export { F as FrameError, P as PageSnapshot, R as RecordedRequest, W as WebViewInfo, a as WxaTestApi } from './testApi-CEA0oH79.mjs';
6
+ export { F as FrameError, P as PageSnapshot, R as RecordedRequest, W as WebViewInfo, a as WxaTestApi } from './testApi-BqpWSa3B.mjs';
7
7
 
8
8
  /**
9
9
  * 样式预处理:project.config.json 的 setting.useCompilerPlugins 开启 less / sass 时,
@@ -213,7 +213,7 @@ declare const DEFAULT_DEVICE: DeviceProfile;
213
213
 
214
214
  /**
215
215
  * 开发服务器扩展:检查器这类开发工具把要用的扩展点打包成一个扩展,
216
- * 由 DevServer 的 extensions 选项或命令行的 --extension 加载。runner 只认这个约定,不认识具体的工具。
216
+ * 由 DevServer 的 extensions 选项、命令行的 --extension 或配置文件的 extensions 加载。runner 只认这个约定,不认识具体的工具。
217
217
  *
218
218
  * (「插件」在 runner 里指小程序插件,见 plugins.ts,所以这里叫扩展)
219
219
  */
@@ -304,6 +304,8 @@ interface DevServerOptions {
304
304
  baseLibCacheDir?: string;
305
305
  watch?: boolean;
306
306
  device?: Partial<DeviceProfile>;
307
+ /** navigateTo / navigateBack 时滑动页面内容,默认 false */
308
+ pageTransition?: boolean;
307
309
  /**
308
310
  * 小程序启动前执行的 setup 文件;默认使用项目目录下的 wxa.setup.ts / .js / .mjs,传 false 不使用
309
311
  * 环境变量在构造时读取,只暴露 WXA_ 前缀的变量
@@ -316,8 +318,6 @@ interface DevServerOptions {
316
318
  * 要先登录开发者工具,会真实请求微信
317
319
  */
318
320
  devtoolsLogin?: boolean;
319
- /** @deprecated 改名为 devtoolsLogin */
320
- realLogin?: boolean;
321
321
  /**
322
322
  * 把浏览器里的报错打印到终端:'errors' 转发运行时错误和未实现 API 的提醒,'all' 再加上 console.warn / console.error。
323
323
  * 默认不转发;命令行默认 'errors',检测到编程助手时 'all'
@@ -367,6 +367,7 @@ declare class DevServer {
367
367
  private readonly fileSessions;
368
368
  private readonly devtoolsLogin;
369
369
  /** 开启 --devtools-login 时才有 */
370
+ private readonly devtools;
370
371
  private readonly ticketSource;
371
372
  private readonly plugins;
372
373
  /** 选项里直接给出的扩展点与各个扩展合并后的结果 */
@@ -415,6 +416,48 @@ declare function findSetupFile(projectPath: string): string | null;
415
416
  */
416
417
  declare function bundleSetup(file: string): Promise<string>;
417
418
 
419
+ /**
420
+ * 命令行 dev 的配置文件 wxa.config.*:项目里长期不变的启动参数。默认导出普通对象,不用引入 wxa-runner,
421
+ * 要类型提示时用 import type { WxaConfig } from 'wxa-runner'
422
+ */
423
+
424
+ declare const CONFIG_FILE_NAMES: string[];
425
+ /** 配置文件的内容;命令行写了的参数优先,路径相对配置文件所在目录 */
426
+ interface WxaConfig {
427
+ /** 小程序项目目录(project.config.json 所在目录),写了之后命令行可以不写项目目录 */
428
+ project?: string;
429
+ /** 端口号,默认 3000,被占用时换用后面空闲的端口 */
430
+ port?: number;
431
+ /** 监听地址,默认 localhost;true 时监听所有地址,局域网也能访问 */
432
+ host?: string | true;
433
+ /** 启动后打开浏览器 */
434
+ open?: boolean;
435
+ /** 基础库版本,默认使用本机已安装的最新兼容版本 */
436
+ baselib?: string;
437
+ /** 监听文件变化并热更新,默认 true */
438
+ watch?: boolean;
439
+ /** 小程序启动前执行的 setup 文件,默认使用项目目录下的 wxa.setup.ts / .js / .mjs;false 不执行 */
440
+ setup?: string | false;
441
+ /** 借本机微信开发者工具的登录态访问微信后台:真实的 code 和手机号、服务市场、插件 */
442
+ devtoolsLogin?: boolean;
443
+ /** true 时 console.warn / console.error 也转发到终端,false 时报错也不转发;默认只转发报错,检测到编程助手时全部转发 */
444
+ forwardConsole?: boolean;
445
+ /** 开发服务器扩展:扩展对象、不带参数调用后返回扩展的函数,或 npm 包名、文件路径。排在命令行 --extension 之前 */
446
+ extensions?: Array<DevServerExtension | (() => DevServerExtension) | string>;
447
+ /** (实验性)navigateTo 和 navigateBack 时播放页面滑动过渡,导航栏和 tabBar 暂不跟随 */
448
+ pageTransition?: boolean;
449
+ /** web-view 的网页在新窗口打开:网页和模拟器域名不同时,Cookie 也能正常使用 */
450
+ webViewWindow?: boolean;
451
+ }
452
+ /** dir 下约定名称的配置文件,没有时为 null */
453
+ declare function findConfigFile(dir: string): string | null;
454
+ /**
455
+ * 加载配置文件,project、setup 和以 . 开头的扩展路径换成绝对路径
456
+ * @param warn 收到不认识的配置项时调用:可能是拼错了,也可能是给更新的版本写的
457
+ * @throws 文件执行出错,或默认导出不是对象
458
+ */
459
+ declare function loadConfigFile(file: string, warn: (message: string) => void): Promise<WxaConfig>;
460
+
418
461
  /**
419
462
  * 开发服务器和本机查看器的网络代理:把 express 的请求转成 Fetch 请求交给 proxyFetch.ts,再把响应流式写回
420
463
  */
@@ -437,4 +480,4 @@ interface WxConfigInput {
437
480
  }
438
481
  declare function createWxConfig(input: WxConfigInput): Record<string, any>;
439
482
 
440
- export { AppBuilder, type AppBuilderOptions, type AppJson, type BuildResult, DEFAULT_DEVICE, DevServer, type DevServerExtension, type DevServerMiddleware, type DevServerOptions, DeviceProfile, type ExtensionContext, type ExtensionEntry, type ExtensionSetup, ForwardConsole, LOCAL_HOST_OF_ANY_ADDRESS, MiniProgram, type UnitJson, type WebviewScript, bundleSetup, createWxConfig, findRuntimeDir, findSetupFile, handleProxy, listen };
483
+ export { AppBuilder, type AppBuilderOptions, type AppJson, type BuildResult, CONFIG_FILE_NAMES, DEFAULT_DEVICE, DevServer, type DevServerExtension, type DevServerMiddleware, type DevServerOptions, DeviceProfile, type ExtensionContext, type ExtensionEntry, type ExtensionSetup, ForwardConsole, LOCAL_HOST_OF_ANY_ADDRESS, MiniProgram, type UnitJson, type WebviewScript, type WxaConfig, bundleSetup, createWxConfig, findConfigFile, findRuntimeDir, findSetupFile, handleProxy, listen, loadConfigFile };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import http, { IncomingMessage, ServerResponse } from 'http';
2
2
  import { ResolvedBaseLib } from '@open-wxa/wxa-runner-extractor';
3
- import { D as DeviceProfile, F as ForwardConsole } from './setup--EIOgQvG.js';
4
- 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--EIOgQvG.js';
3
+ import { D as DeviceProfile, F as ForwardConsole } from './setup-DnirrFJk.js';
4
+ 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-DnirrFJk.js';
5
5
  import { Request, Response } from 'express';
6
- export { F as FrameError, P as PageSnapshot, R as RecordedRequest, W as WebViewInfo, a as WxaTestApi } from './testApi-CfcEUYpC.js';
6
+ export { F as FrameError, P as PageSnapshot, R as RecordedRequest, W as WebViewInfo, a as WxaTestApi } from './testApi-CnKR1f3R.js';
7
7
 
8
8
  /**
9
9
  * 样式预处理:project.config.json 的 setting.useCompilerPlugins 开启 less / sass 时,
@@ -213,7 +213,7 @@ declare const DEFAULT_DEVICE: DeviceProfile;
213
213
 
214
214
  /**
215
215
  * 开发服务器扩展:检查器这类开发工具把要用的扩展点打包成一个扩展,
216
- * 由 DevServer 的 extensions 选项或命令行的 --extension 加载。runner 只认这个约定,不认识具体的工具。
216
+ * 由 DevServer 的 extensions 选项、命令行的 --extension 或配置文件的 extensions 加载。runner 只认这个约定,不认识具体的工具。
217
217
  *
218
218
  * (「插件」在 runner 里指小程序插件,见 plugins.ts,所以这里叫扩展)
219
219
  */
@@ -304,6 +304,8 @@ interface DevServerOptions {
304
304
  baseLibCacheDir?: string;
305
305
  watch?: boolean;
306
306
  device?: Partial<DeviceProfile>;
307
+ /** navigateTo / navigateBack 时滑动页面内容,默认 false */
308
+ pageTransition?: boolean;
307
309
  /**
308
310
  * 小程序启动前执行的 setup 文件;默认使用项目目录下的 wxa.setup.ts / .js / .mjs,传 false 不使用
309
311
  * 环境变量在构造时读取,只暴露 WXA_ 前缀的变量
@@ -316,8 +318,6 @@ interface DevServerOptions {
316
318
  * 要先登录开发者工具,会真实请求微信
317
319
  */
318
320
  devtoolsLogin?: boolean;
319
- /** @deprecated 改名为 devtoolsLogin */
320
- realLogin?: boolean;
321
321
  /**
322
322
  * 把浏览器里的报错打印到终端:'errors' 转发运行时错误和未实现 API 的提醒,'all' 再加上 console.warn / console.error。
323
323
  * 默认不转发;命令行默认 'errors',检测到编程助手时 'all'
@@ -367,6 +367,7 @@ declare class DevServer {
367
367
  private readonly fileSessions;
368
368
  private readonly devtoolsLogin;
369
369
  /** 开启 --devtools-login 时才有 */
370
+ private readonly devtools;
370
371
  private readonly ticketSource;
371
372
  private readonly plugins;
372
373
  /** 选项里直接给出的扩展点与各个扩展合并后的结果 */
@@ -415,6 +416,48 @@ declare function findSetupFile(projectPath: string): string | null;
415
416
  */
416
417
  declare function bundleSetup(file: string): Promise<string>;
417
418
 
419
+ /**
420
+ * 命令行 dev 的配置文件 wxa.config.*:项目里长期不变的启动参数。默认导出普通对象,不用引入 wxa-runner,
421
+ * 要类型提示时用 import type { WxaConfig } from 'wxa-runner'
422
+ */
423
+
424
+ declare const CONFIG_FILE_NAMES: string[];
425
+ /** 配置文件的内容;命令行写了的参数优先,路径相对配置文件所在目录 */
426
+ interface WxaConfig {
427
+ /** 小程序项目目录(project.config.json 所在目录),写了之后命令行可以不写项目目录 */
428
+ project?: string;
429
+ /** 端口号,默认 3000,被占用时换用后面空闲的端口 */
430
+ port?: number;
431
+ /** 监听地址,默认 localhost;true 时监听所有地址,局域网也能访问 */
432
+ host?: string | true;
433
+ /** 启动后打开浏览器 */
434
+ open?: boolean;
435
+ /** 基础库版本,默认使用本机已安装的最新兼容版本 */
436
+ baselib?: string;
437
+ /** 监听文件变化并热更新,默认 true */
438
+ watch?: boolean;
439
+ /** 小程序启动前执行的 setup 文件,默认使用项目目录下的 wxa.setup.ts / .js / .mjs;false 不执行 */
440
+ setup?: string | false;
441
+ /** 借本机微信开发者工具的登录态访问微信后台:真实的 code 和手机号、服务市场、插件 */
442
+ devtoolsLogin?: boolean;
443
+ /** true 时 console.warn / console.error 也转发到终端,false 时报错也不转发;默认只转发报错,检测到编程助手时全部转发 */
444
+ forwardConsole?: boolean;
445
+ /** 开发服务器扩展:扩展对象、不带参数调用后返回扩展的函数,或 npm 包名、文件路径。排在命令行 --extension 之前 */
446
+ extensions?: Array<DevServerExtension | (() => DevServerExtension) | string>;
447
+ /** (实验性)navigateTo 和 navigateBack 时播放页面滑动过渡,导航栏和 tabBar 暂不跟随 */
448
+ pageTransition?: boolean;
449
+ /** web-view 的网页在新窗口打开:网页和模拟器域名不同时,Cookie 也能正常使用 */
450
+ webViewWindow?: boolean;
451
+ }
452
+ /** dir 下约定名称的配置文件,没有时为 null */
453
+ declare function findConfigFile(dir: string): string | null;
454
+ /**
455
+ * 加载配置文件,project、setup 和以 . 开头的扩展路径换成绝对路径
456
+ * @param warn 收到不认识的配置项时调用:可能是拼错了,也可能是给更新的版本写的
457
+ * @throws 文件执行出错,或默认导出不是对象
458
+ */
459
+ declare function loadConfigFile(file: string, warn: (message: string) => void): Promise<WxaConfig>;
460
+
418
461
  /**
419
462
  * 开发服务器和本机查看器的网络代理:把 express 的请求转成 Fetch 请求交给 proxyFetch.ts,再把响应流式写回
420
463
  */
@@ -437,4 +480,4 @@ interface WxConfigInput {
437
480
  }
438
481
  declare function createWxConfig(input: WxConfigInput): Record<string, any>;
439
482
 
440
- export { AppBuilder, type AppBuilderOptions, type AppJson, type BuildResult, DEFAULT_DEVICE, DevServer, type DevServerExtension, type DevServerMiddleware, type DevServerOptions, DeviceProfile, type ExtensionContext, type ExtensionEntry, type ExtensionSetup, ForwardConsole, LOCAL_HOST_OF_ANY_ADDRESS, MiniProgram, type UnitJson, type WebviewScript, bundleSetup, createWxConfig, findRuntimeDir, findSetupFile, handleProxy, listen };
483
+ export { AppBuilder, type AppBuilderOptions, type AppJson, type BuildResult, CONFIG_FILE_NAMES, DEFAULT_DEVICE, DevServer, type DevServerExtension, type DevServerMiddleware, type DevServerOptions, DeviceProfile, type ExtensionContext, type ExtensionEntry, type ExtensionSetup, ForwardConsole, LOCAL_HOST_OF_ANY_ADDRESS, MiniProgram, type UnitJson, type WebviewScript, type WxaConfig, bundleSetup, createWxConfig, findConfigFile, findRuntimeDir, findSetupFile, handleProxy, listen, loadConfigFile };
package/dist/index.js CHANGED
@@ -31,16 +31,19 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
33
  AppBuilder: () => AppBuilder,
34
+ CONFIG_FILE_NAMES: () => CONFIG_FILE_NAMES,
34
35
  DEFAULT_DEVICE: () => DEFAULT_DEVICE,
35
36
  DevServer: () => DevServer,
36
37
  LOCAL_HOST_OF_ANY_ADDRESS: () => LOCAL_HOST_OF_ANY_ADDRESS,
37
38
  MiniProgram: () => MiniProgram,
38
39
  bundleSetup: () => bundleSetup,
39
40
  createWxConfig: () => createWxConfig,
41
+ findConfigFile: () => findConfigFile,
40
42
  findRuntimeDir: () => findRuntimeDir,
41
43
  findSetupFile: () => findSetupFile,
42
44
  handleProxy: () => handleProxy,
43
- listen: () => listen
45
+ listen: () => listen,
46
+ loadConfigFile: () => loadConfigFile
44
47
  });
45
48
  module.exports = __toCommonJS(src_exports);
46
49
 
@@ -160,12 +163,12 @@ var FileStore = class {
160
163
  for (const [key, value] of entries) this.nodes.set(target + key.slice(source.length), value);
161
164
  return {};
162
165
  },
163
- access: ({ path: path7 }) => {
164
- this.existing(path7);
166
+ access: ({ path: path8 }) => {
167
+ this.existing(path8);
165
168
  return {};
166
169
  },
167
- stat: ({ path: path7 }) => {
168
- const node = this.existing(path7);
170
+ stat: ({ path: path8 }) => {
171
+ const node = this.existing(path8);
169
172
  const stats = {
170
173
  mode: node.dir ? 16877 : 33188,
171
174
  size: node.data.length,
@@ -518,8 +521,8 @@ var import_fs2 = __toESM(require("fs"));
518
521
  var import_os2 = __toESM(require("os"));
519
522
  var import_path2 = __toESM(require("path"));
520
523
  var WxmlMirror = class {
521
- constructor(transform) {
522
- this.transform = transform;
524
+ constructor(transform2) {
525
+ this.transform = transform2;
523
526
  this.dir = null;
524
527
  /** 已写入的文件:源内容(判断要不要重新转换)与写入的内容 */
525
528
  this.files = /* @__PURE__ */ new Map();
@@ -1351,9 +1354,9 @@ var DROPPED_RESPONSE_HEADERS = /* @__PURE__ */ new Set(["content-encoding", "con
1351
1354
  var REDIRECT_STATUSES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
1352
1355
  async function proxyFetch(request, options = {}) {
1353
1356
  const url = new URL(request.url);
1354
- const path7 = url.pathname + url.search;
1355
- const target = fromServerPath(PROXY_PREFIX, path7);
1356
- if (!target) return proxyError(400, `invalid url "${path7}"`);
1357
+ const path8 = url.pathname + url.search;
1358
+ const target = fromServerPath(PROXY_PREFIX, path8);
1359
+ if (!target) return proxyError(400, `invalid url "${path8}"`);
1357
1360
  try {
1358
1361
  let current = new URL(target);
1359
1362
  if (options.isAllowed && !options.isAllowed(current)) return forbiddenHost(current, 0);
@@ -1561,11 +1564,12 @@ var import_wxa_runner_extractor = require("@open-wxa/wxa-runner-extractor");
1561
1564
  var AUTO_PORT = 9420;
1562
1565
  var CLI_TIMEOUT = 6e4;
1563
1566
  var AUTOMATOR_TIMEOUT = 5e3;
1567
+ var LOGIN_TIMEOUT = 2e4;
1564
1568
  var WINDOW_TIMEOUT = 1e4;
1565
1569
  var WINDOW_RETRY_INTERVAL = 500;
1566
- function developerTicketSource(projectPath, logger) {
1570
+ function devtoolsSession(projectPath, logger) {
1567
1571
  const cli = (0, import_wxa_runner_extractor.findElectronDevtoolsCli)();
1568
- if (!cli) return async () => (0, import_wxa_runner_extractor.findDeveloperTicket)();
1572
+ if (!cli) return { findTicket: async () => (0, import_wxa_runner_extractor.findDeveloperTicket)(), loginCode: null };
1569
1573
  let automatorPort = null;
1570
1574
  const startOnce = () => {
1571
1575
  if (!automatorPort) {
@@ -1580,15 +1584,31 @@ function developerTicketSource(projectPath, logger) {
1580
1584
  }
1581
1585
  return automatorPort;
1582
1586
  };
1583
- return async () => {
1584
- if (process.env[import_wxa_runner_extractor.TICKET_ENV]) return { newticket: process.env[import_wxa_runner_extractor.TICKET_ENV] };
1587
+ const withRestart = async (run) => {
1585
1588
  const port = startOnce();
1586
1589
  try {
1587
- return await requestTicketOnceWindowReady(await port, projectPath);
1590
+ return await run(await port);
1588
1591
  } catch (error) {
1589
1592
  if (!(error instanceof AutomatorGone)) throw error;
1590
1593
  if (automatorPort === port) automatorPort = null;
1591
- return requestTicketOnceWindowReady(await startOnce(), projectPath);
1594
+ return run(await startOnce());
1595
+ }
1596
+ };
1597
+ let loginCodeNoticed = false;
1598
+ return {
1599
+ findTicket: async () => {
1600
+ if (process.env[import_wxa_runner_extractor.TICKET_ENV]) return { newticket: process.env[import_wxa_runner_extractor.TICKET_ENV] };
1601
+ return withRestart((port) => onceWindowReady(() => requestTicket(port), projectPath));
1602
+ },
1603
+ loginCode: async () => {
1604
+ const code = await withRestart((port) => onceWindowReady(() => requestLoginCode(port), projectPath));
1605
+ if (!loginCodeNoticed) {
1606
+ loginCodeNoticed = true;
1607
+ logger.log(
1608
+ "[wxa-runner] --devtools-login\uFF1A\u5FAE\u4FE1\u540E\u53F0\u4E0D\u7ED9\u8FD9\u4E2A appid \u53D6\u767B\u5F55\u7968\u636E\uFF0Cwx.login \u7684 code \u6539\u7531\u5F00\u53D1\u8005\u5DE5\u5177\u7684\u6A21\u62DF\u5668\u83B7\u53D6\uFF1B\u624B\u673A\u53F7\u3001\u670D\u52A1\u5E02\u573A\u548C\u63D2\u4EF6\u6CA1\u6709\u8FD9\u6761\u8DEF\uFF0C\u8C03\u7528\u65F6\u4F1A\u5931\u8D25"
1609
+ );
1610
+ }
1611
+ return code;
1592
1612
  }
1593
1613
  };
1594
1614
  }
@@ -1596,11 +1616,13 @@ var AutomatorGone = class extends Error {
1596
1616
  };
1597
1617
  var WindowNotFound = class extends Error {
1598
1618
  };
1599
- async function requestTicketOnceWindowReady(port, projectPath) {
1619
+ var TicketNotAllowed = class extends Error {
1620
+ };
1621
+ async function onceWindowReady(run, projectPath) {
1600
1622
  const deadline = Date.now() + WINDOW_TIMEOUT;
1601
1623
  for (; ; ) {
1602
1624
  try {
1603
- return await requestTicket(port);
1625
+ return await run();
1604
1626
  } catch (error) {
1605
1627
  if (!(error instanceof WindowNotFound)) throw error;
1606
1628
  if (Date.now() >= deadline) throw new Error(`\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u91CC\u627E\u4E0D\u5230\u9879\u76EE\u7A97\u53E3\uFF0C\u8BF7\u5728\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u91CC\u6253\u5F00\u9879\u76EE ${projectPath}`);
@@ -1644,7 +1666,7 @@ function cliErrorMessage(stdout) {
1644
1666
  const message = error.match(/message: (['"`])((?:\\.|(?!\1)[^\\])*?)(?:\\n|\1)/)?.[2];
1645
1667
  return message ?? (error.replace(/\s+/g, " ").slice(0, 200) || "\u547D\u4EE4\u884C\u6CA1\u6709\u8F93\u51FA");
1646
1668
  }
1647
- function requestTicket(port) {
1669
+ function sendCommand(port, method, params, timeout) {
1648
1670
  return new Promise((resolve, reject) => {
1649
1671
  const id = `wxa-runner-${(0, import_crypto.randomUUID)()}`;
1650
1672
  const socket = new import_ws2.WebSocket(`ws://127.0.0.1:${port}`);
@@ -1654,15 +1676,13 @@ function requestTicket(port) {
1654
1676
  finish();
1655
1677
  };
1656
1678
  const timer = setTimeout(
1657
- () => settle(
1658
- () => reject(new AutomatorGone(`\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u7684\u81EA\u52A8\u5316\u63A5\u53E3 ${AUTOMATOR_TIMEOUT / 1e3}s \u5185\u6CA1\u6709\u54CD\u5E94\uFF0C\u8BF7\u786E\u8BA4\u5F00\u53D1\u8005\u5DE5\u5177\u91CC\u6253\u5F00\u4E86\u9879\u76EE\u7A97\u53E3`))
1659
- ),
1660
- AUTOMATOR_TIMEOUT
1679
+ () => settle(() => reject(new AutomatorGone(`\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u7684\u81EA\u52A8\u5316\u63A5\u53E3 ${timeout / 1e3}s \u5185\u6CA1\u6709\u54CD\u5E94\uFF0C\u8BF7\u786E\u8BA4\u5F00\u53D1\u8005\u5DE5\u5177\u91CC\u6253\u5F00\u4E86\u9879\u76EE\u7A97\u53E3`))),
1680
+ timeout
1661
1681
  );
1662
1682
  const gone = () => settle(() => reject(new AutomatorGone(`\u8FDE\u4E0D\u4E0A\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u7684\u81EA\u52A8\u5316\u63A5\u53E3\uFF08\u7AEF\u53E3 ${port}\uFF09\uFF0C\u8BF7\u786E\u8BA4\u5F00\u53D1\u8005\u5DE5\u5177\u5728\u8FD0\u884C`)));
1663
1683
  socket.on("error", gone);
1664
1684
  socket.on("close", gone);
1665
- socket.on("open", () => socket.send(JSON.stringify({ id, method: "Tool.getTicket", params: {} })));
1685
+ socket.on("open", () => socket.send(JSON.stringify({ id, method, params })));
1666
1686
  socket.on("message", (data) => {
1667
1687
  let message;
1668
1688
  try {
@@ -1672,16 +1692,33 @@ function requestTicket(port) {
1672
1692
  }
1673
1693
  if (message?.id !== id) return;
1674
1694
  if (message.error) settle(() => reject(explainAutomatorError(String(message.error.message))));
1675
- else if (typeof message.result?.ticket !== "string") settle(() => reject(new Error("\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u6CA1\u6709\u8FD4\u56DE\u767B\u5F55\u7968\u636E")));
1676
- else settle(() => resolve({ newticket: message.result.ticket, expiredAt: message.result.expiredTime }));
1695
+ else settle(() => resolve(message.result ?? {}));
1677
1696
  });
1678
1697
  });
1679
1698
  }
1699
+ async function requestTicket(port) {
1700
+ const result = await sendCommand(port, "Tool.getTicket", {}, AUTOMATOR_TIMEOUT);
1701
+ if (typeof result.ticket !== "string") throw new Error("\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u6CA1\u6709\u8FD4\u56DE\u767B\u5F55\u7968\u636E");
1702
+ return { newticket: result.ticket, expiredAt: result.expiredTime };
1703
+ }
1704
+ async function requestLoginCode(port) {
1705
+ const result = await sendCommand(port, "App.callWxMethod", { method: "login", args: [{}] }, LOGIN_TIMEOUT);
1706
+ const { code, errMsg } = result.result ?? {};
1707
+ if (typeof code !== "string" || !code) {
1708
+ throw new Error(`\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u7684\u6A21\u62DF\u5668\u6CA1\u6709\u8FD4\u56DE code${errMsg ? `\uFF1A${errMsg}` : ""}`);
1709
+ }
1710
+ return code;
1711
+ }
1680
1712
  function explainAutomatorError(message) {
1681
1713
  if (message.includes("GUI project window is not found")) return new WindowNotFound(message);
1682
- if (message.includes("not allow to get")) {
1714
+ if (message.includes("navigate to security settings")) {
1683
1715
  return new Error("\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u4E0D\u5141\u8BB8\u83B7\u53D6\u767B\u5F55\u7968\u636E\uFF0C\u8BF7\u5728\u5F00\u53D1\u8005\u5DE5\u5177\u300C\u8BBE\u7F6E \u2192 \u5B89\u5168\u8BBE\u7F6E\u300D\u91CC\u6253\u5F00\u300C\u5141\u8BB8\u83B7\u53D6\u5DE5\u5177\u767B\u5F55\u7968\u636E\u300D");
1684
1716
  }
1717
+ if (message.includes("not allow to get")) {
1718
+ return new TicketNotAllowed(
1719
+ `\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u4E0D\u7ED9\u8FD9\u4E2A appid \u53D6\u767B\u5F55\u7968\u636E\uFF1A\u662F\u5426\u5141\u8BB8\u7531\u5FAE\u4FE1\u540E\u53F0\u4E0B\u53D1\uFF0C\u8FD9\u4E2A appid \u6CA1\u6709\u5F00\uFF0C\u300C\u8BBE\u7F6E \u2192 \u5B89\u5168\u8BBE\u7F6E\u300D\u91CC\u4E5F\u5C31\u4E0D\u663E\u793A\u300C\u5141\u8BB8\u83B7\u53D6\u5DE5\u5177\u767B\u5F55\u7968\u636E\u300D\uFF0C\u672C\u673A\u6539\u4E0D\u4E86\u3002wx.login \u4F1A\u81EA\u52A8\u6539\u7531\u5F00\u53D1\u8005\u5DE5\u5177\u7684\u6A21\u62DF\u5668\u83B7\u53D6\uFF1B\u624B\u673A\u53F7\u548C\u670D\u52A1\u5E02\u573A\u8BF7\u53BB\u6389 --devtools-login\uFF0C\u7528 setup \u7684 mock \u9020\u6570\u636E\uFF0C\u6216\u53E6\u53D6\u4E00\u5F20\u7968\u636E\u7528\u73AF\u5883\u53D8\u91CF ${import_wxa_runner_extractor.TICKET_ENV} \u4F20\u8FDB\u6765`
1720
+ );
1721
+ }
1685
1722
  if (message.includes("need to login")) return new Error("\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u672A\u767B\u5F55\uFF0C\u8BF7\u5148\u5728\u5F00\u53D1\u8005\u5DE5\u5177\u91CC\u767B\u5F55");
1686
1723
  return new Error(`\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u8FD4\u56DE\u9519\u8BEF\uFF1A${message}`);
1687
1724
  }
@@ -1728,9 +1765,9 @@ var WechatDevClient = class {
1728
1765
  *
1729
1766
  * @throws 没有可用的登录态、超时、连不上、HTTP 状态不是 2xx 时,消息是给用户看的原因
1730
1767
  */
1731
- async post(path7, body, timeout = DEFAULT_TIMEOUT) {
1768
+ async post(path8, body, timeout = DEFAULT_TIMEOUT) {
1732
1769
  const query = new URLSearchParams({ appid: this.appid, newticket: requireUsableTicket(await this.findTicket()), clientversion: CLIENT_VERSION });
1733
- const response = await fetch(`${BASE_URL}/${path7}?${query}`, {
1770
+ const response = await fetch(`${BASE_URL}/${path8}?${query}`, {
1734
1771
  method: "POST",
1735
1772
  headers: { "Content-Type": "application/json" },
1736
1773
  body: JSON.stringify(body),
@@ -1742,8 +1779,8 @@ var WechatDevClient = class {
1742
1779
  return response;
1743
1780
  }
1744
1781
  /** 同 post,返回解析后的 JSON */
1745
- async postJson(path7, body) {
1746
- return (await this.post(path7, body)).json();
1782
+ async postJson(path8, body) {
1783
+ return (await this.post(path8, body)).json();
1747
1784
  }
1748
1785
  };
1749
1786
 
@@ -1842,14 +1879,19 @@ var import_wxa_runner_extractor4 = require("@open-wxa/wxa-runner-extractor");
1842
1879
  var LOOPBACK_ADDRESSES = ["127.0.0.1", "::1", "::ffff:127.0.0.1"];
1843
1880
  var LOOPBACK_HOSTS = ["localhost", "127.0.0.1", "[::1]"];
1844
1881
  var FORWARDED_WX_DATA_APIS = ["webapi_servicemarket_jsapi_comm"];
1845
- function devtoolsLoginRouter(appid, findTicket = import_wxa_runner_extractor4.findDeveloperTicket) {
1882
+ function devtoolsLoginRouter(appid, findTicket = import_wxa_runner_extractor4.findDeveloperTicket, loginCode) {
1846
1883
  const wechat = new WechatDevClient(appid, findTicket);
1847
1884
  const router = import_express.default.Router();
1848
1885
  router.use(onlyLoopback);
1849
1886
  router.post("/login", respond(async () => {
1850
- const body = requireOk(await wechat.postJson("wxa-dev-logic/jslogin", { scope: ["snsapi_base"] }), "\u5FAE\u4FE1\u767B\u5F55\u5931\u8D25");
1851
- if (!body.code) throw new Error("\u5FAE\u4FE1\u767B\u5F55\u5931\u8D25\uFF1A\u5FAE\u4FE1\u6CA1\u6709\u8FD4\u56DE code");
1852
- return { code: body.code };
1887
+ try {
1888
+ const body = requireOk(await wechat.postJson("wxa-dev-logic/jslogin", { scope: ["snsapi_base"] }), "\u5FAE\u4FE1\u767B\u5F55\u5931\u8D25");
1889
+ if (!body.code) throw new Error("\u5FAE\u4FE1\u767B\u5F55\u5931\u8D25\uFF1A\u5FAE\u4FE1\u6CA1\u6709\u8FD4\u56DE code");
1890
+ return { code: body.code };
1891
+ } catch (error) {
1892
+ if (!loginCode || !(error instanceof TicketNotAllowed)) throw error;
1893
+ return { code: await loginCode() };
1894
+ }
1853
1895
  }));
1854
1896
  router.post("/phone-number", respond(async () => {
1855
1897
  const body = await wechat.postJson("wxa-dev-logic/jsgetuserwxphone", { appid });
@@ -2022,7 +2064,7 @@ function applyExtensions(base, extensions, context) {
2022
2064
  if (setup.wxmlTransform) transforms.push(setup.wxmlTransform);
2023
2065
  if (setup.entry) entries.push({ name: extension.name, ...setup.entry });
2024
2066
  }
2025
- const wxmlTransform = transforms.length === 0 ? void 0 : (code, file) => transforms.reduce((result, transform) => transform(result, file), code);
2067
+ const wxmlTransform = transforms.length === 0 ? void 0 : (code, file) => transforms.reduce((result, transform2) => transform2(result, file), code);
2026
2068
  return { webviewScripts, middlewares, wxmlTransform, entries };
2027
2069
  }
2028
2070
 
@@ -2058,11 +2100,9 @@ var DevServer = class {
2058
2100
  if (!this.baseLib.compatible) {
2059
2101
  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`);
2060
2102
  }
2061
- if (options.realLogin !== void 0 && options.devtoolsLogin === void 0) {
2062
- 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");
2063
- }
2064
- this.devtoolsLogin = (options.devtoolsLogin ?? options.realLogin) === true;
2065
- this.ticketSource = this.devtoolsLogin ? developerTicketSource(this.project.projectPath, this.logger) : null;
2103
+ this.devtoolsLogin = options.devtoolsLogin === true;
2104
+ this.devtools = this.devtoolsLogin ? devtoolsSession(this.project.projectPath, this.logger) : null;
2105
+ this.ticketSource = this.devtools?.findTicket ?? null;
2066
2106
  this.plugins = new PluginStore(this.ticketSource && new WechatDevClient(this.project.appid, this.ticketSource), this.logger);
2067
2107
  this.device = { ...DEFAULT_DEVICE, ...options.device };
2068
2108
  this.builder = new AppBuilder(this.project, { wxmlTransform: extended.wxmlTransform });
@@ -2120,7 +2160,10 @@ ${error.message}`);
2120
2160
  this.server = server;
2121
2161
  this.lanAccess = LOCAL_HOST_OF_ANY_ADDRESS.has(host);
2122
2162
  this.address = `http://${LOCAL_HOST_OF_ANY_ADDRESS.get(host) ?? host}:${port}`;
2123
- 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}`));
2163
+ this.ticketSource?.().then(requireUsableTicket).catch((error) => {
2164
+ if (error instanceof TicketNotAllowed && this.devtools?.loginCode) return;
2165
+ 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}`);
2166
+ });
2124
2167
  return this.address;
2125
2168
  }
2126
2169
  async close() {
@@ -2158,6 +2201,7 @@ ${error.message}`);
2158
2201
  appid: this.project.appid,
2159
2202
  baseLibVersion: this.baseLib.version,
2160
2203
  device: this.device,
2204
+ pageTransition: this.options.pageTransition ?? false,
2161
2205
  devtoolsLogin: this.devtoolsLogin,
2162
2206
  socketProxy: true,
2163
2207
  forwardConsole: this.options.forwardConsole ?? false,
@@ -2299,7 +2343,7 @@ ${error.message}`);
2299
2343
  res.status(404).end();
2300
2344
  }
2301
2345
  });
2302
- if (this.ticketSource) app.use("/__wxa__/api", devtoolsLoginRouter(this.project.appid, this.ticketSource));
2346
+ if (this.devtools) app.use("/__wxa__/api", devtoolsLoginRouter(this.project.appid, this.devtools.findTicket, this.devtools.loginCode));
2303
2347
  app.use("/__wxa__/webview", import_express2.default.static(root));
2304
2348
  app.use(import_express2.default.static(root));
2305
2349
  app.use((error, _req, res, _next) => {
@@ -2452,18 +2496,97 @@ function findRuntimeDir(explicit) {
2452
2496
  }
2453
2497
  return found;
2454
2498
  }
2499
+
2500
+ // src/server/config.ts
2501
+ var import_crypto4 = require("crypto");
2502
+ var import_fs7 = __toESM(require("fs"));
2503
+ var import_path7 = __toESM(require("path"));
2504
+ var import_url2 = require("url");
2505
+ var import_esbuild3 = require("esbuild");
2506
+ var CONFIG_FILE_NAMES = ["wxa.config.ts", "wxa.config.mts", "wxa.config.js", "wxa.config.mjs", "wxa.config.cjs"];
2507
+ var CONFIG_KEYS = /* @__PURE__ */ new Set([
2508
+ "project",
2509
+ "port",
2510
+ "host",
2511
+ "open",
2512
+ "baselib",
2513
+ "watch",
2514
+ "setup",
2515
+ "devtoolsLogin",
2516
+ "forwardConsole",
2517
+ "extensions",
2518
+ "pageTransition",
2519
+ "webViewWindow"
2520
+ ]);
2521
+ function findConfigFile(dir) {
2522
+ for (const name of CONFIG_FILE_NAMES) {
2523
+ const file = import_path7.default.join(dir, name);
2524
+ if (import_fs7.default.existsSync(file)) return file;
2525
+ }
2526
+ return null;
2527
+ }
2528
+ async function loadConfigFile(file, warn) {
2529
+ let exported;
2530
+ try {
2531
+ exported = await importConfig(file);
2532
+ } catch (error) {
2533
+ throw new Error(`\u914D\u7F6E\u6587\u4EF6 ${file} \u52A0\u8F7D\u5931\u8D25\uFF1A${error instanceof Error ? error.message : String(error)}`);
2534
+ }
2535
+ if (typeof exported !== "object" || exported === null || Array.isArray(exported)) {
2536
+ throw new Error(`\u914D\u7F6E\u6587\u4EF6 ${file} \u8981\u9ED8\u8BA4\u5BFC\u51FA\u4E00\u4E2A\u5BF9\u8C61\uFF0C\u5982 export default { port: 3000 }`);
2537
+ }
2538
+ const config = exported;
2539
+ const unknown = Object.keys(config).filter((key) => !CONFIG_KEYS.has(key));
2540
+ if (unknown.length) warn(`\u914D\u7F6E\u6587\u4EF6 ${file} \u91CC\u6709\u4E0D\u8BA4\u8BC6\u7684\u914D\u7F6E\u9879\uFF1A${unknown.join("\u3001")}\uFF0C\u5DF2\u5FFD\u7565`);
2541
+ const dir = import_path7.default.dirname(file);
2542
+ const resolve = (value) => import_path7.default.resolve(dir, value);
2543
+ return {
2544
+ ...config,
2545
+ project: config.project && resolve(config.project),
2546
+ setup: typeof config.setup === "string" ? resolve(config.setup) : config.setup,
2547
+ extensions: config.extensions?.map((extension) => typeof extension === "string" && extension.startsWith(".") ? resolve(extension) : extension)
2548
+ };
2549
+ }
2550
+ async function importConfig(file) {
2551
+ if (!/\.m?ts$/.test(file)) return defaultExport(await import((0, import_url2.pathToFileURL)(file).href));
2552
+ const { code } = await (0, import_esbuild3.transform)(import_fs7.default.readFileSync(file, "utf8"), {
2553
+ loader: "ts",
2554
+ format: "esm",
2555
+ platform: "node",
2556
+ sourcefile: file,
2557
+ define: {
2558
+ __dirname: JSON.stringify(import_path7.default.dirname(file)),
2559
+ __filename: JSON.stringify(file),
2560
+ "import.meta.url": JSON.stringify((0, import_url2.pathToFileURL)(file).href)
2561
+ }
2562
+ });
2563
+ const compiled = `${file}.${(0, import_crypto4.randomUUID)()}.mjs`;
2564
+ import_fs7.default.writeFileSync(compiled, code);
2565
+ try {
2566
+ return defaultExport(await import((0, import_url2.pathToFileURL)(compiled).href));
2567
+ } finally {
2568
+ import_fs7.default.rmSync(compiled, { force: true });
2569
+ }
2570
+ }
2571
+ function defaultExport(module2) {
2572
+ const exported = module2.default;
2573
+ return exported?.__esModule ? exported.default : exported;
2574
+ }
2455
2575
  // Annotate the CommonJS export names for ESM import in node:
2456
2576
  0 && (module.exports = {
2457
2577
  AppBuilder,
2578
+ CONFIG_FILE_NAMES,
2458
2579
  DEFAULT_DEVICE,
2459
2580
  DevServer,
2460
2581
  LOCAL_HOST_OF_ANY_ADDRESS,
2461
2582
  MiniProgram,
2462
2583
  bundleSetup,
2463
2584
  createWxConfig,
2585
+ findConfigFile,
2464
2586
  findRuntimeDir,
2465
2587
  findSetupFile,
2466
2588
  handleProxy,
2467
- listen
2589
+ listen,
2590
+ loadConfigFile
2468
2591
  });
2469
2592
  //# sourceMappingURL=index.js.map