@weapp-vite/miniprogram-automator 1.2.13 → 1.2.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -0
- package/dist/index.d.mts +7 -1
- package/dist/index.mjs +333 -10
- package/dist/{launch-yIGimB0F.mjs → launch-DKcPk1N2.mjs} +235 -25
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -107,6 +107,25 @@ await button?.tap()
|
|
|
107
107
|
await page.waitFor(500)
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
+
### 4.5 App-Service 降级(route fallback)能力边界
|
|
111
|
+
|
|
112
|
+
部分 DevTools 版本(如 `2.01.2510290`)的 page-frame 定向协议失效,表现为 `Page.*` RPC 全部超时。此时本库自动切换到 App-Service 降级通道(`App.callFunction` + `wx.createSelectorQuery`),无需任何配置。
|
|
113
|
+
|
|
114
|
+
降级模式下各能力可用性:
|
|
115
|
+
|
|
116
|
+
| 能力 | 状态 | 说明 |
|
|
117
|
+
| --------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------- |
|
|
118
|
+
| `page.$` / `page.$$` | ✅ | 经 route 查询返回降级元素 |
|
|
119
|
+
| `page.data()` / `page.setData()` / `page.callMethod()` | ✅ | 经 App-Service 读写页面数据、调用页面方法 |
|
|
120
|
+
| `element.offset()` / `element.size()` / `element.style()` | ✅ | 实时经 `createSelectorQuery` 读取 rect 与 computedStyle(每次读取都重新查询,不缓存) |
|
|
121
|
+
| `element.attribute('id')` / `element.attribute('data-*')` | ✅ | 经快照读取 id 与 dataset |
|
|
122
|
+
| `element.$()` / `element.$$()` | ❌ 明确报错 | 元素级查询依赖 page-frame 协议;请改用 `page.$()` / `page.$$()` |
|
|
123
|
+
| `element.text()` | ❌ 明确报错 | `SelectorQuery` 无法读取 `innerText`;请改用 `page.data()` 或 `page.callMethod()` 断言文案来源 |
|
|
124
|
+
| `element.tap()` / `trigger()` 等真实交互 | ❌ 明确报错 | AppService 无法合成真实触摸事件;请改用 `page.callMethod()` 或 `evaluate` 间接触发页面逻辑 |
|
|
125
|
+
| `scroll-view` 容器滚动 | ❌ | 需 page-frame 协议支持,降级模式不可用 |
|
|
126
|
+
|
|
127
|
+
> 降级元素上的不支持操作会**立即抛出带替代建议的错误**,不会再像旧版那样挂起 30s 才超时。
|
|
128
|
+
|
|
110
129
|
## 5. 主要导出
|
|
111
130
|
|
|
112
131
|
| 导出 | 说明 |
|
package/dist/index.d.mts
CHANGED
|
@@ -94,6 +94,12 @@ interface IElementOptions {
|
|
|
94
94
|
videoId?: string;
|
|
95
95
|
pageId: number;
|
|
96
96
|
routeFallback?: boolean;
|
|
97
|
+
/** route 降级元素的定位信息:用于只读方法经 App-service 实时重查快照。 */
|
|
98
|
+
routeFallbackSelector?: string;
|
|
99
|
+
routeFallbackIndex?: number;
|
|
100
|
+
routeFallbackRoute?: string;
|
|
101
|
+
routeFallbackQuery?: Record<string, any>;
|
|
102
|
+
routeFallbackScopeSelectors?: string[];
|
|
97
103
|
tagName: string;
|
|
98
104
|
}
|
|
99
105
|
/** ITouch 的类型定义。 */
|
|
@@ -123,7 +129,7 @@ interface ElementQueryOptions {
|
|
|
123
129
|
}
|
|
124
130
|
/** Element 的实现。 */
|
|
125
131
|
declare class Element {
|
|
126
|
-
|
|
132
|
+
protected readonly connection: Connection;
|
|
127
133
|
private elementMap;
|
|
128
134
|
tagName: string;
|
|
129
135
|
protected nodeId: string | null;
|
package/dist/index.mjs
CHANGED
|
@@ -222,6 +222,223 @@ function toStr(value) {
|
|
|
222
222
|
return value == null ? "" : String(value);
|
|
223
223
|
}
|
|
224
224
|
//#endregion
|
|
225
|
+
//#region src/pageRouteSnapshot.ts
|
|
226
|
+
const ROUTE_ELEMENT_SNAPSHOT_TIMEOUT = 5e3;
|
|
227
|
+
const ROUTE_ELEMENT_SNAPSHOT_EMPTY_RETRIES = 2;
|
|
228
|
+
const ROUTE_ELEMENT_SNAPSHOT_RETRY_DELAY = 220;
|
|
229
|
+
/**
|
|
230
|
+
* 页面定位 + 组件作用域遍历逻辑与 Page.renderedNodes 保持一致:
|
|
231
|
+
* 先按 route/query 找到目标页面,再逐层进入自定义组件作用域查询,
|
|
232
|
+
* 返回首个命中作用域里 selectAll(selector)[index] 的快照;未命中返回 null。
|
|
233
|
+
*/
|
|
234
|
+
const ROUTE_ELEMENT_SNAPSHOT_FUNCTION_DECLARATION = `function (route, query, selector, index, scopeSelectors, styleNames) {
|
|
235
|
+
function normalizeRoute(value) {
|
|
236
|
+
return String(value || '').replace(/^\\/+/, '').replace(/\\/+$/g, '');
|
|
237
|
+
}
|
|
238
|
+
function matchesQuery(page, expectedQuery) {
|
|
239
|
+
if (!expectedQuery || !Object.keys(expectedQuery).length) {
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
var actualQuery = page && (page.options || page.query || {});
|
|
243
|
+
return Object.keys(expectedQuery).every(function (key) {
|
|
244
|
+
var actualValue = String(actualQuery[key] == null ? '' : actualQuery[key]);
|
|
245
|
+
var expectedValue = String(expectedQuery[key]);
|
|
246
|
+
var decodedActualValue = actualValue;
|
|
247
|
+
try {
|
|
248
|
+
decodedActualValue = decodeURIComponent(actualValue);
|
|
249
|
+
}
|
|
250
|
+
catch (_) {
|
|
251
|
+
}
|
|
252
|
+
return actualValue === expectedValue
|
|
253
|
+
|| decodedActualValue === expectedValue
|
|
254
|
+
|| actualValue === encodeURIComponent(expectedValue);
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
var pages = typeof getCurrentPages === 'function' ? getCurrentPages() : [];
|
|
258
|
+
var expectedRoute = normalizeRoute(route);
|
|
259
|
+
var page = null;
|
|
260
|
+
for (var pageIndex = pages.length - 1; pageIndex >= 0; pageIndex -= 1) {
|
|
261
|
+
var candidate = pages[pageIndex];
|
|
262
|
+
var candidateRoute = normalizeRoute(candidate && (candidate.path || candidate.route || candidate.__route__));
|
|
263
|
+
if ((!expectedRoute || candidateRoute === expectedRoute) && matchesQuery(candidate, query)) {
|
|
264
|
+
page = candidate;
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (!page) {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
function createSelectorQuery(scope) {
|
|
272
|
+
var selectorQuery = null;
|
|
273
|
+
var createdFromScope = false;
|
|
274
|
+
if (scope && typeof scope.createSelectorQuery === 'function') {
|
|
275
|
+
selectorQuery = scope.createSelectorQuery();
|
|
276
|
+
createdFromScope = true;
|
|
277
|
+
}
|
|
278
|
+
else if (typeof wx !== 'undefined' && wx && typeof wx.createSelectorQuery === 'function') {
|
|
279
|
+
selectorQuery = wx.createSelectorQuery();
|
|
280
|
+
}
|
|
281
|
+
else if (typeof page.createSelectorQuery === 'function') {
|
|
282
|
+
selectorQuery = page.createSelectorQuery();
|
|
283
|
+
}
|
|
284
|
+
if (!selectorQuery) {
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
if (!createdFromScope && typeof selectorQuery.in === 'function') {
|
|
288
|
+
try {
|
|
289
|
+
selectorQuery = selectorQuery.in(scope || page);
|
|
290
|
+
}
|
|
291
|
+
catch (_) {
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return selectorQuery;
|
|
295
|
+
}
|
|
296
|
+
function pushUnique(list, seen, item) {
|
|
297
|
+
if (!item) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
var id = item.is || item.id || item.__wxWebviewId__ || item.__wxExparserNodeId__ || String(list.length);
|
|
301
|
+
if (seen[id]) {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
seen[id] = true;
|
|
305
|
+
list.push(item);
|
|
306
|
+
}
|
|
307
|
+
function collectScopes(root, scopeSelectors) {
|
|
308
|
+
var scopes = [];
|
|
309
|
+
var seen = {};
|
|
310
|
+
var queue = [];
|
|
311
|
+
pushUnique(scopes, seen, root);
|
|
312
|
+
queue.push(root);
|
|
313
|
+
for (var queueIndex = 0; queueIndex < queue.length && queueIndex < 60; queueIndex += 1) {
|
|
314
|
+
var scope = queue[queueIndex];
|
|
315
|
+
if (!scope || typeof scope.selectAllComponents !== 'function') {
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
var children = [];
|
|
319
|
+
var componentSelectors = Array.isArray(scopeSelectors) ? scopeSelectors.slice() : [];
|
|
320
|
+
componentSelectors.push(selector, '*', 'weapp-app-shell', 'weapp-layout-default');
|
|
321
|
+
for (var selectorIndex = 0; selectorIndex < componentSelectors.length; selectorIndex += 1) {
|
|
322
|
+
if (!componentSelectors[selectorIndex]) {
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
try {
|
|
326
|
+
var selected = scope.selectAllComponents(componentSelectors[selectorIndex]);
|
|
327
|
+
if (Array.isArray(selected)) {
|
|
328
|
+
children = children.concat(selected);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
catch (_) {
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
for (var childIndex = 0; childIndex < children.length; childIndex += 1) {
|
|
335
|
+
var child = children[childIndex];
|
|
336
|
+
var previousLength = scopes.length;
|
|
337
|
+
pushUnique(scopes, seen, child);
|
|
338
|
+
if (scopes.length > previousLength) {
|
|
339
|
+
queue.push(child);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return scopes;
|
|
344
|
+
}
|
|
345
|
+
function queryScope(scope) {
|
|
346
|
+
return new Promise(function (resolve) {
|
|
347
|
+
var settled = false;
|
|
348
|
+
var timer = setTimeout(function () {
|
|
349
|
+
if (settled) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
settled = true;
|
|
353
|
+
resolve([]);
|
|
354
|
+
}, 800);
|
|
355
|
+
function finish(nodes) {
|
|
356
|
+
if (settled) {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
settled = true;
|
|
360
|
+
clearTimeout(timer);
|
|
361
|
+
resolve(Array.isArray(nodes) ? nodes : nodes ? [nodes] : []);
|
|
362
|
+
}
|
|
363
|
+
try {
|
|
364
|
+
var selectorQuery = createSelectorQuery(scope);
|
|
365
|
+
if (!selectorQuery) {
|
|
366
|
+
finish([]);
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
selectorQuery
|
|
370
|
+
.selectAll(selector)
|
|
371
|
+
.fields({
|
|
372
|
+
id: true,
|
|
373
|
+
dataset: true,
|
|
374
|
+
rect: true,
|
|
375
|
+
size: true,
|
|
376
|
+
computedStyle: Array.isArray(styleNames) ? styleNames : []
|
|
377
|
+
}, function (nodes) {
|
|
378
|
+
finish(nodes);
|
|
379
|
+
})
|
|
380
|
+
.exec();
|
|
381
|
+
}
|
|
382
|
+
catch (_) {
|
|
383
|
+
finish([]);
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
return new Promise(function (resolve) {
|
|
388
|
+
try {
|
|
389
|
+
var scopes = collectScopes(page, scopeSelectors);
|
|
390
|
+
var scopeIndex = 0;
|
|
391
|
+
function next() {
|
|
392
|
+
if (scopeIndex >= scopes.length) {
|
|
393
|
+
resolve(null);
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
var scope = scopes[scopeIndex];
|
|
397
|
+
scopeIndex += 1;
|
|
398
|
+
queryScope(scope).then(function (nodes) {
|
|
399
|
+
if (nodes.length > 0) {
|
|
400
|
+
resolve(nodes[index] || null);
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
next();
|
|
404
|
+
}, function () {
|
|
405
|
+
next();
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
next();
|
|
409
|
+
}
|
|
410
|
+
catch (_) {
|
|
411
|
+
resolve(null);
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
}`;
|
|
415
|
+
/**
|
|
416
|
+
* 读取降级元素快照。未找到(页面不在/选择器无命中/索引越界)返回 null,
|
|
417
|
+
* 由调用方决定抛错文案;永不因为协议失效而长时间挂起。
|
|
418
|
+
*/
|
|
419
|
+
async function readRouteElementSnapshot(connection, route, query, selector, index, scopeSelectors = [], styleNames = [], timeout = ROUTE_ELEMENT_SNAPSHOT_TIMEOUT) {
|
|
420
|
+
const startedAt = Date.now();
|
|
421
|
+
for (let attempt = 0; attempt <= ROUTE_ELEMENT_SNAPSHOT_EMPTY_RETRIES; attempt += 1) {
|
|
422
|
+
const remaining = attempt === 0 ? timeout : Math.max(1, timeout - (Date.now() - startedAt));
|
|
423
|
+
const { result } = await connection.send("App.callFunction", {
|
|
424
|
+
functionDeclaration: ROUTE_ELEMENT_SNAPSHOT_FUNCTION_DECLARATION,
|
|
425
|
+
args: [
|
|
426
|
+
route,
|
|
427
|
+
query,
|
|
428
|
+
selector,
|
|
429
|
+
index,
|
|
430
|
+
scopeSelectors,
|
|
431
|
+
styleNames
|
|
432
|
+
]
|
|
433
|
+
}, { timeout: remaining });
|
|
434
|
+
if (result != null || attempt === ROUTE_ELEMENT_SNAPSHOT_EMPTY_RETRIES) return result ?? null;
|
|
435
|
+
const retryRemaining = timeout - (Date.now() - startedAt);
|
|
436
|
+
if (retryRemaining <= 0) break;
|
|
437
|
+
await sleep$1(Math.min(ROUTE_ELEMENT_SNAPSHOT_RETRY_DELAY, retryRemaining));
|
|
438
|
+
}
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
//#endregion
|
|
225
442
|
//#region src/Element.ts
|
|
226
443
|
const ELEMENT_QUERY_TIMEOUT = 2500;
|
|
227
444
|
const ELEMENT_WXML_TIMEOUT = 8e3;
|
|
@@ -272,7 +489,17 @@ var Element = class Element {
|
|
|
272
489
|
};
|
|
273
490
|
}
|
|
274
491
|
async offset() {
|
|
275
|
-
|
|
492
|
+
const offset = await this.send("Element.getOffset");
|
|
493
|
+
if (offset?.width !== void 0 && offset.height !== void 0) return offset;
|
|
494
|
+
try {
|
|
495
|
+
const dimensions = await this.size();
|
|
496
|
+
return {
|
|
497
|
+
...offset,
|
|
498
|
+
...dimensions
|
|
499
|
+
};
|
|
500
|
+
} catch {
|
|
501
|
+
return offset;
|
|
502
|
+
}
|
|
276
503
|
}
|
|
277
504
|
async text() {
|
|
278
505
|
return await this.domProperty("innerText");
|
|
@@ -394,8 +621,87 @@ var Element = class Element {
|
|
|
394
621
|
};
|
|
395
622
|
/** App-Service 路由降级元素仅用于只读查询,不能伪装成交互协议元素。 */
|
|
396
623
|
var RouteFallbackElement = class extends Element {
|
|
624
|
+
routeSelector;
|
|
625
|
+
routeIndex;
|
|
626
|
+
routeRoute;
|
|
627
|
+
routeQuery;
|
|
628
|
+
routeScopeSelectors;
|
|
629
|
+
constructor(connection, options, elementMap) {
|
|
630
|
+
super(connection, options, elementMap);
|
|
631
|
+
this.routeSelector = options.routeFallbackSelector;
|
|
632
|
+
this.routeIndex = options.routeFallbackIndex;
|
|
633
|
+
this.routeRoute = options.routeFallbackRoute;
|
|
634
|
+
this.routeQuery = options.routeFallbackQuery;
|
|
635
|
+
this.routeScopeSelectors = options.routeFallbackScopeSelectors ?? [];
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* 经 App-service SelectorQuery 实时重查元素快照(rect/size/dataset/computedStyle)。
|
|
639
|
+
* 每次读取都重新查询,保证滚动/重渲染后拿到的是当前值;未命中返回 null。
|
|
640
|
+
*/
|
|
641
|
+
async readSnapshot(styleNames = []) {
|
|
642
|
+
if (!this.routeSelector || this.routeIndex === void 0 || !this.routeRoute) return null;
|
|
643
|
+
return await readRouteElementSnapshot(this.connection, this.routeRoute, this.routeQuery ?? {}, this.routeSelector, this.routeIndex, this.routeScopeSelectors, styleNames);
|
|
644
|
+
}
|
|
645
|
+
missingSnapshot(what) {
|
|
646
|
+
return /* @__PURE__ */ new Error(`route fallback 元素未取到${what}: ${this.routeSelector ?? "(未知选择器)"}(页面已切换或元素已卸载)`);
|
|
647
|
+
}
|
|
648
|
+
async $(selector, _options = {}) {
|
|
649
|
+
return this.unsupported(`Element.$(${selector})`);
|
|
650
|
+
}
|
|
651
|
+
async $$(selector, _options = {}) {
|
|
652
|
+
return this.unsupported(`Element.$$(${selector})`);
|
|
653
|
+
}
|
|
654
|
+
async offset() {
|
|
655
|
+
const snapshot = await this.readSnapshot();
|
|
656
|
+
if (snapshot?.left === void 0 || snapshot.top === void 0) throw this.missingSnapshot("坐标");
|
|
657
|
+
return {
|
|
658
|
+
left: snapshot.left,
|
|
659
|
+
top: snapshot.top,
|
|
660
|
+
width: snapshot.width,
|
|
661
|
+
height: snapshot.height
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
async size() {
|
|
665
|
+
const snapshot = await this.readSnapshot();
|
|
666
|
+
if (snapshot?.width === void 0 || snapshot.height === void 0) throw this.missingSnapshot("尺寸");
|
|
667
|
+
return {
|
|
668
|
+
width: snapshot.width,
|
|
669
|
+
height: snapshot.height
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
async style(name) {
|
|
673
|
+
if (!isStr(name)) throw new Error("name must be a string");
|
|
674
|
+
const value = (await this.readSnapshot([name]))?.[name];
|
|
675
|
+
if (value === void 0) throw this.missingSnapshot(`样式 ${name}`);
|
|
676
|
+
return value;
|
|
677
|
+
}
|
|
678
|
+
async attribute(name) {
|
|
679
|
+
if (!isStr(name)) throw new Error("name must be a string");
|
|
680
|
+
const snapshot = await this.readSnapshot();
|
|
681
|
+
if (name === "id") return snapshot?.id;
|
|
682
|
+
if (name.startsWith("data-")) {
|
|
683
|
+
const key = name.slice(5).replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
684
|
+
return snapshot?.dataset?.[key];
|
|
685
|
+
}
|
|
686
|
+
throw new Error(`route fallback 元素仅支持读取 id 与 data-* 属性,不支持 attribute(${name})`);
|
|
687
|
+
}
|
|
397
688
|
unsupported(method) {
|
|
398
|
-
throw new Error(`App-Service route fallback 元素不支持 ${method}
|
|
689
|
+
throw new Error(`App-Service route fallback 元素不支持 ${method}(真实交互依赖已失效的 page-frame 协议);可改用 page.callMethod()/evaluate 间接触发页面逻辑`);
|
|
690
|
+
}
|
|
691
|
+
async text() {
|
|
692
|
+
throw new Error("App-Service route fallback 元素不支持 text(SelectorQuery 无法读取 innerText);请改用 page.data() 断言数据源,或 page.callMethod()/evaluate 读取");
|
|
693
|
+
}
|
|
694
|
+
async value() {
|
|
695
|
+
this.unsupported("Element.value");
|
|
696
|
+
}
|
|
697
|
+
async property(name) {
|
|
698
|
+
this.unsupported(`Element.property(${name})`);
|
|
699
|
+
}
|
|
700
|
+
async wxml() {
|
|
701
|
+
this.unsupported("Element.wxml");
|
|
702
|
+
}
|
|
703
|
+
async outerWxml() {
|
|
704
|
+
this.unsupported("Element.outerWxml");
|
|
399
705
|
}
|
|
400
706
|
async tap() {
|
|
401
707
|
this.unsupported("Element.tap");
|
|
@@ -640,7 +946,7 @@ var Connection = class Connection extends EventEmitter {
|
|
|
640
946
|
//#endregion
|
|
641
947
|
//#region src/headless.ts
|
|
642
948
|
async function launchHeadlessAutomator(options) {
|
|
643
|
-
return await (await import("./launch-
|
|
949
|
+
return await (await import("./launch-DKcPk1N2.mjs")).launch({ projectPath: options.projectPath });
|
|
644
950
|
}
|
|
645
951
|
//#endregion
|
|
646
952
|
//#region src/launcher/portLease.ts
|
|
@@ -739,7 +1045,7 @@ async function acquireAutomatorPortLease(preferredPort) {
|
|
|
739
1045
|
}
|
|
740
1046
|
//#endregion
|
|
741
1047
|
//#region package.json
|
|
742
|
-
var version = "1.2.
|
|
1048
|
+
var version = "1.2.14";
|
|
743
1049
|
//#endregion
|
|
744
1050
|
//#region src/Native.ts
|
|
745
1051
|
/** Native 的实现。 */
|
|
@@ -800,14 +1106,19 @@ function resolveFallbackTagName(selector) {
|
|
|
800
1106
|
return "view";
|
|
801
1107
|
}
|
|
802
1108
|
/** 将 App-Service 查询到的渲染节点包装为 Element 兼容对象。 */
|
|
803
|
-
function createRouteFallbackElement(connection, elementMap, pageId, selector, node, index) {
|
|
1109
|
+
function createRouteFallbackElement(connection, elementMap, pageId, selector, node, index, route, query, scopeSelectors = []) {
|
|
804
1110
|
const nodeId = typeof node.id === "string" && node.id ? normalizeFallbackIdPart(node.id) : String(index);
|
|
805
1111
|
const elementId = `__weapp_vite_route_fallback_${pageId}_${normalizeFallbackIdPart(selector)}_${nodeId}_${index}`;
|
|
806
1112
|
return Element.create(connection, {
|
|
807
1113
|
elementId,
|
|
808
1114
|
pageId,
|
|
809
1115
|
routeFallback: true,
|
|
810
|
-
tagName: resolveFallbackTagName(selector)
|
|
1116
|
+
tagName: resolveFallbackTagName(selector),
|
|
1117
|
+
routeFallbackSelector: selector,
|
|
1118
|
+
routeFallbackIndex: index,
|
|
1119
|
+
routeFallbackRoute: route,
|
|
1120
|
+
routeFallbackQuery: query,
|
|
1121
|
+
routeFallbackScopeSelectors: [...scopeSelectors]
|
|
811
1122
|
}, elementMap);
|
|
812
1123
|
}
|
|
813
1124
|
//#endregion
|
|
@@ -976,6 +1287,7 @@ const PAGE_CALL_METHOD_FALLBACK_RETRY_DELAY = 300;
|
|
|
976
1287
|
const PAGE_RENDERED_TIMEOUT = 15e3;
|
|
977
1288
|
const PAGE_RENDERED_POLL_DELAY = 220;
|
|
978
1289
|
const PAGE_RENDERED_QUERY_TIMEOUT = 5e3;
|
|
1290
|
+
const PAGE_RENDERED_EMPTY_RETRIES = 2;
|
|
979
1291
|
const PAGE_ROOT_SELECTORS = [
|
|
980
1292
|
"page",
|
|
981
1293
|
"body",
|
|
@@ -1723,10 +2035,21 @@ var Page = class Page {
|
|
|
1723
2035
|
return (await this.queryRouteElements(selector, options))[0] ?? null;
|
|
1724
2036
|
}
|
|
1725
2037
|
async queryRouteElements(selector, options) {
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
2038
|
+
const timeout = options.timeout ?? PAGE_QUERY_TIMEOUT;
|
|
2039
|
+
const startedAt = Date.now();
|
|
2040
|
+
let nodes = [];
|
|
2041
|
+
for (let attempt = 0; attempt <= PAGE_RENDERED_EMPTY_RETRIES; attempt += 1) {
|
|
2042
|
+
const requestTimeout = attempt === 0 ? timeout : Math.max(1, timeout - (Date.now() - startedAt));
|
|
2043
|
+
nodes = await this.renderedNodes(selector, {
|
|
2044
|
+
componentSelectors: options.componentSelectors,
|
|
2045
|
+
timeout: requestTimeout
|
|
2046
|
+
});
|
|
2047
|
+
if (nodes.length > 0 || attempt === PAGE_RENDERED_EMPTY_RETRIES) break;
|
|
2048
|
+
const remaining = timeout - (Date.now() - startedAt);
|
|
2049
|
+
if (remaining <= 0) break;
|
|
2050
|
+
await sleep$1(Math.min(PAGE_RENDERED_POLL_DELAY, remaining));
|
|
2051
|
+
}
|
|
2052
|
+
return nodes.map((node, index) => createRouteFallbackElement(this.connection, this.elementMap, this.id, selector, node, index, this.path, this.query, options.componentSelectors));
|
|
1730
2053
|
}
|
|
1731
2054
|
async callRouteMethod(method, args, timeout = PAGE_CALL_METHOD_TIMEOUT) {
|
|
1732
2055
|
return await callRouteMethodViaAppService(this.connection, this.path, this.query, method, args, timeout);
|
|
@@ -14854,9 +14854,10 @@ function resolveComponentProperties(clonedNode, scope, definition) {
|
|
|
14854
14854
|
};
|
|
14855
14855
|
}
|
|
14856
14856
|
function createRuntimeComponentInstance(componentScopeId, context, clonedNode, componentEntry, nextProperties, ownerScopeId) {
|
|
14857
|
+
const componentProperties = Object.keys(componentEntry.definition.methods ?? {}).some((key) => key.startsWith("__weapp_vite_")) || Object.hasOwn(componentEntry.definition.properties ?? {}, "__wvSlotOwnerId") ? Object.fromEntries(Object.keys(componentEntry.definition.properties ?? {}).filter((key) => Object.hasOwn(nextProperties, key)).map((key) => [key, nextProperties[key]])) : nextProperties;
|
|
14857
14858
|
const componentInstance = createComponentInstance({
|
|
14858
14859
|
definition: componentEntry.definition,
|
|
14859
|
-
properties:
|
|
14860
|
+
properties: componentProperties,
|
|
14860
14861
|
requestRender: (callback) => context.session.requestRender(callback),
|
|
14861
14862
|
triggerEvent: buildComponentTrigger(componentScopeId, context, clonedNode)
|
|
14862
14863
|
});
|
|
@@ -14869,7 +14870,7 @@ function createRuntimeComponentInstance(componentScopeId, context, clonedNode, c
|
|
|
14869
14870
|
componentInstance.selectOwnerComponent = () => ownerScopeId ? context.componentCache.get(ownerScopeId) ?? null : null;
|
|
14870
14871
|
context.componentCache.set(componentScopeId, componentInstance);
|
|
14871
14872
|
runComponentLifecycle(componentInstance, "created");
|
|
14872
|
-
runComponentObservers(componentInstance.__definition__ ?? componentEntry.definition, componentInstance, Object.keys(
|
|
14873
|
+
runComponentObservers(componentInstance.__definition__ ?? componentEntry.definition, componentInstance, Object.keys(componentProperties), {});
|
|
14873
14874
|
componentInstance.__propertySnapshots = Object.fromEntries(Object.entries(componentInstance.properties).map(([key, propertyValue]) => [key, cloneValue$1(propertyValue)]));
|
|
14874
14875
|
runComponentLifecycle(componentInstance, "attached");
|
|
14875
14876
|
return componentInstance;
|
|
@@ -15138,15 +15139,21 @@ function registerAppDefinition(registries, definition) {
|
|
|
15138
15139
|
registries.appDefinition = definition;
|
|
15139
15140
|
return definition;
|
|
15140
15141
|
}
|
|
15141
|
-
function registerPageDefinition(registries, definition) {
|
|
15142
|
-
const route = registries.currentLoadContext?.route;
|
|
15142
|
+
function registerPageDefinition(registries, definition, explicitRoute) {
|
|
15143
|
+
const route = explicitRoute ?? registries.currentLoadContext?.route;
|
|
15143
15144
|
if (!route) throw new Error("Cannot register Page() without an active page load context in headless runtime.");
|
|
15144
15145
|
if (registries.pages.has(route)) throw createDuplicateRegistrationError("page", route);
|
|
15145
15146
|
registries.pages.set(route, definition);
|
|
15146
15147
|
return definition;
|
|
15147
15148
|
}
|
|
15148
15149
|
function registerComponentDefinition(registries, definition) {
|
|
15149
|
-
if (registries.currentLoadContext?.kind === "page")
|
|
15150
|
+
if (registries.currentLoadContext?.kind === "page") {
|
|
15151
|
+
if (registries.currentLoadContext.componentDefinitions) {
|
|
15152
|
+
registries.currentLoadContext.componentDefinitions.push(definition);
|
|
15153
|
+
return definition;
|
|
15154
|
+
}
|
|
15155
|
+
return registerPageDefinition(registries, normalizeComponentPageDefinition(definition));
|
|
15156
|
+
}
|
|
15150
15157
|
const id = registries.currentLoadContext?.route ?? `anonymous:${registries.components.size}`;
|
|
15151
15158
|
if (registries.components.has(id)) throw createDuplicateRegistrationError("component", id);
|
|
15152
15159
|
registries.components.set(id, definition);
|
|
@@ -15694,15 +15701,24 @@ var RuntimeDiagnostics = class {
|
|
|
15694
15701
|
timestamp: Date.now()
|
|
15695
15702
|
});
|
|
15696
15703
|
}
|
|
15697
|
-
createConsole(baseConsole = console) {
|
|
15704
|
+
createConsole(baseConsole = console, onConsole) {
|
|
15698
15705
|
const runtimeConsole = Object.create(baseConsole);
|
|
15706
|
+
const wrap = (level, method) => (...args) => {
|
|
15707
|
+
if (level === "error" || level === "warn") this.record(level, args);
|
|
15708
|
+
onConsole?.({
|
|
15709
|
+
args: [...args],
|
|
15710
|
+
level
|
|
15711
|
+
});
|
|
15712
|
+
method(...args);
|
|
15713
|
+
};
|
|
15714
|
+
runtimeConsole.debug = wrap("debug", baseConsole.debug.bind(baseConsole));
|
|
15715
|
+
runtimeConsole.info = wrap("info", baseConsole.info.bind(baseConsole));
|
|
15716
|
+
runtimeConsole.log = wrap("log", baseConsole.log.bind(baseConsole));
|
|
15699
15717
|
runtimeConsole.error = (...args) => {
|
|
15700
|
-
|
|
15701
|
-
baseConsole.error(...args);
|
|
15718
|
+
wrap("error", baseConsole.error.bind(baseConsole))(...args);
|
|
15702
15719
|
};
|
|
15703
15720
|
runtimeConsole.warn = (...args) => {
|
|
15704
|
-
|
|
15705
|
-
baseConsole.warn(...args);
|
|
15721
|
+
wrap("warn", baseConsole.warn.bind(baseConsole))(...args);
|
|
15706
15722
|
};
|
|
15707
15723
|
return runtimeConsole;
|
|
15708
15724
|
}
|
|
@@ -16321,12 +16337,15 @@ function parseNumericLikeValue$1(value) {
|
|
|
16321
16337
|
const match = value.match(NUMERIC_LIKE_VALUE_RE$1);
|
|
16322
16338
|
return match ? Number(match[0]) : 0;
|
|
16323
16339
|
}
|
|
16324
|
-
function resolveRect$1(node) {
|
|
16340
|
+
function resolveRect$1(node, windowInfo) {
|
|
16325
16341
|
const style = parseStyleDeclarations$1(node.attribs?.style);
|
|
16326
16342
|
const left = parseNumericLikeValue$1(node.attribs?.["data-sim-left"] ?? style.left);
|
|
16327
16343
|
const top = parseNumericLikeValue$1(node.attribs?.["data-sim-top"] ?? style.top);
|
|
16328
|
-
const
|
|
16329
|
-
const
|
|
16344
|
+
const rawWidth = node.attribs?.["data-sim-width"] ?? style.width;
|
|
16345
|
+
const rawHeight = node.attribs?.["data-sim-height"] ?? style.height;
|
|
16346
|
+
const isTopLevelView = node.name === "view" && node.parent?.name === "page" && windowInfo?.windowWidth != null;
|
|
16347
|
+
const width = rawWidth != null ? parseNumericLikeValue$1(rawWidth) : isTopLevelView && windowInfo ? windowInfo.windowWidth : 0;
|
|
16348
|
+
const height = rawHeight != null ? parseNumericLikeValue$1(rawHeight) : isTopLevelView && windowInfo ? windowInfo.windowHeight : 0;
|
|
16330
16349
|
return {
|
|
16331
16350
|
bottom: top + height,
|
|
16332
16351
|
height,
|
|
@@ -16373,7 +16392,7 @@ function resolveFieldsResult(node, fields, options) {
|
|
|
16373
16392
|
if (fields.dataset) result.dataset = collectDataset$1(node);
|
|
16374
16393
|
if (fields.mark) result.mark = collectMark(node);
|
|
16375
16394
|
if (fields.rect || fields.size) {
|
|
16376
|
-
const rect = resolveRect$1(node);
|
|
16395
|
+
const rect = resolveRect$1(node, options.root.name === "page" ? options.windowInfo : void 0);
|
|
16377
16396
|
if (fields.rect) Object.assign(result, rect);
|
|
16378
16397
|
if (fields.size) {
|
|
16379
16398
|
result.width = rect.width;
|
|
@@ -17161,6 +17180,102 @@ function createHeadlessVideoContext(driver, videoId, scope) {
|
|
|
17161
17180
|
}
|
|
17162
17181
|
//#endregion
|
|
17163
17182
|
//#region ../../mpcore/packages/simulator/src/runtime/moduleLoader.ts
|
|
17183
|
+
const ESM_IMPORT_LINE_RE = /^[ \t]*import[^\n]*$/gm;
|
|
17184
|
+
const ESM_EXPORT_LIST_RE = /^[ \t]*export[ \t]*\{([^}\n]+)\}[ \t]*;?$/gm;
|
|
17185
|
+
const ESM_EXPORT_DECLARATION_RE = /\bexport\s+(const|let|var|function|class)\s+([A-Za-z_$][\w$]*)/g;
|
|
17186
|
+
const ESM_EXPORT_DEFAULT_DECLARATION_RE = /\bexport\s+default\s+(function|class)\s+([A-Za-z_$][\w$]*)/g;
|
|
17187
|
+
function isImportWhitespace(value) {
|
|
17188
|
+
return value === " " || value === " ";
|
|
17189
|
+
}
|
|
17190
|
+
function parseEsmImportLine(line) {
|
|
17191
|
+
let statement = line.trim().slice(6).trim();
|
|
17192
|
+
if (statement.endsWith(";")) statement = statement.slice(0, -1).trimEnd();
|
|
17193
|
+
if (statement.startsWith("\"") || statement.startsWith("'")) {
|
|
17194
|
+
const quote = statement[0];
|
|
17195
|
+
if (statement.endsWith(quote) && statement.length > 1) return {
|
|
17196
|
+
request: statement.slice(1, -1),
|
|
17197
|
+
sideEffect: true
|
|
17198
|
+
};
|
|
17199
|
+
return;
|
|
17200
|
+
}
|
|
17201
|
+
let fromIndex = statement.indexOf("from");
|
|
17202
|
+
while (fromIndex >= 0) {
|
|
17203
|
+
if (isImportWhitespace(statement[fromIndex - 1]) && isImportWhitespace(statement[fromIndex + 4])) break;
|
|
17204
|
+
fromIndex = statement.indexOf("from", fromIndex + 4);
|
|
17205
|
+
}
|
|
17206
|
+
if (fromIndex < 0) return;
|
|
17207
|
+
const specifier = statement.slice(0, fromIndex).trim();
|
|
17208
|
+
const requestLiteral = statement.slice(fromIndex + 4).trim();
|
|
17209
|
+
const quote = requestLiteral[0];
|
|
17210
|
+
if (!specifier || quote !== "\"" && quote !== "'" || requestLiteral.length < 2 || !requestLiteral.endsWith(quote)) return;
|
|
17211
|
+
return {
|
|
17212
|
+
request: requestLiteral.slice(1, -1),
|
|
17213
|
+
sideEffect: false,
|
|
17214
|
+
specifier
|
|
17215
|
+
};
|
|
17216
|
+
}
|
|
17217
|
+
function createEsmImportBindings(specifier, request, index) {
|
|
17218
|
+
const namespaceName = `__headlessEsmImport${index}`;
|
|
17219
|
+
const statements = [`const ${namespaceName} = require(${JSON.stringify(request)});`];
|
|
17220
|
+
const normalizedSpecifier = specifier.trim();
|
|
17221
|
+
if (normalizedSpecifier.startsWith("{") && normalizedSpecifier.endsWith("}")) {
|
|
17222
|
+
const entries = normalizedSpecifier.slice(1, -1).split(",");
|
|
17223
|
+
for (const entry of entries) {
|
|
17224
|
+
const [importedName, localName] = entry.trim().split(/\s+as\s+/);
|
|
17225
|
+
if (!importedName) continue;
|
|
17226
|
+
const bindingName = localName || importedName;
|
|
17227
|
+
statements.push(`const ${bindingName} = ${namespaceName}[${JSON.stringify(importedName)}];`);
|
|
17228
|
+
}
|
|
17229
|
+
return statements.join("\n");
|
|
17230
|
+
}
|
|
17231
|
+
if (normalizedSpecifier.startsWith("* as ")) {
|
|
17232
|
+
const bindingName = normalizedSpecifier.slice(5).trim();
|
|
17233
|
+
statements.push(`const ${bindingName} = ${namespaceName};`);
|
|
17234
|
+
return statements.join("\n");
|
|
17235
|
+
}
|
|
17236
|
+
const [defaultName, namedPart] = normalizedSpecifier.split(/\s*,\s*/, 2);
|
|
17237
|
+
if (defaultName) statements.push(`const ${defaultName.trim()} = ${namespaceName}.default ?? ${namespaceName};`);
|
|
17238
|
+
if (namedPart?.startsWith("{") && namedPart.endsWith("}")) statements.push(createEsmImportBindings(namedPart, request, index).split("\n").slice(1).join("\n"));
|
|
17239
|
+
return statements.join("\n");
|
|
17240
|
+
}
|
|
17241
|
+
function transformEsmToCommonJs(source) {
|
|
17242
|
+
if (!/^\s*(?:import|export)\b/m.test(source)) return source;
|
|
17243
|
+
let importIndex = 0;
|
|
17244
|
+
const exportedNames = [];
|
|
17245
|
+
let transformed = source.replace(ESM_IMPORT_LINE_RE, (line) => {
|
|
17246
|
+
const parsed = parseEsmImportLine(line);
|
|
17247
|
+
if (!parsed) return line;
|
|
17248
|
+
if (parsed.sideEffect) return `require(${JSON.stringify(parsed.request)});`;
|
|
17249
|
+
return createEsmImportBindings(parsed.specifier, parsed.request, importIndex++);
|
|
17250
|
+
});
|
|
17251
|
+
transformed = transformed.replace(ESM_EXPORT_LIST_RE, (_match, entries) => {
|
|
17252
|
+
for (const entry of entries.split(",")) {
|
|
17253
|
+
const [local, exported] = entry.trim().split(/\s+as\s+/);
|
|
17254
|
+
if (local) exportedNames.push({
|
|
17255
|
+
exported: exported || local,
|
|
17256
|
+
local
|
|
17257
|
+
});
|
|
17258
|
+
}
|
|
17259
|
+
return "";
|
|
17260
|
+
});
|
|
17261
|
+
transformed = transformed.replace(ESM_EXPORT_DEFAULT_DECLARATION_RE, (_match, kind, local) => {
|
|
17262
|
+
exportedNames.push({
|
|
17263
|
+
exported: "default",
|
|
17264
|
+
local
|
|
17265
|
+
});
|
|
17266
|
+
return `${kind} ${local}`;
|
|
17267
|
+
});
|
|
17268
|
+
transformed = transformed.replace(ESM_EXPORT_DECLARATION_RE, (_match, kind, local) => {
|
|
17269
|
+
exportedNames.push({
|
|
17270
|
+
exported: local,
|
|
17271
|
+
local
|
|
17272
|
+
});
|
|
17273
|
+
return `${kind} ${local}`;
|
|
17274
|
+
});
|
|
17275
|
+
transformed = transformed.replace(/\bexport\s+default\s+/g, "module.exports.default = ");
|
|
17276
|
+
if (exportedNames.length > 0) transformed += `\n${exportedNames.map(({ exported, local }) => `module.exports[${JSON.stringify(exported)}] = ${local};`).join("\n")}`;
|
|
17277
|
+
return transformed;
|
|
17278
|
+
}
|
|
17164
17279
|
function createRequireNotFoundError(request, importer) {
|
|
17165
17280
|
return /* @__PURE__ */ new Error(`Cannot resolve require("${request}") from ${normalize(importer)} in headless runtime.`);
|
|
17166
17281
|
}
|
|
@@ -17176,7 +17291,7 @@ function resolveRequiredModulePath(artifactSource, importer, request) {
|
|
|
17176
17291
|
for (const candidate of candidates) if (artifactSource.has(candidate)) return candidate;
|
|
17177
17292
|
throw createRequireNotFoundError(request, importer);
|
|
17178
17293
|
}
|
|
17179
|
-
function createExecutionContext(registries, getCurrentPages, getApp, wxDriver, kernel, globals) {
|
|
17294
|
+
function createExecutionContext(registries, getCurrentPages, getApp, wxDriver, kernel, globals, onConsole) {
|
|
17180
17295
|
const wx = createHeadlessWx(wxDriver);
|
|
17181
17296
|
return {
|
|
17182
17297
|
App(definition) {
|
|
@@ -17194,7 +17309,7 @@ function createExecutionContext(registries, getCurrentPages, getApp, wxDriver, k
|
|
|
17194
17309
|
Page(definition) {
|
|
17195
17310
|
return registerPageDefinition(registries, definition);
|
|
17196
17311
|
},
|
|
17197
|
-
console: kernel.diagnostics.createConsole(),
|
|
17312
|
+
console: kernel.diagnostics.createConsole(console, onConsole),
|
|
17198
17313
|
clearInterval: (handle) => kernel.scheduler.clearInterval(handle),
|
|
17199
17314
|
clearTimeout: (handle) => kernel.scheduler.clearTimeout(handle),
|
|
17200
17315
|
getApp,
|
|
@@ -17204,13 +17319,17 @@ function createExecutionContext(registries, getCurrentPages, getApp, wxDriver, k
|
|
|
17204
17319
|
require: void 0,
|
|
17205
17320
|
setInterval: kernel.scheduler.setInterval.bind(kernel.scheduler),
|
|
17206
17321
|
setTimeout: kernel.scheduler.setTimeout.bind(kernel.scheduler),
|
|
17322
|
+
TextDecoder: globalThis.TextDecoder,
|
|
17323
|
+
TextEncoder: globalThis.TextEncoder,
|
|
17324
|
+
URL: globalThis.URL,
|
|
17325
|
+
URLSearchParams: globalThis.URLSearchParams,
|
|
17207
17326
|
wx,
|
|
17208
17327
|
...globals
|
|
17209
17328
|
};
|
|
17210
17329
|
}
|
|
17211
17330
|
function createModuleLoader(registries, getCurrentPages, getApp, wxDriver, options) {
|
|
17212
17331
|
const moduleCache = /* @__PURE__ */ new Map();
|
|
17213
|
-
const executionContext = createExecutionContext(registries, getCurrentPages, getApp, wxDriver, options.kernel, options.globals ?? {});
|
|
17332
|
+
const executionContext = createExecutionContext(registries, getCurrentPages, getApp, wxDriver, options.kernel, options.globals ?? {}, options.onConsole);
|
|
17214
17333
|
executionContext.globalThis = executionContext;
|
|
17215
17334
|
function executeModule(filePath, loadContext) {
|
|
17216
17335
|
const resolvedPath = path.resolve(filePath);
|
|
@@ -17240,9 +17359,13 @@ function createModuleLoader(registries, getCurrentPages, getApp, wxDriver, optio
|
|
|
17240
17359
|
});
|
|
17241
17360
|
localRequire.async = (request) => Promise.resolve().then(() => localRequire(request));
|
|
17242
17361
|
try {
|
|
17243
|
-
new vm.Script(`(function (exports, module, require, __filename, __dirname) { ${source}\n})`, { filename: resolvedPath }).runInNewContext(executionContext)(module.exports, module, localRequire, resolvedPath, path.dirname(resolvedPath));
|
|
17362
|
+
new vm.Script(`(function (exports, module, require, __filename, __dirname) { ${transformEsmToCommonJs(source)}\n})`, { filename: resolvedPath }).runInNewContext(executionContext)(module.exports, module, localRequire, resolvedPath, path.dirname(resolvedPath));
|
|
17244
17363
|
const registeredDefinitions = [...registries.components.values()].filter((definition) => !registeredDefinitionsBefore.has(definition));
|
|
17245
|
-
module.componentDefinitions = [.../* @__PURE__ */ new Set([
|
|
17364
|
+
module.componentDefinitions = [.../* @__PURE__ */ new Set([
|
|
17365
|
+
...requiredComponentDefinitions,
|
|
17366
|
+
...loadContext?.componentDefinitions ?? [],
|
|
17367
|
+
...registeredDefinitions
|
|
17368
|
+
])];
|
|
17246
17369
|
return module;
|
|
17247
17370
|
} finally {
|
|
17248
17371
|
registries.currentLoadContext = previousLoadContext;
|
|
@@ -17258,11 +17381,13 @@ function createModuleLoader(registries, getCurrentPages, getApp, wxDriver, optio
|
|
|
17258
17381
|
return registries.appDefinition;
|
|
17259
17382
|
},
|
|
17260
17383
|
executePageModule(filePath, route) {
|
|
17261
|
-
executeModule(filePath, {
|
|
17384
|
+
const fallbackDefinition = executeModule(filePath, {
|
|
17262
17385
|
kind: "page",
|
|
17263
|
-
route
|
|
17264
|
-
|
|
17265
|
-
|
|
17386
|
+
route,
|
|
17387
|
+
componentDefinitions: []
|
|
17388
|
+
}).componentDefinitions.at(-1);
|
|
17389
|
+
const definition = registries.pages.get(route) ?? (fallbackDefinition ? normalizeComponentPageDefinition(fallbackDefinition) : void 0);
|
|
17390
|
+
if (!registries.pages.has(route) && definition) registerPageDefinition(registries, definition, route);
|
|
17266
17391
|
if (!definition) throw new Error(`Page() was not registered for route "${route}" while executing ${normalize(filePath)}.`);
|
|
17267
17392
|
return definition;
|
|
17268
17393
|
},
|
|
@@ -19015,6 +19140,7 @@ var HeadlessSession = class {
|
|
|
19015
19140
|
appDefinition = null;
|
|
19016
19141
|
appInstance = null;
|
|
19017
19142
|
moduleLoader;
|
|
19143
|
+
eventListeners = /* @__PURE__ */ new Map();
|
|
19018
19144
|
registries;
|
|
19019
19145
|
currentPageInstance = null;
|
|
19020
19146
|
pages = [];
|
|
@@ -19156,7 +19282,8 @@ var HeadlessSession = class {
|
|
|
19156
19282
|
}, {
|
|
19157
19283
|
artifactSource: this.project.artifactSource,
|
|
19158
19284
|
globals: options.globals,
|
|
19159
|
-
kernel: this.kernel
|
|
19285
|
+
kernel: this.kernel,
|
|
19286
|
+
onConsole: (entry) => this.emit("console", entry)
|
|
19160
19287
|
});
|
|
19161
19288
|
}
|
|
19162
19289
|
assertActive() {
|
|
@@ -19170,11 +19297,26 @@ var HeadlessSession = class {
|
|
|
19170
19297
|
this.renderRequestPending = false;
|
|
19171
19298
|
this.wxState.close();
|
|
19172
19299
|
this.moduleLoader.close();
|
|
19300
|
+
this.eventListeners.clear();
|
|
19173
19301
|
this.kernel.close();
|
|
19174
19302
|
}
|
|
19175
19303
|
getDiagnostics() {
|
|
19176
19304
|
return this.kernel.diagnostics.getEntries();
|
|
19177
19305
|
}
|
|
19306
|
+
on(eventName, handler) {
|
|
19307
|
+
if (!eventName || typeof handler !== "function") return;
|
|
19308
|
+
const listeners = this.eventListeners.get(eventName) ?? /* @__PURE__ */ new Set();
|
|
19309
|
+
listeners.add(handler);
|
|
19310
|
+
this.eventListeners.set(eventName, listeners);
|
|
19311
|
+
}
|
|
19312
|
+
removeListener(eventName, handler) {
|
|
19313
|
+
const listeners = this.eventListeners.get(eventName);
|
|
19314
|
+
listeners?.delete(handler);
|
|
19315
|
+
if (listeners?.size === 0) this.eventListeners.delete(eventName);
|
|
19316
|
+
}
|
|
19317
|
+
emit(eventName, ...args) {
|
|
19318
|
+
for (const handler of [...this.eventListeners.get(eventName) ?? []]) handler(...args);
|
|
19319
|
+
}
|
|
19178
19320
|
get isClosed() {
|
|
19179
19321
|
return this.kernel.isClosed;
|
|
19180
19322
|
}
|
|
@@ -19186,6 +19328,10 @@ var HeadlessSession = class {
|
|
|
19186
19328
|
this.assertActive();
|
|
19187
19329
|
return this.pages.slice();
|
|
19188
19330
|
}
|
|
19331
|
+
getWx() {
|
|
19332
|
+
this.assertActive();
|
|
19333
|
+
return this.moduleLoader.wx;
|
|
19334
|
+
}
|
|
19189
19335
|
callWxMethod(methodName, ...args) {
|
|
19190
19336
|
this.assertActive();
|
|
19191
19337
|
const method = this.moduleLoader.wx[methodName];
|
|
@@ -19581,9 +19727,19 @@ var HeadlessSession = class {
|
|
|
19581
19727
|
this.appDefinition = this.moduleLoader.executeAppModule(appModulePath);
|
|
19582
19728
|
this.appInstance = createAppInstance(this.appDefinition);
|
|
19583
19729
|
this.appInstance.onLaunch?.(launchOptions);
|
|
19730
|
+
this.syncRuntimeAppState();
|
|
19584
19731
|
this.appInstance.onShow?.(launchOptions);
|
|
19585
19732
|
return this.appInstance;
|
|
19586
19733
|
}
|
|
19734
|
+
syncRuntimeAppState() {
|
|
19735
|
+
const runtimeApp = this.appInstance?.__wevuRuntimeApp;
|
|
19736
|
+
if (!runtimeApp || typeof runtimeApp !== "object" || !this.appInstance) return;
|
|
19737
|
+
if (runtimeApp.globalData && typeof runtimeApp.globalData === "object") this.appInstance.globalData = {
|
|
19738
|
+
...this.appInstance.globalData,
|
|
19739
|
+
...runtimeApp.globalData
|
|
19740
|
+
};
|
|
19741
|
+
if (Object.hasOwn(runtimeApp, "routes")) this.appInstance.routes = runtimeApp.routes;
|
|
19742
|
+
}
|
|
19587
19743
|
reLaunch(url) {
|
|
19588
19744
|
const target = this.resolveNavigationTarget(url);
|
|
19589
19745
|
this.bootstrap(createAppLaunchOptions(target.normalizedRoute, target.query));
|
|
@@ -20231,7 +20387,10 @@ var HeadlessTestingPageHandle = class {
|
|
|
20231
20387
|
return await runWithTimeout(async () => {
|
|
20232
20388
|
this.session?.renderCurrentPage();
|
|
20233
20389
|
const method = this.page[normalizedMethodName];
|
|
20234
|
-
if (typeof method !== "function")
|
|
20390
|
+
if (typeof method !== "function") {
|
|
20391
|
+
if (options.routeOnly) return;
|
|
20392
|
+
throw new TypeError(`Method "${normalizedMethodName}" does not exist on headless page ${this.page.route}.`);
|
|
20393
|
+
}
|
|
20235
20394
|
return cloneProtocolValue(await method.apply(this.page, args));
|
|
20236
20395
|
}, options.timeout, `Timed out calling page method "${normalizedMethodName}" in headless testing runtime.`);
|
|
20237
20396
|
}
|
|
@@ -20406,6 +20565,24 @@ var HeadlessTestingSessionHandle = class {
|
|
|
20406
20565
|
async close() {
|
|
20407
20566
|
this.session.close();
|
|
20408
20567
|
}
|
|
20568
|
+
on(eventName, handler) {
|
|
20569
|
+
this.session.on(eventName, handler);
|
|
20570
|
+
return this;
|
|
20571
|
+
}
|
|
20572
|
+
removeListener(eventName, handler) {
|
|
20573
|
+
this.session.removeListener(eventName, handler);
|
|
20574
|
+
return this;
|
|
20575
|
+
}
|
|
20576
|
+
off(eventName, handler) {
|
|
20577
|
+
return this.removeListener(eventName, handler);
|
|
20578
|
+
}
|
|
20579
|
+
async evaluate(evaluator, ...args) {
|
|
20580
|
+
return await this.evaluateWithOptions(evaluator, {}, ...args);
|
|
20581
|
+
}
|
|
20582
|
+
async evaluateWithOptions(evaluator, options = {}, ...args) {
|
|
20583
|
+
this.session.assertActive();
|
|
20584
|
+
return await runWithTimeout(() => this.evaluateInRuntime(evaluator, args), options.timeout, "Timed out evaluating code in headless testing runtime.");
|
|
20585
|
+
}
|
|
20409
20586
|
async currentPage() {
|
|
20410
20587
|
this.session.assertActive();
|
|
20411
20588
|
const current = this.session.getCurrentPages().at(-1);
|
|
@@ -20502,12 +20679,45 @@ var HeadlessTestingSessionHandle = class {
|
|
|
20502
20679
|
if (!page) throw new Error(`Failed to ${methodName} route "${route}" through the headless wx runtime.`);
|
|
20503
20680
|
return page;
|
|
20504
20681
|
}
|
|
20682
|
+
async evaluateInRuntime(evaluator, args) {
|
|
20683
|
+
const runtimeGlobals = {
|
|
20684
|
+
getApp: () => this.session.getApp(),
|
|
20685
|
+
getCurrentPages: () => this.session.getCurrentPages(),
|
|
20686
|
+
wx: this.session.getWx()
|
|
20687
|
+
};
|
|
20688
|
+
const host = globalThis;
|
|
20689
|
+
const previous = /* @__PURE__ */ new Map();
|
|
20690
|
+
for (const [name, value] of Object.entries(runtimeGlobals)) {
|
|
20691
|
+
previous.set(name, Object.getOwnPropertyDescriptor(host, name));
|
|
20692
|
+
Object.defineProperty(host, name, {
|
|
20693
|
+
configurable: true,
|
|
20694
|
+
enumerable: false,
|
|
20695
|
+
value,
|
|
20696
|
+
writable: true
|
|
20697
|
+
});
|
|
20698
|
+
}
|
|
20699
|
+
try {
|
|
20700
|
+
const fn = typeof evaluator === "string" ? new vm.Script(`(${evaluator})`).runInThisContext() : evaluator;
|
|
20701
|
+
if (typeof fn !== "function") throw new TypeError("Headless evaluator must be a function or function source string.");
|
|
20702
|
+
return await fn(...args);
|
|
20703
|
+
} finally {
|
|
20704
|
+
for (const [name, descriptor] of previous) if (descriptor) Object.defineProperty(host, name, descriptor);
|
|
20705
|
+
else delete host[name];
|
|
20706
|
+
}
|
|
20707
|
+
}
|
|
20505
20708
|
};
|
|
20506
20709
|
//#endregion
|
|
20507
20710
|
//#region ../../mpcore/packages/simulator/src/testing/launch.ts
|
|
20711
|
+
function resolveInitialRoute(session) {
|
|
20712
|
+
const entryPagePath = typeof session.project.appConfig.entryPagePath === "string" ? session.project.appConfig.entryPagePath.trim().replace(/^\/+/, "") : "";
|
|
20713
|
+
if (entryPagePath && session.project.routes.some((route) => route.route === entryPagePath)) return entryPagePath;
|
|
20714
|
+
return session.project.routes[0]?.route ?? null;
|
|
20715
|
+
}
|
|
20508
20716
|
async function launch(options) {
|
|
20509
20717
|
const session = createHeadlessSession({ projectPath: options.projectPath });
|
|
20510
20718
|
session.bootstrap();
|
|
20719
|
+
const initialRoute = resolveInitialRoute(session);
|
|
20720
|
+
if (initialRoute) session.reLaunch(`/${initialRoute}`);
|
|
20511
20721
|
return new HeadlessTestingSessionHandle(session.project, session);
|
|
20512
20722
|
}
|
|
20513
20723
|
//#endregion
|
package/package.json
CHANGED