@weapp-vite/miniprogram-automator 1.2.23 → 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.
@@ -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, attach) {
950
- definitionAttachments.set(definition, attach);
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.call(instance);
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 ../../node_modules/.pnpm/@babel+parser@8.0.6/node_modules/@babel/parser/lib/index.js
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) node.id = this.parseFunctionId();
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();
@@ -12913,7 +13318,8 @@ function isComponentAttached(instance) {
12913
13318
  return attachmentStates.get(instance) === "attached";
12914
13319
  }
12915
13320
  function hasPendingComponentAttachments(instances) {
12916
- return [...instances].some((instance) => attachmentStates.get(instance) === "attaching");
13321
+ for (const instance of instances) if (attachmentStates.get(instance) === "attaching") return true;
13322
+ return false;
12917
13323
  }
12918
13324
  function flushComponentReady(instances, ready) {
12919
13325
  const all = [...instances];
@@ -12923,19 +13329,71 @@ function flushComponentReady(instances, ready) {
12923
13329
  for (const instance of pending) ready(instance);
12924
13330
  return pending.length > 0;
12925
13331
  }
12926
- function flushComponentAttachments(instances, attach) {
12927
- const pending = [...instances].filter((instance) => !attachmentStates.has(instance));
12928
- for (const instance of pending) attachmentStates.set(instance, "attaching");
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");
12929
13342
  try {
12930
- for (const instance of pending) try {
12931
- attach(instance);
12932
- } finally {
12933
- attachmentStates.set(instance, "attached");
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";
12934
13352
  }
12935
13353
  } finally {
12936
- 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);
13355
+ }
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);
12937
13391
  }
12938
- return pending.length > 0;
13392
+ }
13393
+ function finishComponentConstruction(componentCache, instance) {
13394
+ constructingInstances.delete(instance);
13395
+ const state = constructionStates.get(componentCache);
13396
+ if (--state.depth === 0) constructionStates.delete(componentCache);
12939
13397
  }
12940
13398
  //#endregion
12941
13399
  //#region ../../mpcore/packages/simulator/src/runtime/componentInstance/discardedReady.ts
@@ -12958,6 +13416,35 @@ function flushDiscardedComponentReady(cache, pageScopeId, ready) {
12958
13416
  if (!pending.size) discardedReady.delete(cache);
12959
13417
  }
12960
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
12961
13448
  //#region ../../mpcore/packages/simulator/src/runtime/pageLifecycle.ts
12962
13449
  const loadingPages = /* @__PURE__ */ new WeakSet();
12963
13450
  function isPageBeforeReady(page) {
@@ -13046,98 +13533,6 @@ function createPluginArtifactSource(fallback, miniprogramRootPath, plugins) {
13046
13533
  readText: (filePath) => fallback.readText(resolvePhysicalPath(filePath))
13047
13534
  };
13048
13535
  }
13049
- //#endregion
13050
- //#region ../../mpcore/packages/simulator/src/view/nodeDataset.ts
13051
- const DATASET_NAME_RE = /-([a-z])/g;
13052
- function isDatasetAttribute(attributeName) {
13053
- return attributeName.startsWith("data-") && !attributeName.startsWith("data-sim-");
13054
- }
13055
- function toDatasetKey(attributeName) {
13056
- return attributeName.slice(5).replace(DATASET_NAME_RE, (_match, char) => char.toUpperCase());
13057
- }
13058
- function collectNodeDataset(node) {
13059
- if (node.dataset) return { ...node.dataset };
13060
- const dataset = {};
13061
- for (const [key, value] of Object.entries(node.attribs ?? {})) if (isDatasetAttribute(key)) dataset[toDatasetKey(key)] = value;
13062
- return dataset;
13063
- }
13064
- //#endregion
13065
- //#region ../../mpcore/packages/simulator/src/view/eventBinding.ts
13066
- const LEGACY_EVENT_NAME_RE = /^\w+$/;
13067
- const EVENT_BINDING_PREFIXES = [
13068
- {
13069
- prefix: "bind:",
13070
- priority: 0,
13071
- stopAfter: false
13072
- },
13073
- {
13074
- prefix: "catch:",
13075
- priority: 0,
13076
- stopAfter: true
13077
- },
13078
- {
13079
- prefix: "bind",
13080
- priority: 1,
13081
- stopAfter: false
13082
- },
13083
- {
13084
- prefix: "catch",
13085
- priority: 1,
13086
- stopAfter: true
13087
- }
13088
- ];
13089
- function parseEventBinding(attributeName, method) {
13090
- const definition = EVENT_BINDING_PREFIXES.find((item) => attributeName.startsWith(item.prefix));
13091
- if (!definition) return null;
13092
- const eventName = attributeName.slice(definition.prefix.length);
13093
- if (!eventName || !definition.prefix.endsWith(":") && !LEGACY_EVENT_NAME_RE.test(eventName)) return null;
13094
- return {
13095
- eventName,
13096
- method,
13097
- priority: definition.priority,
13098
- stopAfter: definition.stopAfter
13099
- };
13100
- }
13101
- function collectMiniProgramEventBindings(attributes = {}) {
13102
- const bindings = /* @__PURE__ */ new Map();
13103
- const priorities = /* @__PURE__ */ new Map();
13104
- for (const [attributeName, method] of Object.entries(attributes)) {
13105
- const candidate = parseEventBinding(attributeName, method);
13106
- if (!candidate || candidate.priority < (priorities.get(candidate.eventName) ?? -1)) continue;
13107
- bindings.set(candidate.eventName, {
13108
- method: candidate.method,
13109
- stopAfter: candidate.stopAfter
13110
- });
13111
- priorities.set(candidate.eventName, candidate.priority);
13112
- }
13113
- return bindings;
13114
- }
13115
- function resolveMiniProgramEventBinding(attributes, eventName) {
13116
- return collectMiniProgramEventBindings(attributes).get(eventName) ?? null;
13117
- }
13118
- /** 冒泡时保留原始 target,currentTarget 则属于当前执行绑定的节点。 */
13119
- function createMiniProgramEventPayload(node, eventName, event, origin = node) {
13120
- const dataset = collectNodeDataset(node);
13121
- const nodeId = node.attribs?.id ?? "";
13122
- const isOrigin = node === origin;
13123
- return {
13124
- bubbles: false,
13125
- capturePhase: false,
13126
- composed: false,
13127
- currentTarget: {
13128
- dataset: isOrigin ? event.currentTarget?.dataset ?? event.dataset ?? dataset : dataset,
13129
- id: isOrigin ? event.currentTarget?.id ?? event.id ?? nodeId : nodeId
13130
- },
13131
- detail: event.detail,
13132
- mark: event.mark,
13133
- target: {
13134
- dataset: event.target?.dataset ?? event.dataset ?? (isOrigin ? dataset : collectNodeDataset(origin)),
13135
- id: event.target?.id ?? event.id ?? origin.attribs?.id ?? ""
13136
- },
13137
- timeStamp: Date.now(),
13138
- type: eventName
13139
- };
13140
- }
13141
13536
  const WEVU_NATIVE_INSTANCE_KEY = "__wevuNativeInstance";
13142
13537
  /** 宿主值为 1 时,实现带 routeEventId 的路由阶段契约;不代表微信 SDK 版本。 */
13143
13538
  const WEVU_ROUTE_EVENT_CONTRACT_KEY = "__wevuRouteEventContract";
@@ -17140,9 +17535,10 @@ function maskTemplateAttributes(source) {
17140
17535
  const INLINE_WXS_RE = /<wxs\b([^>]*)(?<!\/)>([\s\S]*?)<\/wxs\s*>/g;
17141
17536
  function parseWxsTemplateDocument(templateSource) {
17142
17537
  const scripts = [];
17143
- const masked = maskTemplateAttributes(templateSource.replace(INLINE_WXS_RE, (_match, attributes, script) => {
17538
+ const source = templateSource.replace(INLINE_WXS_RE, (_match, attributes, script) => {
17144
17539
  return `<wxs data-sim-wxs="${scripts.push(script) - 1}"${attributes}>wxs-source</wxs>`;
17145
- }));
17540
+ });
17541
+ const masked = maskTemplateAttributes(source);
17146
17542
  const document = parseDocument(`<page>${masked.source}</page>`, {
17147
17543
  xmlMode: false,
17148
17544
  decodeEntities: false,
@@ -17150,7 +17546,7 @@ function parseWxsTemplateDocument(templateSource) {
17150
17546
  lowerCaseTags: false,
17151
17547
  recognizeSelfClosing: true
17152
17548
  });
17153
- if (!scripts.length && !masked.attributes.size) return document;
17549
+ if (!scripts.length && !masked.attributes.size && !/[\r\n]/.test(source)) return document;
17154
17550
  const hasAttributes = masked.attributes.size > 0;
17155
17551
  const restore = (node) => {
17156
17552
  if (hasAttributes && node.attribs) {
@@ -17159,7 +17555,9 @@ function parseWxsTemplateDocument(templateSource) {
17159
17555
  if (node.name === "wxs" && Object.hasOwn(node.attribs ?? {}, "data-sim-wxs")) {
17160
17556
  node.children[0].data = scripts[Number(node.attribs["data-sim-wxs"])];
17161
17557
  delete node.attribs["data-sim-wxs"];
17558
+ return;
17162
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 ?? ""));
17163
17561
  for (const child of node.children ?? []) restore(child);
17164
17562
  };
17165
17563
  restore(document);
@@ -17362,67 +17760,6 @@ function resolveComponentGenerics(context, hostNode, ownerJsonPath, ownerFilePat
17362
17760
  }
17363
17761
  return resolved.size > 0 ? resolved : void 0;
17364
17762
  }
17365
- function collectComponentEventBindings(hostNode) {
17366
- return collectMiniProgramEventBindings(hostNode.attribs);
17367
- }
17368
- function buildComponentTrigger(componentScopeId, context, hostNode) {
17369
- const hostDataset = collectDataset(hostNode);
17370
- const hostId = hostNode.attribs?.id ?? "";
17371
- return (instance, eventName, detail, triggerOptions) => {
17372
- const originScope = context.componentScopes.get(componentScopeId);
17373
- const interactionMark = instance.__lastInteractionEvent__?.mark;
17374
- const target = {
17375
- dataset: originScope?.dataset ?? hostDataset,
17376
- id: originScope?.hostId ?? hostId
17377
- };
17378
- let currentScopeId = componentScopeId;
17379
- while (currentScopeId) {
17380
- const currentScope = context.componentScopes.get(currentScopeId);
17381
- const binding = currentScope?.eventBindings?.get(eventName);
17382
- const listenerScope = currentScope?.listenerScopeId ? context.componentScopes.get(currentScope.listenerScopeId) : null;
17383
- const handler = binding && listenerScope ? listenerScope.getMethod(binding.method) : void 0;
17384
- if (handler) handler({
17385
- bubbles: triggerOptions?.bubbles ?? false,
17386
- capturePhase: false,
17387
- composed: triggerOptions?.composed ?? false,
17388
- currentTarget: {
17389
- dataset: currentScope?.dataset ?? hostDataset,
17390
- id: currentScope?.hostId ?? hostId
17391
- },
17392
- detail,
17393
- mark: interactionMark,
17394
- target,
17395
- type: eventName
17396
- });
17397
- if (binding?.stopAfter) break;
17398
- if (!triggerOptions?.bubbles || !triggerOptions?.composed) break;
17399
- currentScopeId = currentScope?.ownerScopeId;
17400
- }
17401
- };
17402
- }
17403
- function syncComponentProperties(instance, definition, nextProperties, bindingExpressions, changedPageKeys) {
17404
- const changedRootKeys = [];
17405
- const previousProperties = {};
17406
- for (const [key, value] of Object.entries(nextProperties)) {
17407
- const nextValue = normalizeComponentPropertyValue(definition, key, value);
17408
- const bindingExpression = bindingExpressions[key];
17409
- const bindingAffected = !!bindingExpression && changedPageKeys.some((changedKey) => {
17410
- return changedKey === bindingExpression || changedKey.startsWith(`${bindingExpression}.`) || changedKey.startsWith(`${bindingExpression}[`);
17411
- });
17412
- const previousSnapshot = instance.__propertySnapshots?.[key];
17413
- if (hasComponentPropertyValueChanged(instance.properties[key], previousSnapshot, nextValue, bindingAffected)) {
17414
- previousProperties[key] = instance.properties[key];
17415
- const deliveredValue = cloneValue$1(nextValue);
17416
- instance.properties[key] = deliveredValue;
17417
- if (Object.hasOwn(definition.properties ?? {}, key)) instance.data[key] = deliveredValue;
17418
- changedRootKeys.push(key);
17419
- }
17420
- instance.__propertySnapshots ??= {};
17421
- instance.__propertySnapshots[key] = cloneValue$1(nextValue);
17422
- }
17423
- if (changedRootKeys.length === 0) return;
17424
- runComponentObservers(definition, instance, changedRootKeys, previousProperties);
17425
- }
17426
17763
  function createComponentScope(clonedNode, scope, componentScopeId, componentInstance, genericComponents, slots) {
17427
17764
  const ownerScopeId = scope.getScopeId().includes("/") ? scope.getScopeId() : void 0;
17428
17765
  return {
@@ -17430,7 +17767,7 @@ function createComponentScope(clonedNode, scope, componentScopeId, componentInst
17430
17767
  classList: String(clonedNode.attribs?.class ?? "").split(CLASS_SPLIT_RE).map((item) => item.trim()).filter(Boolean),
17431
17768
  data: { ...componentInstance.data },
17432
17769
  dataset: collectDataset(clonedNode, wxsScopeData(scope)),
17433
- eventBindings: collectComponentEventBindings(clonedNode),
17770
+ ...bindComponentEventHost(clonedNode),
17434
17771
  getMethod: (methodName) => {
17435
17772
  const method = componentInstance?.[methodName];
17436
17773
  return typeof method === "function" ? method.bind(componentInstance) : void 0;
@@ -17438,6 +17775,7 @@ function createComponentScope(clonedNode, scope, componentScopeId, componentInst
17438
17775
  getScopeId: () => componentScopeId,
17439
17776
  genericComponents,
17440
17777
  hostId: typeof clonedNode.attribs?.id === "string" ? clonedNode.attribs.id : void 0,
17778
+ hostNode: clonedNode,
17441
17779
  id: typeof clonedNode.attribs?.id === "string" ? clonedNode.attribs.id : void 0,
17442
17780
  listenerScopeId: scope.getScopeId(),
17443
17781
  ownerScopeId,
@@ -17460,13 +17798,14 @@ function resolveComponentProperties(clonedNode, scope, definition) {
17460
17798
  bindingExpressions
17461
17799
  };
17462
17800
  }
17463
- function createRuntimeComponentInstance(componentScopeId, context, clonedNode, componentEntry, nextProperties, ownerScopeId, beforeCreated) {
17801
+ function createRuntimeComponentInstance(componentScopeId, context, componentEntry, nextProperties, ownerScopeId, renderTemplate, bindEventsBeforeProperties = false) {
17464
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;
17465
- const componentInstance = createComponentInstance({
17803
+ const instanceOptions = {
17466
17804
  definition: componentEntry.definition,
17467
- requestRender: (callback) => context.session.requestRender(callback),
17468
- triggerEvent: buildComponentTrigger(componentScopeId, context, clonedNode)
17469
- });
17805
+ requestRender: (callback) => context.session.requestRender(callback)
17806
+ };
17807
+ const componentInstance = createComponentInstance(instanceOptions);
17808
+ bindAttachmentBindingScope(componentInstance, context);
17470
17809
  setSelectorQueryScopeId(componentInstance, componentScopeId);
17471
17810
  componentInstance.is = componentEntry.filePath.replace(JS_FILE_RE, "");
17472
17811
  componentInstance.createIntersectionObserver = (options) => context.session.createIntersectionObserver(componentInstance, options);
@@ -17474,19 +17813,29 @@ function createRuntimeComponentInstance(componentScopeId, context, clonedNode, c
17474
17813
  componentInstance.createSelectorQuery = () => context.moduleLoader.wx.createSelectorQuery().in(componentInstance);
17475
17814
  componentInstance.selectComponent = (selector) => resolveNativeComponentSelection(context.session.selectComponentWithin(componentScopeId, selector));
17476
17815
  componentInstance.selectAllComponents = (selector) => context.session.selectAllComponentsWithin(componentScopeId, selector).map(resolveNativeComponentSelection);
17477
- componentInstance.selectOwnerComponent = () => ownerScopeId ? context.componentCache.get(ownerScopeId) ?? null : null;
17816
+ componentInstance.selectOwnerComponent = () => ownerScopeId ? resolveNativeComponentSelection(context.componentCache.get(ownerScopeId)) : null;
17817
+ beginComponentConstruction(context.componentCache, componentScopeId, componentInstance);
17478
17818
  context.componentCache.set(componentScopeId, componentInstance);
17479
- beforeCreated?.(componentInstance);
17480
- runComponentLifecycle(componentInstance, "created");
17481
- componentInstance.__propertySnapshots = Object.fromEntries(Object.entries(componentInstance.properties).map(([key, propertyValue]) => [key, cloneValue$1(propertyValue)]));
17482
- syncComponentProperties(componentInstance, componentInstance.__definition__ ?? componentEntry.definition, componentProperties, {}, []);
17483
- return componentInstance;
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
+ }
17484
17833
  }
17485
- function renderRuntimeComponentTemplate(context, componentEntry, renderNodeTree, componentScope, componentScopeId, seenComponentScopes) {
17834
+ function renderRuntimeComponentTemplate(context, componentEntry, renderNodeTree, componentScope, componentScopeId, renderPass) {
17486
17835
  const componentDocument = parseTemplateDocument(readTemplateSource(context.artifactSource, componentEntry.absoluteTemplatePath));
17487
17836
  const componentRoot = (componentDocument.children ?? [])[0] ?? componentDocument;
17488
17837
  const templateRenderState = prepareTemplateRenderState(context.artifactSource, componentRoot, componentEntry.absoluteTemplatePath, context.project.miniprogramRootPath, getRuntimeWxsLoader(context.moduleLoader, context.artifactSource));
17489
- return renderNodeTree(componentRoot, componentScope, context, path.resolve(context.project.miniprogramRootPath, `${componentEntry.filePath.replace(JS_FILE_RE, "")}.json`), componentEntry.filePath, componentScopeId, seenComponentScopes, templateRenderState);
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);
17490
17839
  }
17491
17840
  //#endregion
17492
17841
  //#region ../../mpcore/packages/simulator/src/runtime/render/index.ts
@@ -17500,24 +17849,33 @@ function expandNodeByFor(node, scope) {
17500
17849
  const items = resolveLoopEntries(resolveRawValueByPath$1(wxsScopeData(scope), forExpression));
17501
17850
  const itemName = node.attribs?.["wx:for-item"]?.trim() || "item";
17502
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;
17503
17854
  return items.map(([index, item]) => ({
17504
17855
  node: cloneNode(node),
17505
17856
  scope: createLoopScope(scope, itemName, indexName, item, index),
17506
- instanceSuffix: `:for-${index}`
17857
+ instanceSuffix: resolveLoopInstanceSuffix(item, index, key, keyOccurrences)
17507
17858
  }));
17508
17859
  }
17509
- function renderNodeVariants(node, scope, context, ownerJsonPath, ownerFilePath, instancePath, seenComponentScopes, templateRenderState) {
17510
- return expandNodeByFor(node, scope).map(({ node: expandedNode, scope: expandedScope, instanceSuffix }) => renderNodeTree(expandedNode, expandedScope, context, ownerJsonPath, ownerFilePath, `${instancePath}${instanceSuffix}`, seenComponentScopes, templateRenderState));
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;
17511
17868
  }
17512
- function renderChildren(children, scope, context, ownerJsonPath, ownerFilePath, instancePath, seenComponentScopes, templateRenderState) {
17869
+ function renderChildren(children, scope, context, ownerJsonPath, ownerFilePath, instancePath, renderPass, templateRenderState, parent) {
17513
17870
  const renderedChildren = [];
17514
17871
  for (const { node: child, index } of selectConditionalChildren(children, (node) => evaluateConditionalBranch(node, scope))) {
17872
+ if (isRenderPassStale(renderPass)) break;
17515
17873
  if (!isTagNode(child)) {
17516
- renderedChildren.push(...renderNodeVariants(child, scope, context, ownerJsonPath, ownerFilePath, `${instancePath}/node-${index}`, seenComponentScopes, templateRenderState));
17874
+ renderedChildren.push(...renderNodeVariants(child, scope, context, ownerJsonPath, ownerFilePath, `${instancePath}/node-${index}`, renderPass, templateRenderState, parent));
17517
17875
  continue;
17518
17876
  }
17519
17877
  if (isTemplateDefinition(child) || child.name === "import" || child.name === "wxs") continue;
17520
- renderedChildren.push(...renderNodeVariants(child, scope, context, ownerJsonPath, ownerFilePath, `${instancePath}/node-${index}`, seenComponentScopes, templateRenderState));
17878
+ renderedChildren.push(...renderNodeVariants(child, scope, context, ownerJsonPath, ownerFilePath, `${instancePath}/node-${index}`, renderPass, templateRenderState, parent));
17521
17879
  }
17522
17880
  return renderedChildren;
17523
17881
  }
@@ -17549,22 +17907,38 @@ function collectComponentSlots(componentNode, scope, ownerJsonPath, ownerFilePat
17549
17907
  }
17550
17908
  return slots;
17551
17909
  }
17552
- function renderNodeTree(node, scope, context, ownerJsonPath, ownerFilePath, instancePath, seenComponentScopes, templateRenderState) {
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;
17553
17926
  if (scope.wxs !== templateRenderState.wxsModules) scope = {
17554
17927
  ...scope,
17555
17928
  wxs: templateRenderState.wxsModules
17556
17929
  };
17557
17930
  const clonedNode = cloneNode(node);
17931
+ registerComponentEventNode(clonedNode, scope, parent);
17558
17932
  if (!isTagNode(clonedNode)) {
17559
17933
  applyNodeBindings(clonedNode, scope);
17560
17934
  return clonedNode;
17561
17935
  }
17562
- if (isTemplateDefinition(clonedNode)) return {
17563
- type: "tag",
17564
- name: "block",
17565
- attribs: {},
17566
- children: []
17567
- };
17936
+ if (isTemplateDefinition(clonedNode)) {
17937
+ clonedNode.name = "block";
17938
+ clonedNode.attribs = {};
17939
+ clonedNode.children = [];
17940
+ return clonedNode;
17941
+ }
17568
17942
  const templateName = resolveTemplateCall(clonedNode, wxsScopeData(scope));
17569
17943
  if (templateName) {
17570
17944
  const definition = templateRenderState.definitions.get(templateName);
@@ -17573,39 +17947,42 @@ function renderNodeTree(node, scope, context, ownerJsonPath, ownerFilePath, inst
17573
17947
  data: resolveTemplateData(clonedNode, wxsScopeData(scope)),
17574
17948
  wxs: definition && templateRenderState.definitionWxsScopes?.get(definition)
17575
17949
  };
17576
- const children = definition && !templateRenderState.stack.includes(templateName) ? renderChildren(definition.children ?? [], templateScope, context, ownerJsonPath, ownerFilePath, `${instancePath}/template-${templateName}`, seenComponentScopes, {
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, {
17577
17956
  definitions: templateRenderState.definitionScopes?.get(definition) ?? templateRenderState.definitions,
17578
17957
  definitionScopes: templateRenderState.definitionScopes,
17579
17958
  definitionWxsScopes: templateRenderState.definitionWxsScopes,
17580
17959
  wxsModules: templateScope.wxs,
17581
17960
  stack: [...templateRenderState.stack, templateName]
17582
- }) : [];
17583
- return {
17584
- type: "tag",
17585
- name: "block",
17586
- attribs: {
17587
- "data-sim-node": instancePath,
17588
- "data-sim-scope": scope.getScopeId()
17589
- },
17590
- children
17591
- };
17961
+ }, clonedNode) : [];
17962
+ return clonedNode;
17592
17963
  }
17593
17964
  if (clonedNode.name === "slot") {
17594
17965
  const slotName = clonedNode.attribs?.name?.trim() || "default";
17595
17966
  const projected = scope.slots?.get(slotName) ?? [];
17596
- const children = projected.length ? selectConditionalChildren(projected.map((entry) => entry.node), (node, index) => evaluateConditionalBranch(node, projected[index].scope)).flatMap(({ index }) => {
17597
- const entry = projected[index];
17598
- return renderNodeVariants(entry.node, entry.scope, context, entry.ownerJsonPath, entry.ownerFilePath, entry.instancePath, seenComponentScopes, entry.templateRenderState);
17599
- }) : renderChildren(clonedNode.children ?? [], scope, context, ownerJsonPath, ownerFilePath, `${instancePath}/slot-fallback-${slotName}`, seenComponentScopes, templateRenderState);
17600
- return {
17601
- type: "tag",
17602
- name: "block",
17603
- attribs: {
17604
- "data-sim-node": instancePath,
17605
- "data-sim-scope": scope.getScopeId()
17606
- },
17607
- 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()
17608
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;
17609
17986
  }
17610
17987
  const componentEntry = resolveComponentRegistryEntry(context, ownerJsonPath, ownerFilePath, clonedNode.name, scope.genericComponents?.get(clonedNode.name));
17611
17988
  if (componentEntry) {
@@ -17614,19 +17991,31 @@ function renderNodeTree(node, scope, context, ownerJsonPath, ownerFilePath, inst
17614
17991
  const { nextProperties, bindingExpressions } = resolveComponentProperties(clonedNode, scope, componentEntry.definition);
17615
17992
  const genericComponents = resolveComponentGenerics(context, clonedNode, ownerJsonPath, ownerFilePath, componentEntry.filePath, scope.genericComponents);
17616
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
+ };
17617
18006
  let componentInstance = context.componentCache.get(componentScopeId);
17618
- if (!componentInstance) componentInstance = createRuntimeComponentInstance(componentScopeId, context, clonedNode, componentEntry, nextProperties, ownerScopeId, (instance) => {
17619
- const initialScope = createComponentScope(clonedNode, scope, componentScopeId, instance, genericComponents, slots);
17620
- context.componentScopes.set(componentScopeId, initialScope);
17621
- renderRuntimeComponentTemplate(context, componentEntry, renderNodeTree, initialScope, componentScopeId, /* @__PURE__ */ new Set());
17622
- });
17623
- else syncComponentProperties(componentInstance, componentInstance.__definition__ ?? componentEntry.definition, nextProperties, bindingExpressions, context.changedPageKeys);
17624
- 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);
17625
18014
  const componentScope = createComponentScope(clonedNode, scope, componentScopeId, componentInstance, genericComponents, slots);
17626
18015
  context.componentScopes.set(componentScopeId, componentScope);
17627
- const renderedComponentRoot = renderRuntimeComponentTemplate(context, componentEntry, renderNodeTree, componentScope, componentScopeId, seenComponentScopes);
18016
+ const renderedComponentRoot = renderRuntimeComponentTemplate(context, componentEntry, renderNodeTree, componentScope, componentScopeId, renderPass);
18017
+ mergeComponentEventRoot(renderedComponentRoot, clonedNode);
17628
18018
  if (renderedComponentRoot.attribs) {
17629
- applyNodeBindings(clonedNode, scope);
17630
18019
  renderedComponentRoot.attribs = {
17631
18020
  ...clonedNode.attribs,
17632
18021
  ...renderedComponentRoot.attribs
@@ -17639,66 +18028,99 @@ function renderNodeTree(node, scope, context, ownerJsonPath, ownerFilePath, inst
17639
18028
  renderedComponentRoot.attribs["data-sim-node"] = instancePath;
17640
18029
  renderedComponentRoot.attribs["data-sim-scope"] = componentScopeId;
17641
18030
  }
18031
+ renderPass.componentRoots.set(componentScopeId, renderedComponentRoot);
17642
18032
  return renderedComponentRoot;
17643
18033
  }
17644
18034
  applyNodeBindings(clonedNode, scope);
17645
18035
  clonedNode.attribs["data-sim-node"] = instancePath;
17646
- clonedNode.children = renderChildren(clonedNode.children ?? [], scope, context, ownerJsonPath, ownerFilePath, `${instancePath}/${clonedNode.name}`, seenComponentScopes, templateRenderState);
18036
+ clonedNode.children = renderChildren(clonedNode.children ?? [], scope, context, ownerJsonPath, ownerFilePath, `${instancePath}/${clonedNode.name}`, renderPass, templateRenderState, clonedNode);
17647
18037
  return clonedNode;
17648
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
+ }
17649
18120
  function renderRuntimePageTree(context, page) {
17650
- const route = page.route.replace(LEADING_SLASH_RE$4, "");
17651
- const resourcePath = context.project.routes.find((item) => item.route === route)?.resourcePath ?? route;
17652
- const templatePath = path.resolve(context.project.miniprogramRootPath, `${resourcePath}.wxml`);
17653
- const document = parseTemplateDocument(readTemplateSource(context.artifactSource, templatePath));
17654
- const pageScopeId = `page:${route}`;
17655
- const pageScope = {
17656
- data: page.data,
17657
- getMethod: (methodName) => {
17658
- const method = page[methodName];
17659
- return typeof method === "function" ? method.bind(page) : void 0;
17660
- },
17661
- getScopeId: () => pageScopeId,
17662
- id: "page-root"
17663
- };
17664
- for (const scopeId of context.componentScopes.keys()) if (scopeId === pageScopeId || scopeId.startsWith(`${pageScopeId}/`)) context.componentScopes.delete(scopeId);
17665
- context.componentScopes.set(pageScopeId, pageScope);
17666
- const seenComponentScopes = /* @__PURE__ */ new Set();
17667
- const root = (document.children ?? [])[0] ?? document;
17668
- const templateRenderState = prepareTemplateRenderState(context.artifactSource, root, templatePath, context.project.miniprogramRootPath, getRuntimeWxsLoader(context.moduleLoader, context.artifactSource));
17669
- const renderedRoot = renderNodeTree(root, pageScope, context, path.resolve(context.project.miniprogramRootPath, `${resourcePath}.json`), `${resourcePath}.js`, pageScopeId, seenComponentScopes, templateRenderState);
17670
- const roots = [renderedRoot];
17671
- if (hasCustomTabBar(context.project.appConfig, route)) roots.push(renderNodeTree({
17672
- type: "tag",
17673
- name: "custom-tab-bar",
17674
- attribs: {},
17675
- children: []
17676
- }, customTabBarHostScope(), context, path.resolve(context.project.miniprogramRootPath, "app.json"), "app.js", pageScopeId, seenComponentScopes, templateRenderState));
17677
- const pageAttached = attachComponentPage(page);
17678
- const pageAttaching = isComponentPageAttaching(page);
17679
- const attached = !pageAttaching && flushComponentAttachments([...seenComponentScopes].map((scopeId) => context.componentCache.get(scopeId)), (instance) => runComponentLifecycle(instance, "attached"));
17680
- const removed = [...context.componentCache].filter(([scopeId]) => scopeId.startsWith(`${pageScopeId}/`) && !seenComponentScopes.has(scopeId));
17681
- for (const [scopeId, instance] of removed) if (isComponentAttached(instance)) runComponentLifecycle(instance, "detached");
17682
- else scheduleDiscardedComponentReady(context.componentCache, scopeId, instance);
17683
- const instances = [...seenComponentScopes].map((scopeId) => context.componentCache.get(scopeId));
17684
- const relationsChanged = !pageAttaching && !hasPendingComponentAttachments(instances) && syncComponentRelations(context.componentCache, seenComponentScopes, pageScopeId);
17685
- for (const [scopeId] of removed) {
17686
- context.componentCache.delete(scopeId);
17687
- context.componentScopes.delete(scopeId);
17688
- }
17689
- if (pageAttached || attached || relationsChanged) return renderRuntimePageTree(context, page);
17690
- if (!isPageBeforeReady(page)) flushDiscardedComponentReady(context.componentCache, pageScopeId, (instance) => runComponentLifecycle(instance, "ready"));
17691
- if (!isPageBeforeReady(page) && flushComponentReady(instances, (instance) => {
17692
- runComponentLifecycle(instance, "ready");
17693
- if (instance !== context.componentCache.get(customTabBarScopeId(route))) instance.__definition__?.pageLifetimes?.show?.call(instance);
17694
- })) return renderRuntimePageTree(context, page);
17695
- const treeRoot = roots.length > 1 ? {
17696
- type: "root",
17697
- children: roots
17698
- } : renderedRoot;
17699
- linkRenderedParents(treeRoot);
18121
+ const { root, roots } = reconcileRuntimePageTree(context, page);
17700
18122
  return {
17701
- root: treeRoot,
18123
+ root,
17702
18124
  wxml: roots.map(serializeDomNode$1).join("")
17703
18125
  };
17704
18126
  }
@@ -17722,15 +18144,23 @@ function createDuplicateRegistrationError(kind, id) {
17722
18144
  function callDefinitionMethod(instance, method, args) {
17723
18145
  if (typeof method === "function") return method.apply(instance, args);
17724
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
+ }
17725
18155
  function normalizeComponentPageDefinition(definition) {
17726
18156
  const { lifetimes = {}, methods = {}, pageLifetimes = {}, ...rest } = definition;
17727
18157
  const created = lifetimes.created ?? rest.created;
17728
18158
  const attached = lifetimes.attached ?? rest.attached;
17729
- const ready = lifetimes.ready ?? rest.ready ?? methods.onReady ?? rest.onReady;
18159
+ const ready = composeDefinitionMethods(lifetimes.ready ?? rest.ready, methods.onReady ?? rest.onReady);
17730
18160
  const detached = lifetimes.detached ?? rest.detached ?? methods.onUnload ?? rest.onUnload;
17731
18161
  const load = methods.onLoad ?? rest.onLoad;
17732
- const show = pageLifetimes.show ?? methods.onShow ?? rest.onShow;
17733
- const hide = pageLifetimes.hide ?? methods.onHide ?? rest.onHide;
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;
17734
18164
  const resize = pageLifetimes.resize ?? methods.onResize ?? rest.onResize;
17735
18165
  const routeDone = pageLifetimes.routeDone ?? methods.onRouteDone ?? rest.onRouteDone;
17736
18166
  const pageDefinition = {
@@ -17758,10 +18188,7 @@ function normalizeComponentPageDefinition(definition) {
17758
18188
  return callDefinitionMethod(this, routeDone, args);
17759
18189
  }
17760
18190
  };
17761
- registerComponentPageAttachment(pageDefinition, function() {
17762
- callDefinitionMethod(this, created, []);
17763
- callDefinitionMethod(this, attached, []);
17764
- });
18191
+ registerComponentPageAttachment(pageDefinition, (instance) => callDefinitionMethod(instance, created, []), (instance) => callDefinitionMethod(instance, attached, []));
17765
18192
  return pageDefinition;
17766
18193
  }
17767
18194
  function createHostRegistries() {
@@ -17952,7 +18379,7 @@ function resolveCapabilityValue(source, schema) {
17952
18379
  }
17953
18380
  return current;
17954
18381
  }
17955
- function createHeadlessWx(driver, runtimeConsole = console) {
18382
+ function createHeadlessWx(driver, runtimeConsole = console, workers) {
17956
18383
  const eventBus = createHeadlessUniEventBus();
17957
18384
  const rpx2px = (value) => {
17958
18385
  const width = driver.getWindowInfoSync().windowWidth;
@@ -17960,6 +18387,10 @@ function createHeadlessWx(driver, runtimeConsole = console) {
17960
18387
  };
17961
18388
  const capabilityTree = {
17962
18389
  canIUse: true,
18390
+ ...workers ? {
18391
+ createWorker: true,
18392
+ preDownloadSubpackage: true
18393
+ } : {},
17963
18394
  canvasToTempFilePath: true,
17964
18395
  chooseImage: { return: {
17965
18396
  errMsg: true,
@@ -18217,6 +18648,14 @@ function createHeadlessWx(driver, runtimeConsole = console) {
18217
18648
  ...eventBus,
18218
18649
  [WEVU_ROUTE_EVENT_CONTRACT_KEY]: 1,
18219
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
+ },
18220
18659
  canIUse: (schema) => typeof schema === "string" && schema.trim() !== "" && resolveCapabilityValue(capabilityTree, schema.trim()) != null,
18221
18660
  canvasToTempFilePath: (option) => invokeWxApi(() => driver.canvasToTempFilePath(option), option),
18222
18661
  chooseImage: (option) => invokeWxApi(() => driver.chooseImage(option ?? {}), option),
@@ -18340,9 +18779,7 @@ function createHeadlessWx(driver, runtimeConsole = console) {
18340
18779
  navigateBack: (option) => runNavigationApi(driver, option, (deferred) => invokeWxApi(() => {
18341
18780
  driver.navigateBack(deferred);
18342
18781
  }, deferred)),
18343
- navigateTo: (option) => runNavigationApi(driver, option, (deferred) => invokeWxApi(() => {
18344
- driver.navigateTo(deferred);
18345
- }, deferred)),
18782
+ navigateTo: (option) => runNavigationApi(driver, option, (deferred) => driver.navigateTo(deferred)),
18346
18783
  nextTick: (callback) => driver.nextTick(callback),
18347
18784
  offBeforeAppRoute: (listener) => driver.offBeforeAppRoute(listener),
18348
18785
  offBeforePageUnload: (listener) => driver.offBeforePageUnload(listener),
@@ -18798,6 +19235,34 @@ function loadProject(projectPath) {
18798
19235
  });
18799
19236
  }
18800
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
18801
19266
  //#region ../../node_modules/.pnpm/boolbase@2.0.0/node_modules/boolbase/dist/index.js
18802
19267
  function trueFunc() {
18803
19268
  return true;
@@ -20224,23 +20689,31 @@ function appendNextSiblings(element, adapter) {
20224
20689
  const selectAll = getSelectorFunction((query, elements, options) => query === falseFunc || !elements || elements.length === 0 ? [] : findAll(query, elements, options));
20225
20690
  //#endregion
20226
20691
  //#region ../../mpcore/packages/simulator/src/view/selectors.ts
20227
- const adapter = {
20228
- isTag: (node) => node.type === "tag",
20229
- getAttributeValue: (node, name) => node.attribs?.[name],
20230
- getChildren: (node) => node.children ?? [],
20231
- getName: (node) => node.name ?? "",
20232
- getParent: (node) => node.parent ?? null,
20233
- getSiblings: (node) => node.parent?.children ?? [node],
20234
- getText: (node) => node.type === "text" ? node.data ?? "" : (node.children ?? []).map((child) => adapter.getText(child)).join(""),
20235
- hasAttrib: (node, name) => Object.hasOwn(node.attribs ?? {}, name),
20236
- removeSubsets: (nodes) => {
20237
- const unique = new Set(nodes);
20238
- return [...unique].filter((node) => {
20239
- for (let parent = node.parent; parent; parent = parent.parent) if (unique.has(parent)) return false;
20240
- return true;
20241
- });
20242
- }
20243
- };
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);
20244
20717
  function resolveComponentTags(selectors) {
20245
20718
  return selectors.map((group) => group.map((token) => {
20246
20719
  if (token.type === SelectorType.Pseudo && Array.isArray(token.data)) return {
@@ -20263,7 +20736,7 @@ function resolveComponentTags(selectors) {
20263
20736
  };
20264
20737
  }));
20265
20738
  }
20266
- function querySelectorAll(root, selector) {
20739
+ function queryWithAdapter(root, selector, adapter) {
20267
20740
  const parsed = parse$1(selector);
20268
20741
  const query = resolveComponentTags(parsed);
20269
20742
  const includesPageRoot = root.type === "tag" && root.name === "page" && parsed.some((group) => group.some((token) => token.type === SelectorType.Tag && token.name === "page"));
@@ -20272,6 +20745,12 @@ function querySelectorAll(root, selector) {
20272
20745
  cacheResults: false
20273
20746
  });
20274
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
+ }
20275
20754
  //#endregion
20276
20755
  //#region ../../node_modules/.pnpm/whynot@5.0.0/node_modules/whynot/dist/whynot.umd.cjs
20277
20756
  var require_whynot_umd = /* @__PURE__ */ __commonJSMin(((exports, module) => {
@@ -38019,7 +38498,10 @@ var import_fontoxpath = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin((
38019
38498
  });
38020
38499
  })))(), 1);
38021
38500
  const xpathEngine = Reflect.get(import_fontoxpath, "default") ?? import_fontoxpath;
38022
- function wrapNode(source, parent = null) {
38501
+ function getRenderedChildren(node) {
38502
+ return node.children ?? [];
38503
+ }
38504
+ function wrapNode(source, getChildren, parent = null) {
38023
38505
  const node = {
38024
38506
  attributes: [],
38025
38507
  children: [],
@@ -38042,7 +38524,7 @@ function wrapNode(source, parent = null) {
38042
38524
  prefix: null,
38043
38525
  value
38044
38526
  }));
38045
- node.children = (source.children ?? []).map((child) => wrapNode(child, node));
38527
+ node.children = getChildren(source).map((child) => wrapNode(child, getChildren, node));
38046
38528
  return node;
38047
38529
  }
38048
38530
  function sibling(node, offset) {
@@ -38062,14 +38544,20 @@ const domFacade = {
38062
38544
  getParentNode: (node) => node.parent,
38063
38545
  getPreviousSibling: (node) => sibling(node, -1)
38064
38546
  };
38065
- function queryXPathElements(root, expression) {
38547
+ function queryElements(root, expression, getChildren) {
38066
38548
  if (!expression.trim()) throw new Error("XPath must be a non-empty expression in headless testing runtime.");
38067
38549
  const document = wrapNode(root.type === "root" ? root : {
38068
38550
  type: "root",
38069
38551
  children: [root]
38070
- });
38552
+ }, getChildren);
38071
38553
  return xpathEngine.evaluateXPathToNodes(expression, document, domFacade).filter((node) => node.nodeType === 1).map((node) => node.source);
38072
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
+ }
38073
38561
  //#endregion
38074
38562
  //#region ../../mpcore/packages/simulator/src/view/nodeHandle.ts
38075
38563
  function escapeText$1(text) {
@@ -38116,7 +38604,7 @@ var HeadlessTestingNodeHandle = class HeadlessTestingNodeHandle {
38116
38604
  this.interactions?.assertActive?.();
38117
38605
  }
38118
38606
  query(selector) {
38119
- const nodes = querySelectorAll(this.node, selector);
38607
+ const nodes = (this.interactions?.projectionOnly ? querySelectorAll : queryInspectionSelectorAll)(this.node, selector);
38120
38608
  const scope = this.node.attribs?.["data-sim-scope"];
38121
38609
  if (!scope || !this.interactions) return nodes;
38122
38610
  return nodes.filter((node) => {
@@ -38135,7 +38623,7 @@ var HeadlessTestingNodeHandle = class HeadlessTestingNodeHandle {
38135
38623
  }
38136
38624
  async getElementsByXpath(expression) {
38137
38625
  this.assertActive();
38138
- 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));
38139
38627
  }
38140
38628
  async attr(name) {
38141
38629
  this.assertActive();
@@ -39416,6 +39904,136 @@ var HeadlessAppLifecycle = class {
39416
39904
  }
39417
39905
  };
39418
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
39419
40037
  //#region ../../mpcore/packages/simulator/src/runtime/runtimeGlobals.ts
39420
40038
  const MINI_PROGRAM_UNAVAILABLE_GLOBAL_NAMES = [
39421
40039
  "window",
@@ -39558,9 +40176,9 @@ function transformEsmToCommonJs(source) {
39558
40176
  function createRequireNotFoundError(request, importer) {
39559
40177
  return /* @__PURE__ */ new Error(`Cannot resolve require("${request}") from ${normalize(importer)} in headless runtime.`);
39560
40178
  }
39561
- function createExecutionContext(registries, getCurrentPages, getApp, wxDriver, kernel, globals, onConsole) {
40179
+ function createExecutionContext(registries, getCurrentPages, getApp, wxDriver, kernel, globals, onConsole, workers) {
39562
40180
  const runtimeConsole = kernel.diagnostics.createConsole(console, onConsole);
39563
- const wx = createHeadlessWx(wxDriver, runtimeConsole);
40181
+ const wx = createHeadlessWx(wxDriver, runtimeConsole, workers);
39564
40182
  return createMiniProgramRuntimeGlobals({
39565
40183
  App(definition) {
39566
40184
  return registerAppDefinition(registries, definition);
@@ -39593,8 +40211,19 @@ function createExecutionContext(registries, getCurrentPages, getApp, wxDriver, k
39593
40211
  }, globals);
39594
40212
  }
39595
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
+ });
39596
40225
  const moduleCache = /* @__PURE__ */ new Map();
39597
- 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);
39598
40227
  executionContext.globalThis = executionContext;
39599
40228
  function executeModule(filePath, loadContext) {
39600
40229
  const resolvedPath = path.resolve(filePath);
@@ -39653,6 +40282,7 @@ function createModuleLoader(registries, getCurrentPages, getApp, wxDriver, optio
39653
40282
  return callback(...args);
39654
40283
  },
39655
40284
  close() {
40285
+ workers.close();
39656
40286
  moduleCache.clear();
39657
40287
  closeRuntimeWxsLoader(loader);
39658
40288
  },
@@ -39707,6 +40337,7 @@ var HeadlessRouteEvents = class {
39707
40337
  nextRouteId = 0;
39708
40338
  nextWebviewId = 0;
39709
40339
  activeEvent;
40340
+ activeState;
39710
40341
  constructor(requestRender, isCurrentPage) {
39711
40342
  this.requestRender = requestRender;
39712
40343
  this.isCurrentPage = isCurrentPage;
@@ -39718,6 +40349,7 @@ var HeadlessRouteEvents = class {
39718
40349
  this.bus.$off(name, listener);
39719
40350
  }
39720
40351
  begin(path, query, openType, renderer, page) {
40352
+ this.cancel(this.activeState, /* @__PURE__ */ new Error("navigateTo:fail navigation was superseded"));
39721
40353
  const previous = page ? this.pages.get(page) : void 0;
39722
40354
  const event = {
39723
40355
  path,
@@ -39734,12 +40366,28 @@ var HeadlessRouteEvents = class {
39734
40366
  }
39735
40367
  attach(page, event) {
39736
40368
  const state = this.pages.get(page);
39737
- this.pages.set(page, {
40369
+ const next = {
39738
40370
  webviewId: event.webviewId,
39739
40371
  ready: state?.ready ?? false,
39740
40372
  event,
39741
- committed: false
39742
- });
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;
39743
40391
  }
39744
40392
  commit(page, event) {
39745
40393
  const state = this.pages.get(page);
@@ -39756,10 +40404,32 @@ var HeadlessRouteEvents = class {
39756
40404
  }
39757
40405
  complete(page, state) {
39758
40406
  this.requestRender(() => {
39759
- if (this.pages.get(page) !== state || this.activeEvent !== state.event || !this.isCurrentPage(page)) return;
39760
- this.bus.$emit("appRouteDone", state.event);
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
+ }
39761
40416
  });
39762
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
+ }
39763
40433
  beforeUnload(page, event) {
39764
40434
  const state = this.pages.get(page);
39765
40435
  if (event && state) this.bus.$emit("beforePageUnload", {
@@ -39770,9 +40440,12 @@ var HeadlessRouteEvents = class {
39770
40440
  webviewId: state.webviewId,
39771
40441
  page
39772
40442
  });
40443
+ this.cancel(state, /* @__PURE__ */ new Error("navigateTo:fail target page was unloaded"));
39773
40444
  this.pages.delete(page);
39774
40445
  }
39775
40446
  close() {
40447
+ this.cancel(this.activeState, /* @__PURE__ */ new Error("navigateTo:fail runtime session closed"));
40448
+ this.activeState = void 0;
39776
40449
  this.activeEvent = void 0;
39777
40450
  this.bus.$off();
39778
40451
  }
@@ -41701,7 +42374,7 @@ var HeadlessSession = class {
41701
42374
  getMenuButtonBoundingClientRect: () => deriveMenuButtonBoundingClientRect(this.systemInfo),
41702
42375
  getNetworkType: () => this.wxState.getNetworkType(),
41703
42376
  navigateBack: (option) => this.navigateBack(option?.delta),
41704
- navigateTo: (option) => this.navigateTo(option.url),
42377
+ navigateTo: (option) => this.routeEvents.navigateTo(() => this.navigateTo(option.url), option),
41705
42378
  pageScrollTo: (option) => this.pageScrollTo(option),
41706
42379
  openDocument: (option) => this.wxState.openDocument(option),
41707
42380
  clearStorageSync: () => this.wxState.clearStorageSync(),
@@ -42016,19 +42689,19 @@ var HeadlessSession = class {
42016
42689
  return this.wxState.getShareMenu();
42017
42690
  }
42018
42691
  selectComponent(selector) {
42019
- this.renderCurrentPage();
42692
+ if (!hasPendingComponentConstruction(this.componentCache)) this.renderCurrentPage();
42020
42693
  return this.selectComponentsWithin(null, selector)[0] ?? null;
42021
42694
  }
42022
42695
  selectAllComponents(selector) {
42023
- this.renderCurrentPage();
42696
+ if (!hasPendingComponentConstruction(this.componentCache)) this.renderCurrentPage();
42024
42697
  return this.selectComponentsWithin(null, selector);
42025
42698
  }
42026
42699
  selectComponentWithin(scopeId, selector) {
42027
- this.renderCurrentPage();
42700
+ if (!hasPendingComponentConstruction(this.componentCache)) this.renderCurrentPage();
42028
42701
  return this.selectComponentsWithin(scopeId, selector)[0] ?? null;
42029
42702
  }
42030
42703
  selectAllComponentsWithin(scopeId, selector) {
42031
- this.renderCurrentPage();
42704
+ if (!hasPendingComponentConstruction(this.componentCache)) this.renderCurrentPage();
42032
42705
  return this.selectComponentsWithin(scopeId, selector);
42033
42706
  }
42034
42707
  selectOwnerComponent(scopeId) {
@@ -42768,6 +43441,7 @@ function createPageRootNodeHandle(options) {
42768
43441
  const tree = session?.getCurrentPages().includes(page) ? session.renderCurrentPage() : renderPageTree(project, page);
42769
43442
  return new HeadlessTestingNodeHandle(tree.root.type === "root" && !options.includeHostRoots ? tree.root.children?.[0] ?? tree.root : tree.root, {
42770
43443
  assertActive: options.assertActive,
43444
+ projectionOnly: options.projectionOnly,
42771
43445
  dispatchNativeEvent: (node, eventName, event, onHandlerResult) => session?.getCurrentPages().at(-1) === page ? session.dispatchNativeNodeEvent(node, eventName, event, onHandlerResult) : false,
42772
43446
  callMethod: (scopeId, methodName, event) => {
42773
43447
  if (session?.getCurrentPages().includes(page)) return session.callScopeMethod(scopeId, methodName, event);
@@ -43130,7 +43804,18 @@ var HeadlessTestingPageHandle = class {
43130
43804
  renderedAccess() {
43131
43805
  return {
43132
43806
  assertActive: () => this.assertActive(),
43133
- findAll: async (selector) => await this.$$(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
+ },
43134
43819
  waitFor: async (ms) => await this.waitFor(ms),
43135
43820
  wxml: async () => await this.wxml()
43136
43821
  };
@@ -43217,6 +43902,10 @@ var HeadlessTestingSessionHandle = class {
43217
43902
  });
43218
43903
  }
43219
43904
  async close() {
43905
+ this.disconnect();
43906
+ }
43907
+ /** 无头会话独占本地 runtime;同步释放与 automator 的连接清理契约一致。 */
43908
+ disconnect() {
43220
43909
  this.session.close();
43221
43910
  }
43222
43911
  async toolInfo() {
@@ -43256,6 +43945,10 @@ var HeadlessTestingSessionHandle = class {
43256
43945
  const normalizedMethodName = normalizeNonEmptyInput(methodName, "wx method name");
43257
43946
  return await runWithTimeout(() => this.session.callWxMethod(normalizedMethodName, ...args), options.timeout, `Timed out calling wx.${normalizedMethodName} in headless testing runtime.`);
43258
43947
  }
43948
+ /** 返回与 DevTools automator 兼容的运行时系统信息。 */
43949
+ async systemInfo() {
43950
+ return await this.callWxMethod("getSystemInfoSync");
43951
+ }
43259
43952
  async scopeSnapshot(scopeId) {
43260
43953
  const normalizedScopeId = normalizeNonEmptyInput(scopeId, "Scope id");
43261
43954
  return this.session.getScopeSnapshot(normalizedScopeId);