@weapp-vite/miniprogram-automator 1.2.22 → 1.3.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/README.md +22 -0
- package/dist/index.d.mts +40 -3
- package/dist/index.mjs +341 -167
- package/dist/{launch-BNQZTcKo.mjs → launch-anJvfi-6.mjs} +1669 -445
- package/dist/operation.d.mts +64 -0
- package/dist/operation.mjs +233 -0
- package/package.json +17 -6
|
@@ -58,6 +58,41 @@ function createAppInstance(definition) {
|
|
|
58
58
|
return instance;
|
|
59
59
|
}
|
|
60
60
|
//#endregion
|
|
61
|
+
//#region ../../mpcore/packages/simulator/src/host/attachmentBindingUpdates.ts
|
|
62
|
+
const bindingUpdates = /* @__PURE__ */ new WeakMap();
|
|
63
|
+
const instanceScopes = /* @__PURE__ */ new WeakMap();
|
|
64
|
+
/** 实例经渲染上下文归属页面,不扩展公开 renderer context 的参数契约。 */
|
|
65
|
+
function bindAttachmentBindingScope(instance, scope) {
|
|
66
|
+
instanceScopes.set(instance, instanceScopes.get(scope) ?? scope);
|
|
67
|
+
}
|
|
68
|
+
/** 仅在挂载回调内同步逻辑绑定,不接管视图提交和 setData 回调。 */
|
|
69
|
+
function runWithAttachmentBindingUpdates(scope, update, callback, value) {
|
|
70
|
+
const previous = bindingUpdates.get(scope);
|
|
71
|
+
const state = previous ?? {
|
|
72
|
+
revision: 0,
|
|
73
|
+
update
|
|
74
|
+
};
|
|
75
|
+
const previousUpdate = state.update;
|
|
76
|
+
state.update = update;
|
|
77
|
+
bindingUpdates.set(scope, state);
|
|
78
|
+
try {
|
|
79
|
+
return callback(value);
|
|
80
|
+
} finally {
|
|
81
|
+
if (previous) state.update = previousUpdate;
|
|
82
|
+
else bindingUpdates.delete(scope);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function getAttachmentBindingRevision(instance) {
|
|
86
|
+
return bindingUpdates.get(instanceScopes.get(instance) ?? instance);
|
|
87
|
+
}
|
|
88
|
+
function synchronizeAttachmentBindings(instance) {
|
|
89
|
+
const state = bindingUpdates.get(instanceScopes.get(instance) ?? instance);
|
|
90
|
+
if (state) {
|
|
91
|
+
state.revision++;
|
|
92
|
+
state.update();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//#endregion
|
|
61
96
|
//#region ../../mpcore/packages/simulator/src/runtime/componentInstance/shared.ts
|
|
62
97
|
const ARRAY_INDEX_PATH_RE$1 = /\[(\d+)\]/g;
|
|
63
98
|
const ARRAY_INDEX_SEGMENT_RE$1 = /^\d+$/;
|
|
@@ -905,6 +940,7 @@ function createComponentInstance(options) {
|
|
|
905
940
|
const previousProperties = Object.fromEntries(propertyKeys.map((key) => [key, cloneValue$1(instance.properties[key])]));
|
|
906
941
|
for (const [key, value] of Object.entries(patch)) assignByPath$1(instance.data, key, value);
|
|
907
942
|
for (const key of dataKeys) instance.properties[key] = instance.data[key];
|
|
943
|
+
synchronizeAttachmentBindings(instance);
|
|
908
944
|
runComponentObservers(definition, instance, changedKeys, previousProperties);
|
|
909
945
|
if (options.requestRender) options.requestRender(callback);
|
|
910
946
|
else callback?.();
|
|
@@ -946,8 +982,11 @@ const componentPageInstances = /* @__PURE__ */ new WeakSet();
|
|
|
946
982
|
function isComponentPageAttaching(instance) {
|
|
947
983
|
return attachingInstances.has(instance);
|
|
948
984
|
}
|
|
949
|
-
function registerComponentPageAttachment(definition,
|
|
950
|
-
definitionAttachments.set(definition,
|
|
985
|
+
function registerComponentPageAttachment(definition, created, attached) {
|
|
986
|
+
definitionAttachments.set(definition, {
|
|
987
|
+
created,
|
|
988
|
+
attached
|
|
989
|
+
});
|
|
951
990
|
}
|
|
952
991
|
function bindComponentPageAttachment(instance, definition) {
|
|
953
992
|
const attach = definitionAttachments.get(definition);
|
|
@@ -958,13 +997,14 @@ function bindComponentPageAttachment(instance, definition) {
|
|
|
958
997
|
if (typeof options?.styleIsolation === "string") instanceStyleIsolation.set(instance, options.styleIsolation);
|
|
959
998
|
}
|
|
960
999
|
}
|
|
961
|
-
function attachComponentPage(instance) {
|
|
1000
|
+
function attachComponentPage(instance, updateBindings) {
|
|
962
1001
|
const attach = instanceAttachments.get(instance);
|
|
963
1002
|
if (!attach) return false;
|
|
964
1003
|
instanceAttachments.delete(instance);
|
|
965
1004
|
attachingInstances.add(instance);
|
|
966
1005
|
try {
|
|
967
|
-
attach.
|
|
1006
|
+
attach.created(instance);
|
|
1007
|
+
runWithAttachmentBindingUpdates(instance, updateBindings, attach.attached, instance);
|
|
968
1008
|
} finally {
|
|
969
1009
|
attachingInstances.delete(instance);
|
|
970
1010
|
}
|
|
@@ -1114,6 +1154,7 @@ function createPageInstance(route, definition, options = {}, pageState = {}) {
|
|
|
1114
1154
|
const rootKey = parseDataPath(key)[0];
|
|
1115
1155
|
if (rootKey) instance.properties[rootKey] = instance.data[rootKey];
|
|
1116
1156
|
}
|
|
1157
|
+
synchronizeAttachmentBindings(instance);
|
|
1117
1158
|
if (pageState.requestRender) pageState.requestRender(callback);
|
|
1118
1159
|
else callback?.();
|
|
1119
1160
|
}
|
|
@@ -1126,6 +1167,262 @@ function createPageInstance(route, definition, options = {}, pageState = {}) {
|
|
|
1126
1167
|
return instance;
|
|
1127
1168
|
}
|
|
1128
1169
|
//#endregion
|
|
1170
|
+
//#region ../../mpcore/packages/simulator/src/view/eventBindingParser.ts
|
|
1171
|
+
const LEGACY_EVENT_NAME_RE = /^\w+$/;
|
|
1172
|
+
const EVENT_BINDING_PREFIXES = [
|
|
1173
|
+
{
|
|
1174
|
+
prefix: "capture-bind:",
|
|
1175
|
+
priority: 0,
|
|
1176
|
+
stopAfter: false,
|
|
1177
|
+
phase: "capture"
|
|
1178
|
+
},
|
|
1179
|
+
{
|
|
1180
|
+
prefix: "capture-catch:",
|
|
1181
|
+
priority: 0,
|
|
1182
|
+
stopAfter: true,
|
|
1183
|
+
phase: "capture"
|
|
1184
|
+
},
|
|
1185
|
+
{
|
|
1186
|
+
prefix: "capture-bind",
|
|
1187
|
+
priority: 1,
|
|
1188
|
+
stopAfter: false,
|
|
1189
|
+
phase: "capture"
|
|
1190
|
+
},
|
|
1191
|
+
{
|
|
1192
|
+
prefix: "capture-catch",
|
|
1193
|
+
priority: 1,
|
|
1194
|
+
stopAfter: true,
|
|
1195
|
+
phase: "capture"
|
|
1196
|
+
},
|
|
1197
|
+
{
|
|
1198
|
+
prefix: "bind:",
|
|
1199
|
+
priority: 0,
|
|
1200
|
+
stopAfter: false,
|
|
1201
|
+
phase: "bubble"
|
|
1202
|
+
},
|
|
1203
|
+
{
|
|
1204
|
+
prefix: "catch:",
|
|
1205
|
+
priority: 0,
|
|
1206
|
+
stopAfter: true,
|
|
1207
|
+
phase: "bubble"
|
|
1208
|
+
},
|
|
1209
|
+
{
|
|
1210
|
+
prefix: "bind",
|
|
1211
|
+
priority: 1,
|
|
1212
|
+
stopAfter: false,
|
|
1213
|
+
phase: "bubble"
|
|
1214
|
+
},
|
|
1215
|
+
{
|
|
1216
|
+
prefix: "catch",
|
|
1217
|
+
priority: 1,
|
|
1218
|
+
stopAfter: true,
|
|
1219
|
+
phase: "bubble"
|
|
1220
|
+
}
|
|
1221
|
+
];
|
|
1222
|
+
function parseEventBinding(attributeName, method) {
|
|
1223
|
+
const definition = EVENT_BINDING_PREFIXES.find((item) => attributeName.startsWith(item.prefix));
|
|
1224
|
+
if (!definition) return null;
|
|
1225
|
+
const eventName = attributeName.slice(definition.prefix.length);
|
|
1226
|
+
if (!eventName || !definition.prefix.endsWith(":") && !LEGACY_EVENT_NAME_RE.test(eventName)) return null;
|
|
1227
|
+
return {
|
|
1228
|
+
eventName,
|
|
1229
|
+
method,
|
|
1230
|
+
phase: definition.phase,
|
|
1231
|
+
priority: definition.priority,
|
|
1232
|
+
stopAfter: definition.stopAfter
|
|
1233
|
+
};
|
|
1234
|
+
}
|
|
1235
|
+
function collectEventBindings(attributes = {}, includeCapture = true) {
|
|
1236
|
+
const eventBindings = /* @__PURE__ */ new Map();
|
|
1237
|
+
let captureEventBindings;
|
|
1238
|
+
const priorities = /* @__PURE__ */ new Map();
|
|
1239
|
+
for (const [attributeName, method] of Object.entries(attributes)) {
|
|
1240
|
+
const candidate = parseEventBinding(attributeName, method);
|
|
1241
|
+
if (!candidate || !includeCapture && candidate.phase === "capture") continue;
|
|
1242
|
+
const eventPriorities = priorities.get(candidate.eventName) ?? {};
|
|
1243
|
+
if (candidate.priority < (eventPriorities[candidate.phase] ?? -1)) continue;
|
|
1244
|
+
(candidate.phase === "bubble" ? eventBindings : captureEventBindings ??= /* @__PURE__ */ new Map()).set(candidate.eventName, {
|
|
1245
|
+
method: candidate.method,
|
|
1246
|
+
stopAfter: candidate.stopAfter
|
|
1247
|
+
});
|
|
1248
|
+
eventPriorities[candidate.phase] = candidate.priority;
|
|
1249
|
+
priorities.set(candidate.eventName, eventPriorities);
|
|
1250
|
+
}
|
|
1251
|
+
return {
|
|
1252
|
+
eventBindings,
|
|
1253
|
+
captureEventBindings
|
|
1254
|
+
};
|
|
1255
|
+
}
|
|
1256
|
+
/** 单事件查询只保留命中的绑定,原生交互不构造整张事件表。 */
|
|
1257
|
+
function resolveEventBindingPhases(attributes, eventName, includeCapture = true) {
|
|
1258
|
+
if (!attributes) return null;
|
|
1259
|
+
let bindings = null;
|
|
1260
|
+
let bubblePriority = -1;
|
|
1261
|
+
let capturePriority = -1;
|
|
1262
|
+
for (const [attributeName, method] of Object.entries(attributes)) {
|
|
1263
|
+
const candidate = parseEventBinding(attributeName, method);
|
|
1264
|
+
if (!candidate || candidate.eventName !== eventName || !includeCapture && candidate.phase === "capture") continue;
|
|
1265
|
+
const priority = candidate.phase === "bubble" ? bubblePriority : capturePriority;
|
|
1266
|
+
if (candidate.priority < priority) continue;
|
|
1267
|
+
bindings ??= {};
|
|
1268
|
+
bindings[candidate.phase] = {
|
|
1269
|
+
method: candidate.method,
|
|
1270
|
+
stopAfter: candidate.stopAfter
|
|
1271
|
+
};
|
|
1272
|
+
if (candidate.phase === "bubble") bubblePriority = candidate.priority;
|
|
1273
|
+
else capturePriority = candidate.priority;
|
|
1274
|
+
}
|
|
1275
|
+
return bindings;
|
|
1276
|
+
}
|
|
1277
|
+
//#endregion
|
|
1278
|
+
//#region ../../mpcore/packages/simulator/src/view/nodeDataset.ts
|
|
1279
|
+
const DATASET_NAME_RE = /-([a-z])/g;
|
|
1280
|
+
function isDatasetAttribute(attributeName) {
|
|
1281
|
+
return attributeName.startsWith("data-") && !attributeName.startsWith("data-sim-");
|
|
1282
|
+
}
|
|
1283
|
+
function toDatasetKey(attributeName) {
|
|
1284
|
+
return attributeName.slice(5).replace(DATASET_NAME_RE, (_match, char) => char.toUpperCase());
|
|
1285
|
+
}
|
|
1286
|
+
function collectNodeDataset(node) {
|
|
1287
|
+
if (node.dataset) return { ...node.dataset };
|
|
1288
|
+
const dataset = {};
|
|
1289
|
+
for (const [key, value] of Object.entries(node.attribs ?? {})) if (isDatasetAttribute(key)) dataset[toDatasetKey(key)] = value;
|
|
1290
|
+
return dataset;
|
|
1291
|
+
}
|
|
1292
|
+
//#endregion
|
|
1293
|
+
//#region ../../mpcore/packages/simulator/src/view/componentEvent.ts
|
|
1294
|
+
const eventNodes = /* @__PURE__ */ new WeakMap();
|
|
1295
|
+
/** 在构造子树之前建立父链,created/observer 中的事件也能访问声明树。 */
|
|
1296
|
+
function registerComponentEventNode(node, scope, parent) {
|
|
1297
|
+
node.parent = parent ?? null;
|
|
1298
|
+
eventNodes.set(node, {
|
|
1299
|
+
scope,
|
|
1300
|
+
target: node
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1303
|
+
/** 宿主与作用域共享同一份分阶段绑定,eventBindings 仍是原有的冒泡存储。 */
|
|
1304
|
+
function bindComponentEventHost(node) {
|
|
1305
|
+
const bindings = collectEventBindings(node.attribs);
|
|
1306
|
+
eventNodes.get(node).bindings = bindings;
|
|
1307
|
+
return bindings;
|
|
1308
|
+
}
|
|
1309
|
+
/** 渲染会把宿主折叠进模板根;事件仍保留两层独立的绑定和声明所有者。 */
|
|
1310
|
+
function mergeComponentEventRoot(root, host) {
|
|
1311
|
+
const entry = eventNodes.get(root);
|
|
1312
|
+
if (entry.target === root) entry.target = {
|
|
1313
|
+
attribs: root.attribs,
|
|
1314
|
+
dataset: root.dataset
|
|
1315
|
+
};
|
|
1316
|
+
entry.hosts ??= [];
|
|
1317
|
+
entry.hosts.push(host);
|
|
1318
|
+
root.parent = host.parent;
|
|
1319
|
+
}
|
|
1320
|
+
/** 按渲染完成后的实际宿主父链排序;输入只包含本轮存活的组件作用域。 */
|
|
1321
|
+
function orderComponentAttachmentScopes(scopeIds, componentScopes) {
|
|
1322
|
+
const hostScopes = /* @__PURE__ */ new Map();
|
|
1323
|
+
for (const scopeId of scopeIds) hostScopes.set(componentScopes.get(scopeId).hostNode, scopeId);
|
|
1324
|
+
const ordered = [];
|
|
1325
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1326
|
+
const visit = (node) => {
|
|
1327
|
+
if (!node || visited.has(node)) return;
|
|
1328
|
+
visited.add(node);
|
|
1329
|
+
visit(node.parent);
|
|
1330
|
+
const hosts = eventNodes.get(node)?.hosts;
|
|
1331
|
+
if (hosts) for (let index = hosts.length - 1; index >= 0; index--) visit(hosts[index]);
|
|
1332
|
+
const scopeId = hostScopes.get(node);
|
|
1333
|
+
if (scopeId !== void 0) ordered.push(scopeId);
|
|
1334
|
+
};
|
|
1335
|
+
for (const host of hostScopes.keys()) visit(host);
|
|
1336
|
+
return ordered;
|
|
1337
|
+
}
|
|
1338
|
+
function buildComponentTrigger(componentScopeId, context) {
|
|
1339
|
+
return (instance, eventName, detail, triggerOptions) => {
|
|
1340
|
+
const originScope = context.componentScopes.get(componentScopeId);
|
|
1341
|
+
const origin = originScope?.hostNode;
|
|
1342
|
+
if (!originScope || !origin) return;
|
|
1343
|
+
const bubbles = triggerOptions?.bubbles ?? false;
|
|
1344
|
+
const capturePhase = triggerOptions?.capturePhase ?? false;
|
|
1345
|
+
const composed = triggerOptions?.composed ?? false;
|
|
1346
|
+
const target = {
|
|
1347
|
+
dataset: originScope.dataset ?? collectNodeDataset(origin),
|
|
1348
|
+
id: originScope.hostId ?? ""
|
|
1349
|
+
};
|
|
1350
|
+
const mark = instance.__lastInteractionEvent__?.mark;
|
|
1351
|
+
const listeners = [];
|
|
1352
|
+
const collect = (entry, isOrigin) => {
|
|
1353
|
+
if (!composed && entry.scope.getScopeId() !== originScope.listenerScopeId) return false;
|
|
1354
|
+
const bindings = entry.bindings ? {
|
|
1355
|
+
bubble: entry.bindings.eventBindings?.get(eventName),
|
|
1356
|
+
capture: entry.bindings.captureEventBindings?.get(eventName)
|
|
1357
|
+
} : resolveEventBindingPhases(entry.target.attribs, eventName);
|
|
1358
|
+
if (bindings?.bubble || bindings?.capture) listeners.push({
|
|
1359
|
+
scope: entry.scope,
|
|
1360
|
+
bindings,
|
|
1361
|
+
currentTarget: isOrigin ? target : {
|
|
1362
|
+
dataset: collectNodeDataset(entry.target),
|
|
1363
|
+
id: entry.target.attribs?.id ?? ""
|
|
1364
|
+
},
|
|
1365
|
+
origin: isOrigin
|
|
1366
|
+
});
|
|
1367
|
+
return true;
|
|
1368
|
+
};
|
|
1369
|
+
let current = origin;
|
|
1370
|
+
while (current) {
|
|
1371
|
+
const entry = eventNodes.get(current);
|
|
1372
|
+
if (entry) {
|
|
1373
|
+
if (!collect(entry, current === origin)) break;
|
|
1374
|
+
let crossedBoundary = false;
|
|
1375
|
+
for (const host of entry.hosts ?? []) if (!collect(eventNodes.get(host), false)) {
|
|
1376
|
+
crossedBoundary = true;
|
|
1377
|
+
break;
|
|
1378
|
+
}
|
|
1379
|
+
if (crossedBoundary) break;
|
|
1380
|
+
}
|
|
1381
|
+
if (!bubbles && !capturePhase) break;
|
|
1382
|
+
current = current.parent;
|
|
1383
|
+
}
|
|
1384
|
+
const handlers = [];
|
|
1385
|
+
let stopped = false;
|
|
1386
|
+
if (capturePhase) for (let index = listeners.length - 1; index >= 0; index--) {
|
|
1387
|
+
const listener = listeners[index];
|
|
1388
|
+
const binding = listener.bindings.capture;
|
|
1389
|
+
if (binding) {
|
|
1390
|
+
handlers.push({
|
|
1391
|
+
scope: listener.scope,
|
|
1392
|
+
binding,
|
|
1393
|
+
currentTarget: listener.currentTarget
|
|
1394
|
+
});
|
|
1395
|
+
if (binding.stopAfter) {
|
|
1396
|
+
stopped = true;
|
|
1397
|
+
break;
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
if (!stopped) for (const listener of listeners) {
|
|
1402
|
+
if (!bubbles && !listener.origin) continue;
|
|
1403
|
+
const binding = listener.bindings.bubble;
|
|
1404
|
+
if (binding) {
|
|
1405
|
+
handlers.push({
|
|
1406
|
+
scope: listener.scope,
|
|
1407
|
+
binding,
|
|
1408
|
+
currentTarget: listener.currentTarget
|
|
1409
|
+
});
|
|
1410
|
+
if (binding.stopAfter) break;
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
for (const { scope, binding, currentTarget } of handlers) scope.getMethod(binding.method)?.({
|
|
1414
|
+
bubbles,
|
|
1415
|
+
capturePhase,
|
|
1416
|
+
composed,
|
|
1417
|
+
currentTarget,
|
|
1418
|
+
detail,
|
|
1419
|
+
mark,
|
|
1420
|
+
target,
|
|
1421
|
+
type: eventName
|
|
1422
|
+
});
|
|
1423
|
+
};
|
|
1424
|
+
}
|
|
1425
|
+
//#endregion
|
|
1129
1426
|
//#region ../../mpcore/packages/simulator/src/view/conditionalChildren.ts
|
|
1130
1427
|
function selectConditionalChildren(children, evaluate) {
|
|
1131
1428
|
const selected = [];
|
|
@@ -1181,11 +1478,77 @@ function customTabBarHostScope() {
|
|
|
1181
1478
|
};
|
|
1182
1479
|
}
|
|
1183
1480
|
//#endregion
|
|
1481
|
+
//#region ../../mpcore/packages/simulator/src/view/inspectionTree.ts
|
|
1482
|
+
const inspectionChildren = /* @__PURE__ */ new WeakMap();
|
|
1483
|
+
const inspectionParents = /* @__PURE__ */ new WeakMap();
|
|
1484
|
+
const emptyChildren = [];
|
|
1485
|
+
function getInspectionChildren(node) {
|
|
1486
|
+
return inspectionChildren.get(node) ?? node.children ?? emptyChildren;
|
|
1487
|
+
}
|
|
1488
|
+
function getInspectionParent(node) {
|
|
1489
|
+
return inspectionParents.get(node) ?? node.parent ?? null;
|
|
1490
|
+
}
|
|
1491
|
+
function collectRenderedNodes(node, nodes) {
|
|
1492
|
+
if (nodes.has(node)) return;
|
|
1493
|
+
nodes.add(node);
|
|
1494
|
+
for (const child of node.children ?? emptyChildren) collectRenderedNodes(child, nodes);
|
|
1495
|
+
}
|
|
1496
|
+
/** 原生检查器保留未投影声明;可见 DOM、WXML 和事件父链不因此增加节点。 */
|
|
1497
|
+
function registerInspectionTrees(root, pass, scopes) {
|
|
1498
|
+
if (!pass.hasUnprojectedSlots) return;
|
|
1499
|
+
const rendered = /* @__PURE__ */ new Set();
|
|
1500
|
+
collectRenderedNodes(root, rendered);
|
|
1501
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
1502
|
+
for (const [entries, group] of pass.slotDeclarations) {
|
|
1503
|
+
if (scopes.get(group.scopeId)?.hostNode !== group.parent) continue;
|
|
1504
|
+
const host = pass.componentRoots.get(group.scopeId);
|
|
1505
|
+
if (!host) continue;
|
|
1506
|
+
for (const entry of entries) for (const node of pass.renderedSlots.get(entry) ?? emptyChildren) candidates.set(node, host);
|
|
1507
|
+
}
|
|
1508
|
+
const nested = /* @__PURE__ */ new Set();
|
|
1509
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1510
|
+
const indexChildren = (node) => {
|
|
1511
|
+
if (visited.has(node)) return;
|
|
1512
|
+
visited.add(node);
|
|
1513
|
+
for (const child of node.children ?? emptyChildren) {
|
|
1514
|
+
nested.add(child);
|
|
1515
|
+
if (!rendered.has(child)) inspectionParents.set(child, node);
|
|
1516
|
+
indexChildren(child);
|
|
1517
|
+
}
|
|
1518
|
+
};
|
|
1519
|
+
for (const node of candidates.keys()) indexChildren(node);
|
|
1520
|
+
for (const [node, host] of candidates) {
|
|
1521
|
+
if (rendered.has(node) || nested.has(node) || node === host) continue;
|
|
1522
|
+
let children = inspectionChildren.get(host);
|
|
1523
|
+
if (!children) {
|
|
1524
|
+
children = [...host.children ?? emptyChildren];
|
|
1525
|
+
inspectionChildren.set(host, children);
|
|
1526
|
+
}
|
|
1527
|
+
children.push(node);
|
|
1528
|
+
inspectionParents.set(node, host);
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
//#endregion
|
|
1184
1532
|
//#region ../../mpcore/packages/simulator/src/view/loopEntries.ts
|
|
1533
|
+
const LOOP_SCOPE_ESCAPE_RE = /[^\w-]/g;
|
|
1534
|
+
function escapeScopeCodeUnit(character) {
|
|
1535
|
+
return `~${character.charCodeAt(0).toString(16).padStart(4, "0")}`;
|
|
1536
|
+
}
|
|
1537
|
+
function encodeLoopCoordinate(value) {
|
|
1538
|
+
return value.replace(LOOP_SCOPE_ESCAPE_RE, escapeScopeCodeUnit);
|
|
1539
|
+
}
|
|
1185
1540
|
function resolveLoopEntries(value) {
|
|
1186
1541
|
if (Array.isArray(value)) return value.map((item, index) => [index, item]);
|
|
1187
1542
|
return value !== null && typeof value === "object" ? Object.entries(value) : [];
|
|
1188
1543
|
}
|
|
1544
|
+
function resolveLoopInstanceSuffix(item, index, key, occurrences) {
|
|
1545
|
+
if (!key || !occurrences) return `:for-${encodeLoopCoordinate(String(index))}`;
|
|
1546
|
+
const value = key === "*this" ? item : item?.[key];
|
|
1547
|
+
const normalized = encodeLoopCoordinate(String(value ?? ""));
|
|
1548
|
+
const occurrence = occurrences.get(normalized) ?? 0;
|
|
1549
|
+
occurrences.set(normalized, occurrence + 1);
|
|
1550
|
+
return `:key-${normalized}${occurrence ? `:duplicate-${occurrence}` : ""}`;
|
|
1551
|
+
}
|
|
1189
1552
|
//#endregion
|
|
1190
1553
|
//#region ../../mpcore/packages/simulator/src/view/renderedTree.ts
|
|
1191
1554
|
/** 在循环展开和组件替换完成后,让父节点关系指向本次渲染树。 */
|
|
@@ -1194,7 +1557,45 @@ function linkRenderedParents(root, parent = null) {
|
|
|
1194
1557
|
for (const child of root.children ?? []) linkRenderedParents(child, root);
|
|
1195
1558
|
}
|
|
1196
1559
|
//#endregion
|
|
1197
|
-
//#region ../../
|
|
1560
|
+
//#region ../../mpcore/packages/simulator/src/view/renderPass.ts
|
|
1561
|
+
const initializedPages = /* @__PURE__ */ new WeakMap();
|
|
1562
|
+
function createRenderPass(propertyUpdate = false, parent) {
|
|
1563
|
+
return {
|
|
1564
|
+
seenComponentScopes: /* @__PURE__ */ new Set(),
|
|
1565
|
+
componentRoots: /* @__PURE__ */ new Map(),
|
|
1566
|
+
slotDeclarations: /* @__PURE__ */ new Map(),
|
|
1567
|
+
renderedSlots: /* @__PURE__ */ new Map(),
|
|
1568
|
+
hasUnprojectedSlots: false,
|
|
1569
|
+
propertyUpdate,
|
|
1570
|
+
bindingUpdate: parent?.bindingUpdate,
|
|
1571
|
+
bindingRevision: parent?.bindingUpdate?.revision ?? 0
|
|
1572
|
+
};
|
|
1573
|
+
}
|
|
1574
|
+
function createPageRenderPass(page, cache) {
|
|
1575
|
+
const pass = createRenderPass(initializedPages.get(cache)?.has(page) ?? false);
|
|
1576
|
+
pass.bindingUpdate = getAttachmentBindingRevision(page);
|
|
1577
|
+
pass.bindingRevision = pass.bindingUpdate?.revision ?? 0;
|
|
1578
|
+
return pass;
|
|
1579
|
+
}
|
|
1580
|
+
function isRenderPassStale(pass) {
|
|
1581
|
+
return pass.bindingUpdate !== void 0 && pass.bindingUpdate.revision !== pass.bindingRevision;
|
|
1582
|
+
}
|
|
1583
|
+
function markPageTreeConstructed(page, cache) {
|
|
1584
|
+
let pages = initializedPages.get(cache);
|
|
1585
|
+
if (!pages) {
|
|
1586
|
+
pages = /* @__PURE__ */ new WeakSet();
|
|
1587
|
+
initializedPages.set(cache, pages);
|
|
1588
|
+
}
|
|
1589
|
+
pages.add(page);
|
|
1590
|
+
}
|
|
1591
|
+
function registerSlotDeclarations(pass, slots, parent, scopeId) {
|
|
1592
|
+
for (const entries of slots.values()) if (!pass.slotDeclarations.has(entries)) pass.slotDeclarations.set(entries, {
|
|
1593
|
+
parent,
|
|
1594
|
+
scopeId
|
|
1595
|
+
});
|
|
1596
|
+
}
|
|
1597
|
+
//#endregion
|
|
1598
|
+
//#region ../../node_modules/.pnpm/@babel+parser@8.0.7/node_modules/@babel/parser/lib/index.js
|
|
1198
1599
|
var Position = class {
|
|
1199
1600
|
constructor(line, col, index) {
|
|
1200
1601
|
this.line = void 0;
|
|
@@ -9263,7 +9664,11 @@ var StatementParser = class extends ExpressionParser {
|
|
|
9263
9664
|
if (isDeclaration) node.id = this.parseFunctionId(requireId);
|
|
9264
9665
|
this.scope.enter(514);
|
|
9265
9666
|
this.prodParam.enter(functionFlags(isAsync, node.generator));
|
|
9266
|
-
if (!isDeclaration
|
|
9667
|
+
if (!isDeclaration && tokenIsIdentifier(this.state.type)) {
|
|
9668
|
+
this.expressionScope.enter(newExpressionScope());
|
|
9669
|
+
node.id = this.parseFunctionId();
|
|
9670
|
+
this.expressionScope.exit();
|
|
9671
|
+
}
|
|
9267
9672
|
this.parseFunctionParams(node, false);
|
|
9268
9673
|
this.parseFunctionBodyAndFinish(node, isDeclaration ? "FunctionDeclaration" : "FunctionExpression");
|
|
9269
9674
|
this.prodParam.exit();
|
|
@@ -12636,6 +13041,48 @@ function getParserClass(pluginsMap) {
|
|
|
12636
13041
|
return cls;
|
|
12637
13042
|
}
|
|
12638
13043
|
//#endregion
|
|
13044
|
+
//#region ../../mpcore/packages/simulator/src/view/templateInterpolation.ts
|
|
13045
|
+
/** 按表达式词法寻找插值边界,字符串和对象内部的大括号不结束绑定。 */
|
|
13046
|
+
function templateInterpolations(source) {
|
|
13047
|
+
const ranges = [];
|
|
13048
|
+
let offset = 0;
|
|
13049
|
+
while (offset < source.length) {
|
|
13050
|
+
const start = source.indexOf("{{", offset);
|
|
13051
|
+
if (start < 0) break;
|
|
13052
|
+
let quote;
|
|
13053
|
+
let depth = 0;
|
|
13054
|
+
let closed = false;
|
|
13055
|
+
for (let i = start + 2; i < source.length; i++) {
|
|
13056
|
+
const char = source[i];
|
|
13057
|
+
if (quote) {
|
|
13058
|
+
if (char === "\\") i++;
|
|
13059
|
+
else if (char === quote) quote = void 0;
|
|
13060
|
+
} else if (char === "'" || char === "\"") quote = char;
|
|
13061
|
+
else if (char === "{") depth++;
|
|
13062
|
+
else if (char === "}") {
|
|
13063
|
+
if (depth === 0 && source[i + 1] === "}") {
|
|
13064
|
+
ranges.push({
|
|
13065
|
+
start,
|
|
13066
|
+
end: i + 2,
|
|
13067
|
+
expression: source.slice(start + 2, i)
|
|
13068
|
+
});
|
|
13069
|
+
offset = i + 2;
|
|
13070
|
+
closed = true;
|
|
13071
|
+
break;
|
|
13072
|
+
}
|
|
13073
|
+
depth--;
|
|
13074
|
+
}
|
|
13075
|
+
}
|
|
13076
|
+
if (!closed) break;
|
|
13077
|
+
}
|
|
13078
|
+
return ranges;
|
|
13079
|
+
}
|
|
13080
|
+
function isTemplateExpression(value) {
|
|
13081
|
+
const trimmed = value.trim();
|
|
13082
|
+
const ranges = templateInterpolations(trimmed);
|
|
13083
|
+
return ranges.length === 1 && ranges[0].start === 0 && ranges[0].end === trimmed.length;
|
|
13084
|
+
}
|
|
13085
|
+
//#endregion
|
|
12639
13086
|
//#region ../../mpcore/packages/simulator/src/view/wxs.ts
|
|
12640
13087
|
const wxsFunctions = /* @__PURE__ */ new WeakSet();
|
|
12641
13088
|
function registerWxsExports(value, visited = /* @__PURE__ */ new Set()) {
|
|
@@ -12689,13 +13136,9 @@ const BLOCKED_MEMBER_KEYS = /* @__PURE__ */ new Set([
|
|
|
12689
13136
|
"prototype"
|
|
12690
13137
|
]);
|
|
12691
13138
|
const NUMERIC_DOT_PATH_RE = /\.(\d+)(?=[.[\s,}\]]|$)/g;
|
|
12692
|
-
function isMustacheOnly$2(value) {
|
|
12693
|
-
const trimmed = value.trim();
|
|
12694
|
-
return trimmed.startsWith("{{") && trimmed.endsWith("}}") && !trimmed.includes("{{", 2);
|
|
12695
|
-
}
|
|
12696
13139
|
function unwrapMustacheExpression(expression) {
|
|
12697
13140
|
const normalized = expression.trim();
|
|
12698
|
-
if (
|
|
13141
|
+
if (isTemplateExpression(normalized)) return normalized.slice(2, -2).trim();
|
|
12699
13142
|
return normalized;
|
|
12700
13143
|
}
|
|
12701
13144
|
function parseTemplateExpression(expression) {
|
|
@@ -12875,7 +13318,8 @@ function isComponentAttached(instance) {
|
|
|
12875
13318
|
return attachmentStates.get(instance) === "attached";
|
|
12876
13319
|
}
|
|
12877
13320
|
function hasPendingComponentAttachments(instances) {
|
|
12878
|
-
|
|
13321
|
+
for (const instance of instances) if (attachmentStates.get(instance) === "attaching") return true;
|
|
13322
|
+
return false;
|
|
12879
13323
|
}
|
|
12880
13324
|
function flushComponentReady(instances, ready) {
|
|
12881
13325
|
const all = [...instances];
|
|
@@ -12885,19 +13329,71 @@ function flushComponentReady(instances, ready) {
|
|
|
12885
13329
|
for (const instance of pending) ready(instance);
|
|
12886
13330
|
return pending.length > 0;
|
|
12887
13331
|
}
|
|
12888
|
-
function flushComponentAttachments(
|
|
12889
|
-
const pending =
|
|
12890
|
-
for (const
|
|
13332
|
+
function flushComponentAttachments(componentCache, scopeIds, attach) {
|
|
13333
|
+
const pending = /* @__PURE__ */ new Map();
|
|
13334
|
+
for (const scopeId of scopeIds) {
|
|
13335
|
+
const instance = componentCache.get(scopeId);
|
|
13336
|
+
if (!instance) continue;
|
|
13337
|
+
const state = attachmentStates.get(instance);
|
|
13338
|
+
if (state === "attaching") return false;
|
|
13339
|
+
if (!state) pending.set(scopeId, instance);
|
|
13340
|
+
}
|
|
13341
|
+
for (const instance of pending.values()) attachmentStates.set(instance, "attaching");
|
|
12891
13342
|
try {
|
|
12892
|
-
for (const instance of pending)
|
|
12893
|
-
|
|
12894
|
-
|
|
12895
|
-
|
|
13343
|
+
for (const [scopeId, instance] of pending) {
|
|
13344
|
+
if (componentCache.get(scopeId) !== instance) return "restart";
|
|
13345
|
+
let restart;
|
|
13346
|
+
try {
|
|
13347
|
+
restart = attach(instance);
|
|
13348
|
+
} finally {
|
|
13349
|
+
attachmentStates.set(instance, "attached");
|
|
13350
|
+
}
|
|
13351
|
+
if (restart) return "restart";
|
|
12896
13352
|
}
|
|
12897
13353
|
} finally {
|
|
12898
|
-
for (const instance of pending) if (attachmentStates.get(instance) === "attaching") attachmentStates.delete(instance);
|
|
13354
|
+
for (const instance of pending.values()) if (attachmentStates.get(instance) === "attaching") attachmentStates.delete(instance);
|
|
12899
13355
|
}
|
|
12900
|
-
return pending.
|
|
13356
|
+
return pending.size > 0 ? "attached" : false;
|
|
13357
|
+
}
|
|
13358
|
+
//#endregion
|
|
13359
|
+
//#region ../../mpcore/packages/simulator/src/runtime/componentInstance/construction.ts
|
|
13360
|
+
const constructionStates = /* @__PURE__ */ new WeakMap();
|
|
13361
|
+
const constructingInstances = /* @__PURE__ */ new WeakSet();
|
|
13362
|
+
function isComponentConstructing(instance) {
|
|
13363
|
+
return instance !== void 0 && constructingInstances.has(instance);
|
|
13364
|
+
}
|
|
13365
|
+
function hasPendingComponentConstruction(componentCache) {
|
|
13366
|
+
return constructionStates.has(componentCache);
|
|
13367
|
+
}
|
|
13368
|
+
function beginComponentConstruction(componentCache, scopeId, instance) {
|
|
13369
|
+
let state = constructionStates.get(componentCache);
|
|
13370
|
+
if (!state) {
|
|
13371
|
+
state = {
|
|
13372
|
+
depth: 0,
|
|
13373
|
+
created: /* @__PURE__ */ new Map()
|
|
13374
|
+
};
|
|
13375
|
+
constructionStates.set(componentCache, state);
|
|
13376
|
+
}
|
|
13377
|
+
state.depth++;
|
|
13378
|
+
state.created.set(scopeId, instance);
|
|
13379
|
+
constructingInstances.add(instance);
|
|
13380
|
+
}
|
|
13381
|
+
function discardComponentConstruction(componentCache, componentScopes, scopeId) {
|
|
13382
|
+
const state = constructionStates.get(componentCache);
|
|
13383
|
+
const prefix = `${scopeId}/`;
|
|
13384
|
+
for (const [createdScopeId, instance] of state.created) {
|
|
13385
|
+
if (createdScopeId !== scopeId && !createdScopeId.startsWith(prefix)) continue;
|
|
13386
|
+
if (componentCache.get(createdScopeId) === instance) {
|
|
13387
|
+
componentCache.delete(createdScopeId);
|
|
13388
|
+
componentScopes.delete(createdScopeId);
|
|
13389
|
+
}
|
|
13390
|
+
state.created.delete(createdScopeId);
|
|
13391
|
+
}
|
|
13392
|
+
}
|
|
13393
|
+
function finishComponentConstruction(componentCache, instance) {
|
|
13394
|
+
constructingInstances.delete(instance);
|
|
13395
|
+
const state = constructionStates.get(componentCache);
|
|
13396
|
+
if (--state.depth === 0) constructionStates.delete(componentCache);
|
|
12901
13397
|
}
|
|
12902
13398
|
//#endregion
|
|
12903
13399
|
//#region ../../mpcore/packages/simulator/src/runtime/componentInstance/discardedReady.ts
|
|
@@ -12920,12 +13416,41 @@ function flushDiscardedComponentReady(cache, pageScopeId, ready) {
|
|
|
12920
13416
|
if (!pending.size) discardedReady.delete(cache);
|
|
12921
13417
|
}
|
|
12922
13418
|
//#endregion
|
|
13419
|
+
//#region ../../mpcore/packages/simulator/src/runtime/componentInstance/propertyBindings.ts
|
|
13420
|
+
const consumedPageChanges = /* @__PURE__ */ new WeakMap();
|
|
13421
|
+
function syncComponentProperties(instance, definition, nextProperties, bindingExpressions, changedPageKeys, beforeObservers) {
|
|
13422
|
+
const freshPageChanges = changedPageKeys.length > 0 && consumedPageChanges.get(instance) !== changedPageKeys;
|
|
13423
|
+
if (freshPageChanges) consumedPageChanges.set(instance, changedPageKeys);
|
|
13424
|
+
const changedRootKeys = [];
|
|
13425
|
+
const previousProperties = {};
|
|
13426
|
+
for (const [key, value] of Object.entries(nextProperties)) {
|
|
13427
|
+
const nextValue = normalizeComponentPropertyValue(definition, key, value);
|
|
13428
|
+
const bindingExpression = bindingExpressions[key];
|
|
13429
|
+
const bindingAffected = freshPageChanges && !!bindingExpression && changedPageKeys.some((changedKey) => {
|
|
13430
|
+
return changedKey === bindingExpression || changedKey.startsWith(`${bindingExpression}.`) || changedKey.startsWith(`${bindingExpression}[`);
|
|
13431
|
+
});
|
|
13432
|
+
const previousSnapshot = instance.__propertySnapshots?.[key];
|
|
13433
|
+
if (hasComponentPropertyValueChanged(instance.properties[key], previousSnapshot, nextValue, bindingAffected)) {
|
|
13434
|
+
previousProperties[key] = instance.properties[key];
|
|
13435
|
+
const deliveredValue = cloneValue$1(nextValue);
|
|
13436
|
+
instance.properties[key] = deliveredValue;
|
|
13437
|
+
if (Object.hasOwn(definition.properties ?? {}, key)) instance.data[key] = deliveredValue;
|
|
13438
|
+
changedRootKeys.push(key);
|
|
13439
|
+
}
|
|
13440
|
+
instance.__propertySnapshots ??= {};
|
|
13441
|
+
instance.__propertySnapshots[key] = cloneValue$1(nextValue);
|
|
13442
|
+
}
|
|
13443
|
+
if (changedRootKeys.length === 0) return;
|
|
13444
|
+
beforeObservers?.(instance, "properties");
|
|
13445
|
+
runComponentObservers(definition, instance, changedRootKeys, previousProperties);
|
|
13446
|
+
}
|
|
13447
|
+
//#endregion
|
|
12923
13448
|
//#region ../../mpcore/packages/simulator/src/runtime/pageLifecycle.ts
|
|
12924
13449
|
const loadingPages = /* @__PURE__ */ new WeakSet();
|
|
12925
13450
|
function isPageBeforeReady(page) {
|
|
12926
13451
|
return loadingPages.has(page);
|
|
12927
13452
|
}
|
|
12928
|
-
function runInitialPageLifecycles(page, query, scheduler, isAlive, renderComponents) {
|
|
13453
|
+
function runInitialPageLifecycles(page, query, scheduler, isAlive, renderComponents, onReady) {
|
|
12929
13454
|
loadingPages.add(page);
|
|
12930
13455
|
try {
|
|
12931
13456
|
renderComponents?.();
|
|
@@ -12940,7 +13465,10 @@ function runInitialPageLifecycles(page, query, scheduler, isAlive, renderCompone
|
|
|
12940
13465
|
scheduler.setTimeout(() => {
|
|
12941
13466
|
if (!isAlive()) return;
|
|
12942
13467
|
page.onReady?.();
|
|
12943
|
-
if (isAlive())
|
|
13468
|
+
if (isAlive()) {
|
|
13469
|
+
page.onRouteDone?.({});
|
|
13470
|
+
onReady?.();
|
|
13471
|
+
}
|
|
12944
13472
|
}, 0);
|
|
12945
13473
|
}
|
|
12946
13474
|
//#endregion
|
|
@@ -13005,61 +13533,11 @@ function createPluginArtifactSource(fallback, miniprogramRootPath, plugins) {
|
|
|
13005
13533
|
readText: (filePath) => fallback.readText(resolvePhysicalPath(filePath))
|
|
13006
13534
|
};
|
|
13007
13535
|
}
|
|
13008
|
-
//#endregion
|
|
13009
|
-
//#region ../../mpcore/packages/simulator/src/view/eventBinding.ts
|
|
13010
|
-
const LEGACY_EVENT_NAME_RE = /^\w+$/;
|
|
13011
|
-
const EVENT_BINDING_PREFIXES = [
|
|
13012
|
-
{
|
|
13013
|
-
prefix: "bind:",
|
|
13014
|
-
priority: 0,
|
|
13015
|
-
stopAfter: false
|
|
13016
|
-
},
|
|
13017
|
-
{
|
|
13018
|
-
prefix: "catch:",
|
|
13019
|
-
priority: 0,
|
|
13020
|
-
stopAfter: true
|
|
13021
|
-
},
|
|
13022
|
-
{
|
|
13023
|
-
prefix: "bind",
|
|
13024
|
-
priority: 1,
|
|
13025
|
-
stopAfter: false
|
|
13026
|
-
},
|
|
13027
|
-
{
|
|
13028
|
-
prefix: "catch",
|
|
13029
|
-
priority: 1,
|
|
13030
|
-
stopAfter: true
|
|
13031
|
-
}
|
|
13032
|
-
];
|
|
13033
|
-
function parseEventBinding(attributeName, method) {
|
|
13034
|
-
const definition = EVENT_BINDING_PREFIXES.find((item) => attributeName.startsWith(item.prefix));
|
|
13035
|
-
if (!definition) return null;
|
|
13036
|
-
const eventName = attributeName.slice(definition.prefix.length);
|
|
13037
|
-
if (!eventName || !definition.prefix.endsWith(":") && !LEGACY_EVENT_NAME_RE.test(eventName)) return null;
|
|
13038
|
-
return {
|
|
13039
|
-
eventName,
|
|
13040
|
-
method,
|
|
13041
|
-
priority: definition.priority,
|
|
13042
|
-
stopAfter: definition.stopAfter
|
|
13043
|
-
};
|
|
13044
|
-
}
|
|
13045
|
-
function collectMiniProgramEventBindings(attributes = {}) {
|
|
13046
|
-
const bindings = /* @__PURE__ */ new Map();
|
|
13047
|
-
const priorities = /* @__PURE__ */ new Map();
|
|
13048
|
-
for (const [attributeName, method] of Object.entries(attributes)) {
|
|
13049
|
-
const candidate = parseEventBinding(attributeName, method);
|
|
13050
|
-
if (!candidate || candidate.priority < (priorities.get(candidate.eventName) ?? -1)) continue;
|
|
13051
|
-
bindings.set(candidate.eventName, {
|
|
13052
|
-
method: candidate.method,
|
|
13053
|
-
stopAfter: candidate.stopAfter
|
|
13054
|
-
});
|
|
13055
|
-
priorities.set(candidate.eventName, candidate.priority);
|
|
13056
|
-
}
|
|
13057
|
-
return bindings;
|
|
13058
|
-
}
|
|
13059
|
-
function resolveMiniProgramEventBinding(attributes, eventName) {
|
|
13060
|
-
return collectMiniProgramEventBindings(attributes).get(eventName) ?? null;
|
|
13061
|
-
}
|
|
13062
13536
|
const WEVU_NATIVE_INSTANCE_KEY = "__wevuNativeInstance";
|
|
13537
|
+
/** 宿主值为 1 时,实现带 routeEventId 的路由阶段契约;不代表微信 SDK 版本。 */
|
|
13538
|
+
const WEVU_ROUTE_EVENT_CONTRACT_KEY = "__wevuRouteEventContract";
|
|
13539
|
+
/** 宿主负责 pageScrollTo 的实际滚动事件,框架不再合成第二份通知。 */
|
|
13540
|
+
const WEVU_PAGE_SCROLL_EVENT_CONTRACT_KEY = "__wevuPageScrollEventContract";
|
|
13063
13541
|
//#endregion
|
|
13064
13542
|
//#region ../../mpcore/packages/simulator/src/view/selectorQueryScope.ts
|
|
13065
13543
|
const MPCORE_COMPONENT_SCOPE_ID = "__mpcoreComponentScopeId";
|
|
@@ -13168,21 +13646,6 @@ function resolveMiniProgramComponent(ownerFilePath, request, miniprogramRootPath
|
|
|
13168
13646
|
return npmPath ? relative(miniprogramRootPath, npmPath).slice(0, -3) : localPath;
|
|
13169
13647
|
}
|
|
13170
13648
|
//#endregion
|
|
13171
|
-
//#region ../../mpcore/packages/simulator/src/view/nodeDataset.ts
|
|
13172
|
-
const DATASET_NAME_RE = /-([a-z])/g;
|
|
13173
|
-
function isDatasetAttribute(attributeName) {
|
|
13174
|
-
return attributeName.startsWith("data-") && !attributeName.startsWith("data-sim-");
|
|
13175
|
-
}
|
|
13176
|
-
function toDatasetKey(attributeName) {
|
|
13177
|
-
return attributeName.slice(5).replace(DATASET_NAME_RE, (_match, char) => char.toUpperCase());
|
|
13178
|
-
}
|
|
13179
|
-
function collectNodeDataset(node) {
|
|
13180
|
-
if (node.dataset) return { ...node.dataset };
|
|
13181
|
-
const dataset = {};
|
|
13182
|
-
for (const [key, value] of Object.entries(node.attribs ?? {})) if (isDatasetAttribute(key)) dataset[toDatasetKey(key)] = value;
|
|
13183
|
-
return dataset;
|
|
13184
|
-
}
|
|
13185
|
-
//#endregion
|
|
13186
13649
|
//#region ../../mpcore/packages/simulator/src/view/templateImports.ts
|
|
13187
13650
|
function createImportedTemplateState(root, filePath, loadImport, loadWxs) {
|
|
13188
13651
|
const documents = /* @__PURE__ */ new Map();
|
|
@@ -13234,12 +13697,15 @@ function createImportedTemplateState(root, filePath, loadImport, loadWxs) {
|
|
|
13234
13697
|
}
|
|
13235
13698
|
//#endregion
|
|
13236
13699
|
//#region ../../mpcore/packages/simulator/src/view/templateText.ts
|
|
13237
|
-
|
|
13238
|
-
|
|
13239
|
-
|
|
13240
|
-
|
|
13241
|
-
|
|
13242
|
-
|
|
13700
|
+
function interpolateTemplateText(input, data, emptyNull = false) {
|
|
13701
|
+
let output = "";
|
|
13702
|
+
let offset = 0;
|
|
13703
|
+
for (const range of templateInterpolations(input)) {
|
|
13704
|
+
const value = resolveTemplateExpression(data, range.expression);
|
|
13705
|
+
output += input.slice(offset, range.start) + (value === void 0 || emptyNull && value === null ? "" : String(value));
|
|
13706
|
+
offset = range.end;
|
|
13707
|
+
}
|
|
13708
|
+
return output + input.slice(offset);
|
|
13243
13709
|
}
|
|
13244
13710
|
//#endregion
|
|
13245
13711
|
//#region ../../node_modules/.pnpm/entities@8.1.0/node_modules/entities/dist/decode-codepoint.js
|
|
@@ -17015,24 +17481,83 @@ function parseDocument(data, options) {
|
|
|
17015
17481
|
return handler.root;
|
|
17016
17482
|
}
|
|
17017
17483
|
//#endregion
|
|
17484
|
+
//#region ../../mpcore/packages/simulator/src/view/templateAttributes.ts
|
|
17485
|
+
function decodeAttribute(raw, quote) {
|
|
17486
|
+
const source = raw.replaceAll(`\\${quote}`, quote);
|
|
17487
|
+
let value = "";
|
|
17488
|
+
let offset = 0;
|
|
17489
|
+
for (const range of templateInterpolations(source)) {
|
|
17490
|
+
value += source.slice(offset, range.start).replace(/\\([\s\S])/g, "$1");
|
|
17491
|
+
value += source.slice(range.start, range.end);
|
|
17492
|
+
offset = range.end;
|
|
17493
|
+
}
|
|
17494
|
+
return value + source.slice(offset).replace(/\\([\s\S])/g, "$1");
|
|
17495
|
+
}
|
|
17496
|
+
/** 先保存 WXML 属性词法值,再交给 HTML parser 构建标签树,避免反斜杠引号提前截断属性。 */
|
|
17497
|
+
function maskTemplateAttributes(source) {
|
|
17498
|
+
const attributes = /* @__PURE__ */ new Map();
|
|
17499
|
+
if (!source.includes("\\")) return {
|
|
17500
|
+
source,
|
|
17501
|
+
attributes
|
|
17502
|
+
};
|
|
17503
|
+
let prefix = "__mpcore_quoted_attribute_";
|
|
17504
|
+
while (source.includes(prefix)) prefix += "_";
|
|
17505
|
+
let output = "";
|
|
17506
|
+
let offset = 0;
|
|
17507
|
+
for (let i = 0; i < source.length; i++) {
|
|
17508
|
+
if (source.startsWith("<!--", i)) {
|
|
17509
|
+
const end = source.indexOf("-->", i + 4);
|
|
17510
|
+
i = end < 0 ? source.length : end + 2;
|
|
17511
|
+
continue;
|
|
17512
|
+
}
|
|
17513
|
+
if (source[i] !== "<" || !/[a-z_]/i.test(source[i + 1] ?? "")) continue;
|
|
17514
|
+
for (i++; i < source.length && source[i] !== ">"; i++) {
|
|
17515
|
+
if (source[i] !== "\"" && source[i] !== "'") continue;
|
|
17516
|
+
const quote = source[i];
|
|
17517
|
+
const start = i + 1;
|
|
17518
|
+
for (i++; i < source.length && source[i] !== quote; i++) if (source[i] === "\\") i++;
|
|
17519
|
+
if (i >= source.length) break;
|
|
17520
|
+
const raw = source.slice(start, i);
|
|
17521
|
+
if (!raw.includes("\\")) continue;
|
|
17522
|
+
const token = `${prefix}${attributes.size}`;
|
|
17523
|
+
attributes.set(token, decodeAttribute(raw, quote));
|
|
17524
|
+
output += source.slice(offset, start) + token;
|
|
17525
|
+
offset = i;
|
|
17526
|
+
}
|
|
17527
|
+
}
|
|
17528
|
+
return {
|
|
17529
|
+
source: output + source.slice(offset),
|
|
17530
|
+
attributes
|
|
17531
|
+
};
|
|
17532
|
+
}
|
|
17533
|
+
//#endregion
|
|
17018
17534
|
//#region ../../mpcore/packages/simulator/src/view/wxsDocument.ts
|
|
17019
17535
|
const INLINE_WXS_RE = /<wxs\b([^>]*)(?<!\/)>([\s\S]*?)<\/wxs\s*>/g;
|
|
17020
17536
|
function parseWxsTemplateDocument(templateSource) {
|
|
17021
17537
|
const scripts = [];
|
|
17022
|
-
const
|
|
17538
|
+
const source = templateSource.replace(INLINE_WXS_RE, (_match, attributes, script) => {
|
|
17023
17539
|
return `<wxs data-sim-wxs="${scripts.push(script) - 1}"${attributes}>wxs-source</wxs>`;
|
|
17024
|
-
})
|
|
17540
|
+
});
|
|
17541
|
+
const masked = maskTemplateAttributes(source);
|
|
17542
|
+
const document = parseDocument(`<page>${masked.source}</page>`, {
|
|
17025
17543
|
xmlMode: false,
|
|
17026
17544
|
decodeEntities: false,
|
|
17027
17545
|
lowerCaseAttributeNames: false,
|
|
17028
17546
|
lowerCaseTags: false,
|
|
17029
17547
|
recognizeSelfClosing: true
|
|
17030
17548
|
});
|
|
17549
|
+
if (!scripts.length && !masked.attributes.size && !/[\r\n]/.test(source)) return document;
|
|
17550
|
+
const hasAttributes = masked.attributes.size > 0;
|
|
17031
17551
|
const restore = (node) => {
|
|
17552
|
+
if (hasAttributes && node.attribs) {
|
|
17553
|
+
for (const [name, token] of Object.entries(node.attribs)) if (masked.attributes.has(token)) node.attribs[name] = masked.attributes.get(token);
|
|
17554
|
+
}
|
|
17032
17555
|
if (node.name === "wxs" && Object.hasOwn(node.attribs ?? {}, "data-sim-wxs")) {
|
|
17033
17556
|
node.children[0].data = scripts[Number(node.attribs["data-sim-wxs"])];
|
|
17034
17557
|
delete node.attribs["data-sim-wxs"];
|
|
17558
|
+
return;
|
|
17035
17559
|
}
|
|
17560
|
+
if (node.children) node.children = node.children.filter((child) => child.type !== "text" || !/^[\t\r\n ]+$/.test(child.data ?? "") || !/[\r\n]/.test(child.data ?? ""));
|
|
17036
17561
|
for (const child of node.children ?? []) restore(child);
|
|
17037
17562
|
};
|
|
17038
17563
|
restore(document);
|
|
@@ -17040,7 +17565,6 @@ function parseWxsTemplateDocument(templateSource) {
|
|
|
17040
17565
|
}
|
|
17041
17566
|
//#endregion
|
|
17042
17567
|
//#region ../../mpcore/packages/simulator/src/runtime/render/shared.ts
|
|
17043
|
-
const TEMPLATE_INTERPOLATION_RE$1 = /\{\{([^{}]+)\}\}/g;
|
|
17044
17568
|
const LEADING_SLASH_RE$4 = /^\/+/;
|
|
17045
17569
|
const EVENT_BINDING_ATTRS = [
|
|
17046
17570
|
"bindtap",
|
|
@@ -17060,8 +17584,7 @@ const STRUCTURAL_ATTRS = [
|
|
|
17060
17584
|
const CLASS_SPLIT_RE = /\s+/;
|
|
17061
17585
|
const JS_FILE_RE = /\.js$/;
|
|
17062
17586
|
function isMustacheOnly$1(value) {
|
|
17063
|
-
|
|
17064
|
-
return trimmed.startsWith("{{") && trimmed.endsWith("}}") && !trimmed.includes("{{", 2);
|
|
17587
|
+
return isTemplateExpression(value);
|
|
17065
17588
|
}
|
|
17066
17589
|
function collectDataset(node, source) {
|
|
17067
17590
|
if (node.dataset) return collectNodeDataset(node);
|
|
@@ -17086,17 +17609,11 @@ function cloneNode(node) {
|
|
|
17086
17609
|
children: node.children?.map((child) => cloneNode(child))
|
|
17087
17610
|
};
|
|
17088
17611
|
}
|
|
17089
|
-
function resolveValueByPath$1(source, expression) {
|
|
17090
|
-
return resolveTemplateExpression(source, expression) ?? "";
|
|
17091
|
-
}
|
|
17092
17612
|
function resolveRawValueByPath$1(source, expression) {
|
|
17093
17613
|
return resolveTemplateExpression(source, expression);
|
|
17094
17614
|
}
|
|
17095
17615
|
function interpolateTemplate$1(input, data) {
|
|
17096
|
-
return input
|
|
17097
|
-
const value = resolveValueByPath$1(data, expression);
|
|
17098
|
-
return typeof value === "string" ? value : String(value);
|
|
17099
|
-
});
|
|
17616
|
+
return interpolateTemplateText(input, data, true);
|
|
17100
17617
|
}
|
|
17101
17618
|
function readTemplateSource(artifactSource, filePath) {
|
|
17102
17619
|
const source = artifactSource.readText(filePath);
|
|
@@ -17223,7 +17740,7 @@ function resolveUsingComponents(context, ownerJsonPath, ownerFilePath) {
|
|
|
17223
17740
|
return /* @__PURE__ */ new Map();
|
|
17224
17741
|
}
|
|
17225
17742
|
}
|
|
17226
|
-
function resolveComponentGenerics(context, hostNode, ownerJsonPath, ownerFilePath, componentFilePath) {
|
|
17743
|
+
function resolveComponentGenerics(context, hostNode, ownerJsonPath, ownerFilePath, componentFilePath, ownerGenerics) {
|
|
17227
17744
|
const componentJsonPath = path.resolve(context.project.miniprogramRootPath, `${componentFilePath.replace(JS_FILE_RE, "")}.json`);
|
|
17228
17745
|
const componentGenerics = readComponentConfig(context.artifactSource, componentJsonPath).componentGenerics;
|
|
17229
17746
|
if (!componentGenerics || typeof componentGenerics !== "object" || Array.isArray(componentGenerics)) return;
|
|
@@ -17231,7 +17748,7 @@ function resolveComponentGenerics(context, hostNode, ownerJsonPath, ownerFilePat
|
|
|
17231
17748
|
const resolved = /* @__PURE__ */ new Map();
|
|
17232
17749
|
for (const [genericName, definition] of Object.entries(componentGenerics)) {
|
|
17233
17750
|
const selectedAlias = hostNode.attribs?.[`generic:${genericName}`];
|
|
17234
|
-
const selectedPath = selectedAlias ? ownerComponents.get(selectedAlias) : void 0;
|
|
17751
|
+
const selectedPath = selectedAlias ? ownerGenerics?.get(selectedAlias) ?? ownerComponents.get(selectedAlias) : void 0;
|
|
17235
17752
|
if (selectedPath) {
|
|
17236
17753
|
resolved.set(genericName, selectedPath);
|
|
17237
17754
|
continue;
|
|
@@ -17243,66 +17760,6 @@ function resolveComponentGenerics(context, hostNode, ownerJsonPath, ownerFilePat
|
|
|
17243
17760
|
}
|
|
17244
17761
|
return resolved.size > 0 ? resolved : void 0;
|
|
17245
17762
|
}
|
|
17246
|
-
function collectComponentEventBindings(hostNode) {
|
|
17247
|
-
return collectMiniProgramEventBindings(hostNode.attribs);
|
|
17248
|
-
}
|
|
17249
|
-
function buildComponentTrigger(componentScopeId, context, hostNode) {
|
|
17250
|
-
const hostDataset = collectDataset(hostNode);
|
|
17251
|
-
const hostId = hostNode.attribs?.id ?? "";
|
|
17252
|
-
return (instance, eventName, detail, triggerOptions) => {
|
|
17253
|
-
const originScope = context.componentScopes.get(componentScopeId);
|
|
17254
|
-
const interactionMark = instance.__lastInteractionEvent__?.mark;
|
|
17255
|
-
const target = {
|
|
17256
|
-
dataset: originScope?.dataset ?? hostDataset,
|
|
17257
|
-
id: originScope?.hostId ?? hostId
|
|
17258
|
-
};
|
|
17259
|
-
let currentScopeId = componentScopeId;
|
|
17260
|
-
while (currentScopeId) {
|
|
17261
|
-
const currentScope = context.componentScopes.get(currentScopeId);
|
|
17262
|
-
const binding = currentScope?.eventBindings?.get(eventName);
|
|
17263
|
-
const listenerScope = currentScope?.listenerScopeId ? context.componentScopes.get(currentScope.listenerScopeId) : null;
|
|
17264
|
-
const handler = binding && listenerScope ? listenerScope.getMethod(binding.method) : void 0;
|
|
17265
|
-
if (handler) handler({
|
|
17266
|
-
bubbles: triggerOptions?.bubbles ?? false,
|
|
17267
|
-
capturePhase: false,
|
|
17268
|
-
composed: triggerOptions?.composed ?? false,
|
|
17269
|
-
currentTarget: {
|
|
17270
|
-
dataset: currentScope?.dataset ?? hostDataset,
|
|
17271
|
-
id: currentScope?.hostId ?? hostId
|
|
17272
|
-
},
|
|
17273
|
-
detail,
|
|
17274
|
-
mark: interactionMark,
|
|
17275
|
-
target,
|
|
17276
|
-
type: eventName
|
|
17277
|
-
});
|
|
17278
|
-
if (binding?.stopAfter) break;
|
|
17279
|
-
if (!triggerOptions?.bubbles || !triggerOptions?.composed) break;
|
|
17280
|
-
currentScopeId = currentScope?.ownerScopeId;
|
|
17281
|
-
}
|
|
17282
|
-
};
|
|
17283
|
-
}
|
|
17284
|
-
function syncComponentProperties(instance, definition, nextProperties, bindingExpressions, changedPageKeys) {
|
|
17285
|
-
const changedRootKeys = [];
|
|
17286
|
-
const previousProperties = {};
|
|
17287
|
-
for (const [key, value] of Object.entries(nextProperties)) {
|
|
17288
|
-
const nextValue = normalizeComponentPropertyValue(definition, key, value);
|
|
17289
|
-
const bindingExpression = bindingExpressions[key];
|
|
17290
|
-
const bindingAffected = !!bindingExpression && changedPageKeys.some((changedKey) => {
|
|
17291
|
-
return changedKey === bindingExpression || changedKey.startsWith(`${bindingExpression}.`) || changedKey.startsWith(`${bindingExpression}[`);
|
|
17292
|
-
});
|
|
17293
|
-
const previousSnapshot = instance.__propertySnapshots?.[key];
|
|
17294
|
-
if (hasComponentPropertyValueChanged(instance.properties[key], previousSnapshot, nextValue, bindingAffected)) {
|
|
17295
|
-
previousProperties[key] = instance.properties[key];
|
|
17296
|
-
instance.properties[key] = nextValue;
|
|
17297
|
-
if (Object.hasOwn(definition.properties ?? {}, key)) instance.data[key] = nextValue;
|
|
17298
|
-
changedRootKeys.push(key);
|
|
17299
|
-
}
|
|
17300
|
-
instance.__propertySnapshots ??= {};
|
|
17301
|
-
instance.__propertySnapshots[key] = cloneValue$1(nextValue);
|
|
17302
|
-
}
|
|
17303
|
-
if (changedRootKeys.length === 0) return;
|
|
17304
|
-
runComponentObservers(definition, instance, changedRootKeys, previousProperties);
|
|
17305
|
-
}
|
|
17306
17763
|
function createComponentScope(clonedNode, scope, componentScopeId, componentInstance, genericComponents, slots) {
|
|
17307
17764
|
const ownerScopeId = scope.getScopeId().includes("/") ? scope.getScopeId() : void 0;
|
|
17308
17765
|
return {
|
|
@@ -17310,7 +17767,7 @@ function createComponentScope(clonedNode, scope, componentScopeId, componentInst
|
|
|
17310
17767
|
classList: String(clonedNode.attribs?.class ?? "").split(CLASS_SPLIT_RE).map((item) => item.trim()).filter(Boolean),
|
|
17311
17768
|
data: { ...componentInstance.data },
|
|
17312
17769
|
dataset: collectDataset(clonedNode, wxsScopeData(scope)),
|
|
17313
|
-
|
|
17770
|
+
...bindComponentEventHost(clonedNode),
|
|
17314
17771
|
getMethod: (methodName) => {
|
|
17315
17772
|
const method = componentInstance?.[methodName];
|
|
17316
17773
|
return typeof method === "function" ? method.bind(componentInstance) : void 0;
|
|
@@ -17318,6 +17775,7 @@ function createComponentScope(clonedNode, scope, componentScopeId, componentInst
|
|
|
17318
17775
|
getScopeId: () => componentScopeId,
|
|
17319
17776
|
genericComponents,
|
|
17320
17777
|
hostId: typeof clonedNode.attribs?.id === "string" ? clonedNode.attribs.id : void 0,
|
|
17778
|
+
hostNode: clonedNode,
|
|
17321
17779
|
id: typeof clonedNode.attribs?.id === "string" ? clonedNode.attribs.id : void 0,
|
|
17322
17780
|
listenerScopeId: scope.getScopeId(),
|
|
17323
17781
|
ownerScopeId,
|
|
@@ -17340,13 +17798,14 @@ function resolveComponentProperties(clonedNode, scope, definition) {
|
|
|
17340
17798
|
bindingExpressions
|
|
17341
17799
|
};
|
|
17342
17800
|
}
|
|
17343
|
-
function createRuntimeComponentInstance(componentScopeId, context,
|
|
17801
|
+
function createRuntimeComponentInstance(componentScopeId, context, componentEntry, nextProperties, ownerScopeId, renderTemplate, bindEventsBeforeProperties = false) {
|
|
17344
17802
|
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;
|
|
17345
|
-
const
|
|
17803
|
+
const instanceOptions = {
|
|
17346
17804
|
definition: componentEntry.definition,
|
|
17347
|
-
requestRender: (callback) => context.session.requestRender(callback)
|
|
17348
|
-
|
|
17349
|
-
|
|
17805
|
+
requestRender: (callback) => context.session.requestRender(callback)
|
|
17806
|
+
};
|
|
17807
|
+
const componentInstance = createComponentInstance(instanceOptions);
|
|
17808
|
+
bindAttachmentBindingScope(componentInstance, context);
|
|
17350
17809
|
setSelectorQueryScopeId(componentInstance, componentScopeId);
|
|
17351
17810
|
componentInstance.is = componentEntry.filePath.replace(JS_FILE_RE, "");
|
|
17352
17811
|
componentInstance.createIntersectionObserver = (options) => context.session.createIntersectionObserver(componentInstance, options);
|
|
@@ -17354,19 +17813,29 @@ function createRuntimeComponentInstance(componentScopeId, context, clonedNode, c
|
|
|
17354
17813
|
componentInstance.createSelectorQuery = () => context.moduleLoader.wx.createSelectorQuery().in(componentInstance);
|
|
17355
17814
|
componentInstance.selectComponent = (selector) => resolveNativeComponentSelection(context.session.selectComponentWithin(componentScopeId, selector));
|
|
17356
17815
|
componentInstance.selectAllComponents = (selector) => context.session.selectAllComponentsWithin(componentScopeId, selector).map(resolveNativeComponentSelection);
|
|
17357
|
-
componentInstance.selectOwnerComponent = () => ownerScopeId ? context.componentCache.get(ownerScopeId)
|
|
17816
|
+
componentInstance.selectOwnerComponent = () => ownerScopeId ? resolveNativeComponentSelection(context.componentCache.get(ownerScopeId)) : null;
|
|
17817
|
+
beginComponentConstruction(context.componentCache, componentScopeId, componentInstance);
|
|
17358
17818
|
context.componentCache.set(componentScopeId, componentInstance);
|
|
17359
|
-
|
|
17360
|
-
|
|
17361
|
-
|
|
17362
|
-
|
|
17363
|
-
|
|
17819
|
+
try {
|
|
17820
|
+
renderTemplate?.(componentInstance, "defaults");
|
|
17821
|
+
runComponentLifecycle(componentInstance, "created");
|
|
17822
|
+
if (bindEventsBeforeProperties) instanceOptions.triggerEvent = buildComponentTrigger(componentScopeId, context);
|
|
17823
|
+
componentInstance.__propertySnapshots = Object.fromEntries(Object.entries(componentInstance.properties).map(([key, propertyValue]) => [key, cloneValue$1(propertyValue)]));
|
|
17824
|
+
syncComponentProperties(componentInstance, componentInstance.__definition__ ?? componentEntry.definition, componentProperties, {}, [], renderTemplate);
|
|
17825
|
+
if (!bindEventsBeforeProperties) instanceOptions.triggerEvent = buildComponentTrigger(componentScopeId, context);
|
|
17826
|
+
return componentInstance;
|
|
17827
|
+
} catch (error) {
|
|
17828
|
+
discardComponentConstruction(context.componentCache, context.componentScopes, componentScopeId);
|
|
17829
|
+
throw error;
|
|
17830
|
+
} finally {
|
|
17831
|
+
finishComponentConstruction(context.componentCache, componentInstance);
|
|
17832
|
+
}
|
|
17364
17833
|
}
|
|
17365
|
-
function renderRuntimeComponentTemplate(context, componentEntry, renderNodeTree, componentScope, componentScopeId,
|
|
17834
|
+
function renderRuntimeComponentTemplate(context, componentEntry, renderNodeTree, componentScope, componentScopeId, renderPass) {
|
|
17366
17835
|
const componentDocument = parseTemplateDocument(readTemplateSource(context.artifactSource, componentEntry.absoluteTemplatePath));
|
|
17367
17836
|
const componentRoot = (componentDocument.children ?? [])[0] ?? componentDocument;
|
|
17368
17837
|
const templateRenderState = prepareTemplateRenderState(context.artifactSource, componentRoot, componentEntry.absoluteTemplatePath, context.project.miniprogramRootPath, getRuntimeWxsLoader(context.moduleLoader, context.artifactSource));
|
|
17369
|
-
return renderNodeTree(componentRoot, componentScope, context, path.resolve(context.project.miniprogramRootPath, `${componentEntry.filePath.replace(JS_FILE_RE, "")}.json`), componentEntry.filePath, componentScopeId,
|
|
17838
|
+
return renderNodeTree(componentRoot, componentScope, context, path.resolve(context.project.miniprogramRootPath, `${componentEntry.filePath.replace(JS_FILE_RE, "")}.json`), componentEntry.filePath, componentScopeId, renderPass, templateRenderState, componentScope.hostNode);
|
|
17370
17839
|
}
|
|
17371
17840
|
//#endregion
|
|
17372
17841
|
//#region ../../mpcore/packages/simulator/src/runtime/render/index.ts
|
|
@@ -17380,24 +17849,33 @@ function expandNodeByFor(node, scope) {
|
|
|
17380
17849
|
const items = resolveLoopEntries(resolveRawValueByPath$1(wxsScopeData(scope), forExpression));
|
|
17381
17850
|
const itemName = node.attribs?.["wx:for-item"]?.trim() || "item";
|
|
17382
17851
|
const indexName = node.attribs?.["wx:for-index"]?.trim() || "index";
|
|
17852
|
+
const key = node.attribs?.["wx:key"]?.trim();
|
|
17853
|
+
const keyOccurrences = key ? /* @__PURE__ */ new Map() : void 0;
|
|
17383
17854
|
return items.map(([index, item]) => ({
|
|
17384
17855
|
node: cloneNode(node),
|
|
17385
17856
|
scope: createLoopScope(scope, itemName, indexName, item, index),
|
|
17386
|
-
instanceSuffix:
|
|
17857
|
+
instanceSuffix: resolveLoopInstanceSuffix(item, index, key, keyOccurrences)
|
|
17387
17858
|
}));
|
|
17388
17859
|
}
|
|
17389
|
-
function renderNodeVariants(node, scope, context, ownerJsonPath, ownerFilePath, instancePath,
|
|
17390
|
-
|
|
17860
|
+
function renderNodeVariants(node, scope, context, ownerJsonPath, ownerFilePath, instancePath, renderPass, templateRenderState, parent) {
|
|
17861
|
+
const nodes = [];
|
|
17862
|
+
if (isRenderPassStale(renderPass)) return nodes;
|
|
17863
|
+
for (const { node: expandedNode, scope: expandedScope, instanceSuffix } of expandNodeByFor(node, scope)) {
|
|
17864
|
+
if (isRenderPassStale(renderPass)) break;
|
|
17865
|
+
nodes.push(renderNodeTree(expandedNode, expandedScope, context, ownerJsonPath, ownerFilePath, `${instancePath}${instanceSuffix}`, renderPass, templateRenderState, parent));
|
|
17866
|
+
}
|
|
17867
|
+
return nodes;
|
|
17391
17868
|
}
|
|
17392
|
-
function renderChildren(children, scope, context, ownerJsonPath, ownerFilePath, instancePath,
|
|
17869
|
+
function renderChildren(children, scope, context, ownerJsonPath, ownerFilePath, instancePath, renderPass, templateRenderState, parent) {
|
|
17393
17870
|
const renderedChildren = [];
|
|
17394
17871
|
for (const { node: child, index } of selectConditionalChildren(children, (node) => evaluateConditionalBranch(node, scope))) {
|
|
17872
|
+
if (isRenderPassStale(renderPass)) break;
|
|
17395
17873
|
if (!isTagNode(child)) {
|
|
17396
|
-
renderedChildren.push(...renderNodeVariants(child, scope, context, ownerJsonPath, ownerFilePath, `${instancePath}/node-${index}`,
|
|
17874
|
+
renderedChildren.push(...renderNodeVariants(child, scope, context, ownerJsonPath, ownerFilePath, `${instancePath}/node-${index}`, renderPass, templateRenderState, parent));
|
|
17397
17875
|
continue;
|
|
17398
17876
|
}
|
|
17399
17877
|
if (isTemplateDefinition(child) || child.name === "import" || child.name === "wxs") continue;
|
|
17400
|
-
renderedChildren.push(...renderNodeVariants(child, scope, context, ownerJsonPath, ownerFilePath, `${instancePath}/node-${index}`,
|
|
17878
|
+
renderedChildren.push(...renderNodeVariants(child, scope, context, ownerJsonPath, ownerFilePath, `${instancePath}/node-${index}`, renderPass, templateRenderState, parent));
|
|
17401
17879
|
}
|
|
17402
17880
|
return renderedChildren;
|
|
17403
17881
|
}
|
|
@@ -17429,22 +17907,38 @@ function collectComponentSlots(componentNode, scope, ownerJsonPath, ownerFilePat
|
|
|
17429
17907
|
}
|
|
17430
17908
|
return slots;
|
|
17431
17909
|
}
|
|
17432
|
-
function
|
|
17910
|
+
function reconcileSlotDeclarations(context, renderPass) {
|
|
17911
|
+
for (const [entries, { parent, scopeId }] of renderPass.slotDeclarations) {
|
|
17912
|
+
if (isRenderPassStale(renderPass)) return;
|
|
17913
|
+
if (isComponentConstructing(context.componentCache.get(scopeId))) continue;
|
|
17914
|
+
for (const { index } of selectConditionalChildren(entries.map((entry) => entry.node), (node, index) => evaluateConditionalBranch(node, entries[index].scope))) {
|
|
17915
|
+
if (isRenderPassStale(renderPass)) return;
|
|
17916
|
+
const entry = entries[index];
|
|
17917
|
+
if (renderPass.renderedSlots.has(entry)) continue;
|
|
17918
|
+
const nodes = renderNodeVariants(entry.node, entry.scope, context, entry.ownerJsonPath, entry.ownerFilePath, entry.instancePath, renderPass, entry.templateRenderState, parent);
|
|
17919
|
+
renderPass.renderedSlots.set(entry, nodes);
|
|
17920
|
+
renderPass.hasUnprojectedSlots ||= nodes.length > 0;
|
|
17921
|
+
}
|
|
17922
|
+
}
|
|
17923
|
+
}
|
|
17924
|
+
function renderNodeTree(node, scope, context, ownerJsonPath, ownerFilePath, instancePath, renderPass, templateRenderState, parent) {
|
|
17925
|
+
if (isRenderPassStale(renderPass)) return node;
|
|
17433
17926
|
if (scope.wxs !== templateRenderState.wxsModules) scope = {
|
|
17434
17927
|
...scope,
|
|
17435
17928
|
wxs: templateRenderState.wxsModules
|
|
17436
17929
|
};
|
|
17437
17930
|
const clonedNode = cloneNode(node);
|
|
17931
|
+
registerComponentEventNode(clonedNode, scope, parent);
|
|
17438
17932
|
if (!isTagNode(clonedNode)) {
|
|
17439
17933
|
applyNodeBindings(clonedNode, scope);
|
|
17440
17934
|
return clonedNode;
|
|
17441
17935
|
}
|
|
17442
|
-
if (isTemplateDefinition(clonedNode))
|
|
17443
|
-
|
|
17444
|
-
|
|
17445
|
-
|
|
17446
|
-
|
|
17447
|
-
}
|
|
17936
|
+
if (isTemplateDefinition(clonedNode)) {
|
|
17937
|
+
clonedNode.name = "block";
|
|
17938
|
+
clonedNode.attribs = {};
|
|
17939
|
+
clonedNode.children = [];
|
|
17940
|
+
return clonedNode;
|
|
17941
|
+
}
|
|
17448
17942
|
const templateName = resolveTemplateCall(clonedNode, wxsScopeData(scope));
|
|
17449
17943
|
if (templateName) {
|
|
17450
17944
|
const definition = templateRenderState.definitions.get(templateName);
|
|
@@ -17453,60 +17947,75 @@ function renderNodeTree(node, scope, context, ownerJsonPath, ownerFilePath, inst
|
|
|
17453
17947
|
data: resolveTemplateData(clonedNode, wxsScopeData(scope)),
|
|
17454
17948
|
wxs: definition && templateRenderState.definitionWxsScopes?.get(definition)
|
|
17455
17949
|
};
|
|
17456
|
-
|
|
17950
|
+
clonedNode.name = "block";
|
|
17951
|
+
clonedNode.attribs = {
|
|
17952
|
+
"data-sim-node": instancePath,
|
|
17953
|
+
"data-sim-scope": scope.getScopeId()
|
|
17954
|
+
};
|
|
17955
|
+
clonedNode.children = definition && !templateRenderState.stack.includes(templateName) ? renderChildren(definition.children ?? [], templateScope, context, ownerJsonPath, ownerFilePath, `${instancePath}/template-${templateName}`, renderPass, {
|
|
17457
17956
|
definitions: templateRenderState.definitionScopes?.get(definition) ?? templateRenderState.definitions,
|
|
17458
17957
|
definitionScopes: templateRenderState.definitionScopes,
|
|
17459
17958
|
definitionWxsScopes: templateRenderState.definitionWxsScopes,
|
|
17460
17959
|
wxsModules: templateScope.wxs,
|
|
17461
17960
|
stack: [...templateRenderState.stack, templateName]
|
|
17462
|
-
}) : [];
|
|
17463
|
-
return
|
|
17464
|
-
type: "tag",
|
|
17465
|
-
name: "block",
|
|
17466
|
-
attribs: {
|
|
17467
|
-
"data-sim-node": instancePath,
|
|
17468
|
-
"data-sim-scope": scope.getScopeId()
|
|
17469
|
-
},
|
|
17470
|
-
children
|
|
17471
|
-
};
|
|
17961
|
+
}, clonedNode) : [];
|
|
17962
|
+
return clonedNode;
|
|
17472
17963
|
}
|
|
17473
17964
|
if (clonedNode.name === "slot") {
|
|
17474
17965
|
const slotName = clonedNode.attribs?.name?.trim() || "default";
|
|
17475
17966
|
const projected = scope.slots?.get(slotName) ?? [];
|
|
17476
|
-
const
|
|
17477
|
-
|
|
17478
|
-
|
|
17479
|
-
|
|
17480
|
-
|
|
17481
|
-
|
|
17482
|
-
name: "block",
|
|
17483
|
-
attribs: {
|
|
17484
|
-
"data-sim-node": instancePath,
|
|
17485
|
-
"data-sim-scope": scope.getScopeId()
|
|
17486
|
-
},
|
|
17487
|
-
children
|
|
17967
|
+
const fallbackChildren = clonedNode.children ?? [];
|
|
17968
|
+
bindComponentEventHost(clonedNode);
|
|
17969
|
+
clonedNode.name = "block";
|
|
17970
|
+
clonedNode.attribs = {
|
|
17971
|
+
"data-sim-node": instancePath,
|
|
17972
|
+
"data-sim-scope": scope.getScopeId()
|
|
17488
17973
|
};
|
|
17974
|
+
const slotOwner = scope.getScopeId();
|
|
17975
|
+
if (projected.length && slotOwner && isComponentConstructing(context.componentCache.get(slotOwner))) {
|
|
17976
|
+
clonedNode.children = [];
|
|
17977
|
+
return clonedNode;
|
|
17978
|
+
}
|
|
17979
|
+
clonedNode.children = projected.length ? selectConditionalChildren(projected.map((entry) => entry.node), (node, index) => evaluateConditionalBranch(node, projected[index].scope)).flatMap(({ index }) => {
|
|
17980
|
+
const entry = projected[index];
|
|
17981
|
+
const nodes = renderNodeVariants(entry.node, entry.scope, context, entry.ownerJsonPath, entry.ownerFilePath, entry.instancePath, renderPass, entry.templateRenderState, clonedNode);
|
|
17982
|
+
renderPass.renderedSlots.set(entry, nodes);
|
|
17983
|
+
return nodes;
|
|
17984
|
+
}) : renderChildren(fallbackChildren, scope, context, ownerJsonPath, ownerFilePath, `${instancePath}/slot-fallback-${slotName}`, renderPass, templateRenderState, clonedNode);
|
|
17985
|
+
return clonedNode;
|
|
17489
17986
|
}
|
|
17490
17987
|
const componentEntry = resolveComponentRegistryEntry(context, ownerJsonPath, ownerFilePath, clonedNode.name, scope.genericComponents?.get(clonedNode.name));
|
|
17491
17988
|
if (componentEntry) {
|
|
17492
17989
|
const componentScopeId = `${instancePath}/${clonedNode.name}`;
|
|
17493
17990
|
const ownerScopeId = scope.getScopeId().includes("/") ? scope.getScopeId() : void 0;
|
|
17494
17991
|
const { nextProperties, bindingExpressions } = resolveComponentProperties(clonedNode, scope, componentEntry.definition);
|
|
17495
|
-
const genericComponents = resolveComponentGenerics(context, clonedNode, ownerJsonPath, ownerFilePath, componentEntry.filePath);
|
|
17992
|
+
const genericComponents = resolveComponentGenerics(context, clonedNode, ownerJsonPath, ownerFilePath, componentEntry.filePath, scope.genericComponents);
|
|
17496
17993
|
const slots = collectComponentSlots(clonedNode, scope, ownerJsonPath, ownerFilePath, componentScopeId, templateRenderState);
|
|
17994
|
+
registerSlotDeclarations(renderPass, slots, clonedNode, componentScopeId);
|
|
17995
|
+
applyNodeBindings(clonedNode, scope);
|
|
17996
|
+
const reconcilePrivateTemplate = (instance, phase) => {
|
|
17997
|
+
let bindingPass;
|
|
17998
|
+
do {
|
|
17999
|
+
const bindingScope = createComponentScope(clonedNode, scope, componentScopeId, instance, genericComponents, slots);
|
|
18000
|
+
context.componentScopes.set(componentScopeId, bindingScope);
|
|
18001
|
+
bindingPass = createRenderPass(phase === "properties", renderPass);
|
|
18002
|
+
renderRuntimeComponentTemplate(context, componentEntry, renderNodeTree, bindingScope, componentScopeId, bindingPass);
|
|
18003
|
+
reconcileSlotDeclarations(context, bindingPass);
|
|
18004
|
+
} while (isRenderPassStale(bindingPass));
|
|
18005
|
+
};
|
|
17497
18006
|
let componentInstance = context.componentCache.get(componentScopeId);
|
|
17498
|
-
if (!componentInstance) componentInstance = createRuntimeComponentInstance(componentScopeId, context,
|
|
17499
|
-
|
|
17500
|
-
context.componentScopes.set(componentScopeId,
|
|
17501
|
-
|
|
17502
|
-
}
|
|
17503
|
-
|
|
17504
|
-
seenComponentScopes.add(componentScopeId);
|
|
18007
|
+
if (!componentInstance) componentInstance = createRuntimeComponentInstance(componentScopeId, context, componentEntry, nextProperties, ownerScopeId, reconcilePrivateTemplate, renderPass.propertyUpdate);
|
|
18008
|
+
else {
|
|
18009
|
+
context.componentScopes.set(componentScopeId, createComponentScope(clonedNode, scope, componentScopeId, componentInstance, genericComponents, slots));
|
|
18010
|
+
syncComponentProperties(componentInstance, componentInstance.__definition__ ?? componentEntry.definition, nextProperties, bindingExpressions, context.changedPageKeys, reconcilePrivateTemplate);
|
|
18011
|
+
}
|
|
18012
|
+
if (isRenderPassStale(renderPass)) return clonedNode;
|
|
18013
|
+
renderPass.seenComponentScopes.add(componentScopeId);
|
|
17505
18014
|
const componentScope = createComponentScope(clonedNode, scope, componentScopeId, componentInstance, genericComponents, slots);
|
|
17506
18015
|
context.componentScopes.set(componentScopeId, componentScope);
|
|
17507
|
-
const renderedComponentRoot = renderRuntimeComponentTemplate(context, componentEntry, renderNodeTree, componentScope, componentScopeId,
|
|
18016
|
+
const renderedComponentRoot = renderRuntimeComponentTemplate(context, componentEntry, renderNodeTree, componentScope, componentScopeId, renderPass);
|
|
18017
|
+
mergeComponentEventRoot(renderedComponentRoot, clonedNode);
|
|
17508
18018
|
if (renderedComponentRoot.attribs) {
|
|
17509
|
-
applyNodeBindings(clonedNode, scope);
|
|
17510
18019
|
renderedComponentRoot.attribs = {
|
|
17511
18020
|
...clonedNode.attribs,
|
|
17512
18021
|
...renderedComponentRoot.attribs
|
|
@@ -17519,66 +18028,99 @@ function renderNodeTree(node, scope, context, ownerJsonPath, ownerFilePath, inst
|
|
|
17519
18028
|
renderedComponentRoot.attribs["data-sim-node"] = instancePath;
|
|
17520
18029
|
renderedComponentRoot.attribs["data-sim-scope"] = componentScopeId;
|
|
17521
18030
|
}
|
|
18031
|
+
renderPass.componentRoots.set(componentScopeId, renderedComponentRoot);
|
|
17522
18032
|
return renderedComponentRoot;
|
|
17523
18033
|
}
|
|
17524
18034
|
applyNodeBindings(clonedNode, scope);
|
|
17525
18035
|
clonedNode.attribs["data-sim-node"] = instancePath;
|
|
17526
|
-
clonedNode.children = renderChildren(clonedNode.children ?? [], scope, context, ownerJsonPath, ownerFilePath, `${instancePath}/${clonedNode.name}`,
|
|
18036
|
+
clonedNode.children = renderChildren(clonedNode.children ?? [], scope, context, ownerJsonPath, ownerFilePath, `${instancePath}/${clonedNode.name}`, renderPass, templateRenderState, clonedNode);
|
|
17527
18037
|
return clonedNode;
|
|
17528
18038
|
}
|
|
18039
|
+
function reconcileRuntimePageTree(context, page, bindingsOnly = false) {
|
|
18040
|
+
for (;;) {
|
|
18041
|
+
bindAttachmentBindingScope(context, page);
|
|
18042
|
+
if (page.__lastChangedKeys__) context.changedPageKeys = page.__lastChangedKeys__;
|
|
18043
|
+
const route = page.route.replace(LEADING_SLASH_RE$4, "");
|
|
18044
|
+
const resourcePath = context.project.routes.find((item) => item.route === route)?.resourcePath ?? route;
|
|
18045
|
+
const templatePath = path.resolve(context.project.miniprogramRootPath, `${resourcePath}.wxml`);
|
|
18046
|
+
const document = parseTemplateDocument(readTemplateSource(context.artifactSource, templatePath));
|
|
18047
|
+
const pageScopeId = `page:${route}`;
|
|
18048
|
+
const pageScope = {
|
|
18049
|
+
data: page.data,
|
|
18050
|
+
getMethod: (methodName) => {
|
|
18051
|
+
const method = page[methodName];
|
|
18052
|
+
return typeof method === "function" ? method.bind(page) : void 0;
|
|
18053
|
+
},
|
|
18054
|
+
getScopeId: () => pageScopeId,
|
|
18055
|
+
id: "page-root"
|
|
18056
|
+
};
|
|
18057
|
+
for (const scopeId of context.componentScopes.keys()) if (scopeId === pageScopeId || scopeId.startsWith(`${pageScopeId}/`)) context.componentScopes.delete(scopeId);
|
|
18058
|
+
context.componentScopes.set(pageScopeId, pageScope);
|
|
18059
|
+
const renderPass = createPageRenderPass(page, context.componentCache);
|
|
18060
|
+
const root = (document.children ?? [])[0] ?? document;
|
|
18061
|
+
const templateRenderState = prepareTemplateRenderState(context.artifactSource, root, templatePath, context.project.miniprogramRootPath, getRuntimeWxsLoader(context.moduleLoader, context.artifactSource));
|
|
18062
|
+
const renderedRoot = renderNodeTree(root, pageScope, context, path.resolve(context.project.miniprogramRootPath, `${resourcePath}.json`), `${resourcePath}.js`, pageScopeId, renderPass, templateRenderState);
|
|
18063
|
+
const roots = [renderedRoot];
|
|
18064
|
+
if (hasCustomTabBar(context.project.appConfig, route)) roots.push(renderNodeTree({
|
|
18065
|
+
type: "tag",
|
|
18066
|
+
name: "custom-tab-bar",
|
|
18067
|
+
attribs: {},
|
|
18068
|
+
children: []
|
|
18069
|
+
}, customTabBarHostScope(), context, path.resolve(context.project.miniprogramRootPath, "app.json"), "app.js", pageScopeId, renderPass, templateRenderState));
|
|
18070
|
+
reconcileSlotDeclarations(context, renderPass);
|
|
18071
|
+
if (isRenderPassStale(renderPass)) continue;
|
|
18072
|
+
markPageTreeConstructed(page, context.componentCache);
|
|
18073
|
+
const treeRoot = roots.length > 1 ? {
|
|
18074
|
+
type: "root",
|
|
18075
|
+
children: roots
|
|
18076
|
+
} : renderedRoot;
|
|
18077
|
+
linkRenderedParents(treeRoot);
|
|
18078
|
+
registerInspectionTrees(treeRoot, renderPass, context.componentScopes);
|
|
18079
|
+
if (bindingsOnly) return {
|
|
18080
|
+
root: treeRoot,
|
|
18081
|
+
roots
|
|
18082
|
+
};
|
|
18083
|
+
let bindingsRefreshed = false;
|
|
18084
|
+
const updateBindings = () => {
|
|
18085
|
+
bindingsRefreshed = true;
|
|
18086
|
+
if (hasPendingComponentConstruction(context.componentCache)) return;
|
|
18087
|
+
reconcileRuntimePageTree(context, page, true);
|
|
18088
|
+
};
|
|
18089
|
+
const pageAttached = attachComponentPage(page, updateBindings);
|
|
18090
|
+
if (bindingsRefreshed) continue;
|
|
18091
|
+
const pageAttaching = isComponentPageAttaching(page);
|
|
18092
|
+
const scopeIds = orderComponentAttachmentScopes(renderPass.seenComponentScopes, context.componentScopes);
|
|
18093
|
+
const instances = scopeIds.map((scopeId) => context.componentCache.get(scopeId));
|
|
18094
|
+
const attached = !pageAttaching && runWithAttachmentBindingUpdates(page, updateBindings, (scopes) => flushComponentAttachments(context.componentCache, scopes, (instance) => {
|
|
18095
|
+
runComponentLifecycle(instance, "attached");
|
|
18096
|
+
return bindingsRefreshed;
|
|
18097
|
+
}), scopeIds);
|
|
18098
|
+
if (attached === "restart") continue;
|
|
18099
|
+
const canFinalize = !pageAttaching && !hasPendingComponentAttachments(instances);
|
|
18100
|
+
const removed = canFinalize ? [...context.componentCache].filter(([scopeId]) => scopeId.startsWith(`${pageScopeId}/`) && !renderPass.seenComponentScopes.has(scopeId)) : [];
|
|
18101
|
+
for (const [scopeId, instance] of removed) if (isComponentAttached(instance)) runComponentLifecycle(instance, "detached");
|
|
18102
|
+
else scheduleDiscardedComponentReady(context.componentCache, scopeId, instance);
|
|
18103
|
+
const relationsChanged = canFinalize && syncComponentRelations(context.componentCache, renderPass.seenComponentScopes, pageScopeId);
|
|
18104
|
+
for (const [scopeId] of removed) {
|
|
18105
|
+
context.componentCache.delete(scopeId);
|
|
18106
|
+
context.componentScopes.delete(scopeId);
|
|
18107
|
+
}
|
|
18108
|
+
if (pageAttached || attached || relationsChanged) continue;
|
|
18109
|
+
if (canFinalize && !isPageBeforeReady(page)) flushDiscardedComponentReady(context.componentCache, pageScopeId, (instance) => runComponentLifecycle(instance, "ready"));
|
|
18110
|
+
if (canFinalize && !isPageBeforeReady(page) && flushComponentReady(instances, (instance) => {
|
|
18111
|
+
runComponentLifecycle(instance, "ready");
|
|
18112
|
+
if (instance !== context.componentCache.get(customTabBarScopeId(route))) instance.__definition__?.pageLifetimes?.show?.call(instance);
|
|
18113
|
+
})) continue;
|
|
18114
|
+
return {
|
|
18115
|
+
root: treeRoot,
|
|
18116
|
+
roots
|
|
18117
|
+
};
|
|
18118
|
+
}
|
|
18119
|
+
}
|
|
17529
18120
|
function renderRuntimePageTree(context, page) {
|
|
17530
|
-
const
|
|
17531
|
-
const resourcePath = context.project.routes.find((item) => item.route === route)?.resourcePath ?? route;
|
|
17532
|
-
const templatePath = path.resolve(context.project.miniprogramRootPath, `${resourcePath}.wxml`);
|
|
17533
|
-
const document = parseTemplateDocument(readTemplateSource(context.artifactSource, templatePath));
|
|
17534
|
-
const pageScopeId = `page:${route}`;
|
|
17535
|
-
const pageScope = {
|
|
17536
|
-
data: page.data,
|
|
17537
|
-
getMethod: (methodName) => {
|
|
17538
|
-
const method = page[methodName];
|
|
17539
|
-
return typeof method === "function" ? method.bind(page) : void 0;
|
|
17540
|
-
},
|
|
17541
|
-
getScopeId: () => pageScopeId,
|
|
17542
|
-
id: "page-root"
|
|
17543
|
-
};
|
|
17544
|
-
for (const scopeId of context.componentScopes.keys()) if (scopeId === pageScopeId || scopeId.startsWith(`${pageScopeId}/`)) context.componentScopes.delete(scopeId);
|
|
17545
|
-
context.componentScopes.set(pageScopeId, pageScope);
|
|
17546
|
-
const seenComponentScopes = /* @__PURE__ */ new Set();
|
|
17547
|
-
const root = (document.children ?? [])[0] ?? document;
|
|
17548
|
-
const templateRenderState = prepareTemplateRenderState(context.artifactSource, root, templatePath, context.project.miniprogramRootPath, getRuntimeWxsLoader(context.moduleLoader, context.artifactSource));
|
|
17549
|
-
const renderedRoot = renderNodeTree(root, pageScope, context, path.resolve(context.project.miniprogramRootPath, `${resourcePath}.json`), `${resourcePath}.js`, pageScopeId, seenComponentScopes, templateRenderState);
|
|
17550
|
-
const roots = [renderedRoot];
|
|
17551
|
-
if (hasCustomTabBar(context.project.appConfig, route)) roots.push(renderNodeTree({
|
|
17552
|
-
type: "tag",
|
|
17553
|
-
name: "custom-tab-bar",
|
|
17554
|
-
attribs: {},
|
|
17555
|
-
children: []
|
|
17556
|
-
}, customTabBarHostScope(), context, path.resolve(context.project.miniprogramRootPath, "app.json"), "app.js", pageScopeId, seenComponentScopes, templateRenderState));
|
|
17557
|
-
const pageAttached = attachComponentPage(page);
|
|
17558
|
-
const pageAttaching = isComponentPageAttaching(page);
|
|
17559
|
-
const attached = !pageAttaching && flushComponentAttachments([...seenComponentScopes].map((scopeId) => context.componentCache.get(scopeId)), (instance) => runComponentLifecycle(instance, "attached"));
|
|
17560
|
-
const removed = [...context.componentCache].filter(([scopeId]) => scopeId.startsWith(`${pageScopeId}/`) && !seenComponentScopes.has(scopeId));
|
|
17561
|
-
for (const [scopeId, instance] of removed) if (isComponentAttached(instance)) runComponentLifecycle(instance, "detached");
|
|
17562
|
-
else scheduleDiscardedComponentReady(context.componentCache, scopeId, instance);
|
|
17563
|
-
const instances = [...seenComponentScopes].map((scopeId) => context.componentCache.get(scopeId));
|
|
17564
|
-
const relationsChanged = !pageAttaching && !hasPendingComponentAttachments(instances) && syncComponentRelations(context.componentCache, seenComponentScopes, pageScopeId);
|
|
17565
|
-
for (const [scopeId] of removed) {
|
|
17566
|
-
context.componentCache.delete(scopeId);
|
|
17567
|
-
context.componentScopes.delete(scopeId);
|
|
17568
|
-
}
|
|
17569
|
-
if (pageAttached || attached || relationsChanged) return renderRuntimePageTree(context, page);
|
|
17570
|
-
if (!isPageBeforeReady(page)) flushDiscardedComponentReady(context.componentCache, pageScopeId, (instance) => runComponentLifecycle(instance, "ready"));
|
|
17571
|
-
if (!isPageBeforeReady(page) && flushComponentReady(instances, (instance) => {
|
|
17572
|
-
runComponentLifecycle(instance, "ready");
|
|
17573
|
-
if (instance !== context.componentCache.get(customTabBarScopeId(route))) instance.__definition__?.pageLifetimes?.show?.call(instance);
|
|
17574
|
-
})) return renderRuntimePageTree(context, page);
|
|
17575
|
-
const treeRoot = roots.length > 1 ? {
|
|
17576
|
-
type: "root",
|
|
17577
|
-
children: roots
|
|
17578
|
-
} : renderedRoot;
|
|
17579
|
-
linkRenderedParents(treeRoot);
|
|
18121
|
+
const { root, roots } = reconcileRuntimePageTree(context, page);
|
|
17580
18122
|
return {
|
|
17581
|
-
root
|
|
18123
|
+
root,
|
|
17582
18124
|
wxml: roots.map(serializeDomNode$1).join("")
|
|
17583
18125
|
};
|
|
17584
18126
|
}
|
|
@@ -17602,15 +18144,23 @@ function createDuplicateRegistrationError(kind, id) {
|
|
|
17602
18144
|
function callDefinitionMethod(instance, method, args) {
|
|
17603
18145
|
if (typeof method === "function") return method.apply(instance, args);
|
|
17604
18146
|
}
|
|
18147
|
+
function composeDefinitionMethods(primary, secondary) {
|
|
18148
|
+
if (typeof primary !== "function" || typeof secondary !== "function" || primary === secondary) return primary ?? secondary;
|
|
18149
|
+
return function composedDefinitionMethod(...args) {
|
|
18150
|
+
const result = primary.apply(this, args);
|
|
18151
|
+
secondary.apply(this, args);
|
|
18152
|
+
return result;
|
|
18153
|
+
};
|
|
18154
|
+
}
|
|
17605
18155
|
function normalizeComponentPageDefinition(definition) {
|
|
17606
18156
|
const { lifetimes = {}, methods = {}, pageLifetimes = {}, ...rest } = definition;
|
|
17607
18157
|
const created = lifetimes.created ?? rest.created;
|
|
17608
18158
|
const attached = lifetimes.attached ?? rest.attached;
|
|
17609
|
-
const ready = lifetimes.ready ?? rest.ready
|
|
18159
|
+
const ready = composeDefinitionMethods(lifetimes.ready ?? rest.ready, methods.onReady ?? rest.onReady);
|
|
17610
18160
|
const detached = lifetimes.detached ?? rest.detached ?? methods.onUnload ?? rest.onUnload;
|
|
17611
18161
|
const load = methods.onLoad ?? rest.onLoad;
|
|
17612
|
-
const show = pageLifetimes.show
|
|
17613
|
-
const hide = pageLifetimes.hide
|
|
18162
|
+
const show = typeof pageLifetimes.show === "function" ? composeDefinitionMethods(pageLifetimes.show, methods.onShow) : methods.onShow ?? rest.onShow;
|
|
18163
|
+
const hide = typeof pageLifetimes.hide === "function" ? composeDefinitionMethods(pageLifetimes.hide, methods.onHide) : methods.onHide ?? rest.onHide;
|
|
17614
18164
|
const resize = pageLifetimes.resize ?? methods.onResize ?? rest.onResize;
|
|
17615
18165
|
const routeDone = pageLifetimes.routeDone ?? methods.onRouteDone ?? rest.onRouteDone;
|
|
17616
18166
|
const pageDefinition = {
|
|
@@ -17638,10 +18188,7 @@ function normalizeComponentPageDefinition(definition) {
|
|
|
17638
18188
|
return callDefinitionMethod(this, routeDone, args);
|
|
17639
18189
|
}
|
|
17640
18190
|
};
|
|
17641
|
-
registerComponentPageAttachment(pageDefinition,
|
|
17642
|
-
callDefinitionMethod(this, created, []);
|
|
17643
|
-
callDefinitionMethod(this, attached, []);
|
|
17644
|
-
});
|
|
18191
|
+
registerComponentPageAttachment(pageDefinition, (instance) => callDefinitionMethod(instance, created, []), (instance) => callDefinitionMethod(instance, attached, []));
|
|
17645
18192
|
return pageDefinition;
|
|
17646
18193
|
}
|
|
17647
18194
|
function createHostRegistries() {
|
|
@@ -17775,7 +18322,13 @@ var StartupNavigationQueue = class {
|
|
|
17775
18322
|
}
|
|
17776
18323
|
}
|
|
17777
18324
|
run(option, operation) {
|
|
17778
|
-
if (!this.collecting)
|
|
18325
|
+
if (!this.collecting) {
|
|
18326
|
+
const submitted = option && { ...option };
|
|
18327
|
+
this.scheduler.queueMicrotask(() => {
|
|
18328
|
+
this.scheduler.setTimeout(() => operation(submitted), 0);
|
|
18329
|
+
});
|
|
18330
|
+
return;
|
|
18331
|
+
}
|
|
17779
18332
|
const defer = (callback) => callback ? (...args) => {
|
|
17780
18333
|
this.scheduler.setTimeout(() => callback(...args), 0);
|
|
17781
18334
|
} : void 0;
|
|
@@ -17826,7 +18379,7 @@ function resolveCapabilityValue(source, schema) {
|
|
|
17826
18379
|
}
|
|
17827
18380
|
return current;
|
|
17828
18381
|
}
|
|
17829
|
-
function createHeadlessWx(driver, runtimeConsole = console) {
|
|
18382
|
+
function createHeadlessWx(driver, runtimeConsole = console, workers) {
|
|
17830
18383
|
const eventBus = createHeadlessUniEventBus();
|
|
17831
18384
|
const rpx2px = (value) => {
|
|
17832
18385
|
const width = driver.getWindowInfoSync().windowWidth;
|
|
@@ -17834,6 +18387,10 @@ function createHeadlessWx(driver, runtimeConsole = console) {
|
|
|
17834
18387
|
};
|
|
17835
18388
|
const capabilityTree = {
|
|
17836
18389
|
canIUse: true,
|
|
18390
|
+
...workers ? {
|
|
18391
|
+
createWorker: true,
|
|
18392
|
+
preDownloadSubpackage: true
|
|
18393
|
+
} : {},
|
|
17837
18394
|
canvasToTempFilePath: true,
|
|
17838
18395
|
chooseImage: { return: {
|
|
17839
18396
|
errMsg: true,
|
|
@@ -18034,6 +18591,14 @@ function createHeadlessWx(driver, runtimeConsole = console) {
|
|
|
18034
18591
|
navigateBack: true,
|
|
18035
18592
|
navigateTo: true,
|
|
18036
18593
|
nextTick: true,
|
|
18594
|
+
offBeforeAppRoute: true,
|
|
18595
|
+
offBeforePageUnload: true,
|
|
18596
|
+
offAppRoute: true,
|
|
18597
|
+
offAppRouteDone: true,
|
|
18598
|
+
onBeforeAppRoute: true,
|
|
18599
|
+
onBeforePageUnload: true,
|
|
18600
|
+
onAppRoute: true,
|
|
18601
|
+
onAppRouteDone: true,
|
|
18037
18602
|
offAppHide: true,
|
|
18038
18603
|
offAppShow: true,
|
|
18039
18604
|
offNetworkStatusChange: true,
|
|
@@ -18081,6 +18646,16 @@ function createHeadlessWx(driver, runtimeConsole = console) {
|
|
|
18081
18646
|
};
|
|
18082
18647
|
return {
|
|
18083
18648
|
...eventBus,
|
|
18649
|
+
[WEVU_ROUTE_EVENT_CONTRACT_KEY]: 1,
|
|
18650
|
+
[WEVU_PAGE_SCROLL_EVENT_CONTRACT_KEY]: 1,
|
|
18651
|
+
createWorker: (script) => {
|
|
18652
|
+
if (!workers) throw new Error("Worker host is unavailable");
|
|
18653
|
+
return workers.createWorker(script);
|
|
18654
|
+
},
|
|
18655
|
+
preDownloadSubpackage: (option) => {
|
|
18656
|
+
if (!workers) throw new Error("Worker host is unavailable");
|
|
18657
|
+
workers.preDownloadSubpackage(option);
|
|
18658
|
+
},
|
|
18084
18659
|
canIUse: (schema) => typeof schema === "string" && schema.trim() !== "" && resolveCapabilityValue(capabilityTree, schema.trim()) != null,
|
|
18085
18660
|
canvasToTempFilePath: (option) => invokeWxApi(() => driver.canvasToTempFilePath(option), option),
|
|
18086
18661
|
chooseImage: (option) => invokeWxApi(() => driver.chooseImage(option ?? {}), option),
|
|
@@ -18204,10 +18779,16 @@ function createHeadlessWx(driver, runtimeConsole = console) {
|
|
|
18204
18779
|
navigateBack: (option) => runNavigationApi(driver, option, (deferred) => invokeWxApi(() => {
|
|
18205
18780
|
driver.navigateBack(deferred);
|
|
18206
18781
|
}, deferred)),
|
|
18207
|
-
navigateTo: (option) => runNavigationApi(driver, option, (deferred) =>
|
|
18208
|
-
driver.navigateTo(deferred);
|
|
18209
|
-
}, deferred)),
|
|
18782
|
+
navigateTo: (option) => runNavigationApi(driver, option, (deferred) => driver.navigateTo(deferred)),
|
|
18210
18783
|
nextTick: (callback) => driver.nextTick(callback),
|
|
18784
|
+
offBeforeAppRoute: (listener) => driver.offBeforeAppRoute(listener),
|
|
18785
|
+
offBeforePageUnload: (listener) => driver.offBeforePageUnload(listener),
|
|
18786
|
+
offAppRoute: (listener) => driver.offAppRoute(listener),
|
|
18787
|
+
offAppRouteDone: (listener) => driver.offAppRouteDone(listener),
|
|
18788
|
+
onBeforeAppRoute: (listener) => driver.onBeforeAppRoute(listener),
|
|
18789
|
+
onBeforePageUnload: (listener) => driver.onBeforePageUnload(listener),
|
|
18790
|
+
onAppRoute: (listener) => driver.onAppRoute(listener),
|
|
18791
|
+
onAppRouteDone: (listener) => driver.onAppRouteDone(listener),
|
|
18211
18792
|
offAppHide: (callback) => driver.offAppHide(callback),
|
|
18212
18793
|
offAppShow: (callback) => driver.offAppShow(callback),
|
|
18213
18794
|
offNetworkStatusChange: (callback) => driver.offNetworkStatusChange(callback),
|
|
@@ -18367,6 +18948,7 @@ var RuntimeScheduler = class {
|
|
|
18367
18948
|
intervals = /* @__PURE__ */ new Set();
|
|
18368
18949
|
timeouts = /* @__PURE__ */ new Set();
|
|
18369
18950
|
closed = false;
|
|
18951
|
+
canceledHandle;
|
|
18370
18952
|
onError;
|
|
18371
18953
|
constructor(onError) {
|
|
18372
18954
|
this.onError = onError;
|
|
@@ -18388,7 +18970,7 @@ var RuntimeScheduler = class {
|
|
|
18388
18970
|
this.timeouts.clear();
|
|
18389
18971
|
}
|
|
18390
18972
|
queueMicrotask(handler) {
|
|
18391
|
-
if (this.closed)
|
|
18973
|
+
if (this.closed) return;
|
|
18392
18974
|
queueMicrotask(() => {
|
|
18393
18975
|
if (this.closed) return;
|
|
18394
18976
|
try {
|
|
@@ -18399,7 +18981,7 @@ var RuntimeScheduler = class {
|
|
|
18399
18981
|
});
|
|
18400
18982
|
}
|
|
18401
18983
|
setInterval(handler, timeout, ...args) {
|
|
18402
|
-
if (this.closed)
|
|
18984
|
+
if (this.closed) return this.getCanceledHandle();
|
|
18403
18985
|
const handle = setInterval(() => {
|
|
18404
18986
|
try {
|
|
18405
18987
|
handler(...args);
|
|
@@ -18411,7 +18993,7 @@ var RuntimeScheduler = class {
|
|
|
18411
18993
|
return handle;
|
|
18412
18994
|
}
|
|
18413
18995
|
setTimeout(handler, timeout, ...args) {
|
|
18414
|
-
if (this.closed)
|
|
18996
|
+
if (this.closed) return this.getCanceledHandle();
|
|
18415
18997
|
const handle = setTimeout(() => {
|
|
18416
18998
|
this.timeouts.delete(handle);
|
|
18417
18999
|
try {
|
|
@@ -18423,6 +19005,14 @@ var RuntimeScheduler = class {
|
|
|
18423
19005
|
this.timeouts.add(handle);
|
|
18424
19006
|
return handle;
|
|
18425
19007
|
}
|
|
19008
|
+
/** 已销毁 realm 的 Promise 回调不能复活任务,也不能向外泄漏异步拒绝。 */
|
|
19009
|
+
getCanceledHandle() {
|
|
19010
|
+
if (!this.canceledHandle) {
|
|
19011
|
+
this.canceledHandle = setTimeout(() => {}, 0);
|
|
19012
|
+
clearTimeout(this.canceledHandle);
|
|
19013
|
+
}
|
|
19014
|
+
return this.canceledHandle;
|
|
19015
|
+
}
|
|
18426
19016
|
};
|
|
18427
19017
|
//#endregion
|
|
18428
19018
|
//#region ../../mpcore/packages/simulator/src/kernel/runtimeKernel.ts
|
|
@@ -18645,6 +19235,34 @@ function loadProject(projectPath) {
|
|
|
18645
19235
|
});
|
|
18646
19236
|
}
|
|
18647
19237
|
//#endregion
|
|
19238
|
+
//#region ../../mpcore/packages/simulator/src/view/eventBinding.ts
|
|
19239
|
+
function resolveMiniProgramEventBinding(attributes, eventName) {
|
|
19240
|
+
return resolveEventBindingPhases(attributes, eventName, false)?.bubble ?? null;
|
|
19241
|
+
}
|
|
19242
|
+
/** 冒泡时保留原始 target,currentTarget 则属于当前执行绑定的节点。 */
|
|
19243
|
+
function createMiniProgramEventPayload(node, eventName, event, origin = node) {
|
|
19244
|
+
const dataset = collectNodeDataset(node);
|
|
19245
|
+
const nodeId = node.attribs?.id ?? "";
|
|
19246
|
+
const isOrigin = node === origin;
|
|
19247
|
+
return {
|
|
19248
|
+
bubbles: false,
|
|
19249
|
+
capturePhase: false,
|
|
19250
|
+
composed: false,
|
|
19251
|
+
currentTarget: {
|
|
19252
|
+
dataset: isOrigin ? event.currentTarget?.dataset ?? event.dataset ?? dataset : dataset,
|
|
19253
|
+
id: isOrigin ? event.currentTarget?.id ?? event.id ?? nodeId : nodeId
|
|
19254
|
+
},
|
|
19255
|
+
detail: event.detail,
|
|
19256
|
+
mark: event.mark,
|
|
19257
|
+
target: {
|
|
19258
|
+
dataset: event.target?.dataset ?? event.dataset ?? (isOrigin ? dataset : collectNodeDataset(origin)),
|
|
19259
|
+
id: event.target?.id ?? event.id ?? origin.attribs?.id ?? ""
|
|
19260
|
+
},
|
|
19261
|
+
timeStamp: Date.now(),
|
|
19262
|
+
type: eventName
|
|
19263
|
+
};
|
|
19264
|
+
}
|
|
19265
|
+
//#endregion
|
|
18648
19266
|
//#region ../../node_modules/.pnpm/boolbase@2.0.0/node_modules/boolbase/dist/index.js
|
|
18649
19267
|
function trueFunc() {
|
|
18650
19268
|
return true;
|
|
@@ -20071,23 +20689,31 @@ function appendNextSiblings(element, adapter) {
|
|
|
20071
20689
|
const selectAll = getSelectorFunction((query, elements, options) => query === falseFunc || !elements || elements.length === 0 ? [] : findAll(query, elements, options));
|
|
20072
20690
|
//#endregion
|
|
20073
20691
|
//#region ../../mpcore/packages/simulator/src/view/selectors.ts
|
|
20074
|
-
|
|
20075
|
-
|
|
20076
|
-
|
|
20077
|
-
|
|
20078
|
-
|
|
20079
|
-
|
|
20080
|
-
|
|
20081
|
-
|
|
20082
|
-
|
|
20083
|
-
|
|
20084
|
-
|
|
20085
|
-
|
|
20086
|
-
|
|
20087
|
-
|
|
20088
|
-
|
|
20089
|
-
|
|
20090
|
-
|
|
20692
|
+
function createAdapter(getChildren, getParent) {
|
|
20693
|
+
const adapter = {
|
|
20694
|
+
isTag: (node) => node.type === "tag",
|
|
20695
|
+
getAttributeValue: (node, name) => node.attribs?.[name],
|
|
20696
|
+
getChildren,
|
|
20697
|
+
getName: (node) => node.name ?? "",
|
|
20698
|
+
getParent,
|
|
20699
|
+
getSiblings: (node) => {
|
|
20700
|
+
const parent = getParent(node);
|
|
20701
|
+
return parent ? getChildren(parent) : [node];
|
|
20702
|
+
},
|
|
20703
|
+
getText: (node) => node.type === "text" ? node.data ?? "" : getChildren(node).map((child) => adapter.getText(child)).join(""),
|
|
20704
|
+
hasAttrib: (node, name) => Object.hasOwn(node.attribs ?? {}, name),
|
|
20705
|
+
removeSubsets: (nodes) => {
|
|
20706
|
+
const unique = new Set(nodes);
|
|
20707
|
+
return [...unique].filter((node) => {
|
|
20708
|
+
for (let parent = getParent(node); parent; parent = getParent(parent)) if (unique.has(parent)) return false;
|
|
20709
|
+
return true;
|
|
20710
|
+
});
|
|
20711
|
+
}
|
|
20712
|
+
};
|
|
20713
|
+
return adapter;
|
|
20714
|
+
}
|
|
20715
|
+
const renderedAdapter = createAdapter((node) => node.children ?? [], (node) => node.parent ?? null);
|
|
20716
|
+
const inspectionAdapter = createAdapter(getInspectionChildren, getInspectionParent);
|
|
20091
20717
|
function resolveComponentTags(selectors) {
|
|
20092
20718
|
return selectors.map((group) => group.map((token) => {
|
|
20093
20719
|
if (token.type === SelectorType.Pseudo && Array.isArray(token.data)) return {
|
|
@@ -20110,7 +20736,7 @@ function resolveComponentTags(selectors) {
|
|
|
20110
20736
|
};
|
|
20111
20737
|
}));
|
|
20112
20738
|
}
|
|
20113
|
-
function
|
|
20739
|
+
function queryWithAdapter(root, selector, adapter) {
|
|
20114
20740
|
const parsed = parse$1(selector);
|
|
20115
20741
|
const query = resolveComponentTags(parsed);
|
|
20116
20742
|
const includesPageRoot = root.type === "tag" && root.name === "page" && parsed.some((group) => group.some((token) => token.type === SelectorType.Tag && token.name === "page"));
|
|
@@ -20119,6 +20745,12 @@ function querySelectorAll(root, selector) {
|
|
|
20119
20745
|
cacheResults: false
|
|
20120
20746
|
});
|
|
20121
20747
|
}
|
|
20748
|
+
function querySelectorAll(root, selector) {
|
|
20749
|
+
return queryWithAdapter(root, selector, renderedAdapter);
|
|
20750
|
+
}
|
|
20751
|
+
function queryInspectionSelectorAll(root, selector) {
|
|
20752
|
+
return queryWithAdapter(root, selector, inspectionAdapter);
|
|
20753
|
+
}
|
|
20122
20754
|
//#endregion
|
|
20123
20755
|
//#region ../../node_modules/.pnpm/whynot@5.0.0/node_modules/whynot/dist/whynot.umd.cjs
|
|
20124
20756
|
var require_whynot_umd = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
@@ -37866,7 +38498,10 @@ var import_fontoxpath = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin((
|
|
|
37866
38498
|
});
|
|
37867
38499
|
})))(), 1);
|
|
37868
38500
|
const xpathEngine = Reflect.get(import_fontoxpath, "default") ?? import_fontoxpath;
|
|
37869
|
-
function
|
|
38501
|
+
function getRenderedChildren(node) {
|
|
38502
|
+
return node.children ?? [];
|
|
38503
|
+
}
|
|
38504
|
+
function wrapNode(source, getChildren, parent = null) {
|
|
37870
38505
|
const node = {
|
|
37871
38506
|
attributes: [],
|
|
37872
38507
|
children: [],
|
|
@@ -37889,7 +38524,7 @@ function wrapNode(source, parent = null) {
|
|
|
37889
38524
|
prefix: null,
|
|
37890
38525
|
value
|
|
37891
38526
|
}));
|
|
37892
|
-
node.children = (source
|
|
38527
|
+
node.children = getChildren(source).map((child) => wrapNode(child, getChildren, node));
|
|
37893
38528
|
return node;
|
|
37894
38529
|
}
|
|
37895
38530
|
function sibling(node, offset) {
|
|
@@ -37909,40 +38544,25 @@ const domFacade = {
|
|
|
37909
38544
|
getParentNode: (node) => node.parent,
|
|
37910
38545
|
getPreviousSibling: (node) => sibling(node, -1)
|
|
37911
38546
|
};
|
|
37912
|
-
function
|
|
38547
|
+
function queryElements(root, expression, getChildren) {
|
|
37913
38548
|
if (!expression.trim()) throw new Error("XPath must be a non-empty expression in headless testing runtime.");
|
|
37914
38549
|
const document = wrapNode(root.type === "root" ? root : {
|
|
37915
38550
|
type: "root",
|
|
37916
38551
|
children: [root]
|
|
37917
|
-
});
|
|
38552
|
+
}, getChildren);
|
|
37918
38553
|
return xpathEngine.evaluateXPathToNodes(expression, document, domFacade).filter((node) => node.nodeType === 1).map((node) => node.source);
|
|
37919
38554
|
}
|
|
38555
|
+
function queryXPathElements(root, expression) {
|
|
38556
|
+
return queryElements(root, expression, getRenderedChildren);
|
|
38557
|
+
}
|
|
38558
|
+
function queryInspectionXPathElements(root, expression) {
|
|
38559
|
+
return queryElements(root, expression, getInspectionChildren);
|
|
38560
|
+
}
|
|
37920
38561
|
//#endregion
|
|
37921
38562
|
//#region ../../mpcore/packages/simulator/src/view/nodeHandle.ts
|
|
37922
38563
|
function escapeText$1(text) {
|
|
37923
38564
|
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
37924
38565
|
}
|
|
37925
|
-
function createEventPayload$1(node, eventName, event) {
|
|
37926
|
-
const dataset = collectNodeDataset(node);
|
|
37927
|
-
const nodeId = node.attribs?.id ?? "";
|
|
37928
|
-
return {
|
|
37929
|
-
bubbles: false,
|
|
37930
|
-
capturePhase: false,
|
|
37931
|
-
composed: false,
|
|
37932
|
-
currentTarget: {
|
|
37933
|
-
dataset: event.currentTarget?.dataset ?? event.dataset ?? dataset,
|
|
37934
|
-
id: event.currentTarget?.id ?? event.id ?? nodeId
|
|
37935
|
-
},
|
|
37936
|
-
detail: event.detail,
|
|
37937
|
-
mark: event.mark,
|
|
37938
|
-
target: {
|
|
37939
|
-
dataset: event.target?.dataset ?? event.dataset ?? dataset,
|
|
37940
|
-
id: event.target?.id ?? event.id ?? nodeId
|
|
37941
|
-
},
|
|
37942
|
-
timeStamp: Date.now(),
|
|
37943
|
-
type: eventName
|
|
37944
|
-
};
|
|
37945
|
-
}
|
|
37946
38566
|
function createValueEventDetail(value, detail) {
|
|
37947
38567
|
return {
|
|
37948
38568
|
value,
|
|
@@ -37984,7 +38604,7 @@ var HeadlessTestingNodeHandle = class HeadlessTestingNodeHandle {
|
|
|
37984
38604
|
this.interactions?.assertActive?.();
|
|
37985
38605
|
}
|
|
37986
38606
|
query(selector) {
|
|
37987
|
-
const nodes = querySelectorAll(this.node, selector);
|
|
38607
|
+
const nodes = (this.interactions?.projectionOnly ? querySelectorAll : queryInspectionSelectorAll)(this.node, selector);
|
|
37988
38608
|
const scope = this.node.attribs?.["data-sim-scope"];
|
|
37989
38609
|
if (!scope || !this.interactions) return nodes;
|
|
37990
38610
|
return nodes.filter((node) => {
|
|
@@ -38003,7 +38623,7 @@ var HeadlessTestingNodeHandle = class HeadlessTestingNodeHandle {
|
|
|
38003
38623
|
}
|
|
38004
38624
|
async getElementsByXpath(expression) {
|
|
38005
38625
|
this.assertActive();
|
|
38006
|
-
return queryXPathElements(this.node, expression).map((node) => new HeadlessTestingNodeHandle(node, this.interactions));
|
|
38626
|
+
return (this.interactions?.projectionOnly ? queryXPathElements : queryInspectionXPathElements)(this.node, expression).map((node) => new HeadlessTestingNodeHandle(node, this.interactions));
|
|
38007
38627
|
}
|
|
38008
38628
|
async attr(name) {
|
|
38009
38629
|
this.assertActive();
|
|
@@ -38052,9 +38672,18 @@ var HeadlessTestingNodeHandle = class HeadlessTestingNodeHandle {
|
|
|
38052
38672
|
if (!this.interactions) throw new Error("Node interactions are not available for this headless testing node.");
|
|
38053
38673
|
const normalizedEventName = eventName.trim();
|
|
38054
38674
|
if (!normalizedEventName) throw new Error("Event name must be a non-empty string in headless testing runtime.");
|
|
38675
|
+
let handlerResults;
|
|
38676
|
+
const nativeHandled = this.interactions.dispatchNativeEvent?.(this.node, normalizedEventName, event, normalizedEventName === "tap" ? (result) => {
|
|
38677
|
+
handlerResults ??= [];
|
|
38678
|
+
handlerResults.push(result);
|
|
38679
|
+
} : void 0);
|
|
38680
|
+
if (normalizedEventName === "tap" && nativeHandled) return handlerResults ? (await Promise.all(handlerResults))[0] : void 0;
|
|
38055
38681
|
const binding = resolveMiniProgramEventBinding(this.node.attribs, normalizedEventName);
|
|
38056
|
-
if (!binding?.method)
|
|
38057
|
-
|
|
38682
|
+
if (!binding?.method) {
|
|
38683
|
+
if (nativeHandled) return;
|
|
38684
|
+
throw new Error(`No ${normalizedEventName} binding was found on <${this.node.name ?? "unknown"}> in headless testing runtime.`);
|
|
38685
|
+
}
|
|
38686
|
+
return await this.interactions.callMethod(this.node.attribs?.["data-sim-scope"] ?? null, binding.method, createMiniProgramEventPayload(this.node, normalizedEventName, event));
|
|
38058
38687
|
}
|
|
38059
38688
|
async tap(event = {}) {
|
|
38060
38689
|
return await this.trigger("tap", event);
|
|
@@ -38161,35 +38790,9 @@ function renderPageTree(project, page) {
|
|
|
38161
38790
|
};
|
|
38162
38791
|
}
|
|
38163
38792
|
//#endregion
|
|
38164
|
-
//#region ../../mpcore/packages/simulator/src/view/
|
|
38165
|
-
const LEADING_MARK_PREFIX_RE = /^mark[:\-]?/;
|
|
38166
|
-
const MARK_NAME_RE = /[:\-]([a-z])/g;
|
|
38793
|
+
//#region ../../mpcore/packages/simulator/src/view/nodeGeometry.ts
|
|
38167
38794
|
const NUMERIC_LIKE_VALUE_RE$1 = /-?\d+(?:\.\d+)?/;
|
|
38168
|
-
|
|
38169
|
-
return attributeName.replace(LEADING_MARK_PREFIX_RE, "").replace(MARK_NAME_RE, (_match, char) => char.toUpperCase());
|
|
38170
|
-
}
|
|
38171
|
-
function collectMark(node) {
|
|
38172
|
-
const mark = {};
|
|
38173
|
-
for (const [key, value] of Object.entries(node.attribs ?? {})) {
|
|
38174
|
-
if (!(key.startsWith("mark:") || key.startsWith("mark-"))) continue;
|
|
38175
|
-
mark[toMarkKey(key)] = value;
|
|
38176
|
-
}
|
|
38177
|
-
return mark;
|
|
38178
|
-
}
|
|
38179
|
-
function createScopedRoot(node) {
|
|
38180
|
-
return {
|
|
38181
|
-
children: [node],
|
|
38182
|
-
type: "root"
|
|
38183
|
-
};
|
|
38184
|
-
}
|
|
38185
|
-
function findNodeByScopeId(root, scopeId) {
|
|
38186
|
-
if (root.attribs?.["data-sim-scope"] === scopeId) return root;
|
|
38187
|
-
for (const child of root.children ?? []) {
|
|
38188
|
-
const match = findNodeByScopeId(child, scopeId);
|
|
38189
|
-
if (match) return match;
|
|
38190
|
-
}
|
|
38191
|
-
return null;
|
|
38192
|
-
}
|
|
38795
|
+
const PIXEL_LENGTH_RE = /^\d+(?:\.\d+)?px$|^0$/;
|
|
38193
38796
|
function parseStyleDeclarations$1(styleValue) {
|
|
38194
38797
|
const declarations = {};
|
|
38195
38798
|
if (!styleValue) return declarations;
|
|
@@ -38224,18 +38827,229 @@ function resolveRect$1(node, windowInfo) {
|
|
|
38224
38827
|
width
|
|
38225
38828
|
};
|
|
38226
38829
|
}
|
|
38830
|
+
/** 仅支持显式 px 视口和单个 view 内容盒;未知样式布局不作为已测量滚动范围。 */
|
|
38831
|
+
function resolveNativeScrollBounds(node) {
|
|
38832
|
+
const viewport = parseStyleDeclarations$1(node.attribs?.style);
|
|
38833
|
+
let content;
|
|
38834
|
+
const visit = (parent) => {
|
|
38835
|
+
for (const child of parent.children ?? []) {
|
|
38836
|
+
if (child.type === "comment" || child.type === "text" && !child.data?.trim()) continue;
|
|
38837
|
+
if (child.name === "block") {
|
|
38838
|
+
if (!visit(child)) return false;
|
|
38839
|
+
} else {
|
|
38840
|
+
if (content || child.name !== "view") return false;
|
|
38841
|
+
content = child;
|
|
38842
|
+
}
|
|
38843
|
+
}
|
|
38844
|
+
return true;
|
|
38845
|
+
};
|
|
38846
|
+
if (!visit(node)) return {
|
|
38847
|
+
maxTop: 0,
|
|
38848
|
+
maxLeft: 0
|
|
38849
|
+
};
|
|
38850
|
+
const box = parseStyleDeclarations$1(content?.attribs?.style);
|
|
38851
|
+
const supported = content && !viewport.display && !box.display && !box.position && !box.transform;
|
|
38852
|
+
const extent = (dimension) => {
|
|
38853
|
+
const viewportValue = viewport[dimension];
|
|
38854
|
+
const contentValue = box[dimension];
|
|
38855
|
+
if (!supported || !viewportValue || !contentValue || !PIXEL_LENGTH_RE.test(viewportValue) || !PIXEL_LENGTH_RE.test(contentValue)) return 0;
|
|
38856
|
+
const size = parseNumericLikeValue$1(viewportValue);
|
|
38857
|
+
const contentSize = parseNumericLikeValue$1(contentValue);
|
|
38858
|
+
return size > 0 && Number.isFinite(size) && Number.isFinite(contentSize) ? Math.max(0, contentSize - size) : 0;
|
|
38859
|
+
};
|
|
38860
|
+
return {
|
|
38861
|
+
maxTop: extent("height"),
|
|
38862
|
+
maxLeft: extent("width")
|
|
38863
|
+
};
|
|
38864
|
+
}
|
|
38865
|
+
//#endregion
|
|
38866
|
+
//#region ../../mpcore/packages/simulator/src/view/nativeNode.ts
|
|
38867
|
+
const pageScrollViews = /* @__PURE__ */ new WeakMap();
|
|
38868
|
+
function coordinate(value) {
|
|
38869
|
+
const number = Number(value);
|
|
38870
|
+
return Number.isFinite(number) ? Math.max(0, number) : 0;
|
|
38871
|
+
}
|
|
38872
|
+
function nodeKey(node) {
|
|
38873
|
+
return node.attribs?.["data-sim-node"] ?? "";
|
|
38874
|
+
}
|
|
38875
|
+
/** 原生滚动位置独立于绑定值;未改变的绑定不能覆盖用户滚动。 */
|
|
38876
|
+
function syncNativeScrollViews(page, root, emit) {
|
|
38877
|
+
let states = pageScrollViews.get(page);
|
|
38878
|
+
let present;
|
|
38879
|
+
const visit = (node) => {
|
|
38880
|
+
if (node.name === "scroll-view") {
|
|
38881
|
+
const key = nodeKey(node);
|
|
38882
|
+
present ??= /* @__PURE__ */ new Set();
|
|
38883
|
+
present.add(key);
|
|
38884
|
+
if (!states) {
|
|
38885
|
+
states = /* @__PURE__ */ new Map();
|
|
38886
|
+
pageScrollViews.set(page, states);
|
|
38887
|
+
}
|
|
38888
|
+
const boundTop = coordinate(node.attribs?.["scroll-top"]);
|
|
38889
|
+
const boundLeft = coordinate(node.attribs?.["scroll-left"]);
|
|
38890
|
+
const bounds = resolveNativeScrollBounds(node);
|
|
38891
|
+
const scrollY = node.attribs?.["scroll-y"];
|
|
38892
|
+
const scrollX = node.attribs?.["scroll-x"];
|
|
38893
|
+
const maxTop = scrollY !== void 0 && scrollY !== "false" && scrollY !== "0" ? bounds.maxTop : 0;
|
|
38894
|
+
const maxLeft = scrollX !== void 0 && scrollX !== "false" && scrollX !== "0" ? bounds.maxLeft : 0;
|
|
38895
|
+
let state = states.get(key);
|
|
38896
|
+
const previousTop = state?.scrollTop ?? 0;
|
|
38897
|
+
const previousLeft = state?.scrollLeft ?? 0;
|
|
38898
|
+
if (!state) {
|
|
38899
|
+
state = {
|
|
38900
|
+
boundTop,
|
|
38901
|
+
boundLeft,
|
|
38902
|
+
scrollTop: boundTop,
|
|
38903
|
+
scrollLeft: boundLeft,
|
|
38904
|
+
maxTop,
|
|
38905
|
+
maxLeft
|
|
38906
|
+
};
|
|
38907
|
+
states.set(key, state);
|
|
38908
|
+
} else {
|
|
38909
|
+
if (state.boundTop !== boundTop) {
|
|
38910
|
+
state.scrollTop = boundTop;
|
|
38911
|
+
state.boundTop = boundTop;
|
|
38912
|
+
}
|
|
38913
|
+
if (state.boundLeft !== boundLeft) {
|
|
38914
|
+
state.scrollLeft = boundLeft;
|
|
38915
|
+
state.boundLeft = boundLeft;
|
|
38916
|
+
}
|
|
38917
|
+
}
|
|
38918
|
+
state.maxTop = maxTop;
|
|
38919
|
+
state.maxLeft = maxLeft;
|
|
38920
|
+
state.scrollTop = Math.min(state.scrollTop, maxTop);
|
|
38921
|
+
state.scrollLeft = Math.min(state.scrollLeft, maxLeft);
|
|
38922
|
+
if (state.scrollTop !== previousTop || state.scrollLeft !== previousLeft) {
|
|
38923
|
+
const binding = resolveMiniProgramEventBinding(node.attribs, "scroll");
|
|
38924
|
+
if (binding?.method) {
|
|
38925
|
+
const target = {
|
|
38926
|
+
id: node.attribs?.id ?? "",
|
|
38927
|
+
dataset: collectNodeDataset(node)
|
|
38928
|
+
};
|
|
38929
|
+
emit(node.attribs?.["data-sim-scope"] ?? null, binding.method, {
|
|
38930
|
+
type: "scroll",
|
|
38931
|
+
timeStamp: Date.now(),
|
|
38932
|
+
detail: {
|
|
38933
|
+
scrollTop: state.scrollTop,
|
|
38934
|
+
scrollLeft: state.scrollLeft
|
|
38935
|
+
},
|
|
38936
|
+
target,
|
|
38937
|
+
currentTarget: target
|
|
38938
|
+
});
|
|
38939
|
+
}
|
|
38940
|
+
}
|
|
38941
|
+
}
|
|
38942
|
+
for (const child of node.children ?? []) visit(child);
|
|
38943
|
+
};
|
|
38944
|
+
visit(root);
|
|
38945
|
+
if (states) {
|
|
38946
|
+
for (const key of states.keys()) if (!present?.has(key)) states.delete(key);
|
|
38947
|
+
}
|
|
38948
|
+
}
|
|
38949
|
+
function getNativeScrollOffset(page, node) {
|
|
38950
|
+
const state = pageScrollViews.get(page)?.get(nodeKey(node));
|
|
38951
|
+
return {
|
|
38952
|
+
scrollTop: state?.scrollTop ?? 0,
|
|
38953
|
+
scrollLeft: state?.scrollLeft ?? 0
|
|
38954
|
+
};
|
|
38955
|
+
}
|
|
38956
|
+
/** 原生交互更新宿主状态,导航仍交给统一 wx 路由入口。 */
|
|
38957
|
+
function dispatchNativeNodeEvent(page, node, eventName, event, wx, resolveMethod, onHandlerResult) {
|
|
38958
|
+
if (eventName === "scroll" && node.name === "scroll-view") {
|
|
38959
|
+
const detail = event.detail;
|
|
38960
|
+
const state = pageScrollViews.get(page)?.get(nodeKey(node));
|
|
38961
|
+
if (state) {
|
|
38962
|
+
if (detail?.scrollTop !== void 0) state.scrollTop = Math.min(coordinate(detail.scrollTop), state.maxTop);
|
|
38963
|
+
if (detail?.scrollLeft !== void 0) state.scrollLeft = Math.min(coordinate(detail.scrollLeft), state.maxLeft);
|
|
38964
|
+
}
|
|
38965
|
+
event.detail = Object.assign(event.detail && typeof event.detail === "object" ? event.detail : {}, getNativeScrollOffset(page, node));
|
|
38966
|
+
return true;
|
|
38967
|
+
}
|
|
38968
|
+
if (eventName !== "tap") return false;
|
|
38969
|
+
let current = node;
|
|
38970
|
+
let navigator;
|
|
38971
|
+
let handlers;
|
|
38972
|
+
let handled = false;
|
|
38973
|
+
while (current) {
|
|
38974
|
+
if (!navigator && current.name === "navigator") navigator = current;
|
|
38975
|
+
const binding = resolveMiniProgramEventBinding(current.attribs, "tap");
|
|
38976
|
+
if (binding?.method) {
|
|
38977
|
+
const call = resolveMethod(current.attribs?.["data-sim-scope"] ?? null, binding.method);
|
|
38978
|
+
handlers ??= [];
|
|
38979
|
+
handlers.push({
|
|
38980
|
+
node: current,
|
|
38981
|
+
call
|
|
38982
|
+
});
|
|
38983
|
+
handled = true;
|
|
38984
|
+
}
|
|
38985
|
+
if (binding?.stopAfter) {
|
|
38986
|
+
handled = true;
|
|
38987
|
+
break;
|
|
38988
|
+
}
|
|
38989
|
+
current = current.parent;
|
|
38990
|
+
}
|
|
38991
|
+
if (handlers) for (const handler of handlers) {
|
|
38992
|
+
const result = handler.call(createMiniProgramEventPayload(handler.node, "tap", event, node));
|
|
38993
|
+
onHandlerResult?.(result);
|
|
38994
|
+
}
|
|
38995
|
+
if (!navigator) return handled;
|
|
38996
|
+
const openType = navigator.attribs?.["open-type"] ?? "navigate";
|
|
38997
|
+
const url = navigator.attribs?.url ?? "";
|
|
38998
|
+
switch (openType) {
|
|
38999
|
+
case "navigate":
|
|
39000
|
+
wx.navigateTo({ url });
|
|
39001
|
+
break;
|
|
39002
|
+
case "redirect":
|
|
39003
|
+
wx.redirectTo({ url });
|
|
39004
|
+
break;
|
|
39005
|
+
case "switchTab":
|
|
39006
|
+
wx.switchTab({ url });
|
|
39007
|
+
break;
|
|
39008
|
+
case "reLaunch":
|
|
39009
|
+
wx.reLaunch({ url });
|
|
39010
|
+
break;
|
|
39011
|
+
case "navigateBack":
|
|
39012
|
+
wx.navigateBack({ delta: Number(navigator.attribs?.delta ?? 1) });
|
|
39013
|
+
break;
|
|
39014
|
+
default: return handled;
|
|
39015
|
+
}
|
|
39016
|
+
return true;
|
|
39017
|
+
}
|
|
39018
|
+
//#endregion
|
|
39019
|
+
//#region ../../mpcore/packages/simulator/src/view/selectorQuery.ts
|
|
39020
|
+
const LEADING_MARK_PREFIX_RE = /^mark[:\-]?/;
|
|
39021
|
+
const MARK_NAME_RE = /[:\-]([a-z])/g;
|
|
39022
|
+
function toMarkKey(attributeName) {
|
|
39023
|
+
return attributeName.replace(LEADING_MARK_PREFIX_RE, "").replace(MARK_NAME_RE, (_match, char) => char.toUpperCase());
|
|
39024
|
+
}
|
|
39025
|
+
function collectMark(node) {
|
|
39026
|
+
const mark = {};
|
|
39027
|
+
for (const [key, value] of Object.entries(node.attribs ?? {})) {
|
|
39028
|
+
if (!(key.startsWith("mark:") || key.startsWith("mark-"))) continue;
|
|
39029
|
+
mark[toMarkKey(key)] = value;
|
|
39030
|
+
}
|
|
39031
|
+
return mark;
|
|
39032
|
+
}
|
|
39033
|
+
function createScopedRoot(node) {
|
|
39034
|
+
return {
|
|
39035
|
+
children: [node],
|
|
39036
|
+
type: "root"
|
|
39037
|
+
};
|
|
39038
|
+
}
|
|
39039
|
+
function findNodeByScopeId(root, scopeId) {
|
|
39040
|
+
if (root.attribs?.["data-sim-scope"] === scopeId) return root;
|
|
39041
|
+
for (const child of root.children ?? []) {
|
|
39042
|
+
const match = findNodeByScopeId(child, scopeId);
|
|
39043
|
+
if (match) return match;
|
|
39044
|
+
}
|
|
39045
|
+
return null;
|
|
39046
|
+
}
|
|
38227
39047
|
function resolveSelectorScrollTop(root, selector) {
|
|
38228
39048
|
const normalizedSelector = selector?.trim();
|
|
38229
39049
|
if (!normalizedSelector) return null;
|
|
38230
39050
|
const match = querySelectorAll(root, normalizedSelector)[0];
|
|
38231
39051
|
return match ? resolveRect$1(match).top : null;
|
|
38232
39052
|
}
|
|
38233
|
-
function resolveScrollOffset(node) {
|
|
38234
|
-
return {
|
|
38235
|
-
scrollLeft: parseNumericLikeValue$1(node.attribs?.["data-sim-scroll-left"]),
|
|
38236
|
-
scrollTop: parseNumericLikeValue$1(node.attribs?.["data-sim-scroll-top"])
|
|
38237
|
-
};
|
|
38238
|
-
}
|
|
38239
39053
|
function resolvePropertyValue(node, propertyName) {
|
|
38240
39054
|
const normalizedPropertyName = propertyName.trim();
|
|
38241
39055
|
if (!normalizedPropertyName) return;
|
|
@@ -38268,7 +39082,7 @@ function resolveFieldsResult(node, fields, options) {
|
|
|
38268
39082
|
result.height = rect.height;
|
|
38269
39083
|
}
|
|
38270
39084
|
}
|
|
38271
|
-
if (fields.scrollOffset) Object.assign(result,
|
|
39085
|
+
if (fields.scrollOffset) Object.assign(result, getNativeScrollOffset(options.page, node));
|
|
38272
39086
|
if (Array.isArray(fields.properties) && fields.properties.length > 0) Object.assign(result, pickProperties(node, fields.properties));
|
|
38273
39087
|
if (Array.isArray(fields.computedStyle) && fields.computedStyle.length > 0) Object.assign(result, pickComputedStyle(node, fields.computedStyle));
|
|
38274
39088
|
if (fields.context) result.context = options.resolveContext?.(node) ?? { type: "unsupported-context" };
|
|
@@ -39090,6 +39904,136 @@ var HeadlessAppLifecycle = class {
|
|
|
39090
39904
|
}
|
|
39091
39905
|
};
|
|
39092
39906
|
//#endregion
|
|
39907
|
+
//#region ../../mpcore/packages/simulator/src/runtime/workerHost.ts
|
|
39908
|
+
/** 两种 simulator 共用消息与生命周期;每个 worker 独立模块缓存、全局对象及计时器。 */
|
|
39909
|
+
function createWorkerHost(options) {
|
|
39910
|
+
const active = /* @__PURE__ */ new Set();
|
|
39911
|
+
let closed = false;
|
|
39912
|
+
const configuredRoot = () => {
|
|
39913
|
+
return JSON.parse(options.read(join(options.root, "app.json")) ?? "{}").workers;
|
|
39914
|
+
};
|
|
39915
|
+
return {
|
|
39916
|
+
apis: {
|
|
39917
|
+
preDownloadSubpackage(option) {
|
|
39918
|
+
const config = configuredRoot();
|
|
39919
|
+
if (option.packageType !== "workers" || typeof config !== "object" || !config.isSubpackage) {
|
|
39920
|
+
const error = /* @__PURE__ */ new Error("preDownloadSubpackage:fail workers subpackage is not configured");
|
|
39921
|
+
option.fail?.(error);
|
|
39922
|
+
option.complete?.();
|
|
39923
|
+
return;
|
|
39924
|
+
}
|
|
39925
|
+
const result = { errMsg: "preDownloadSubpackage:ok" };
|
|
39926
|
+
option.success?.(result);
|
|
39927
|
+
option.complete?.(result);
|
|
39928
|
+
},
|
|
39929
|
+
createWorker(scriptPath) {
|
|
39930
|
+
if (closed) throw new Error("createWorker:fail runtime has closed");
|
|
39931
|
+
const config = configuredRoot();
|
|
39932
|
+
const directory = typeof config === "string" ? config : config?.path;
|
|
39933
|
+
if (!directory || typeof scriptPath !== "string" || !scriptPath.endsWith(".js")) throw new Error("createWorker:fail invalid worker path");
|
|
39934
|
+
const root = normalize(join(options.root, directory));
|
|
39935
|
+
const entry = normalize(join(options.root, scriptPath));
|
|
39936
|
+
const inside = (file) => file.startsWith(`${root}/`);
|
|
39937
|
+
if (!inside(entry) || options.read(entry) === void 0) throw new Error("createWorker:fail worker entry is outside the configured directory or missing");
|
|
39938
|
+
const messages = /* @__PURE__ */ new Set();
|
|
39939
|
+
const incoming = /* @__PURE__ */ new Set();
|
|
39940
|
+
const errors = /* @__PURE__ */ new Set();
|
|
39941
|
+
const cache = /* @__PURE__ */ new Map();
|
|
39942
|
+
let terminated = false;
|
|
39943
|
+
const scheduler = new RuntimeScheduler((error) => {
|
|
39944
|
+
const value = { message: error instanceof Error ? error.message : String(error) };
|
|
39945
|
+
if (errors.size) for (const listener of errors) listener(value);
|
|
39946
|
+
else options.console.error(value.message);
|
|
39947
|
+
});
|
|
39948
|
+
const post = (listeners, message) => {
|
|
39949
|
+
if (terminated) return;
|
|
39950
|
+
const copy = structuredClone(message);
|
|
39951
|
+
scheduler.setTimeout(() => {
|
|
39952
|
+
for (const listener of [...listeners]) {
|
|
39953
|
+
if (terminated) break;
|
|
39954
|
+
listener(copy);
|
|
39955
|
+
}
|
|
39956
|
+
}, 0);
|
|
39957
|
+
};
|
|
39958
|
+
const globals = {
|
|
39959
|
+
wx: void 0,
|
|
39960
|
+
App: void 0,
|
|
39961
|
+
Page: void 0,
|
|
39962
|
+
Component: void 0,
|
|
39963
|
+
getApp: void 0,
|
|
39964
|
+
console: options.console,
|
|
39965
|
+
worker: {
|
|
39966
|
+
onMessage: (listener) => incoming.add(listener),
|
|
39967
|
+
postMessage: (message) => post(messages, message)
|
|
39968
|
+
},
|
|
39969
|
+
setTimeout: scheduler.setTimeout.bind(scheduler),
|
|
39970
|
+
clearTimeout: scheduler.clearTimeout.bind(scheduler),
|
|
39971
|
+
setInterval: scheduler.setInterval.bind(scheduler),
|
|
39972
|
+
clearInterval: scheduler.clearInterval.bind(scheduler)
|
|
39973
|
+
};
|
|
39974
|
+
globals.globalThis = globals;
|
|
39975
|
+
const execute = options.createExecutor(globals);
|
|
39976
|
+
const load = (file) => {
|
|
39977
|
+
if (terminated) return;
|
|
39978
|
+
if (!inside(file)) throw new Error("Worker require cannot leave its configured directory");
|
|
39979
|
+
if (cache.has(file)) return cache.get(file).exports;
|
|
39980
|
+
const source = options.read(file);
|
|
39981
|
+
if (source === void 0) throw new Error(`Missing worker module: ${file}`);
|
|
39982
|
+
if (file.endsWith(".json")) return JSON.parse(source);
|
|
39983
|
+
const module = { exports: {} };
|
|
39984
|
+
cache.set(file, module);
|
|
39985
|
+
const require = Object.assign((request) => {
|
|
39986
|
+
const target = resolveMiniProgramModule(file, request, root, (candidate) => options.read(candidate) !== void 0);
|
|
39987
|
+
if (!target) throw new Error(`Cannot resolve worker module: ${request}`);
|
|
39988
|
+
return load(target);
|
|
39989
|
+
}, { async: (request) => Promise.resolve().then(() => require(request)) });
|
|
39990
|
+
try {
|
|
39991
|
+
execute(source, file, module, require);
|
|
39992
|
+
} catch (error) {
|
|
39993
|
+
cache.delete(file);
|
|
39994
|
+
throw error;
|
|
39995
|
+
}
|
|
39996
|
+
return module.exports;
|
|
39997
|
+
};
|
|
39998
|
+
const worker = {
|
|
39999
|
+
onMessage: (listener) => {
|
|
40000
|
+
messages.add(listener);
|
|
40001
|
+
},
|
|
40002
|
+
offMessage: (listener) => {
|
|
40003
|
+
if (listener) messages.delete(listener);
|
|
40004
|
+
else messages.clear();
|
|
40005
|
+
},
|
|
40006
|
+
postMessage: (message) => post(incoming, message),
|
|
40007
|
+
onError: (listener) => {
|
|
40008
|
+
errors.add(listener);
|
|
40009
|
+
},
|
|
40010
|
+
offError: (listener) => {
|
|
40011
|
+
if (listener) errors.delete(listener);
|
|
40012
|
+
else errors.clear();
|
|
40013
|
+
},
|
|
40014
|
+
terminate() {
|
|
40015
|
+
if (terminated) return;
|
|
40016
|
+
terminated = true;
|
|
40017
|
+
scheduler.close();
|
|
40018
|
+
messages.clear();
|
|
40019
|
+
incoming.clear();
|
|
40020
|
+
errors.clear();
|
|
40021
|
+
cache.clear();
|
|
40022
|
+
active.delete(worker);
|
|
40023
|
+
}
|
|
40024
|
+
};
|
|
40025
|
+
active.add(worker);
|
|
40026
|
+
scheduler.setTimeout(() => load(entry), 0);
|
|
40027
|
+
return worker;
|
|
40028
|
+
}
|
|
40029
|
+
},
|
|
40030
|
+
close() {
|
|
40031
|
+
closed = true;
|
|
40032
|
+
for (const worker of active) worker.terminate();
|
|
40033
|
+
}
|
|
40034
|
+
};
|
|
40035
|
+
}
|
|
40036
|
+
//#endregion
|
|
39093
40037
|
//#region ../../mpcore/packages/simulator/src/runtime/runtimeGlobals.ts
|
|
39094
40038
|
const MINI_PROGRAM_UNAVAILABLE_GLOBAL_NAMES = [
|
|
39095
40039
|
"window",
|
|
@@ -39232,9 +40176,9 @@ function transformEsmToCommonJs(source) {
|
|
|
39232
40176
|
function createRequireNotFoundError(request, importer) {
|
|
39233
40177
|
return /* @__PURE__ */ new Error(`Cannot resolve require("${request}") from ${normalize(importer)} in headless runtime.`);
|
|
39234
40178
|
}
|
|
39235
|
-
function createExecutionContext(registries, getCurrentPages, getApp, wxDriver, kernel, globals, onConsole) {
|
|
40179
|
+
function createExecutionContext(registries, getCurrentPages, getApp, wxDriver, kernel, globals, onConsole, workers) {
|
|
39236
40180
|
const runtimeConsole = kernel.diagnostics.createConsole(console, onConsole);
|
|
39237
|
-
const wx = createHeadlessWx(wxDriver, runtimeConsole);
|
|
40181
|
+
const wx = createHeadlessWx(wxDriver, runtimeConsole, workers);
|
|
39238
40182
|
return createMiniProgramRuntimeGlobals({
|
|
39239
40183
|
App(definition) {
|
|
39240
40184
|
return registerAppDefinition(registries, definition);
|
|
@@ -39267,8 +40211,19 @@ function createExecutionContext(registries, getCurrentPages, getApp, wxDriver, k
|
|
|
39267
40211
|
}, globals);
|
|
39268
40212
|
}
|
|
39269
40213
|
function createModuleLoader(registries, getCurrentPages, getApp, wxDriver, options) {
|
|
40214
|
+
const workers = createWorkerHost({
|
|
40215
|
+
root: options.miniprogramRootPath,
|
|
40216
|
+
read: (file) => options.artifactSource.readText(file),
|
|
40217
|
+
console: options.kernel.diagnostics.createConsole(console, options.onConsole),
|
|
40218
|
+
createExecutor(globals) {
|
|
40219
|
+
const realm = vm.createContext(globals);
|
|
40220
|
+
return (source, file, module, require) => {
|
|
40221
|
+
new vm.Script(`(function(module, exports, require) {${source}\n})`, { filename: file }).runInContext(realm)(module, module.exports, require);
|
|
40222
|
+
};
|
|
40223
|
+
}
|
|
40224
|
+
});
|
|
39270
40225
|
const moduleCache = /* @__PURE__ */ new Map();
|
|
39271
|
-
const executionContext = createExecutionContext(registries, getCurrentPages, getApp, wxDriver, options.kernel, options.globals ?? {}, options.onConsole);
|
|
40226
|
+
const executionContext = createExecutionContext(registries, getCurrentPages, getApp, wxDriver, options.kernel, options.globals ?? {}, options.onConsole, workers.apis);
|
|
39272
40227
|
executionContext.globalThis = executionContext;
|
|
39273
40228
|
function executeModule(filePath, loadContext) {
|
|
39274
40229
|
const resolvedPath = path.resolve(filePath);
|
|
@@ -39327,6 +40282,7 @@ function createModuleLoader(registries, getCurrentPages, getApp, wxDriver, optio
|
|
|
39327
40282
|
return callback(...args);
|
|
39328
40283
|
},
|
|
39329
40284
|
close() {
|
|
40285
|
+
workers.close();
|
|
39330
40286
|
moduleCache.clear();
|
|
39331
40287
|
closeRuntimeWxsLoader(loader);
|
|
39332
40288
|
},
|
|
@@ -39359,6 +40315,142 @@ function createModuleLoader(registries, getCurrentPages, getApp, wxDriver, optio
|
|
|
39359
40315
|
return loader;
|
|
39360
40316
|
}
|
|
39361
40317
|
//#endregion
|
|
40318
|
+
//#region ../../mpcore/packages/simulator/src/runtime/packageAsset.ts
|
|
40319
|
+
const assetExtension = /\.(?:png|jpe?g|gif|svg|cer|mp3|aac|m4a|mp4|wav|ogg|silk|wasm|br|cur|ico|skel|crt|cert|gltf|glb|bin|ktx|hdr|exr|xnet|pem)$/i;
|
|
40320
|
+
/** 包内静态资源只读且每次访问宿主当前产物,不缓存为用户文件,也不允许越过包根目录。 */
|
|
40321
|
+
function createPackageAssetReader(read) {
|
|
40322
|
+
return (filePath) => {
|
|
40323
|
+
if (filePath.includes("\\") || filePath.includes(":") || filePath.includes("\0")) return;
|
|
40324
|
+
const relative = posix.normalize(filePath.replace(/^\/+/, ""));
|
|
40325
|
+
if (relative === ".." || relative.startsWith("../") || !assetExtension.test(relative)) return;
|
|
40326
|
+
return read(relative);
|
|
40327
|
+
};
|
|
40328
|
+
}
|
|
40329
|
+
//#endregion
|
|
40330
|
+
//#region ../../mpcore/packages/simulator/src/runtime/routeEvents.ts
|
|
40331
|
+
/** 两种模拟器共用路由身份与监听状态,完成事件由页面就绪和宿主提交驱动。 */
|
|
40332
|
+
var HeadlessRouteEvents = class {
|
|
40333
|
+
requestRender;
|
|
40334
|
+
isCurrentPage;
|
|
40335
|
+
bus = createHeadlessUniEventBus();
|
|
40336
|
+
pages = /* @__PURE__ */ new WeakMap();
|
|
40337
|
+
nextRouteId = 0;
|
|
40338
|
+
nextWebviewId = 0;
|
|
40339
|
+
activeEvent;
|
|
40340
|
+
activeState;
|
|
40341
|
+
constructor(requestRender, isCurrentPage) {
|
|
40342
|
+
this.requestRender = requestRender;
|
|
40343
|
+
this.isCurrentPage = isCurrentPage;
|
|
40344
|
+
}
|
|
40345
|
+
on(name, listener) {
|
|
40346
|
+
this.bus.$on(name, listener);
|
|
40347
|
+
}
|
|
40348
|
+
off(name, listener) {
|
|
40349
|
+
this.bus.$off(name, listener);
|
|
40350
|
+
}
|
|
40351
|
+
begin(path, query, openType, renderer, page) {
|
|
40352
|
+
this.cancel(this.activeState, /* @__PURE__ */ new Error("navigateTo:fail navigation was superseded"));
|
|
40353
|
+
const previous = page ? this.pages.get(page) : void 0;
|
|
40354
|
+
const event = {
|
|
40355
|
+
path,
|
|
40356
|
+
query: { ...query },
|
|
40357
|
+
openType: this.nextRouteId === 0 ? "appLaunch" : openType,
|
|
40358
|
+
renderer: previous?.event.renderer ?? (renderer === "skyline" || renderer === "xr-frame" ? renderer : "webview"),
|
|
40359
|
+
routeEventId: String(++this.nextRouteId),
|
|
40360
|
+
webviewId: previous?.webviewId ?? ++this.nextWebviewId,
|
|
40361
|
+
timeStamp: Date.now()
|
|
40362
|
+
};
|
|
40363
|
+
this.activeEvent = event;
|
|
40364
|
+
this.bus.$emit("beforeAppRoute", event);
|
|
40365
|
+
return event;
|
|
40366
|
+
}
|
|
40367
|
+
attach(page, event) {
|
|
40368
|
+
const state = this.pages.get(page);
|
|
40369
|
+
const next = {
|
|
40370
|
+
webviewId: event.webviewId,
|
|
40371
|
+
ready: state?.ready ?? false,
|
|
40372
|
+
event,
|
|
40373
|
+
committed: false,
|
|
40374
|
+
completed: false
|
|
40375
|
+
};
|
|
40376
|
+
this.pages.set(page, next);
|
|
40377
|
+
if (this.activeEvent === event) this.activeState = next;
|
|
40378
|
+
}
|
|
40379
|
+
/** 原生 navigateTo 的回调属于本次路由记录,不能以当前页面或提交栈代替完成。 */
|
|
40380
|
+
navigateTo(operation, callbacks) {
|
|
40381
|
+
let state;
|
|
40382
|
+
try {
|
|
40383
|
+
state = this.pages.get(operation());
|
|
40384
|
+
} catch (error) {
|
|
40385
|
+
this.settle(callbacks, error);
|
|
40386
|
+
return;
|
|
40387
|
+
}
|
|
40388
|
+
if (!state || state.canceled) this.settle(callbacks, state?.canceled ?? /* @__PURE__ */ new Error("navigateTo:fail target page was unloaded"));
|
|
40389
|
+
else if (state.completed) this.settle(callbacks);
|
|
40390
|
+
else state.callbacks = callbacks;
|
|
40391
|
+
}
|
|
40392
|
+
commit(page, event) {
|
|
40393
|
+
const state = this.pages.get(page);
|
|
40394
|
+
if (!state || state.event !== event || this.activeEvent !== event || !this.isCurrentPage(page)) return;
|
|
40395
|
+
state.committed = true;
|
|
40396
|
+
this.bus.$emit("appRoute", event);
|
|
40397
|
+
if (state.ready) this.complete(page, state);
|
|
40398
|
+
}
|
|
40399
|
+
ready(page) {
|
|
40400
|
+
const state = this.pages.get(page);
|
|
40401
|
+
if (!state) return;
|
|
40402
|
+
state.ready = true;
|
|
40403
|
+
if (state.committed) this.complete(page, state);
|
|
40404
|
+
}
|
|
40405
|
+
complete(page, state) {
|
|
40406
|
+
this.requestRender(() => {
|
|
40407
|
+
if (state.completed || state.canceled || this.pages.get(page) !== state || this.activeEvent !== state.event || !this.isCurrentPage(page)) return;
|
|
40408
|
+
state.completed = true;
|
|
40409
|
+
const callbacks = state.callbacks;
|
|
40410
|
+
state.callbacks = void 0;
|
|
40411
|
+
try {
|
|
40412
|
+
this.bus.$emit("appRouteDone", state.event);
|
|
40413
|
+
} finally {
|
|
40414
|
+
this.settle(callbacks);
|
|
40415
|
+
}
|
|
40416
|
+
});
|
|
40417
|
+
}
|
|
40418
|
+
settle(callbacks, error) {
|
|
40419
|
+
try {
|
|
40420
|
+
if (error) callbacks?.fail?.(error);
|
|
40421
|
+
else callbacks?.success?.();
|
|
40422
|
+
} finally {
|
|
40423
|
+
callbacks?.complete?.();
|
|
40424
|
+
}
|
|
40425
|
+
}
|
|
40426
|
+
cancel(state, error) {
|
|
40427
|
+
if (!state || state.completed || state.canceled) return;
|
|
40428
|
+
state.canceled = error;
|
|
40429
|
+
const callbacks = state.callbacks;
|
|
40430
|
+
state.callbacks = void 0;
|
|
40431
|
+
this.settle(callbacks, error);
|
|
40432
|
+
}
|
|
40433
|
+
beforeUnload(page, event) {
|
|
40434
|
+
const state = this.pages.get(page);
|
|
40435
|
+
if (event && state) this.bus.$emit("beforePageUnload", {
|
|
40436
|
+
...event,
|
|
40437
|
+
path: page.route,
|
|
40438
|
+
query: { ...page.options },
|
|
40439
|
+
renderer: state.event.renderer,
|
|
40440
|
+
webviewId: state.webviewId,
|
|
40441
|
+
page
|
|
40442
|
+
});
|
|
40443
|
+
this.cancel(state, /* @__PURE__ */ new Error("navigateTo:fail target page was unloaded"));
|
|
40444
|
+
this.pages.delete(page);
|
|
40445
|
+
}
|
|
40446
|
+
close() {
|
|
40447
|
+
this.cancel(this.activeState, /* @__PURE__ */ new Error("navigateTo:fail runtime session closed"));
|
|
40448
|
+
this.activeState = void 0;
|
|
40449
|
+
this.activeEvent = void 0;
|
|
40450
|
+
this.bus.$off();
|
|
40451
|
+
}
|
|
40452
|
+
};
|
|
40453
|
+
//#endregion
|
|
39362
40454
|
//#region ../../mpcore/packages/simulator/src/runtime/systemInfo.ts
|
|
39363
40455
|
function createDefaultLocationResult() {
|
|
39364
40456
|
return {
|
|
@@ -40153,7 +41245,7 @@ function createHeadlessWxState(scheduler = nativeScheduler, options = {}) {
|
|
|
40153
41245
|
const readFile = (filePath, encoding) => {
|
|
40154
41246
|
normalizeEncoding(encoding);
|
|
40155
41247
|
const normalizedPath = normalizeFsPath(filePath);
|
|
40156
|
-
const fileContent = files.get(normalizedPath);
|
|
41248
|
+
const fileContent = files.get(normalizedPath) ?? options.readPackageFile?.(normalizedPath);
|
|
40157
41249
|
if (fileContent == null) throw new Error(`readFile:fail no such file or directory, open '${normalizedPath}'`);
|
|
40158
41250
|
return {
|
|
40159
41251
|
data: fileContent,
|
|
@@ -41201,6 +42293,7 @@ var HeadlessSession = class {
|
|
|
41201
42293
|
appInstance = null;
|
|
41202
42294
|
appLifecycle = new HeadlessAppLifecycle();
|
|
41203
42295
|
moduleLoader;
|
|
42296
|
+
routeEvents = new HeadlessRouteEvents((callback) => this.requestRender(callback), (page) => this.currentPageInstance === page && !this.kernel.isClosed);
|
|
41204
42297
|
eventListeners = /* @__PURE__ */ new Map();
|
|
41205
42298
|
registries;
|
|
41206
42299
|
currentPageInstance = null;
|
|
@@ -41228,7 +42321,10 @@ var HeadlessSession = class {
|
|
|
41228
42321
|
constructor(options) {
|
|
41229
42322
|
if (!options.project && !options.projectPath) throw new Error("Headless runtime requires projectPath or project.");
|
|
41230
42323
|
this.project = options.project ?? loadProject(options.projectPath);
|
|
41231
|
-
this.wxState = createHeadlessWxState(this.kernel.scheduler, {
|
|
42324
|
+
this.wxState = createHeadlessWxState(this.kernel.scheduler, {
|
|
42325
|
+
strictMocks: options.strictHostMocks,
|
|
42326
|
+
readPackageFile: createPackageAssetReader((relative) => this.project.artifactSource.readText(path.join(this.project.miniprogramRootPath, relative)))
|
|
42327
|
+
});
|
|
41232
42328
|
this.registries = createHostRegistries();
|
|
41233
42329
|
const rawTabBarList = Array.isArray(this.project.appConfig.tabBar?.list) ? this.project.appConfig.tabBar.list : [];
|
|
41234
42330
|
this.tabBarRoutes = /* @__PURE__ */ new Set();
|
|
@@ -41278,7 +42374,7 @@ var HeadlessSession = class {
|
|
|
41278
42374
|
getMenuButtonBoundingClientRect: () => deriveMenuButtonBoundingClientRect(this.systemInfo),
|
|
41279
42375
|
getNetworkType: () => this.wxState.getNetworkType(),
|
|
41280
42376
|
navigateBack: (option) => this.navigateBack(option?.delta),
|
|
41281
|
-
navigateTo: (option) => this.navigateTo(option.url),
|
|
42377
|
+
navigateTo: (option) => this.routeEvents.navigateTo(() => this.navigateTo(option.url), option),
|
|
41282
42378
|
pageScrollTo: (option) => this.pageScrollTo(option),
|
|
41283
42379
|
openDocument: (option) => this.wxState.openDocument(option),
|
|
41284
42380
|
clearStorageSync: () => this.wxState.clearStorageSync(),
|
|
@@ -41295,6 +42391,14 @@ var HeadlessSession = class {
|
|
|
41295
42391
|
saveImageToPhotosAlbum: (option) => this.wxState.saveImageToPhotosAlbum(option),
|
|
41296
42392
|
saveVideoToPhotosAlbum: (option) => this.wxState.saveVideoToPhotosAlbum(option),
|
|
41297
42393
|
nextTick: (callback) => this.kernel.scheduler.queueMicrotask(() => callback?.()),
|
|
42394
|
+
offBeforeAppRoute: (listener) => this.routeEvents.off("beforeAppRoute", listener),
|
|
42395
|
+
offBeforePageUnload: (listener) => this.routeEvents.off("beforePageUnload", listener),
|
|
42396
|
+
offAppRoute: (listener) => this.routeEvents.off("appRoute", listener),
|
|
42397
|
+
offAppRouteDone: (listener) => this.routeEvents.off("appRouteDone", listener),
|
|
42398
|
+
onBeforeAppRoute: (listener) => this.routeEvents.on("beforeAppRoute", listener),
|
|
42399
|
+
onBeforePageUnload: (listener) => this.routeEvents.on("beforePageUnload", listener),
|
|
42400
|
+
onAppRoute: (listener) => this.routeEvents.on("appRoute", listener),
|
|
42401
|
+
onAppRouteDone: (listener) => this.routeEvents.on("appRouteDone", listener),
|
|
41298
42402
|
offAppHide: (callback) => this.appLifecycle.offAppHide(callback),
|
|
41299
42403
|
offAppShow: (callback) => this.appLifecycle.offAppShow(callback),
|
|
41300
42404
|
offNetworkStatusChange: (callback) => this.wxState.offNetworkStatusChange(callback),
|
|
@@ -41347,15 +42451,28 @@ var HeadlessSession = class {
|
|
|
41347
42451
|
}
|
|
41348
42452
|
close() {
|
|
41349
42453
|
if (this.kernel.isClosed) return;
|
|
41350
|
-
|
|
42454
|
+
let failure;
|
|
42455
|
+
try {
|
|
42456
|
+
this.unloadAllPages(void 0, true);
|
|
42457
|
+
} catch (error) {
|
|
42458
|
+
failure = { error };
|
|
42459
|
+
}
|
|
41351
42460
|
this.canvasContexts.clear();
|
|
41352
42461
|
this.renderRequestCallbacks.length = 0;
|
|
41353
42462
|
this.renderRequestPending = false;
|
|
41354
42463
|
this.appLifecycle.close();
|
|
42464
|
+
this.routeEvents.close();
|
|
41355
42465
|
this.wxState.close();
|
|
41356
42466
|
this.moduleLoader.close();
|
|
41357
|
-
this.eventListeners.clear();
|
|
41358
42467
|
this.kernel.close();
|
|
42468
|
+
try {
|
|
42469
|
+
this.emit("close");
|
|
42470
|
+
} catch (error) {
|
|
42471
|
+
failure ??= { error };
|
|
42472
|
+
} finally {
|
|
42473
|
+
this.eventListeners.clear();
|
|
42474
|
+
}
|
|
42475
|
+
if (failure) throw failure.error;
|
|
41359
42476
|
}
|
|
41360
42477
|
getDiagnostics() {
|
|
41361
42478
|
return this.kernel.diagnostics.getEntries();
|
|
@@ -41372,7 +42489,14 @@ var HeadlessSession = class {
|
|
|
41372
42489
|
if (listeners?.size === 0) this.eventListeners.delete(eventName);
|
|
41373
42490
|
}
|
|
41374
42491
|
emit(eventName, ...args) {
|
|
41375
|
-
|
|
42492
|
+
let failure;
|
|
42493
|
+
for (const handler of [...this.eventListeners.get(eventName) ?? []]) try {
|
|
42494
|
+
handler(...args);
|
|
42495
|
+
} catch (error) {
|
|
42496
|
+
if (eventName !== "close") throw error;
|
|
42497
|
+
failure ??= { error };
|
|
42498
|
+
}
|
|
42499
|
+
if (failure) throw failure.error;
|
|
41376
42500
|
}
|
|
41377
42501
|
get isClosed() {
|
|
41378
42502
|
return this.kernel.isClosed;
|
|
@@ -41454,6 +42578,11 @@ var HeadlessSession = class {
|
|
|
41454
42578
|
selectOwnerComponent: (scopeId) => this.selectOwnerComponent(scopeId)
|
|
41455
42579
|
}
|
|
41456
42580
|
}, current);
|
|
42581
|
+
syncNativeScrollViews(current, rendered.root, (scopeId, method, event) => {
|
|
42582
|
+
this.kernel.scheduler.queueMicrotask(() => {
|
|
42583
|
+
if (this.currentPageInstance === current) this.callScopeMethod(scopeId, method, event);
|
|
42584
|
+
});
|
|
42585
|
+
});
|
|
41457
42586
|
const componentScopePrefix = `page:${stripLeadingSlash(current.route)}`;
|
|
41458
42587
|
for (const [scopeId, instance] of this.componentCache.entries()) if (scopeId.startsWith(componentScopePrefix)) this.selectorQueryScopeSnapshots.set(instance, {
|
|
41459
42588
|
page: current,
|
|
@@ -41462,6 +42591,11 @@ var HeadlessSession = class {
|
|
|
41462
42591
|
});
|
|
41463
42592
|
return rendered;
|
|
41464
42593
|
}
|
|
42594
|
+
dispatchNativeNodeEvent(node, eventName, event, onHandlerResult) {
|
|
42595
|
+
const current = this.requireCurrentPage("dispatchNativeNodeEvent()");
|
|
42596
|
+
if (eventName === "tap") this.renderCurrentPage();
|
|
42597
|
+
return dispatchNativeNodeEvent(current, node, eventName, event, this.moduleLoader.wx, (scopeId, method) => this.resolveScopeMethod(current, scopeId, method), onHandlerResult);
|
|
42598
|
+
}
|
|
41465
42599
|
requestRender(callback) {
|
|
41466
42600
|
this.assertActive();
|
|
41467
42601
|
if (callback) this.renderRequestCallbacks.push(callback);
|
|
@@ -41509,21 +42643,27 @@ var HeadlessSession = class {
|
|
|
41509
42643
|
callScopeMethod(scopeId, methodName, event) {
|
|
41510
42644
|
const current = this.requireCurrentPage(`scope method "${methodName}"`);
|
|
41511
42645
|
this.renderCurrentPage();
|
|
41512
|
-
|
|
42646
|
+
return this.resolveScopeMethod(current, scopeId, methodName)(event);
|
|
42647
|
+
}
|
|
42648
|
+
/** 解析本次交互的实例,后续回调不随重渲染或导航切换所有者。 */
|
|
42649
|
+
resolveScopeMethod(current, scopeId, methodName) {
|
|
42650
|
+
if (!scopeId || scopeId === `page:${stripLeadingSlash(current.route)}`) return (event) => {
|
|
41513
42651
|
const method = current[methodName];
|
|
41514
42652
|
if (typeof method !== "function") throw new TypeError(`Method "${methodName}" does not exist on headless page ${current.route}.`);
|
|
41515
42653
|
return method.call(current, event);
|
|
41516
|
-
}
|
|
42654
|
+
};
|
|
41517
42655
|
const instance = this.componentCache.get(scopeId);
|
|
41518
42656
|
if (!instance) throw new Error(`Unknown scope "${scopeId}" in headless runtime.`);
|
|
41519
|
-
|
|
41520
|
-
|
|
41521
|
-
|
|
41522
|
-
|
|
42657
|
+
return (event) => {
|
|
42658
|
+
instance.__lastInteractionEvent__ = {
|
|
42659
|
+
currentTarget: event.currentTarget,
|
|
42660
|
+
mark: event.mark,
|
|
42661
|
+
target: event.target
|
|
42662
|
+
};
|
|
42663
|
+
const method = instance[methodName];
|
|
42664
|
+
if (typeof method !== "function") throw new TypeError(`Method "${methodName}" does not exist on headless component scope "${scopeId}".`);
|
|
42665
|
+
return method.call(instance, event);
|
|
41523
42666
|
};
|
|
41524
|
-
const method = instance[methodName];
|
|
41525
|
-
if (typeof method !== "function") throw new TypeError(`Method "${methodName}" does not exist on headless component scope "${scopeId}".`);
|
|
41526
|
-
return method.call(instance, event);
|
|
41527
42667
|
}
|
|
41528
42668
|
callScopeMethodDirect(scopeId, methodName, ...args) {
|
|
41529
42669
|
const normalizedScopeId = scopeId.trim();
|
|
@@ -41549,19 +42689,19 @@ var HeadlessSession = class {
|
|
|
41549
42689
|
return this.wxState.getShareMenu();
|
|
41550
42690
|
}
|
|
41551
42691
|
selectComponent(selector) {
|
|
41552
|
-
this.renderCurrentPage();
|
|
42692
|
+
if (!hasPendingComponentConstruction(this.componentCache)) this.renderCurrentPage();
|
|
41553
42693
|
return this.selectComponentsWithin(null, selector)[0] ?? null;
|
|
41554
42694
|
}
|
|
41555
42695
|
selectAllComponents(selector) {
|
|
41556
|
-
this.renderCurrentPage();
|
|
42696
|
+
if (!hasPendingComponentConstruction(this.componentCache)) this.renderCurrentPage();
|
|
41557
42697
|
return this.selectComponentsWithin(null, selector);
|
|
41558
42698
|
}
|
|
41559
42699
|
selectComponentWithin(scopeId, selector) {
|
|
41560
|
-
this.renderCurrentPage();
|
|
42700
|
+
if (!hasPendingComponentConstruction(this.componentCache)) this.renderCurrentPage();
|
|
41561
42701
|
return this.selectComponentsWithin(scopeId, selector)[0] ?? null;
|
|
41562
42702
|
}
|
|
41563
42703
|
selectAllComponentsWithin(scopeId, selector) {
|
|
41564
|
-
this.renderCurrentPage();
|
|
42704
|
+
if (!hasPendingComponentConstruction(this.componentCache)) this.renderCurrentPage();
|
|
41565
42705
|
return this.selectComponentsWithin(scopeId, selector);
|
|
41566
42706
|
}
|
|
41567
42707
|
selectOwnerComponent(scopeId) {
|
|
@@ -41804,11 +42944,13 @@ var HeadlessSession = class {
|
|
|
41804
42944
|
const target = this.resolveNavigationTarget(url);
|
|
41805
42945
|
const launchedPage = this.bootstrapNavigation(createAppLaunchOptions(target.normalizedRoute, target.query));
|
|
41806
42946
|
if (launchedPage) return launchedPage;
|
|
41807
|
-
this.
|
|
41808
|
-
|
|
42947
|
+
const event = this.beginRoute(target, "reLaunch");
|
|
42948
|
+
this.unloadAllPages(event);
|
|
42949
|
+
const pageInstance = this.createFreshPage(target, event);
|
|
41809
42950
|
this.pages.push(pageInstance);
|
|
41810
42951
|
this.currentPageInstance = pageInstance;
|
|
41811
42952
|
this.runInitialPageLifecycles(pageInstance, target.query);
|
|
42953
|
+
this.routeEvents.commit(pageInstance, event);
|
|
41812
42954
|
return pageInstance;
|
|
41813
42955
|
}
|
|
41814
42956
|
navigateTo(url) {
|
|
@@ -41817,12 +42959,14 @@ var HeadlessSession = class {
|
|
|
41817
42959
|
if (launchedPage) return launchedPage;
|
|
41818
42960
|
if (this.pages.length >= PAGE_STACK_LIMIT) throw new Error(`Cannot navigateTo() beyond a ${PAGE_STACK_LIMIT}-page stack in headless runtime.`);
|
|
41819
42961
|
if (this.isTabBarRoute(target.routeRecord.route)) throw new Error(`wx.navigateTo() cannot open a tabBar page in headless runtime: ${url}`);
|
|
42962
|
+
const event = this.beginRoute(target, "navigateTo");
|
|
41820
42963
|
this.currentPageInstance?.onHide?.();
|
|
41821
42964
|
if (this.currentPageInstance) this.runPageComponentLifetime(this.currentPageInstance.route, "hide");
|
|
41822
|
-
const pageInstance = this.createFreshPage(target);
|
|
42965
|
+
const pageInstance = this.createFreshPage(target, event);
|
|
41823
42966
|
this.pages.push(pageInstance);
|
|
41824
42967
|
this.currentPageInstance = pageInstance;
|
|
41825
42968
|
this.runInitialPageLifecycles(pageInstance, target.query);
|
|
42969
|
+
this.routeEvents.commit(pageInstance, event);
|
|
41826
42970
|
return pageInstance;
|
|
41827
42971
|
}
|
|
41828
42972
|
redirectTo(url) {
|
|
@@ -41830,15 +42974,17 @@ var HeadlessSession = class {
|
|
|
41830
42974
|
const launchedPage = this.bootstrapNavigation(createAppLaunchOptions(target.normalizedRoute, target.query));
|
|
41831
42975
|
if (launchedPage) return launchedPage;
|
|
41832
42976
|
if (this.isTabBarRoute(target.routeRecord.route)) throw new Error(`wx.redirectTo() cannot open a tabBar page in headless runtime: ${url}`);
|
|
42977
|
+
const event = this.beginRoute(target, "redirectTo");
|
|
41833
42978
|
const current = this.currentPageInstance;
|
|
41834
42979
|
if (current) {
|
|
42980
|
+
this.unloadPage(current, event);
|
|
41835
42981
|
this.pages.pop();
|
|
41836
|
-
this.unloadPage(current);
|
|
41837
42982
|
}
|
|
41838
|
-
const pageInstance = this.createFreshPage(target);
|
|
42983
|
+
const pageInstance = this.createFreshPage(target, event);
|
|
41839
42984
|
this.pages.push(pageInstance);
|
|
41840
42985
|
this.currentPageInstance = pageInstance;
|
|
41841
42986
|
this.runInitialPageLifecycles(pageInstance, target.query);
|
|
42987
|
+
this.routeEvents.commit(pageInstance, event);
|
|
41842
42988
|
return pageInstance;
|
|
41843
42989
|
}
|
|
41844
42990
|
navigateBack(delta = 1) {
|
|
@@ -41846,12 +42992,17 @@ var HeadlessSession = class {
|
|
|
41846
42992
|
if (launchedPage) return launchedPage;
|
|
41847
42993
|
if (this.pages.length <= 1) return this.currentPageInstance;
|
|
41848
42994
|
const removableCount = Math.min(Number.isFinite(delta) ? Math.max(1, Math.trunc(delta)) : 1, this.pages.length - 1);
|
|
41849
|
-
const
|
|
41850
|
-
|
|
41851
|
-
|
|
42995
|
+
const nextPage = this.pages[this.pages.length - removableCount - 1];
|
|
42996
|
+
const event = this.routeEvents.begin(nextPage.route, nextPage.options, "navigateBack", void 0, nextPage);
|
|
42997
|
+
this.routeEvents.attach(nextPage, event);
|
|
42998
|
+
for (let index = 0; index < removableCount; index++) {
|
|
42999
|
+
this.unloadPage(this.pages.at(-1), event);
|
|
43000
|
+
this.pages.pop();
|
|
43001
|
+
}
|
|
41852
43002
|
this.currentPageInstance = nextPage;
|
|
41853
43003
|
nextPage?.onShow?.();
|
|
41854
43004
|
if (nextPage) this.runPageComponentLifetime(nextPage.route, "show");
|
|
43005
|
+
this.routeEvents.commit(nextPage, event);
|
|
41855
43006
|
return nextPage;
|
|
41856
43007
|
}
|
|
41857
43008
|
switchTab(url) {
|
|
@@ -41869,6 +43020,8 @@ var HeadlessSession = class {
|
|
|
41869
43020
|
const current = this.currentPageInstance;
|
|
41870
43021
|
const cachedTarget = this.tabPages.get(target.routeRecord.route) ?? null;
|
|
41871
43022
|
if (current === cachedTarget && current) return () => {};
|
|
43023
|
+
const event = this.beginRoute(target, "switchTab", cachedTarget ?? void 0);
|
|
43024
|
+
if (cachedTarget) this.routeEvents.attach(cachedTarget, event);
|
|
41872
43025
|
if (current && current !== cachedTarget) {
|
|
41873
43026
|
current.onHide?.();
|
|
41874
43027
|
this.runPageComponentLifetime(current.route, "hide");
|
|
@@ -41878,7 +43031,7 @@ var HeadlessSession = class {
|
|
|
41878
43031
|
let nextPage = cachedTarget;
|
|
41879
43032
|
let shouldRunInitialLifecycles = false;
|
|
41880
43033
|
if (!nextPage) {
|
|
41881
|
-
nextPage = this.createFreshPage(target);
|
|
43034
|
+
nextPage = this.createFreshPage(target, event);
|
|
41882
43035
|
this.tabPages.set(target.routeRecord.route, nextPage);
|
|
41883
43036
|
shouldRunInitialLifecycles = true;
|
|
41884
43037
|
}
|
|
@@ -41891,7 +43044,8 @@ var HeadlessSession = class {
|
|
|
41891
43044
|
this.runPageComponentLifetime(nextPage.route, "show");
|
|
41892
43045
|
}
|
|
41893
43046
|
return () => {
|
|
41894
|
-
for (const page of pagesToUnload) this.unloadPage(page);
|
|
43047
|
+
for (const page of pagesToUnload) this.unloadPage(page, event);
|
|
43048
|
+
this.routeEvents.commit(nextPage, event);
|
|
41895
43049
|
};
|
|
41896
43050
|
}
|
|
41897
43051
|
pageScrollTo(option) {
|
|
@@ -41986,17 +43140,20 @@ var HeadlessSession = class {
|
|
|
41986
43140
|
windowInfo: this.getWindowInfo()
|
|
41987
43141
|
});
|
|
41988
43142
|
}
|
|
41989
|
-
|
|
43143
|
+
beginRoute(target, openType, page) {
|
|
43144
|
+
return this.routeEvents.begin(target.normalizedRoute, target.query, openType, target.pageConfig?.renderer ?? this.project.appConfig.renderer, page);
|
|
43145
|
+
}
|
|
43146
|
+
createFreshPage(target, event) {
|
|
41990
43147
|
const resourcePath = target.routeRecord.resourcePath ?? target.routeRecord.route;
|
|
41991
43148
|
const pageModulePath = path.resolve(this.project.miniprogramRootPath, `${resourcePath}.js`);
|
|
41992
|
-
const pageConfigPath = path.resolve(this.project.miniprogramRootPath, `${resourcePath}.json`);
|
|
41993
43149
|
const pageDefinition = this.moduleLoader.executePageModule(pageModulePath, target.routeRecord.route);
|
|
41994
|
-
const pageConfig =
|
|
43150
|
+
const pageConfig = target.pageConfig;
|
|
41995
43151
|
const pageInstance = createPageInstance(`/${target.routeRecord.route}`, pageDefinition, target.query, {
|
|
41996
43152
|
background: resolveBackgroundSnapshot(this.project.appConfig, pageConfig),
|
|
41997
43153
|
navigationBar: resolveNavigationBarSnapshot(this.project.appConfig, pageConfig),
|
|
41998
43154
|
requestRender: (callback) => this.requestRender(callback)
|
|
41999
43155
|
});
|
|
43156
|
+
this.routeEvents.attach(pageInstance, event);
|
|
42000
43157
|
pageInstance.createIntersectionObserver = (options) => this.createIntersectionObserver(pageInstance, options);
|
|
42001
43158
|
pageInstance.createMediaQueryObserver = () => this.createMediaQueryObserver(pageInstance);
|
|
42002
43159
|
pageInstance.selectComponent = (selector) => resolveNativeComponentSelection(this.selectComponent(selector));
|
|
@@ -42009,7 +43166,7 @@ var HeadlessSession = class {
|
|
|
42009
43166
|
return pageInstance;
|
|
42010
43167
|
}
|
|
42011
43168
|
runInitialPageLifecycles(pageInstance, query) {
|
|
42012
|
-
runInitialPageLifecycles(pageInstance, query, this.kernel.scheduler, () => this.pages.includes(pageInstance) || this.tabPages.get(pageInstance.route) === pageInstance, () => this.renderCurrentPage());
|
|
43169
|
+
runInitialPageLifecycles(pageInstance, query, this.kernel.scheduler, () => this.pages.includes(pageInstance) || this.tabPages.get(pageInstance.route) === pageInstance, () => this.renderCurrentPage(), () => this.routeEvents.ready(pageInstance));
|
|
42013
43170
|
}
|
|
42014
43171
|
isTabBarRoute(route) {
|
|
42015
43172
|
return this.tabBarRoutes.has(stripLeadingSlash(route));
|
|
@@ -42038,24 +43195,45 @@ var HeadlessSession = class {
|
|
|
42038
43195
|
return {
|
|
42039
43196
|
normalizedRoute,
|
|
42040
43197
|
query,
|
|
42041
|
-
routeRecord
|
|
43198
|
+
routeRecord,
|
|
43199
|
+
pageConfig: readJsonObject(this.project, path.resolve(this.project.miniprogramRootPath, `${routeRecord.resourcePath ?? routeRecord.route}.json`))
|
|
42042
43200
|
};
|
|
42043
43201
|
}
|
|
42044
|
-
unloadAllPages() {
|
|
43202
|
+
unloadAllPages(event, closing = false) {
|
|
42045
43203
|
const pagesToUnload = /* @__PURE__ */ new Set([...this.pages, ...this.tabPages.values()]);
|
|
42046
|
-
|
|
43204
|
+
let failure;
|
|
43205
|
+
for (const page of [...pagesToUnload].reverse()) try {
|
|
43206
|
+
this.unloadPage(page, event, closing);
|
|
43207
|
+
} catch (error) {
|
|
43208
|
+
if (!closing) throw error;
|
|
43209
|
+
failure ??= { error };
|
|
43210
|
+
}
|
|
42047
43211
|
this.pages.length = 0;
|
|
42048
43212
|
this.tabPages.clear();
|
|
42049
43213
|
this.componentCache.clear();
|
|
42050
43214
|
this.componentScopes.clear();
|
|
42051
43215
|
this.clearMediaQueryObservers();
|
|
42052
43216
|
this.currentPageInstance = null;
|
|
43217
|
+
if (failure) throw failure.error;
|
|
42053
43218
|
}
|
|
42054
|
-
unloadPage(page) {
|
|
42055
|
-
|
|
43219
|
+
unloadPage(page, event, closing = false) {
|
|
43220
|
+
let failure;
|
|
43221
|
+
try {
|
|
43222
|
+
this.routeEvents.beforeUnload(page, event);
|
|
43223
|
+
page.onUnload?.();
|
|
43224
|
+
} catch (error) {
|
|
43225
|
+
if (!closing) throw error;
|
|
43226
|
+
failure = { error };
|
|
43227
|
+
}
|
|
42056
43228
|
this.clearMediaQueryObservers(page);
|
|
42057
|
-
|
|
43229
|
+
try {
|
|
43230
|
+
this.detachPageComponents(page.route, closing);
|
|
43231
|
+
} catch (error) {
|
|
43232
|
+
if (!closing) throw error;
|
|
43233
|
+
failure ??= { error };
|
|
43234
|
+
}
|
|
42058
43235
|
this.tabPages.delete(stripLeadingSlash(page.route));
|
|
43236
|
+
if (failure) throw failure.error;
|
|
42059
43237
|
}
|
|
42060
43238
|
requireCurrentPage(action) {
|
|
42061
43239
|
this.bootstrap();
|
|
@@ -42083,15 +43261,27 @@ var HeadlessSession = class {
|
|
|
42083
43261
|
const directlyOwned = rootScopeId ? matches.filter(([, scope]) => scope.ownerScopeId === rootScopeId) : [];
|
|
42084
43262
|
return (directlyOwned.length > 0 ? directlyOwned : matches).map(([candidateScopeId]) => this.componentCache.get(candidateScopeId)).filter(Boolean);
|
|
42085
43263
|
}
|
|
42086
|
-
detachPageComponents(route) {
|
|
43264
|
+
detachPageComponents(route, closing = false) {
|
|
42087
43265
|
const prefix = `page:${stripLeadingSlash(route)}`;
|
|
42088
43266
|
const removed = [...this.componentCache].filter(([scopeId]) => scopeId.startsWith(prefix));
|
|
42089
|
-
|
|
42090
|
-
|
|
43267
|
+
let failure;
|
|
43268
|
+
for (const [, instance] of removed) try {
|
|
43269
|
+
runComponentLifecycle(instance, "detached");
|
|
43270
|
+
} catch (error) {
|
|
43271
|
+
if (!closing) throw error;
|
|
43272
|
+
failure ??= { error };
|
|
43273
|
+
}
|
|
43274
|
+
try {
|
|
43275
|
+
detachComponentRelations(removed.map(([, instance]) => instance));
|
|
43276
|
+
} catch (error) {
|
|
43277
|
+
if (!closing) throw error;
|
|
43278
|
+
failure ??= { error };
|
|
43279
|
+
}
|
|
42091
43280
|
for (const [scopeId] of removed) {
|
|
42092
43281
|
this.componentCache.delete(scopeId);
|
|
42093
43282
|
this.componentScopes.delete(scopeId);
|
|
42094
43283
|
}
|
|
43284
|
+
if (failure) throw failure.error;
|
|
42095
43285
|
}
|
|
42096
43286
|
notifyMediaQueryObservers() {
|
|
42097
43287
|
for (const entry of [...this.mediaQueryObservers]) {
|
|
@@ -42251,6 +43441,8 @@ function createPageRootNodeHandle(options) {
|
|
|
42251
43441
|
const tree = session?.getCurrentPages().includes(page) ? session.renderCurrentPage() : renderPageTree(project, page);
|
|
42252
43442
|
return new HeadlessTestingNodeHandle(tree.root.type === "root" && !options.includeHostRoots ? tree.root.children?.[0] ?? tree.root : tree.root, {
|
|
42253
43443
|
assertActive: options.assertActive,
|
|
43444
|
+
projectionOnly: options.projectionOnly,
|
|
43445
|
+
dispatchNativeEvent: (node, eventName, event, onHandlerResult) => session?.getCurrentPages().at(-1) === page ? session.dispatchNativeNodeEvent(node, eventName, event, onHandlerResult) : false,
|
|
42254
43446
|
callMethod: (scopeId, methodName, event) => {
|
|
42255
43447
|
if (session?.getCurrentPages().includes(page)) return session.callScopeMethod(scopeId, methodName, event);
|
|
42256
43448
|
const method = page[methodName];
|
|
@@ -42612,7 +43804,18 @@ var HeadlessTestingPageHandle = class {
|
|
|
42612
43804
|
renderedAccess() {
|
|
42613
43805
|
return {
|
|
42614
43806
|
assertActive: () => this.assertActive(),
|
|
42615
|
-
findAll: async (selector) =>
|
|
43807
|
+
findAll: async (selector) => {
|
|
43808
|
+
this.assertActive();
|
|
43809
|
+
const root = createPageRootNodeHandle({
|
|
43810
|
+
assertActive: () => this.assertActive(),
|
|
43811
|
+
createPageHandle: () => this,
|
|
43812
|
+
page: this.page,
|
|
43813
|
+
project: this.project,
|
|
43814
|
+
projectionOnly: true,
|
|
43815
|
+
session: this.session
|
|
43816
|
+
});
|
|
43817
|
+
return selector === "page" ? [root] : await root.$$(selector);
|
|
43818
|
+
},
|
|
42616
43819
|
waitFor: async (ms) => await this.waitFor(ms),
|
|
42617
43820
|
wxml: async () => await this.wxml()
|
|
42618
43821
|
};
|
|
@@ -42690,11 +43893,19 @@ var HeadlessTestingScopeHandle = class HeadlessTestingScopeHandle {
|
|
|
42690
43893
|
var HeadlessTestingSessionHandle = class {
|
|
42691
43894
|
project;
|
|
42692
43895
|
session;
|
|
43896
|
+
pendingNavigations = /* @__PURE__ */ new Set();
|
|
42693
43897
|
constructor(project, session) {
|
|
42694
43898
|
this.project = project;
|
|
42695
43899
|
this.session = session;
|
|
43900
|
+
this.session.on("close", () => {
|
|
43901
|
+
for (const reject of this.pendingNavigations) reject(/* @__PURE__ */ new Error("The headless testing session closed before navigation completed."));
|
|
43902
|
+
});
|
|
42696
43903
|
}
|
|
42697
43904
|
async close() {
|
|
43905
|
+
this.disconnect();
|
|
43906
|
+
}
|
|
43907
|
+
/** 无头会话独占本地 runtime;同步释放与 automator 的连接清理契约一致。 */
|
|
43908
|
+
disconnect() {
|
|
42698
43909
|
this.session.close();
|
|
42699
43910
|
}
|
|
42700
43911
|
async toolInfo() {
|
|
@@ -42734,6 +43945,10 @@ var HeadlessTestingSessionHandle = class {
|
|
|
42734
43945
|
const normalizedMethodName = normalizeNonEmptyInput(methodName, "wx method name");
|
|
42735
43946
|
return await runWithTimeout(() => this.session.callWxMethod(normalizedMethodName, ...args), options.timeout, `Timed out calling wx.${normalizedMethodName} in headless testing runtime.`);
|
|
42736
43947
|
}
|
|
43948
|
+
/** 返回与 DevTools automator 兼容的运行时系统信息。 */
|
|
43949
|
+
async systemInfo() {
|
|
43950
|
+
return await this.callWxMethod("getSystemInfoSync");
|
|
43951
|
+
}
|
|
42737
43952
|
async scopeSnapshot(scopeId) {
|
|
42738
43953
|
const normalizedScopeId = normalizeNonEmptyInput(scopeId, "Scope id");
|
|
42739
43954
|
return this.session.getScopeSnapshot(normalizedScopeId);
|
|
@@ -42761,23 +43976,23 @@ var HeadlessTestingSessionHandle = class {
|
|
|
42761
43976
|
}, `Timed out waiting for ${normalizedCount} component(s) matching "${normalizedSelector}" in headless testing runtime.`, options);
|
|
42762
43977
|
}
|
|
42763
43978
|
async reLaunch(route) {
|
|
42764
|
-
this.invokeNavigation("reLaunch", { url: route });
|
|
43979
|
+
await this.invokeNavigation("reLaunch", { url: route });
|
|
42765
43980
|
return await this.requireCurrentPage("reLaunch", route);
|
|
42766
43981
|
}
|
|
42767
43982
|
async navigateTo(route) {
|
|
42768
|
-
this.invokeNavigation("navigateTo", { url: route });
|
|
43983
|
+
await this.invokeNavigation("navigateTo", { url: route });
|
|
42769
43984
|
return await this.requireCurrentPage("navigateTo", route);
|
|
42770
43985
|
}
|
|
42771
43986
|
async redirectTo(route) {
|
|
42772
|
-
this.invokeNavigation("redirectTo", { url: route });
|
|
43987
|
+
await this.invokeNavigation("redirectTo", { url: route });
|
|
42773
43988
|
return await this.requireCurrentPage("redirectTo", route);
|
|
42774
43989
|
}
|
|
42775
43990
|
async navigateBack(delta = 1) {
|
|
42776
|
-
this.invokeNavigation("navigateBack", { delta });
|
|
43991
|
+
await this.invokeNavigation("navigateBack", { delta });
|
|
42777
43992
|
return await this.currentPage();
|
|
42778
43993
|
}
|
|
42779
43994
|
async switchTab(route) {
|
|
42780
|
-
this.invokeNavigation("switchTab", { url: route });
|
|
43995
|
+
await this.invokeNavigation("switchTab", { url: route });
|
|
42781
43996
|
return await this.requireCurrentPage("switchTab", route);
|
|
42782
43997
|
}
|
|
42783
43998
|
async waitForCurrentPage(route, options = {}) {
|
|
@@ -42814,21 +44029,30 @@ var HeadlessTestingSessionHandle = class {
|
|
|
42814
44029
|
this.session.triggerRouteDone(options);
|
|
42815
44030
|
}
|
|
42816
44031
|
invokeNavigation(methodName, options) {
|
|
42817
|
-
|
|
42818
|
-
|
|
42819
|
-
|
|
42820
|
-
|
|
42821
|
-
|
|
42822
|
-
|
|
44032
|
+
return new Promise((resolve, reject) => {
|
|
44033
|
+
const rejectNavigation = (error) => {
|
|
44034
|
+
this.pendingNavigations.delete(rejectNavigation);
|
|
44035
|
+
reject(error);
|
|
44036
|
+
};
|
|
44037
|
+
this.pendingNavigations.add(rejectNavigation);
|
|
44038
|
+
try {
|
|
44039
|
+
this.session.callWxMethod(methodName, {
|
|
44040
|
+
...options,
|
|
44041
|
+
success: () => {
|
|
44042
|
+
this.pendingNavigations.delete(rejectNavigation);
|
|
44043
|
+
resolve();
|
|
44044
|
+
},
|
|
44045
|
+
fail: (error) => {
|
|
44046
|
+
const details = error && typeof error === "object" ? error : void 0;
|
|
44047
|
+
const reason = typeof details?.errMsg === "string" ? details.errMsg : typeof details?.message === "string" ? details.message : String(error);
|
|
44048
|
+
const target = options.url ? ` route "${options.url}"` : "";
|
|
44049
|
+
rejectNavigation(new Error(`Failed to ${methodName}${target} through the headless wx runtime: ${reason}`, { cause: error }));
|
|
44050
|
+
}
|
|
44051
|
+
});
|
|
44052
|
+
} catch (error) {
|
|
44053
|
+
rejectNavigation(error);
|
|
42823
44054
|
}
|
|
42824
44055
|
});
|
|
42825
|
-
if (failure.received) {
|
|
42826
|
-
const error = failure.error;
|
|
42827
|
-
const details = error && typeof error === "object" ? error : void 0;
|
|
42828
|
-
const reason = typeof details?.errMsg === "string" ? details.errMsg : typeof details?.message === "string" ? details.message : String(error);
|
|
42829
|
-
const target = options.url ? ` route "${options.url}"` : "";
|
|
42830
|
-
throw new Error(`Failed to ${methodName}${target} through the headless wx runtime: ${reason}`, { cause: error });
|
|
42831
|
-
}
|
|
42832
44056
|
}
|
|
42833
44057
|
async requireCurrentPage(methodName, route) {
|
|
42834
44058
|
const page = await this.currentPage();
|