@weapp-vite/miniprogram-automator 1.2.23 → 1.4.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.
@@ -8,7 +8,7 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
8
8
  var __getOwnPropNames = Object.getOwnPropertyNames;
9
9
  var __getProtoOf = Object.getPrototypeOf;
10
10
  var __hasOwnProp = Object.prototype.hasOwnProperty;
11
- var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
11
+ var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod, mod.exports), cb = null), mod.exports);
12
12
  var __exportAll = (all, no_symbols) => {
13
13
  let target = {};
14
14
  for (var name in all) __defProp(target, name, {
@@ -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+$/;
@@ -128,11 +163,14 @@ function runPropertyObservers(definition, instance, changedKeys, previousPropert
128
163
  const changedRoots = new Set(changedKeys.map((key) => parseDataPath$1(key)[0]));
129
164
  for (const changedKey of Object.keys(propOptions)) {
130
165
  if (!changedRoots.has(changedKey)) continue;
166
+ const value = instance.properties[changedKey];
167
+ if ((value === null || typeof value !== "object") && Object.hasOwn(previousProperties, changedKey) && Object.is(value, previousProperties[changedKey])) continue;
131
168
  const option = propOptions[changedKey];
132
169
  if (!option || typeof option !== "object" || Array.isArray(option)) continue;
133
170
  const observer = typeof option.observer === "string" ? instance[option.observer] : option.observer;
134
171
  if (typeof observer !== "function") continue;
135
- observer.call(instance, instance.properties[changedKey], previousProperties[changedKey]);
172
+ const changedPath = parseDataPath$1(changedKeys.find((key) => parseDataPath$1(key)[0] === changedKey)).map((segment) => isArrayIndexSegment$1(segment) ? Number(segment) : segment);
173
+ observer.call(instance, value, previousProperties[changedKey], changedPath);
136
174
  }
137
175
  }
138
176
  function normalizeObserverPattern(pattern) {
@@ -343,7 +381,6 @@ function resolveInitialProperties(definition, properties) {
343
381
  if (option && typeof option === "object" && !Array.isArray(option) && "value" in option) resolved[key] = resolvePropertyDefaultValue(option, definition);
344
382
  else resolved[key] = cloneValue$1(resolveDefaultValueByType(normalizePropertyType(option)));
345
383
  }
346
- for (const [key, value] of Object.entries(properties)) if (!(key in resolved)) resolved[key] = value;
347
384
  return resolved;
348
385
  }
349
386
  //#endregion
@@ -743,7 +780,7 @@ const basename = function(p, extension) {
743
780
  }
744
781
  return extension && lastSegment.endsWith(extension) ? lastSegment.slice(0, -extension.length) : lastSegment;
745
782
  };
746
- const parse$2 = function(p) {
783
+ const parse$3 = function(p) {
747
784
  const root = _PATH_ROOT_RE.exec(p)?.[0]?.replace(/\\/g, "/") || "";
748
785
  const base = basename(p);
749
786
  const extension = extname(base);
@@ -769,7 +806,7 @@ const _path = {
769
806
  matchesGlob,
770
807
  normalize,
771
808
  normalizeString,
772
- parse: parse$2,
809
+ parse: parse$3,
773
810
  relative,
774
811
  resolve,
775
812
  sep: "/",
@@ -804,11 +841,11 @@ const opposite = {
804
841
  function definitions(instance) {
805
842
  return Object.entries(instance.__definition__?.relations ?? {});
806
843
  }
807
- function includesBehavior(behaviors, target) {
808
- return Array.isArray(behaviors) && behaviors.some((behavior) => behavior === target || behavior && typeof behavior === "object" && includesBehavior(behavior.behaviors, target));
844
+ function includesBehavior$1(behaviors, target) {
845
+ return Array.isArray(behaviors) && behaviors.some((behavior) => behavior === target || behavior && typeof behavior === "object" && includesBehavior$1(behavior.behaviors, target));
809
846
  }
810
847
  function matches(source, key, relation, target) {
811
- if (relation.target) return includesBehavior(target.__definition__?.behaviors, relation.target);
848
+ if (relation.target) return includesBehavior$1(target.__definition__?.behaviors, relation.target);
812
849
  const targetPath = key.startsWith("/") ? key.slice(1) : posix.join(posix.dirname(String(source.is)), key);
813
850
  return posix.normalize(targetPath) === target.is;
814
851
  }
@@ -882,29 +919,24 @@ function detachComponentRelations(instances) {
882
919
  //#region ../../mpcore/packages/simulator/src/runtime/componentInstance/index.ts
883
920
  function createComponentInstance(options) {
884
921
  const definition = normalizeComponentDefinition(options.definition);
885
- const inputProperties = resolveInitialProperties(definition, options.properties ?? {});
886
922
  const data = {
887
923
  ...resolveInitialData$1(definition),
888
- ...Object.fromEntries(Object.keys(definition.properties ?? {}).map((key) => [key, inputProperties[key]]))
924
+ ...resolveInitialProperties(definition, options.properties ?? {})
889
925
  };
890
926
  const instance = {
891
927
  __definition__: definition,
892
928
  data,
893
929
  __propertySnapshots__: {},
894
- properties: {
895
- ...data,
896
- ...inputProperties
897
- },
930
+ properties: data,
898
931
  getRelationNodes(key) {
899
932
  return getComponentRelationNodes(instance, key);
900
933
  },
901
934
  setData(patch, callback) {
902
935
  const changedKeys = Object.keys(patch);
903
- const dataKeys = [...new Set(changedKeys.map((key) => parseDataPath$1(key)[0]))];
904
- const propertyKeys = dataKeys.filter((key) => Object.hasOwn(definition.properties ?? {}, key));
936
+ const propertyKeys = [...new Set(changedKeys.map((key) => parseDataPath$1(key)[0]))].filter((key) => Object.hasOwn(definition.properties ?? {}, key));
905
937
  const previousProperties = Object.fromEntries(propertyKeys.map((key) => [key, cloneValue$1(instance.properties[key])]));
906
938
  for (const [key, value] of Object.entries(patch)) assignByPath$1(instance.data, key, value);
907
- for (const key of dataKeys) instance.properties[key] = instance.data[key];
939
+ synchronizeAttachmentBindings(instance);
908
940
  runComponentObservers(definition, instance, changedKeys, previousProperties);
909
941
  if (options.requestRender) options.requestRender(callback);
910
942
  else callback?.();
@@ -946,8 +978,11 @@ const componentPageInstances = /* @__PURE__ */ new WeakSet();
946
978
  function isComponentPageAttaching(instance) {
947
979
  return attachingInstances.has(instance);
948
980
  }
949
- function registerComponentPageAttachment(definition, attach) {
950
- definitionAttachments.set(definition, attach);
981
+ function registerComponentPageAttachment(definition, created, attached) {
982
+ definitionAttachments.set(definition, {
983
+ created,
984
+ attached
985
+ });
951
986
  }
952
987
  function bindComponentPageAttachment(instance, definition) {
953
988
  const attach = definitionAttachments.get(definition);
@@ -958,13 +993,14 @@ function bindComponentPageAttachment(instance, definition) {
958
993
  if (typeof options?.styleIsolation === "string") instanceStyleIsolation.set(instance, options.styleIsolation);
959
994
  }
960
995
  }
961
- function attachComponentPage(instance) {
996
+ function attachComponentPage(instance, updateBindings) {
962
997
  const attach = instanceAttachments.get(instance);
963
998
  if (!attach) return false;
964
999
  instanceAttachments.delete(instance);
965
1000
  attachingInstances.add(instance);
966
1001
  try {
967
- attach.call(instance);
1002
+ attach.created(instance);
1003
+ runWithAttachmentBindingUpdates(instance, updateBindings, attach.attached, instance);
968
1004
  } finally {
969
1005
  attachingInstances.delete(instance);
970
1006
  }
@@ -1114,6 +1150,7 @@ function createPageInstance(route, definition, options = {}, pageState = {}) {
1114
1150
  const rootKey = parseDataPath(key)[0];
1115
1151
  if (rootKey) instance.properties[rootKey] = instance.data[rootKey];
1116
1152
  }
1153
+ synchronizeAttachmentBindings(instance);
1117
1154
  if (pageState.requestRender) pageState.requestRender(callback);
1118
1155
  else callback?.();
1119
1156
  }
@@ -1126,6 +1163,287 @@ function createPageInstance(route, definition, options = {}, pageState = {}) {
1126
1163
  return instance;
1127
1164
  }
1128
1165
  //#endregion
1166
+ //#region ../../mpcore/packages/simulator/src/view/eventBindingParser.ts
1167
+ const LEGACY_EVENT_NAME_RE = /^\w+$/;
1168
+ const EVENT_BINDING_PREFIXES = [
1169
+ {
1170
+ prefix: "capture-bind:",
1171
+ priority: 0,
1172
+ stopAfter: false,
1173
+ phase: "capture"
1174
+ },
1175
+ {
1176
+ prefix: "capture-catch:",
1177
+ priority: 0,
1178
+ stopAfter: true,
1179
+ phase: "capture"
1180
+ },
1181
+ {
1182
+ prefix: "capture-bind",
1183
+ priority: 1,
1184
+ stopAfter: false,
1185
+ phase: "capture"
1186
+ },
1187
+ {
1188
+ prefix: "capture-catch",
1189
+ priority: 1,
1190
+ stopAfter: true,
1191
+ phase: "capture"
1192
+ },
1193
+ {
1194
+ prefix: "bind:",
1195
+ priority: 0,
1196
+ stopAfter: false,
1197
+ phase: "bubble"
1198
+ },
1199
+ {
1200
+ prefix: "catch:",
1201
+ priority: 0,
1202
+ stopAfter: true,
1203
+ phase: "bubble"
1204
+ },
1205
+ {
1206
+ prefix: "bind",
1207
+ priority: 1,
1208
+ stopAfter: false,
1209
+ phase: "bubble"
1210
+ },
1211
+ {
1212
+ prefix: "catch",
1213
+ priority: 1,
1214
+ stopAfter: true,
1215
+ phase: "bubble"
1216
+ }
1217
+ ];
1218
+ function parseEventBinding(attributeName, method) {
1219
+ const definition = EVENT_BINDING_PREFIXES.find((item) => attributeName.startsWith(item.prefix));
1220
+ if (!definition) return null;
1221
+ const eventName = attributeName.slice(definition.prefix.length);
1222
+ if (!eventName || !definition.prefix.endsWith(":") && !LEGACY_EVENT_NAME_RE.test(eventName)) return null;
1223
+ return {
1224
+ eventName,
1225
+ method,
1226
+ phase: definition.phase,
1227
+ priority: definition.priority,
1228
+ stopAfter: definition.stopAfter
1229
+ };
1230
+ }
1231
+ function collectEventBindings(attributes = {}, includeCapture = true) {
1232
+ const eventBindings = /* @__PURE__ */ new Map();
1233
+ let captureEventBindings;
1234
+ const priorities = /* @__PURE__ */ new Map();
1235
+ for (const [attributeName, method] of Object.entries(attributes)) {
1236
+ const candidate = parseEventBinding(attributeName, method);
1237
+ if (!candidate || !includeCapture && candidate.phase === "capture") continue;
1238
+ const eventPriorities = priorities.get(candidate.eventName) ?? {};
1239
+ if (candidate.priority < (eventPriorities[candidate.phase] ?? -1)) continue;
1240
+ (candidate.phase === "bubble" ? eventBindings : captureEventBindings ??= /* @__PURE__ */ new Map()).set(candidate.eventName, {
1241
+ method: candidate.method,
1242
+ stopAfter: candidate.stopAfter
1243
+ });
1244
+ eventPriorities[candidate.phase] = candidate.priority;
1245
+ priorities.set(candidate.eventName, eventPriorities);
1246
+ }
1247
+ return {
1248
+ eventBindings,
1249
+ captureEventBindings
1250
+ };
1251
+ }
1252
+ /** 单事件查询只保留命中的绑定,原生交互不构造整张事件表。 */
1253
+ function resolveEventBindingPhases(attributes, eventName, includeCapture = true) {
1254
+ if (!attributes) return null;
1255
+ let bindings = null;
1256
+ let bubblePriority = -1;
1257
+ let capturePriority = -1;
1258
+ for (const [attributeName, method] of Object.entries(attributes)) {
1259
+ const candidate = parseEventBinding(attributeName, method);
1260
+ if (!candidate || candidate.eventName !== eventName || !includeCapture && candidate.phase === "capture") continue;
1261
+ const priority = candidate.phase === "bubble" ? bubblePriority : capturePriority;
1262
+ if (candidate.priority < priority) continue;
1263
+ bindings ??= {};
1264
+ bindings[candidate.phase] = {
1265
+ method: candidate.method,
1266
+ stopAfter: candidate.stopAfter
1267
+ };
1268
+ if (candidate.phase === "bubble") bubblePriority = candidate.priority;
1269
+ else capturePriority = candidate.priority;
1270
+ }
1271
+ return bindings;
1272
+ }
1273
+ //#endregion
1274
+ //#region ../../mpcore/packages/simulator/src/view/nodeDataset.ts
1275
+ const DATASET_NAME_RE = /-([a-z])/g;
1276
+ function isDatasetAttribute(attributeName) {
1277
+ return attributeName.startsWith("data-") && !attributeName.startsWith("data-sim-");
1278
+ }
1279
+ function toDatasetKey(attributeName) {
1280
+ return attributeName.slice(5).replace(DATASET_NAME_RE, (_match, char) => char.toUpperCase());
1281
+ }
1282
+ function collectNodeDataset(node) {
1283
+ if (node.dataset) return { ...node.dataset };
1284
+ const dataset = {};
1285
+ for (const [key, value] of Object.entries(node.attribs ?? {})) if (isDatasetAttribute(key)) dataset[toDatasetKey(key)] = value;
1286
+ return dataset;
1287
+ }
1288
+ //#endregion
1289
+ //#region ../../mpcore/packages/simulator/src/view/componentEvent.ts
1290
+ const eventNodes = /* @__PURE__ */ new WeakMap();
1291
+ /** 原生默认行为读取声明所有者与原始属性,避免组件根折叠覆盖宿主边界。 */
1292
+ function getNativeNodeOwner(node) {
1293
+ const entry = eventNodes.get(node);
1294
+ return entry ? {
1295
+ scopeId: entry.scope.getScopeId(),
1296
+ target: entry.target
1297
+ } : void 0;
1298
+ }
1299
+ /** 原生冒泡包含折叠的宿主绑定;catch 只截断其后的绑定和祖先。 */
1300
+ function visitNativeNodeEventBindings(node, eventName, visit) {
1301
+ const collect = (entry) => {
1302
+ const binding = entry.bindings ? entry.bindings.eventBindings?.get(eventName) : resolveEventBindingPhases(entry.target.attribs, eventName, false)?.bubble;
1303
+ if (binding) visit(entry.scope.getScopeId(), entry.target, binding);
1304
+ return binding?.stopAfter ?? false;
1305
+ };
1306
+ const entry = eventNodes.get(node);
1307
+ if (!entry) {
1308
+ const binding = resolveEventBindingPhases(node.attribs, eventName, false)?.bubble;
1309
+ if (binding) visit(node.attribs?.["data-sim-scope"] ?? null, node, binding);
1310
+ return binding?.stopAfter ?? false;
1311
+ }
1312
+ if (collect(entry)) return true;
1313
+ for (const host of entry.hosts ?? []) if (collect(eventNodes.get(host))) return true;
1314
+ return false;
1315
+ }
1316
+ /** 在构造子树之前建立父链,created/observer 中的事件也能访问声明树。 */
1317
+ function registerComponentEventNode(node, scope, parent) {
1318
+ node.parent = parent ?? null;
1319
+ eventNodes.set(node, {
1320
+ scope,
1321
+ target: node
1322
+ });
1323
+ }
1324
+ /** 宿主与作用域共享同一份分阶段绑定,eventBindings 仍是原有的冒泡存储。 */
1325
+ function bindComponentEventHost(node) {
1326
+ const bindings = collectEventBindings(node.attribs);
1327
+ eventNodes.get(node).bindings = bindings;
1328
+ return bindings;
1329
+ }
1330
+ /** 渲染会把宿主折叠进模板根;事件仍保留两层独立的绑定和声明所有者。 */
1331
+ function mergeComponentEventRoot(root, host) {
1332
+ const entry = eventNodes.get(root);
1333
+ if (entry.target === root) entry.target = {
1334
+ attribs: root.attribs,
1335
+ dataset: root.dataset
1336
+ };
1337
+ entry.hosts ??= [];
1338
+ entry.hosts.push(host);
1339
+ root.parent = host.parent;
1340
+ }
1341
+ /** 按渲染完成后的实际宿主父链排序;输入只包含本轮存活的组件作用域。 */
1342
+ function orderComponentAttachmentScopes(scopeIds, componentScopes) {
1343
+ const hostScopes = /* @__PURE__ */ new Map();
1344
+ for (const scopeId of scopeIds) hostScopes.set(componentScopes.get(scopeId).hostNode, scopeId);
1345
+ const ordered = [];
1346
+ const visited = /* @__PURE__ */ new Set();
1347
+ const visit = (node) => {
1348
+ if (!node || visited.has(node)) return;
1349
+ visited.add(node);
1350
+ visit(node.parent);
1351
+ const hosts = eventNodes.get(node)?.hosts;
1352
+ if (hosts) for (let index = hosts.length - 1; index >= 0; index--) visit(hosts[index]);
1353
+ const scopeId = hostScopes.get(node);
1354
+ if (scopeId !== void 0) ordered.push(scopeId);
1355
+ };
1356
+ for (const host of hostScopes.keys()) visit(host);
1357
+ return ordered;
1358
+ }
1359
+ function buildComponentTrigger(componentScopeId, context) {
1360
+ return (instance, eventName, detail, triggerOptions) => {
1361
+ const originScope = context.componentScopes.get(componentScopeId);
1362
+ const origin = originScope?.hostNode;
1363
+ if (!originScope || !origin) return;
1364
+ const bubbles = triggerOptions?.bubbles ?? false;
1365
+ const capturePhase = triggerOptions?.capturePhase ?? false;
1366
+ const composed = triggerOptions?.composed ?? false;
1367
+ const target = {
1368
+ dataset: originScope.dataset ?? collectNodeDataset(origin),
1369
+ id: originScope.hostId ?? ""
1370
+ };
1371
+ const mark = instance.__lastInteractionEvent__?.mark;
1372
+ const listeners = [];
1373
+ const collect = (entry, isOrigin) => {
1374
+ if (!composed && entry.scope.getScopeId() !== originScope.listenerScopeId) return false;
1375
+ const bindings = entry.bindings ? {
1376
+ bubble: entry.bindings.eventBindings?.get(eventName),
1377
+ capture: entry.bindings.captureEventBindings?.get(eventName)
1378
+ } : resolveEventBindingPhases(entry.target.attribs, eventName);
1379
+ if (bindings?.bubble || bindings?.capture) listeners.push({
1380
+ scope: entry.scope,
1381
+ bindings,
1382
+ currentTarget: isOrigin ? target : {
1383
+ dataset: collectNodeDataset(entry.target),
1384
+ id: entry.target.attribs?.id ?? ""
1385
+ },
1386
+ origin: isOrigin
1387
+ });
1388
+ return true;
1389
+ };
1390
+ let current = origin;
1391
+ while (current) {
1392
+ const entry = eventNodes.get(current);
1393
+ if (entry) {
1394
+ if (!collect(entry, current === origin)) break;
1395
+ let crossedBoundary = false;
1396
+ for (const host of entry.hosts ?? []) if (!collect(eventNodes.get(host), false)) {
1397
+ crossedBoundary = true;
1398
+ break;
1399
+ }
1400
+ if (crossedBoundary) break;
1401
+ }
1402
+ if (!bubbles && !capturePhase) break;
1403
+ current = current.parent;
1404
+ }
1405
+ const handlers = [];
1406
+ let stopped = false;
1407
+ if (capturePhase) for (let index = listeners.length - 1; index >= 0; index--) {
1408
+ const listener = listeners[index];
1409
+ const binding = listener.bindings.capture;
1410
+ if (binding) {
1411
+ handlers.push({
1412
+ scope: listener.scope,
1413
+ binding,
1414
+ currentTarget: listener.currentTarget
1415
+ });
1416
+ if (binding.stopAfter) {
1417
+ stopped = true;
1418
+ break;
1419
+ }
1420
+ }
1421
+ }
1422
+ if (!stopped) for (const listener of listeners) {
1423
+ if (!bubbles && !listener.origin) continue;
1424
+ const binding = listener.bindings.bubble;
1425
+ if (binding) {
1426
+ handlers.push({
1427
+ scope: listener.scope,
1428
+ binding,
1429
+ currentTarget: listener.currentTarget
1430
+ });
1431
+ if (binding.stopAfter) break;
1432
+ }
1433
+ }
1434
+ for (const { scope, binding, currentTarget } of handlers) scope.getMethod(binding.method)?.({
1435
+ bubbles,
1436
+ capturePhase,
1437
+ composed,
1438
+ currentTarget,
1439
+ detail,
1440
+ mark,
1441
+ target,
1442
+ type: eventName
1443
+ });
1444
+ };
1445
+ }
1446
+ //#endregion
1129
1447
  //#region ../../mpcore/packages/simulator/src/view/conditionalChildren.ts
1130
1448
  function selectConditionalChildren(children, evaluate) {
1131
1449
  const selected = [];
@@ -1181,11 +1499,77 @@ function customTabBarHostScope() {
1181
1499
  };
1182
1500
  }
1183
1501
  //#endregion
1502
+ //#region ../../mpcore/packages/simulator/src/view/inspectionTree.ts
1503
+ const inspectionChildren = /* @__PURE__ */ new WeakMap();
1504
+ const inspectionParents = /* @__PURE__ */ new WeakMap();
1505
+ const emptyChildren = [];
1506
+ function getInspectionChildren(node) {
1507
+ return inspectionChildren.get(node) ?? node.children ?? emptyChildren;
1508
+ }
1509
+ function getInspectionParent(node) {
1510
+ return inspectionParents.get(node) ?? node.parent ?? null;
1511
+ }
1512
+ function collectRenderedNodes(node, nodes) {
1513
+ if (nodes.has(node)) return;
1514
+ nodes.add(node);
1515
+ for (const child of node.children ?? emptyChildren) collectRenderedNodes(child, nodes);
1516
+ }
1517
+ /** 原生检查器保留未投影声明;可见 DOM、WXML 和事件父链不因此增加节点。 */
1518
+ function registerInspectionTrees(root, pass, scopes) {
1519
+ if (!pass.hasUnprojectedSlots) return;
1520
+ const rendered = /* @__PURE__ */ new Set();
1521
+ collectRenderedNodes(root, rendered);
1522
+ const candidates = /* @__PURE__ */ new Map();
1523
+ for (const [entries, group] of pass.slotDeclarations) {
1524
+ if (scopes.get(group.scopeId)?.hostNode !== group.parent) continue;
1525
+ const host = pass.componentRoots.get(group.scopeId);
1526
+ if (!host) continue;
1527
+ for (const entry of entries) for (const node of pass.renderedSlots.get(entry) ?? emptyChildren) candidates.set(node, host);
1528
+ }
1529
+ const nested = /* @__PURE__ */ new Set();
1530
+ const visited = /* @__PURE__ */ new Set();
1531
+ const indexChildren = (node) => {
1532
+ if (visited.has(node)) return;
1533
+ visited.add(node);
1534
+ for (const child of node.children ?? emptyChildren) {
1535
+ nested.add(child);
1536
+ if (!rendered.has(child)) inspectionParents.set(child, node);
1537
+ indexChildren(child);
1538
+ }
1539
+ };
1540
+ for (const node of candidates.keys()) indexChildren(node);
1541
+ for (const [node, host] of candidates) {
1542
+ if (rendered.has(node) || nested.has(node) || node === host) continue;
1543
+ let children = inspectionChildren.get(host);
1544
+ if (!children) {
1545
+ children = [...host.children ?? emptyChildren];
1546
+ inspectionChildren.set(host, children);
1547
+ }
1548
+ children.push(node);
1549
+ inspectionParents.set(node, host);
1550
+ }
1551
+ }
1552
+ //#endregion
1184
1553
  //#region ../../mpcore/packages/simulator/src/view/loopEntries.ts
1554
+ const LOOP_SCOPE_ESCAPE_RE = /[^\w-]/g;
1555
+ function escapeScopeCodeUnit(character) {
1556
+ return `~${character.charCodeAt(0).toString(16).padStart(4, "0")}`;
1557
+ }
1558
+ function encodeLoopCoordinate(value) {
1559
+ return value.replace(LOOP_SCOPE_ESCAPE_RE, escapeScopeCodeUnit);
1560
+ }
1185
1561
  function resolveLoopEntries(value) {
1186
1562
  if (Array.isArray(value)) return value.map((item, index) => [index, item]);
1187
1563
  return value !== null && typeof value === "object" ? Object.entries(value) : [];
1188
1564
  }
1565
+ function resolveLoopInstanceSuffix(item, index, key, occurrences) {
1566
+ if (!key || !occurrences) return `:for-${encodeLoopCoordinate(String(index))}`;
1567
+ const value = key === "*this" ? item : item?.[key];
1568
+ const normalized = encodeLoopCoordinate(String(value ?? ""));
1569
+ const occurrence = occurrences.get(normalized) ?? 0;
1570
+ occurrences.set(normalized, occurrence + 1);
1571
+ return `:key-${normalized}${occurrence ? `:duplicate-${occurrence}` : ""}`;
1572
+ }
1189
1573
  //#endregion
1190
1574
  //#region ../../mpcore/packages/simulator/src/view/renderedTree.ts
1191
1575
  /** 在循环展开和组件替换完成后,让父节点关系指向本次渲染树。 */
@@ -1194,7 +1578,45 @@ function linkRenderedParents(root, parent = null) {
1194
1578
  for (const child of root.children ?? []) linkRenderedParents(child, root);
1195
1579
  }
1196
1580
  //#endregion
1197
- //#region ../../node_modules/.pnpm/@babel+parser@8.0.6/node_modules/@babel/parser/lib/index.js
1581
+ //#region ../../mpcore/packages/simulator/src/view/renderPass.ts
1582
+ const initializedPages = /* @__PURE__ */ new WeakMap();
1583
+ function createRenderPass(propertyUpdate = false, parent) {
1584
+ return {
1585
+ seenComponentScopes: /* @__PURE__ */ new Set(),
1586
+ componentRoots: /* @__PURE__ */ new Map(),
1587
+ slotDeclarations: /* @__PURE__ */ new Map(),
1588
+ renderedSlots: /* @__PURE__ */ new Map(),
1589
+ hasUnprojectedSlots: false,
1590
+ propertyUpdate,
1591
+ bindingUpdate: parent?.bindingUpdate,
1592
+ bindingRevision: parent?.bindingUpdate?.revision ?? 0
1593
+ };
1594
+ }
1595
+ function createPageRenderPass(page, cache) {
1596
+ const pass = createRenderPass(initializedPages.get(cache)?.has(page) ?? false);
1597
+ pass.bindingUpdate = getAttachmentBindingRevision(page);
1598
+ pass.bindingRevision = pass.bindingUpdate?.revision ?? 0;
1599
+ return pass;
1600
+ }
1601
+ function isRenderPassStale(pass) {
1602
+ return pass.bindingUpdate !== void 0 && pass.bindingUpdate.revision !== pass.bindingRevision;
1603
+ }
1604
+ function markPageTreeConstructed(page, cache) {
1605
+ let pages = initializedPages.get(cache);
1606
+ if (!pages) {
1607
+ pages = /* @__PURE__ */ new WeakSet();
1608
+ initializedPages.set(cache, pages);
1609
+ }
1610
+ pages.add(page);
1611
+ }
1612
+ function registerSlotDeclarations(pass, slots, parent, scopeId) {
1613
+ for (const entries of slots.values()) if (!pass.slotDeclarations.has(entries)) pass.slotDeclarations.set(entries, {
1614
+ parent,
1615
+ scopeId
1616
+ });
1617
+ }
1618
+ //#endregion
1619
+ //#region ../../node_modules/.pnpm/@babel+parser@8.0.7/node_modules/@babel/parser/lib/index.js
1198
1620
  var Position = class {
1199
1621
  constructor(line, col, index) {
1200
1622
  this.line = void 0;
@@ -9263,7 +9685,11 @@ var StatementParser = class extends ExpressionParser {
9263
9685
  if (isDeclaration) node.id = this.parseFunctionId(requireId);
9264
9686
  this.scope.enter(514);
9265
9687
  this.prodParam.enter(functionFlags(isAsync, node.generator));
9266
- if (!isDeclaration) node.id = this.parseFunctionId();
9688
+ if (!isDeclaration && tokenIsIdentifier(this.state.type)) {
9689
+ this.expressionScope.enter(newExpressionScope());
9690
+ node.id = this.parseFunctionId();
9691
+ this.expressionScope.exit();
9692
+ }
9267
9693
  this.parseFunctionParams(node, false);
9268
9694
  this.parseFunctionBodyAndFinish(node, isDeclaration ? "FunctionDeclaration" : "FunctionExpression");
9269
9695
  this.prodParam.exit();
@@ -12596,6 +13022,29 @@ var Parser$1 = class extends StatementParser {
12596
13022
  return result;
12597
13023
  }
12598
13024
  };
13025
+ function parse$2(input, options) {
13026
+ if (options?.sourceType === "unambiguous") {
13027
+ options = { ...options };
13028
+ try {
13029
+ options.sourceType = "module";
13030
+ const parser = getParser(options, input);
13031
+ const ast = parser.parse();
13032
+ if (parser.sawUnambiguousESM) return ast;
13033
+ if (parser.ambiguousScriptDifferentAst) try {
13034
+ options.sourceType = "script";
13035
+ return getParser(options, input).parse();
13036
+ } catch {}
13037
+ else ast.program.sourceType = "script";
13038
+ return ast;
13039
+ } catch (moduleError) {
13040
+ try {
13041
+ options.sourceType = "script";
13042
+ return getParser(options, input).parse();
13043
+ } catch {}
13044
+ throw moduleError;
13045
+ }
13046
+ } else return getParser(options, input).parse();
13047
+ }
12599
13048
  function parseExpression(input, options) {
12600
13049
  const parser = getParser(options, input);
12601
13050
  if (parser.options.strictMode) parser.state.strict = true;
@@ -12913,7 +13362,8 @@ function isComponentAttached(instance) {
12913
13362
  return attachmentStates.get(instance) === "attached";
12914
13363
  }
12915
13364
  function hasPendingComponentAttachments(instances) {
12916
- return [...instances].some((instance) => attachmentStates.get(instance) === "attaching");
13365
+ for (const instance of instances) if (attachmentStates.get(instance) === "attaching") return true;
13366
+ return false;
12917
13367
  }
12918
13368
  function flushComponentReady(instances, ready) {
12919
13369
  const all = [...instances];
@@ -12923,19 +13373,71 @@ function flushComponentReady(instances, ready) {
12923
13373
  for (const instance of pending) ready(instance);
12924
13374
  return pending.length > 0;
12925
13375
  }
12926
- function flushComponentAttachments(instances, attach) {
12927
- const pending = [...instances].filter((instance) => !attachmentStates.has(instance));
12928
- for (const instance of pending) attachmentStates.set(instance, "attaching");
13376
+ function flushComponentAttachments(componentCache, scopeIds, attach) {
13377
+ const pending = /* @__PURE__ */ new Map();
13378
+ for (const scopeId of scopeIds) {
13379
+ const instance = componentCache.get(scopeId);
13380
+ if (!instance) continue;
13381
+ const state = attachmentStates.get(instance);
13382
+ if (state === "attaching") return false;
13383
+ if (!state) pending.set(scopeId, instance);
13384
+ }
13385
+ for (const instance of pending.values()) attachmentStates.set(instance, "attaching");
12929
13386
  try {
12930
- for (const instance of pending) try {
12931
- attach(instance);
12932
- } finally {
12933
- attachmentStates.set(instance, "attached");
13387
+ for (const [scopeId, instance] of pending) {
13388
+ if (componentCache.get(scopeId) !== instance) return "restart";
13389
+ let restart;
13390
+ try {
13391
+ restart = attach(instance);
13392
+ } finally {
13393
+ attachmentStates.set(instance, "attached");
13394
+ }
13395
+ if (restart) return "restart";
12934
13396
  }
12935
13397
  } finally {
12936
- for (const instance of pending) if (attachmentStates.get(instance) === "attaching") attachmentStates.delete(instance);
13398
+ for (const instance of pending.values()) if (attachmentStates.get(instance) === "attaching") attachmentStates.delete(instance);
12937
13399
  }
12938
- return pending.length > 0;
13400
+ return pending.size > 0 ? "attached" : false;
13401
+ }
13402
+ //#endregion
13403
+ //#region ../../mpcore/packages/simulator/src/runtime/componentInstance/construction.ts
13404
+ const constructionStates = /* @__PURE__ */ new WeakMap();
13405
+ const constructingInstances = /* @__PURE__ */ new WeakSet();
13406
+ function isComponentConstructing(instance) {
13407
+ return instance !== void 0 && constructingInstances.has(instance);
13408
+ }
13409
+ function hasPendingComponentConstruction(componentCache) {
13410
+ return constructionStates.has(componentCache);
13411
+ }
13412
+ function beginComponentConstruction(componentCache, scopeId, instance) {
13413
+ let state = constructionStates.get(componentCache);
13414
+ if (!state) {
13415
+ state = {
13416
+ depth: 0,
13417
+ created: /* @__PURE__ */ new Map()
13418
+ };
13419
+ constructionStates.set(componentCache, state);
13420
+ }
13421
+ state.depth++;
13422
+ state.created.set(scopeId, instance);
13423
+ constructingInstances.add(instance);
13424
+ }
13425
+ function discardComponentConstruction(componentCache, componentScopes, scopeId) {
13426
+ const state = constructionStates.get(componentCache);
13427
+ const prefix = `${scopeId}/`;
13428
+ for (const [createdScopeId, instance] of state.created) {
13429
+ if (createdScopeId !== scopeId && !createdScopeId.startsWith(prefix)) continue;
13430
+ if (componentCache.get(createdScopeId) === instance) {
13431
+ componentCache.delete(createdScopeId);
13432
+ componentScopes.delete(createdScopeId);
13433
+ }
13434
+ state.created.delete(createdScopeId);
13435
+ }
13436
+ }
13437
+ function finishComponentConstruction(componentCache, instance) {
13438
+ constructingInstances.delete(instance);
13439
+ const state = constructionStates.get(componentCache);
13440
+ if (--state.depth === 0) constructionStates.delete(componentCache);
12939
13441
  }
12940
13442
  //#endregion
12941
13443
  //#region ../../mpcore/packages/simulator/src/runtime/componentInstance/discardedReady.ts
@@ -12958,6 +13460,61 @@ function flushDiscardedComponentReady(cache, pageScopeId, ready) {
12958
13460
  if (!pending.size) discardedReady.delete(cache);
12959
13461
  }
12960
13462
  //#endregion
13463
+ //#region ../../mpcore/packages/simulator/src/runtime/componentInstance/propertyBindings.ts
13464
+ const consumedPageChanges = /* @__PURE__ */ new WeakMap();
13465
+ function syncComponentProperties(instance, definition, nextProperties, bindingExpressions, changedPageKeys, beforeObservers) {
13466
+ const freshPageChanges = changedPageKeys.length > 0 && consumedPageChanges.get(instance) !== changedPageKeys;
13467
+ if (freshPageChanges) consumedPageChanges.set(instance, changedPageKeys);
13468
+ const changedRootKeys = [];
13469
+ const previousProperties = {};
13470
+ for (const [key, value] of Object.entries(nextProperties)) {
13471
+ if (!Object.hasOwn(definition.properties ?? {}, key)) continue;
13472
+ const nextValue = normalizeComponentPropertyValue(definition, key, value);
13473
+ const bindingExpression = bindingExpressions[key];
13474
+ const bindingAffected = freshPageChanges && !!bindingExpression && changedPageKeys.some((changedKey) => {
13475
+ return changedKey === bindingExpression || changedKey.startsWith(`${bindingExpression}.`) || changedKey.startsWith(`${bindingExpression}[`);
13476
+ });
13477
+ const previousSnapshot = instance.__propertySnapshots?.[key];
13478
+ if (hasComponentPropertyValueChanged(instance.properties[key], previousSnapshot, nextValue, bindingAffected)) {
13479
+ previousProperties[key] = instance.properties[key];
13480
+ const deliveredValue = cloneValue$1(nextValue);
13481
+ instance.properties[key] = deliveredValue;
13482
+ changedRootKeys.push(key);
13483
+ }
13484
+ instance.__propertySnapshots ??= {};
13485
+ instance.__propertySnapshots[key] = cloneValue$1(nextValue);
13486
+ }
13487
+ if (changedRootKeys.length === 0) return;
13488
+ beforeObservers?.(instance, "properties");
13489
+ runComponentObservers(definition, instance, changedRootKeys, previousProperties);
13490
+ }
13491
+ //#endregion
13492
+ //#region ../../mpcore/packages/simulator/src/runtime/componentInstance/reconcileRemoved.ts
13493
+ /** 用户卸载钩子失败也要完成关系与缓存收尾,避免下一次渲染重复卸载同一实例。 */
13494
+ function reconcileRemovedComponents(cache, scopes, activeScopeIds, pageScopeId) {
13495
+ const removed = [...cache].filter(([scopeId]) => scopeId.startsWith(`${pageScopeId}/`) && !activeScopeIds.has(scopeId));
13496
+ let failure;
13497
+ for (const [scopeId, instance] of removed) try {
13498
+ if (isComponentAttached(instance)) runComponentLifecycle(instance, "detached");
13499
+ else scheduleDiscardedComponentReady(cache, scopeId, instance);
13500
+ } catch (error) {
13501
+ failure ??= { error };
13502
+ }
13503
+ let relationsChanged = false;
13504
+ try {
13505
+ relationsChanged = syncComponentRelations(cache, activeScopeIds, pageScopeId);
13506
+ } catch (error) {
13507
+ failure ??= { error };
13508
+ } finally {
13509
+ for (const [scopeId] of removed) {
13510
+ cache.delete(scopeId);
13511
+ scopes.delete(scopeId);
13512
+ }
13513
+ }
13514
+ if (failure) throw failure.error;
13515
+ return relationsChanged;
13516
+ }
13517
+ //#endregion
12961
13518
  //#region ../../mpcore/packages/simulator/src/runtime/pageLifecycle.ts
12962
13519
  const loadingPages = /* @__PURE__ */ new WeakSet();
12963
13520
  function isPageBeforeReady(page) {
@@ -13047,97 +13604,9 @@ function createPluginArtifactSource(fallback, miniprogramRootPath, plugins) {
13047
13604
  };
13048
13605
  }
13049
13606
  //#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
- }
13607
+ //#region ../../@weapp-core/constants/dist/index.js
13608
+ /** 无头测试后端不支持所请求的自动化协议。 */
13609
+ const HEADLESS_PROTOCOL_UNSUPPORTED = "HEADLESS_PROTOCOL_UNSUPPORTED";
13141
13610
  const WEVU_NATIVE_INSTANCE_KEY = "__wevuNativeInstance";
13142
13611
  /** 宿主值为 1 时,实现带 routeEventId 的路由阶段契约;不代表微信 SDK 版本。 */
13143
13612
  const WEVU_ROUTE_EVENT_CONTRACT_KEY = "__wevuRouteEventContract";
@@ -13251,66 +13720,155 @@ function resolveMiniProgramComponent(ownerFilePath, request, miniprogramRootPath
13251
13720
  return npmPath ? relative(miniprogramRootPath, npmPath).slice(0, -3) : localPath;
13252
13721
  }
13253
13722
  //#endregion
13254
- //#region ../../mpcore/packages/simulator/src/view/templateImports.ts
13255
- function createImportedTemplateState(root, filePath, loadImport, loadWxs) {
13256
- const documents = /* @__PURE__ */ new Map();
13257
- const definitionScopes = /* @__PURE__ */ new Map();
13258
- const definitionWxsScopes = /* @__PURE__ */ new Map();
13259
- const collect = (node, owner) => {
13260
- const existing = documents.get(owner);
13261
- if (existing) return existing;
13262
- const state = createTemplateRenderState(node);
13263
- const wxsModules = Object.create(null);
13264
- const ownDefinitions = new Map(state.definitions);
13265
- const document = {
13266
- state,
13267
- exports: ownDefinitions
13268
- };
13269
- documents.set(owner, document);
13270
- const visit = (child) => {
13271
- if (child.name === "wxs" && loadWxs) {
13272
- const name = child.attribs?.module;
13273
- if (!name || !/^[a-z_$][\w$]*$/i.test(name) || [
13274
- "__proto__",
13275
- "constructor",
13276
- "prototype"
13277
- ].includes(name)) throw new Error(`Invalid WXS module name in ${owner}: ${String(name)}`);
13278
- if (Object.hasOwn(wxsModules, name)) throw new Error(`Duplicate WXS module in ${owner}: ${name}`);
13279
- wxsModules[name] = loadWxs(owner, child);
13280
- return;
13281
- }
13282
- if (child.name === "import" && child.attribs?.src) {
13283
- const source = child.attribs.src;
13284
- const imported = loadImport(owner, source.endsWith(".wxml") ? source : `${source}.wxml`);
13285
- const importedDocument = collect(imported.root, imported.filePath);
13286
- for (const [name, definition] of importedDocument.exports) state.definitions.set(name, definition);
13287
- }
13288
- for (const nested of child.children ?? []) visit(nested);
13289
- };
13290
- visit(node);
13291
- if (Object.keys(wxsModules).length) state.wxsModules = wxsModules;
13292
- for (const [name, definition] of ownDefinitions) {
13293
- state.definitions.set(name, definition);
13294
- definitionScopes.set(definition, state.definitions);
13295
- if (state.wxsModules) definitionWxsScopes.set(definition, state.wxsModules);
13296
- }
13297
- state.definitionScopes = definitionScopes;
13298
- state.definitionWxsScopes = definitionWxsScopes;
13299
- return document;
13723
+ //#region ../../mpcore/packages/simulator/src/view/nodeEvents.ts
13724
+ /** 正确字段优先;复制触点,避免处理器修改调用者持有的数据。 */
13725
+ function readTouchEventPayload(event) {
13726
+ const changedTouches = event.changedTouches ?? event.changeTouches;
13727
+ return {
13728
+ ...event.touches === void 0 ? {} : { touches: event.touches.map((touch) => ({ ...touch })) },
13729
+ ...changedTouches === void 0 ? {} : { changedTouches: changedTouches.map((touch) => ({ ...touch })) }
13300
13730
  };
13301
- return collect(root, filePath).state;
13302
13731
  }
13303
13732
  //#endregion
13304
- //#region ../../mpcore/packages/simulator/src/view/templateText.ts
13305
- function interpolateTemplateText(input, data, emptyNull = false) {
13306
- let output = "";
13307
- let offset = 0;
13308
- for (const range of templateInterpolations(input)) {
13309
- const value = resolveTemplateExpression(data, range.expression);
13310
- output += input.slice(offset, range.start) + (value === void 0 || emptyNull && value === null ? "" : String(value));
13311
- offset = range.end;
13733
+ //#region ../../mpcore/packages/simulator/src/view/eventBinding.ts
13734
+ function resolveMiniProgramEventBinding(attributes, eventName) {
13735
+ return resolveEventBindingPhases(attributes, eventName, false)?.bubble ?? null;
13736
+ }
13737
+ /** 冒泡时保留原始 target,currentTarget 则属于当前执行绑定的节点。 */
13738
+ function createMiniProgramEventPayload(node, eventName, event, origin = node) {
13739
+ const dataset = collectNodeDataset(node);
13740
+ const nodeId = node.attribs?.id ?? "";
13741
+ const isOrigin = node === origin;
13742
+ return {
13743
+ ...readTouchEventPayload(event),
13744
+ bubbles: false,
13745
+ capturePhase: false,
13746
+ composed: false,
13747
+ currentTarget: {
13748
+ dataset: isOrigin ? event.currentTarget?.dataset ?? event.dataset ?? dataset : dataset,
13749
+ id: isOrigin ? event.currentTarget?.id ?? event.id ?? nodeId : nodeId
13750
+ },
13751
+ detail: event.detail,
13752
+ mark: event.mark,
13753
+ target: {
13754
+ dataset: event.target?.dataset ?? event.dataset ?? (isOrigin ? dataset : collectNodeDataset(origin)),
13755
+ id: event.target?.id ?? event.id ?? origin.attribs?.id ?? ""
13756
+ },
13757
+ timeStamp: Date.now(),
13758
+ type: eventName
13759
+ };
13760
+ }
13761
+ //#endregion
13762
+ //#region ../../mpcore/packages/simulator/src/view/nativeForm.ts
13763
+ const pickerValues = /* @__PURE__ */ new WeakMap();
13764
+ /** 保存 picker 的原始数组属性,不能从渲染后的逗号字符串反推选项值。 */
13765
+ function setNativePickerValue(node, value) {
13766
+ if (node.name === "picker") pickerValues.set(node, value);
13767
+ }
13768
+ function includesBehavior(definition, name, visited = /* @__PURE__ */ new Set()) {
13769
+ if (visited.has(definition)) return false;
13770
+ visited.add(definition);
13771
+ return Array.isArray(definition.behaviors) && definition.behaviors.some((behavior) => behavior === name || behavior !== null && typeof behavior === "object" && includesBehavior(behavior, name, visited));
13772
+ }
13773
+ function nativeOwner(node) {
13774
+ return getNativeNodeOwner(node) ?? {
13775
+ scopeId: node.attribs?.["data-sim-scope"] ?? null,
13776
+ target: node
13777
+ };
13778
+ }
13779
+ function participatingScopes(formScope, pageScope, resolveComponent) {
13780
+ const scopes = /* @__PURE__ */ new Set([null, pageScope]);
13781
+ let current = formScope;
13782
+ while (current && !scopes.has(current)) {
13783
+ scopes.add(current);
13784
+ current = resolveComponent?.(current)?.ownerScopeId ?? null;
13312
13785
  }
13313
- return output + input.slice(offset);
13786
+ return scopes;
13787
+ }
13788
+ function participates(scopeId, formScopes, behavior, resolveComponent) {
13789
+ const visited = /* @__PURE__ */ new Set();
13790
+ while (!formScopes.has(scopeId)) {
13791
+ if (!scopeId || visited.has(scopeId)) return false;
13792
+ visited.add(scopeId);
13793
+ const component = resolveComponent?.(scopeId);
13794
+ if (!component || !includesBehavior(component.definition, behavior)) return false;
13795
+ scopeId = component.ownerScopeId;
13796
+ }
13797
+ return true;
13798
+ }
13799
+ function checked(attributes, name) {
13800
+ const value = attributes?.[name];
13801
+ return value !== void 0 && value !== "false" && value !== "0";
13802
+ }
13803
+ function choiceValues(group, pageScope, resolveComponent) {
13804
+ const values = [];
13805
+ const scopes = participatingScopes(nativeOwner(group).scopeId, pageScope, resolveComponent);
13806
+ const choice = group.name === "radio-group" ? "radio" : "checkbox";
13807
+ const visit = (node) => {
13808
+ if (node.name === "form" || node.name === "radio-group" || node.name === "checkbox-group") return;
13809
+ if (node.name === choice) {
13810
+ const owner = nativeOwner(node);
13811
+ if (checked(owner.target.attribs, "checked") && participates(owner.scopeId, scopes, "wx://form-field-group", resolveComponent)) values.push(owner.target.attribs?.value ?? "");
13812
+ }
13813
+ for (const child of node.children ?? []) visit(child);
13814
+ };
13815
+ for (const child of group.children ?? []) visit(child);
13816
+ return values;
13817
+ }
13818
+ function collectFormValues(form, scopes, pageScope, resolveComponent) {
13819
+ const values = Object.create(null);
13820
+ const visit = (node) => {
13821
+ if (node.name === "form") return;
13822
+ const owner = nativeOwner(node);
13823
+ const attributes = owner.target.attribs;
13824
+ const name = attributes?.name;
13825
+ if (attributes && name && participates(owner.scopeId, scopes, "wx://form-field-group", resolveComponent)) switch (node.name) {
13826
+ case "input":
13827
+ case "textarea":
13828
+ values[name] = attributes.value ?? "";
13829
+ break;
13830
+ case "switch":
13831
+ values[name] = checked(attributes, "checked");
13832
+ break;
13833
+ case "slider":
13834
+ values[name] = Number(attributes.value ?? 0);
13835
+ break;
13836
+ case "picker": {
13837
+ const mode = attributes.mode ?? "selector";
13838
+ if (mode === "multiSelector" || mode === "region") {
13839
+ const value = pickerValues.get(node);
13840
+ values[name] = Array.isArray(value) ? value.slice() : [];
13841
+ } else values[name] = mode === "selector" ? Number(attributes.value ?? 0) : attributes.value ?? "";
13842
+ break;
13843
+ }
13844
+ case "radio-group":
13845
+ values[name] = choiceValues(node, pageScope, resolveComponent)[0] ?? "";
13846
+ break;
13847
+ case "checkbox-group": values[name] = choiceValues(node, pageScope, resolveComponent);
13848
+ }
13849
+ for (const child of node.children ?? []) visit(child);
13850
+ };
13851
+ for (const child of form.children ?? []) visit(child);
13852
+ return values;
13853
+ }
13854
+ /** 先固定提交目标、字段值和方法所有者,再允许 tap 回调改变组件树。 */
13855
+ function prepareNativeFormSubmit(button, pageScope, event, resolveMethod, resolveComponent) {
13856
+ const owner = nativeOwner(button);
13857
+ if (owner.target.attribs?.["form-type"] !== "submit" || checked(owner.target.attribs, "disabled")) return;
13858
+ let form = button.parent;
13859
+ while (form && form.name !== "form") form = form.parent;
13860
+ if (!form) return;
13861
+ const formOwner = nativeOwner(form);
13862
+ const scopes = participatingScopes(formOwner.scopeId, pageScope, resolveComponent);
13863
+ if (!participates(owner.scopeId, scopes, "wx://form-field-button", resolveComponent)) return;
13864
+ const binding = resolveMiniProgramEventBinding(formOwner.target.attribs, "submit");
13865
+ return {
13866
+ call: binding?.method ? resolveMethod(formOwner.scopeId, binding.method) : void 0,
13867
+ event: createMiniProgramEventPayload(formOwner.target, "submit", {
13868
+ detail: { value: collectFormValues(form, scopes, pageScope, resolveComponent) },
13869
+ mark: event.mark
13870
+ })
13871
+ };
13314
13872
  }
13315
13873
  //#endregion
13316
13874
  //#region ../../node_modules/.pnpm/entities@8.1.0/node_modules/entities/dist/decode-codepoint.js
@@ -16359,7 +16917,7 @@ const attributeEscapeRegex = /["&\u{A0}]/gu;
16359
16917
  * following {@link https://html.spec.whatwg.org/multipage/parsing.html#escapingString}.
16360
16918
  * @param data String to escape.
16361
16919
  */
16362
- function escapeAttribute$3(data) {
16920
+ function escapeAttribute$2(data) {
16363
16921
  return escapeWithRegex(attributeEscapeRegex, data);
16364
16922
  }
16365
16923
  const textEscapeRegex = /[&<>\u{A0}]/gu;
@@ -16471,7 +17029,7 @@ function replaceQuotes(value) {
16471
17029
  */
16472
17030
  function formatAttributes(attributes, options, xmlMode) {
16473
17031
  if (!attributes) return "";
16474
- const encode = (options.encodeEntities ?? options.decodeEntities) === false ? replaceQuotes : xmlMode || options.encodeEntities !== "utf8" ? encodeXML : escapeAttribute$3;
17032
+ const encode = (options.encodeEntities ?? options.decodeEntities) === false ? replaceQuotes : xmlMode || options.encodeEntities !== "utf8" ? encodeXML : escapeAttribute$2;
16475
17033
  const isForeign = xmlMode === "foreign";
16476
17034
  const showEmpty = !!(options.emptyAttrs ?? xmlMode);
16477
17035
  let result = "";
@@ -17086,6 +17644,344 @@ function parseDocument(data, options) {
17086
17644
  return handler.root;
17087
17645
  }
17088
17646
  //#endregion
17647
+ //#region ../../@weapp-core/shared/dist/rich-text.js
17648
+ const ALLOWED_TAGS = /* @__PURE__ */ new Set([
17649
+ "a",
17650
+ "abbr",
17651
+ "address",
17652
+ "article",
17653
+ "aside",
17654
+ "b",
17655
+ "bdi",
17656
+ "bdo",
17657
+ "big",
17658
+ "blockquote",
17659
+ "br",
17660
+ "caption",
17661
+ "center",
17662
+ "cite",
17663
+ "code",
17664
+ "col",
17665
+ "colgroup",
17666
+ "dd",
17667
+ "del",
17668
+ "div",
17669
+ "dl",
17670
+ "dt",
17671
+ "em",
17672
+ "fieldset",
17673
+ "font",
17674
+ "h1",
17675
+ "h2",
17676
+ "h3",
17677
+ "h4",
17678
+ "h5",
17679
+ "h6",
17680
+ "header",
17681
+ "hr",
17682
+ "i",
17683
+ "img",
17684
+ "ins",
17685
+ "label",
17686
+ "legend",
17687
+ "li",
17688
+ "mark",
17689
+ "ol",
17690
+ "p",
17691
+ "pre",
17692
+ "q",
17693
+ "rt",
17694
+ "ruby",
17695
+ "s",
17696
+ "section",
17697
+ "small",
17698
+ "span",
17699
+ "strong",
17700
+ "sub",
17701
+ "sup",
17702
+ "table",
17703
+ "tbody",
17704
+ "td",
17705
+ "tfoot",
17706
+ "th",
17707
+ "thead",
17708
+ "tr",
17709
+ "tt",
17710
+ "u",
17711
+ "ul"
17712
+ ]);
17713
+ const ALLOWED_ATTRIBUTES = /* @__PURE__ */ new Set([
17714
+ "abbr",
17715
+ "align",
17716
+ "alt",
17717
+ "axis",
17718
+ "bgcolor",
17719
+ "border",
17720
+ "cellpadding",
17721
+ "cellspacing",
17722
+ "class",
17723
+ "colspan",
17724
+ "dir",
17725
+ "height",
17726
+ "href",
17727
+ "id",
17728
+ "lang",
17729
+ "rowspan",
17730
+ "scope",
17731
+ "span",
17732
+ "start",
17733
+ "style",
17734
+ "summary",
17735
+ "target",
17736
+ "title",
17737
+ "type",
17738
+ "valign",
17739
+ "width"
17740
+ ]);
17741
+ const URL_ATTRIBUTES = /* @__PURE__ */ new Set(["href", "src"]);
17742
+ const ALLOWED_STYLE_PROPERTIES = /* @__PURE__ */ new Set([
17743
+ "background",
17744
+ "background-color",
17745
+ "border",
17746
+ "border-bottom",
17747
+ "border-color",
17748
+ "border-left",
17749
+ "border-radius",
17750
+ "border-right",
17751
+ "border-style",
17752
+ "border-top",
17753
+ "border-width",
17754
+ "color",
17755
+ "display",
17756
+ "font-family",
17757
+ "font-size",
17758
+ "font-style",
17759
+ "font-weight",
17760
+ "height",
17761
+ "letter-spacing",
17762
+ "line-height",
17763
+ "margin",
17764
+ "margin-bottom",
17765
+ "margin-left",
17766
+ "margin-right",
17767
+ "margin-top",
17768
+ "max-height",
17769
+ "max-width",
17770
+ "min-height",
17771
+ "min-width",
17772
+ "opacity",
17773
+ "overflow",
17774
+ "padding",
17775
+ "padding-bottom",
17776
+ "padding-left",
17777
+ "padding-right",
17778
+ "padding-top",
17779
+ "text-align",
17780
+ "text-decoration",
17781
+ "text-indent",
17782
+ "text-overflow",
17783
+ "text-transform",
17784
+ "vertical-align",
17785
+ "white-space",
17786
+ "width",
17787
+ "word-break",
17788
+ "word-wrap"
17789
+ ]);
17790
+ function isRecord(value) {
17791
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
17792
+ }
17793
+ function isSafeUrl(value, attribute) {
17794
+ const normalized = Array.from(value).filter((character) => character.charCodeAt(0) > 32).join("").toLowerCase();
17795
+ if (!normalized || normalized.startsWith("#") || normalized.startsWith("/") || normalized.startsWith("./") || normalized.startsWith("../")) return true;
17796
+ if (/^(?:https?:|mailto:|tel:)/.test(normalized)) return true;
17797
+ return attribute === "src" && /^data:image\/(?:gif|jpe?g|png|webp);base64,/.test(normalized);
17798
+ }
17799
+ function isSafeStyleValue(value) {
17800
+ const normalized = value.toLowerCase().replace(/\s+/g, "");
17801
+ return !normalized.includes("url(") && !normalized.includes("expression(") && !normalized.includes("javascript:") && !normalized.includes("behavior:") && !normalized.includes("-moz-binding");
17802
+ }
17803
+ function sanitizeRichTextStyle(value) {
17804
+ const source = isRecord(value) ? Object.entries(value).map(([property, propertyValue]) => `${property}:${String(propertyValue)}`).join(";") : String(value ?? "");
17805
+ const declarations = [];
17806
+ for (const declaration of source.split(";")) {
17807
+ const separator = declaration.indexOf(":");
17808
+ if (separator <= 0) continue;
17809
+ const property = declaration.slice(0, separator).trim().toLowerCase();
17810
+ const propertyValue = declaration.slice(separator + 1).trim();
17811
+ if (ALLOWED_STYLE_PROPERTIES.has(property) && propertyValue && isSafeStyleValue(propertyValue)) declarations.push(`${property}: ${propertyValue}`);
17812
+ }
17813
+ return declarations.join("; ");
17814
+ }
17815
+ function sanitizeRichTextAttributes(value, tagName) {
17816
+ if (!isRecord(value)) return {};
17817
+ const attributes = {};
17818
+ for (const [rawName, rawValue] of Object.entries(value)) {
17819
+ const name = rawName.trim().toLowerCase();
17820
+ if (name.startsWith("on") || name === "srcdoc") continue;
17821
+ if (!ALLOWED_ATTRIBUTES.has(name) && name !== "src" && !name.startsWith("aria-") && !name.startsWith("data-")) continue;
17822
+ if (name === "src" && tagName !== "img") continue;
17823
+ const serialized = name === "style" ? sanitizeRichTextStyle(rawValue) : String(rawValue ?? "");
17824
+ if (!serialized || URL_ATTRIBUTES.has(name) && !isSafeUrl(serialized, name)) continue;
17825
+ attributes[name] = serialized;
17826
+ }
17827
+ if (tagName === "a") attributes.rel = "noopener noreferrer";
17828
+ return attributes;
17829
+ }
17830
+ function convertSourceNode(value) {
17831
+ if (!isRecord(value)) return [];
17832
+ if (value.type === "text" || !value.name && typeof value.text === "string") return [{
17833
+ type: "text",
17834
+ text: String(value.text ?? "")
17835
+ }];
17836
+ const children = Array.isArray(value.children) ? value.children.flatMap(convertSourceNode) : [];
17837
+ const name = String(value.name ?? "").toLowerCase();
17838
+ if (!ALLOWED_TAGS.has(name)) return children;
17839
+ return [{
17840
+ type: "element",
17841
+ name,
17842
+ attrs: sanitizeRichTextAttributes(value.attrs, name),
17843
+ children
17844
+ }];
17845
+ }
17846
+ function convertParsedNode(node) {
17847
+ if (node.type === "text") return [{
17848
+ type: "text",
17849
+ text: node.data
17850
+ }];
17851
+ if (node.type !== "tag") return [];
17852
+ const element = node;
17853
+ const children = element.children.flatMap(convertParsedNode);
17854
+ const name = element.name.toLowerCase();
17855
+ if (!ALLOWED_TAGS.has(name)) return children;
17856
+ return [{
17857
+ type: "element",
17858
+ name,
17859
+ attrs: sanitizeRichTextAttributes(element.attribs, name),
17860
+ children
17861
+ }];
17862
+ }
17863
+ function normalizeRichTextNodes(value) {
17864
+ if (typeof value === "string") return parseDocument(value, {
17865
+ decodeEntities: true,
17866
+ lowerCaseAttributeNames: true,
17867
+ lowerCaseTags: true
17868
+ }).children.flatMap(convertParsedNode);
17869
+ return (Array.isArray(value) ? value : [value]).flatMap(convertSourceNode);
17870
+ }
17871
+ //#endregion
17872
+ //#region ../../mpcore/packages/simulator/src/view/richText.ts
17873
+ function materializeNode(node) {
17874
+ if (node.type === "text") return {
17875
+ type: "text",
17876
+ data: node.text
17877
+ };
17878
+ for (const name of Object.keys(node.attrs)) if (name.startsWith("data-sim-")) delete node.attrs[name];
17879
+ return {
17880
+ type: "tag",
17881
+ name: node.name,
17882
+ attribs: node.attrs,
17883
+ children: node.children.map(materializeNode)
17884
+ };
17885
+ }
17886
+ /** 富文本直接成为已渲染节点,不能再进入 WXML 表达式或组件处理流程。 */
17887
+ function renderRichTextNodes(value) {
17888
+ return normalizeRichTextNodes(value).map(materializeNode);
17889
+ }
17890
+ //#endregion
17891
+ //#region ../../mpcore/packages/simulator/src/view/templateIncludes.ts
17892
+ /** 展开 include 的可渲染内容,并保留嵌套引用的来源文件。 */
17893
+ function expandTemplateIncludes(root, filePath, loadTemplate) {
17894
+ const owners = /* @__PURE__ */ new WeakMap();
17895
+ const expand = (node, owner, stack, included = false) => {
17896
+ if (included && (isTemplateDefinition(node) || node.name === "wxs")) return;
17897
+ owners.set(node, owner);
17898
+ if (node.name === "include" && node.attribs?.src) {
17899
+ const source = node.attribs.src;
17900
+ const loaded = loadTemplate(owner, source.endsWith(".wxml") ? source : `${source}.wxml`);
17901
+ if (stack.includes(loaded.filePath)) throw new Error("Circular WXML include is not supported");
17902
+ const { src: _src, ...attributes } = node.attribs;
17903
+ node.name = "block";
17904
+ node.attribs = attributes;
17905
+ node.children = (loaded.root.children ?? []).flatMap((child) => {
17906
+ const expanded = expand(child, loaded.filePath, [...stack, loaded.filePath], true);
17907
+ return expanded ? [expanded] : [];
17908
+ });
17909
+ return node;
17910
+ }
17911
+ node.children = node.children?.flatMap((child) => {
17912
+ const expanded = expand(child, owner, stack, included);
17913
+ return expanded ? [expanded] : [];
17914
+ });
17915
+ return node;
17916
+ };
17917
+ expand(root, filePath, [filePath]);
17918
+ return owners;
17919
+ }
17920
+ //#endregion
17921
+ //#region ../../mpcore/packages/simulator/src/view/templateImports.ts
17922
+ function createImportedTemplateState(root, filePath, loadImport, loadWxs) {
17923
+ const documents = /* @__PURE__ */ new Map();
17924
+ const definitionScopes = /* @__PURE__ */ new Map();
17925
+ const definitionWxsScopes = /* @__PURE__ */ new Map();
17926
+ const collect = (node, owner) => {
17927
+ const existing = documents.get(owner);
17928
+ if (existing) return existing;
17929
+ const owners = expandTemplateIncludes(node, owner, loadImport);
17930
+ const state = createTemplateRenderState(node);
17931
+ const wxsModules = Object.create(null);
17932
+ const ownDefinitions = new Map(state.definitions);
17933
+ const document = {
17934
+ state,
17935
+ exports: ownDefinitions
17936
+ };
17937
+ documents.set(owner, document);
17938
+ const visit = (child) => {
17939
+ const sourceOwner = owners.get(child) ?? owner;
17940
+ if (child.name === "wxs" && loadWxs) {
17941
+ const name = child.attribs?.module;
17942
+ if (!name || !/^[a-z_$][\w$]*$/i.test(name) || [
17943
+ "__proto__",
17944
+ "constructor",
17945
+ "prototype"
17946
+ ].includes(name)) throw new Error(`Invalid WXS module name in ${owner}: ${String(name)}`);
17947
+ if (Object.hasOwn(wxsModules, name)) throw new Error(`Duplicate WXS module in ${owner}: ${name}`);
17948
+ wxsModules[name] = loadWxs(sourceOwner, child);
17949
+ return;
17950
+ }
17951
+ if (child.name === "import" && child.attribs?.src) {
17952
+ const source = child.attribs.src;
17953
+ const imported = loadImport(sourceOwner, source.endsWith(".wxml") ? source : `${source}.wxml`);
17954
+ const importedDocument = collect(imported.root, imported.filePath);
17955
+ for (const [name, definition] of importedDocument.exports) state.definitions.set(name, definition);
17956
+ }
17957
+ for (const nested of child.children ?? []) visit(nested);
17958
+ };
17959
+ visit(node);
17960
+ if (Object.keys(wxsModules).length) state.wxsModules = wxsModules;
17961
+ for (const [name, definition] of ownDefinitions) {
17962
+ state.definitions.set(name, definition);
17963
+ definitionScopes.set(definition, state.definitions);
17964
+ if (state.wxsModules) definitionWxsScopes.set(definition, state.wxsModules);
17965
+ }
17966
+ state.definitionScopes = definitionScopes;
17967
+ state.definitionWxsScopes = definitionWxsScopes;
17968
+ return document;
17969
+ };
17970
+ return collect(root, filePath).state;
17971
+ }
17972
+ //#endregion
17973
+ //#region ../../mpcore/packages/simulator/src/view/templateText.ts
17974
+ function interpolateTemplateText(input, data, emptyNull = false) {
17975
+ let output = "";
17976
+ let offset = 0;
17977
+ for (const range of templateInterpolations(input)) {
17978
+ const value = resolveTemplateExpression(data, range.expression);
17979
+ output += input.slice(offset, range.start) + (value === void 0 || emptyNull && value === null ? "" : String(value));
17980
+ offset = range.end;
17981
+ }
17982
+ return output + input.slice(offset);
17983
+ }
17984
+ //#endregion
17089
17985
  //#region ../../mpcore/packages/simulator/src/view/templateAttributes.ts
17090
17986
  function decodeAttribute(raw, quote) {
17091
17987
  const source = raw.replaceAll(`\\${quote}`, quote);
@@ -17140,17 +18036,18 @@ function maskTemplateAttributes(source) {
17140
18036
  const INLINE_WXS_RE = /<wxs\b([^>]*)(?<!\/)>([\s\S]*?)<\/wxs\s*>/g;
17141
18037
  function parseWxsTemplateDocument(templateSource) {
17142
18038
  const scripts = [];
17143
- const masked = maskTemplateAttributes(templateSource.replace(INLINE_WXS_RE, (_match, attributes, script) => {
18039
+ const source = templateSource.replace(INLINE_WXS_RE, (_match, attributes, script) => {
17144
18040
  return `<wxs data-sim-wxs="${scripts.push(script) - 1}"${attributes}>wxs-source</wxs>`;
17145
- }));
18041
+ });
18042
+ const masked = maskTemplateAttributes(source);
17146
18043
  const document = parseDocument(`<page>${masked.source}</page>`, {
17147
- xmlMode: false,
18044
+ xmlMode: true,
17148
18045
  decodeEntities: false,
17149
18046
  lowerCaseAttributeNames: false,
17150
18047
  lowerCaseTags: false,
17151
18048
  recognizeSelfClosing: true
17152
18049
  });
17153
- if (!scripts.length && !masked.attributes.size) return document;
18050
+ if (!scripts.length && !masked.attributes.size && !/[\r\n]/.test(source)) return document;
17154
18051
  const hasAttributes = masked.attributes.size > 0;
17155
18052
  const restore = (node) => {
17156
18053
  if (hasAttributes && node.attribs) {
@@ -17159,7 +18056,9 @@ function parseWxsTemplateDocument(templateSource) {
17159
18056
  if (node.name === "wxs" && Object.hasOwn(node.attribs ?? {}, "data-sim-wxs")) {
17160
18057
  node.children[0].data = scripts[Number(node.attribs["data-sim-wxs"])];
17161
18058
  delete node.attribs["data-sim-wxs"];
18059
+ return;
17162
18060
  }
18061
+ if (node.children) node.children = node.children.filter((child) => child.type !== "text" || !/^[\t\r\n ]+$/.test(child.data ?? "") || !/[\r\n]/.test(child.data ?? ""));
17163
18062
  for (const child of node.children ?? []) restore(child);
17164
18063
  };
17165
18064
  restore(document);
@@ -17200,7 +18099,7 @@ function collectDataset(node, source) {
17200
18099
  function escapeText$2(text) {
17201
18100
  return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
17202
18101
  }
17203
- function escapeAttribute$2(value) {
18102
+ function escapeAttribute$1(value) {
17204
18103
  return escapeText$2(value).replaceAll("\"", "&quot;");
17205
18104
  }
17206
18105
  function cloneNode(node) {
@@ -17245,7 +18144,7 @@ function serializeDomNode$1(node) {
17245
18144
  if (node.type === "root") return (node.children ?? []).map(serializeDomNode$1).join("");
17246
18145
  const tagName = node.name ?? "";
17247
18146
  if (!tagName) return (node.children ?? []).map(serializeDomNode$1).join("");
17248
- return `<${tagName}${Object.entries(node.attribs ?? {}).map(([key, value]) => ` ${key}="${escapeAttribute$2(value)}"`).join("")}>${(node.children ?? []).map(serializeDomNode$1).join("")}</${tagName}>`;
18147
+ return `<${tagName}${Object.entries(node.attribs ?? {}).map(([key, value]) => ` ${key}="${escapeAttribute$1(value)}"`).join("")}>${(node.children ?? []).map(serializeDomNode$1).join("")}</${tagName}>`;
17249
18148
  }
17250
18149
  function isTagNode(node) {
17251
18150
  return node.type === "tag" && typeof node.name === "string";
@@ -17264,7 +18163,7 @@ function resolveComponentAttributeValue(value, scope) {
17264
18163
  }
17265
18164
  return interpolateTemplate$1(value, wxsScopeData(scope));
17266
18165
  }
17267
- function applyNodeBindings(node, scope) {
18166
+ function applyNodeBindings(node, scope, richText = false) {
17268
18167
  if (!isTagNode(node)) {
17269
18168
  if (node.type === "text" && typeof node.data === "string") node.data = interpolateTemplateText(node.data, wxsScopeData(scope));
17270
18169
  return;
@@ -17272,6 +18171,7 @@ function applyNodeBindings(node, scope) {
17272
18171
  node.attribs ??= {};
17273
18172
  node.attribs["data-sim-scope"] = scope.getScopeId();
17274
18173
  for (const key of STRUCTURAL_ATTRS) delete node.attribs[key];
18174
+ if (richText) node.children = [];
17275
18175
  let dataset;
17276
18176
  for (const [key, value] of Object.entries({ ...node.attribs })) {
17277
18177
  if (EVENT_BINDING_ATTRS.includes(key)) {
@@ -17279,6 +18179,8 @@ function applyNodeBindings(node, scope) {
17279
18179
  continue;
17280
18180
  }
17281
18181
  const resolvedValue = typeof value === "string" ? resolveAttributeValue(value, scope) : value;
18182
+ if (key === "value") setNativePickerValue(node, resolvedValue);
18183
+ if (richText && key === "nodes") node.children = renderRichTextNodes(resolvedValue);
17282
18184
  node.attribs[key] = String(resolvedValue ?? "");
17283
18185
  if (isDatasetAttribute(key)) {
17284
18186
  dataset ??= {};
@@ -17305,8 +18207,7 @@ function createLoopScope(scope, itemName, indexName, item, index) {
17305
18207
  //#endregion
17306
18208
  //#region ../../mpcore/packages/simulator/src/runtime/render/component.ts
17307
18209
  function resolveComponentRegistryEntry(context, ownerJsonPath, ownerFilePath, alias, genericComponentBasePath) {
17308
- const usingComponents = resolveUsingComponents(context, ownerJsonPath, ownerFilePath);
17309
- const componentBasePath = genericComponentBasePath ?? usingComponents.get(alias);
18210
+ const componentBasePath = genericComponentBasePath ?? resolveUsingComponent(context, ownerJsonPath, ownerFilePath, alias);
17310
18211
  if (!componentBasePath) return null;
17311
18212
  const filePath = `${componentBasePath}.js`;
17312
18213
  const templatePath = `${componentBasePath}.wxml`;
@@ -17327,30 +18228,25 @@ function readComponentConfig(artifactSource, jsonPath) {
17327
18228
  return {};
17328
18229
  }
17329
18230
  }
17330
- function resolveUsingComponents(context, ownerJsonPath, ownerFilePath) {
18231
+ function resolveUsingComponent(context, ownerJsonPath, ownerFilePath, alias) {
17331
18232
  try {
17332
18233
  const usingComponents = readComponentConfig(context.artifactSource, ownerJsonPath).usingComponents;
17333
- if (!usingComponents || typeof usingComponents !== "object" || Array.isArray(usingComponents)) return /* @__PURE__ */ new Map();
17334
- const resolved = /* @__PURE__ */ new Map();
17335
- for (const [alias, rawPath] of Object.entries(usingComponents)) {
17336
- if (typeof rawPath !== "string") continue;
17337
- const basePath = resolvePluginRequest(context.project.plugins, rawPath, "publicComponent")?.resourcePath ?? resolveMiniProgramComponent(ownerFilePath, rawPath, context.project.miniprogramRootPath, (candidate) => context.artifactSource.readText(candidate) !== void 0);
17338
- resolved.set(alias, basePath.replace(LEADING_SLASH_RE$4, ""));
17339
- }
17340
- return resolved;
18234
+ if (!usingComponents || typeof usingComponents !== "object" || Array.isArray(usingComponents)) return;
18235
+ const rawPath = usingComponents[alias];
18236
+ if (typeof rawPath !== "string") return;
18237
+ return (resolvePluginRequest(context.project.plugins, rawPath, "publicComponent")?.resourcePath ?? resolveMiniProgramComponent(ownerFilePath, rawPath, context.project.miniprogramRootPath, (candidate) => context.artifactSource.has(candidate))).replace(LEADING_SLASH_RE$4, "");
17341
18238
  } catch {
17342
- return /* @__PURE__ */ new Map();
18239
+ return;
17343
18240
  }
17344
18241
  }
17345
18242
  function resolveComponentGenerics(context, hostNode, ownerJsonPath, ownerFilePath, componentFilePath, ownerGenerics) {
17346
18243
  const componentJsonPath = path.resolve(context.project.miniprogramRootPath, `${componentFilePath.replace(JS_FILE_RE, "")}.json`);
17347
18244
  const componentGenerics = readComponentConfig(context.artifactSource, componentJsonPath).componentGenerics;
17348
18245
  if (!componentGenerics || typeof componentGenerics !== "object" || Array.isArray(componentGenerics)) return;
17349
- const ownerComponents = resolveUsingComponents(context, ownerJsonPath, ownerFilePath);
17350
18246
  const resolved = /* @__PURE__ */ new Map();
17351
18247
  for (const [genericName, definition] of Object.entries(componentGenerics)) {
17352
18248
  const selectedAlias = hostNode.attribs?.[`generic:${genericName}`];
17353
- const selectedPath = selectedAlias ? ownerGenerics?.get(selectedAlias) ?? ownerComponents.get(selectedAlias) : void 0;
18249
+ const selectedPath = selectedAlias ? ownerGenerics?.get(selectedAlias) ?? resolveUsingComponent(context, ownerJsonPath, ownerFilePath, selectedAlias) : void 0;
17354
18250
  if (selectedPath) {
17355
18251
  resolved.set(genericName, selectedPath);
17356
18252
  continue;
@@ -17362,67 +18258,6 @@ function resolveComponentGenerics(context, hostNode, ownerJsonPath, ownerFilePat
17362
18258
  }
17363
18259
  return resolved.size > 0 ? resolved : void 0;
17364
18260
  }
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
18261
  function createComponentScope(clonedNode, scope, componentScopeId, componentInstance, genericComponents, slots) {
17427
18262
  const ownerScopeId = scope.getScopeId().includes("/") ? scope.getScopeId() : void 0;
17428
18263
  return {
@@ -17430,7 +18265,7 @@ function createComponentScope(clonedNode, scope, componentScopeId, componentInst
17430
18265
  classList: String(clonedNode.attribs?.class ?? "").split(CLASS_SPLIT_RE).map((item) => item.trim()).filter(Boolean),
17431
18266
  data: { ...componentInstance.data },
17432
18267
  dataset: collectDataset(clonedNode, wxsScopeData(scope)),
17433
- eventBindings: collectComponentEventBindings(clonedNode),
18268
+ ...bindComponentEventHost(clonedNode),
17434
18269
  getMethod: (methodName) => {
17435
18270
  const method = componentInstance?.[methodName];
17436
18271
  return typeof method === "function" ? method.bind(componentInstance) : void 0;
@@ -17438,6 +18273,7 @@ function createComponentScope(clonedNode, scope, componentScopeId, componentInst
17438
18273
  getScopeId: () => componentScopeId,
17439
18274
  genericComponents,
17440
18275
  hostId: typeof clonedNode.attribs?.id === "string" ? clonedNode.attribs.id : void 0,
18276
+ hostNode: clonedNode,
17441
18277
  id: typeof clonedNode.attribs?.id === "string" ? clonedNode.attribs.id : void 0,
17442
18278
  listenerScopeId: scope.getScopeId(),
17443
18279
  ownerScopeId,
@@ -17460,13 +18296,13 @@ function resolveComponentProperties(clonedNode, scope, definition) {
17460
18296
  bindingExpressions
17461
18297
  };
17462
18298
  }
17463
- function createRuntimeComponentInstance(componentScopeId, context, clonedNode, componentEntry, nextProperties, ownerScopeId, beforeCreated) {
17464
- 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({
18299
+ function createRuntimeComponentInstance(componentScopeId, context, componentEntry, nextProperties, ownerScopeId, renderTemplate, bindEventsBeforeProperties = false) {
18300
+ const instanceOptions = {
17466
18301
  definition: componentEntry.definition,
17467
- requestRender: (callback) => context.session.requestRender(callback),
17468
- triggerEvent: buildComponentTrigger(componentScopeId, context, clonedNode)
17469
- });
18302
+ requestRender: (callback) => context.session.requestRender(callback)
18303
+ };
18304
+ const componentInstance = createComponentInstance(instanceOptions);
18305
+ bindAttachmentBindingScope(componentInstance, context);
17470
18306
  setSelectorQueryScopeId(componentInstance, componentScopeId);
17471
18307
  componentInstance.is = componentEntry.filePath.replace(JS_FILE_RE, "");
17472
18308
  componentInstance.createIntersectionObserver = (options) => context.session.createIntersectionObserver(componentInstance, options);
@@ -17474,19 +18310,29 @@ function createRuntimeComponentInstance(componentScopeId, context, clonedNode, c
17474
18310
  componentInstance.createSelectorQuery = () => context.moduleLoader.wx.createSelectorQuery().in(componentInstance);
17475
18311
  componentInstance.selectComponent = (selector) => resolveNativeComponentSelection(context.session.selectComponentWithin(componentScopeId, selector));
17476
18312
  componentInstance.selectAllComponents = (selector) => context.session.selectAllComponentsWithin(componentScopeId, selector).map(resolveNativeComponentSelection);
17477
- componentInstance.selectOwnerComponent = () => ownerScopeId ? context.componentCache.get(ownerScopeId) ?? null : null;
18313
+ componentInstance.selectOwnerComponent = () => ownerScopeId ? resolveNativeComponentSelection(context.componentCache.get(ownerScopeId)) : null;
18314
+ beginComponentConstruction(context.componentCache, componentScopeId, componentInstance);
17478
18315
  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;
18316
+ try {
18317
+ renderTemplate?.(componentInstance, "defaults");
18318
+ runComponentLifecycle(componentInstance, "created");
18319
+ if (bindEventsBeforeProperties) instanceOptions.triggerEvent = buildComponentTrigger(componentScopeId, context);
18320
+ componentInstance.__propertySnapshots = Object.fromEntries(Object.entries(componentInstance.properties).map(([key, propertyValue]) => [key, cloneValue$1(propertyValue)]));
18321
+ syncComponentProperties(componentInstance, componentInstance.__definition__ ?? componentEntry.definition, nextProperties, {}, [], renderTemplate);
18322
+ if (!bindEventsBeforeProperties) instanceOptions.triggerEvent = buildComponentTrigger(componentScopeId, context);
18323
+ return componentInstance;
18324
+ } catch (error) {
18325
+ discardComponentConstruction(context.componentCache, context.componentScopes, componentScopeId);
18326
+ throw error;
18327
+ } finally {
18328
+ finishComponentConstruction(context.componentCache, componentInstance);
18329
+ }
17484
18330
  }
17485
- function renderRuntimeComponentTemplate(context, componentEntry, renderNodeTree, componentScope, componentScopeId, seenComponentScopes) {
18331
+ function renderRuntimeComponentTemplate(context, componentEntry, renderNodeTree, componentScope, componentScopeId, renderPass) {
17486
18332
  const componentDocument = parseTemplateDocument(readTemplateSource(context.artifactSource, componentEntry.absoluteTemplatePath));
17487
18333
  const componentRoot = (componentDocument.children ?? [])[0] ?? componentDocument;
17488
18334
  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);
18335
+ 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
18336
  }
17491
18337
  //#endregion
17492
18338
  //#region ../../mpcore/packages/simulator/src/runtime/render/index.ts
@@ -17500,24 +18346,33 @@ function expandNodeByFor(node, scope) {
17500
18346
  const items = resolveLoopEntries(resolveRawValueByPath$1(wxsScopeData(scope), forExpression));
17501
18347
  const itemName = node.attribs?.["wx:for-item"]?.trim() || "item";
17502
18348
  const indexName = node.attribs?.["wx:for-index"]?.trim() || "index";
18349
+ const key = node.attribs?.["wx:key"]?.trim();
18350
+ const keyOccurrences = key ? /* @__PURE__ */ new Map() : void 0;
17503
18351
  return items.map(([index, item]) => ({
17504
18352
  node: cloneNode(node),
17505
18353
  scope: createLoopScope(scope, itemName, indexName, item, index),
17506
- instanceSuffix: `:for-${index}`
18354
+ instanceSuffix: resolveLoopInstanceSuffix(item, index, key, keyOccurrences)
17507
18355
  }));
17508
18356
  }
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));
18357
+ function renderNodeVariants(node, scope, context, ownerJsonPath, ownerFilePath, instancePath, renderPass, templateRenderState, parent) {
18358
+ const nodes = [];
18359
+ if (isRenderPassStale(renderPass)) return nodes;
18360
+ for (const { node: expandedNode, scope: expandedScope, instanceSuffix } of expandNodeByFor(node, scope)) {
18361
+ if (isRenderPassStale(renderPass)) break;
18362
+ nodes.push(renderNodeTree(expandedNode, expandedScope, context, ownerJsonPath, ownerFilePath, `${instancePath}${instanceSuffix}`, renderPass, templateRenderState, parent));
18363
+ }
18364
+ return nodes;
17511
18365
  }
17512
- function renderChildren(children, scope, context, ownerJsonPath, ownerFilePath, instancePath, seenComponentScopes, templateRenderState) {
18366
+ function renderChildren(children, scope, context, ownerJsonPath, ownerFilePath, instancePath, renderPass, templateRenderState, parent) {
17513
18367
  const renderedChildren = [];
17514
18368
  for (const { node: child, index } of selectConditionalChildren(children, (node) => evaluateConditionalBranch(node, scope))) {
18369
+ if (isRenderPassStale(renderPass)) break;
17515
18370
  if (!isTagNode(child)) {
17516
- renderedChildren.push(...renderNodeVariants(child, scope, context, ownerJsonPath, ownerFilePath, `${instancePath}/node-${index}`, seenComponentScopes, templateRenderState));
18371
+ renderedChildren.push(...renderNodeVariants(child, scope, context, ownerJsonPath, ownerFilePath, `${instancePath}/node-${index}`, renderPass, templateRenderState, parent));
17517
18372
  continue;
17518
18373
  }
17519
18374
  if (isTemplateDefinition(child) || child.name === "import" || child.name === "wxs") continue;
17520
- renderedChildren.push(...renderNodeVariants(child, scope, context, ownerJsonPath, ownerFilePath, `${instancePath}/node-${index}`, seenComponentScopes, templateRenderState));
18375
+ renderedChildren.push(...renderNodeVariants(child, scope, context, ownerJsonPath, ownerFilePath, `${instancePath}/node-${index}`, renderPass, templateRenderState, parent));
17521
18376
  }
17522
18377
  return renderedChildren;
17523
18378
  }
@@ -17549,22 +18404,38 @@ function collectComponentSlots(componentNode, scope, ownerJsonPath, ownerFilePat
17549
18404
  }
17550
18405
  return slots;
17551
18406
  }
17552
- function renderNodeTree(node, scope, context, ownerJsonPath, ownerFilePath, instancePath, seenComponentScopes, templateRenderState) {
18407
+ function reconcileSlotDeclarations(context, renderPass) {
18408
+ for (const [entries, { parent, scopeId }] of renderPass.slotDeclarations) {
18409
+ if (isRenderPassStale(renderPass)) return;
18410
+ if (isComponentConstructing(context.componentCache.get(scopeId))) continue;
18411
+ for (const { index } of selectConditionalChildren(entries.map((entry) => entry.node), (node, index) => evaluateConditionalBranch(node, entries[index].scope))) {
18412
+ if (isRenderPassStale(renderPass)) return;
18413
+ const entry = entries[index];
18414
+ if (renderPass.renderedSlots.has(entry)) continue;
18415
+ const nodes = renderNodeVariants(entry.node, entry.scope, context, entry.ownerJsonPath, entry.ownerFilePath, entry.instancePath, renderPass, entry.templateRenderState, parent);
18416
+ renderPass.renderedSlots.set(entry, nodes);
18417
+ renderPass.hasUnprojectedSlots ||= nodes.length > 0;
18418
+ }
18419
+ }
18420
+ }
18421
+ function renderNodeTree(node, scope, context, ownerJsonPath, ownerFilePath, instancePath, renderPass, templateRenderState, parent) {
18422
+ if (isRenderPassStale(renderPass)) return node;
17553
18423
  if (scope.wxs !== templateRenderState.wxsModules) scope = {
17554
18424
  ...scope,
17555
18425
  wxs: templateRenderState.wxsModules
17556
18426
  };
17557
18427
  const clonedNode = cloneNode(node);
18428
+ registerComponentEventNode(clonedNode, scope, parent);
17558
18429
  if (!isTagNode(clonedNode)) {
17559
18430
  applyNodeBindings(clonedNode, scope);
17560
18431
  return clonedNode;
17561
18432
  }
17562
- if (isTemplateDefinition(clonedNode)) return {
17563
- type: "tag",
17564
- name: "block",
17565
- attribs: {},
17566
- children: []
17567
- };
18433
+ if (isTemplateDefinition(clonedNode)) {
18434
+ clonedNode.name = "block";
18435
+ clonedNode.attribs = {};
18436
+ clonedNode.children = [];
18437
+ return clonedNode;
18438
+ }
17568
18439
  const templateName = resolveTemplateCall(clonedNode, wxsScopeData(scope));
17569
18440
  if (templateName) {
17570
18441
  const definition = templateRenderState.definitions.get(templateName);
@@ -17573,39 +18444,42 @@ function renderNodeTree(node, scope, context, ownerJsonPath, ownerFilePath, inst
17573
18444
  data: resolveTemplateData(clonedNode, wxsScopeData(scope)),
17574
18445
  wxs: definition && templateRenderState.definitionWxsScopes?.get(definition)
17575
18446
  };
17576
- const children = definition && !templateRenderState.stack.includes(templateName) ? renderChildren(definition.children ?? [], templateScope, context, ownerJsonPath, ownerFilePath, `${instancePath}/template-${templateName}`, seenComponentScopes, {
18447
+ clonedNode.name = "block";
18448
+ clonedNode.attribs = {
18449
+ "data-sim-node": instancePath,
18450
+ "data-sim-scope": scope.getScopeId()
18451
+ };
18452
+ clonedNode.children = definition && !templateRenderState.stack.includes(templateName) ? renderChildren(definition.children ?? [], templateScope, context, ownerJsonPath, ownerFilePath, `${instancePath}/template-${templateName}`, renderPass, {
17577
18453
  definitions: templateRenderState.definitionScopes?.get(definition) ?? templateRenderState.definitions,
17578
18454
  definitionScopes: templateRenderState.definitionScopes,
17579
18455
  definitionWxsScopes: templateRenderState.definitionWxsScopes,
17580
18456
  wxsModules: templateScope.wxs,
17581
18457
  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
- };
18458
+ }, clonedNode) : [];
18459
+ return clonedNode;
17592
18460
  }
17593
18461
  if (clonedNode.name === "slot") {
17594
18462
  const slotName = clonedNode.attribs?.name?.trim() || "default";
17595
18463
  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
18464
+ const fallbackChildren = clonedNode.children ?? [];
18465
+ bindComponentEventHost(clonedNode);
18466
+ clonedNode.name = "block";
18467
+ clonedNode.attribs = {
18468
+ "data-sim-node": instancePath,
18469
+ "data-sim-scope": scope.getScopeId()
17608
18470
  };
18471
+ const slotOwner = scope.getScopeId();
18472
+ if (projected.length && slotOwner && isComponentConstructing(context.componentCache.get(slotOwner))) {
18473
+ clonedNode.children = [];
18474
+ return clonedNode;
18475
+ }
18476
+ clonedNode.children = projected.length ? selectConditionalChildren(projected.map((entry) => entry.node), (node, index) => evaluateConditionalBranch(node, projected[index].scope)).flatMap(({ index }) => {
18477
+ const entry = projected[index];
18478
+ const nodes = renderNodeVariants(entry.node, entry.scope, context, entry.ownerJsonPath, entry.ownerFilePath, entry.instancePath, renderPass, entry.templateRenderState, clonedNode);
18479
+ renderPass.renderedSlots.set(entry, nodes);
18480
+ return nodes;
18481
+ }) : renderChildren(fallbackChildren, scope, context, ownerJsonPath, ownerFilePath, `${instancePath}/slot-fallback-${slotName}`, renderPass, templateRenderState, clonedNode);
18482
+ return clonedNode;
17609
18483
  }
17610
18484
  const componentEntry = resolveComponentRegistryEntry(context, ownerJsonPath, ownerFilePath, clonedNode.name, scope.genericComponents?.get(clonedNode.name));
17611
18485
  if (componentEntry) {
@@ -17614,19 +18488,31 @@ function renderNodeTree(node, scope, context, ownerJsonPath, ownerFilePath, inst
17614
18488
  const { nextProperties, bindingExpressions } = resolveComponentProperties(clonedNode, scope, componentEntry.definition);
17615
18489
  const genericComponents = resolveComponentGenerics(context, clonedNode, ownerJsonPath, ownerFilePath, componentEntry.filePath, scope.genericComponents);
17616
18490
  const slots = collectComponentSlots(clonedNode, scope, ownerJsonPath, ownerFilePath, componentScopeId, templateRenderState);
18491
+ registerSlotDeclarations(renderPass, slots, clonedNode, componentScopeId);
18492
+ applyNodeBindings(clonedNode, scope);
18493
+ const reconcilePrivateTemplate = (instance, phase) => {
18494
+ let bindingPass;
18495
+ do {
18496
+ const bindingScope = createComponentScope(clonedNode, scope, componentScopeId, instance, genericComponents, slots);
18497
+ context.componentScopes.set(componentScopeId, bindingScope);
18498
+ bindingPass = createRenderPass(phase === "properties", renderPass);
18499
+ renderRuntimeComponentTemplate(context, componentEntry, renderNodeTree, bindingScope, componentScopeId, bindingPass);
18500
+ reconcileSlotDeclarations(context, bindingPass);
18501
+ } while (isRenderPassStale(bindingPass));
18502
+ };
17617
18503
  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);
18504
+ if (!componentInstance) componentInstance = createRuntimeComponentInstance(componentScopeId, context, componentEntry, nextProperties, ownerScopeId, reconcilePrivateTemplate, renderPass.propertyUpdate);
18505
+ else {
18506
+ context.componentScopes.set(componentScopeId, createComponentScope(clonedNode, scope, componentScopeId, componentInstance, genericComponents, slots));
18507
+ syncComponentProperties(componentInstance, componentInstance.__definition__ ?? componentEntry.definition, nextProperties, bindingExpressions, context.changedPageKeys, reconcilePrivateTemplate);
18508
+ }
18509
+ if (isRenderPassStale(renderPass)) return clonedNode;
18510
+ renderPass.seenComponentScopes.add(componentScopeId);
17625
18511
  const componentScope = createComponentScope(clonedNode, scope, componentScopeId, componentInstance, genericComponents, slots);
17626
18512
  context.componentScopes.set(componentScopeId, componentScope);
17627
- const renderedComponentRoot = renderRuntimeComponentTemplate(context, componentEntry, renderNodeTree, componentScope, componentScopeId, seenComponentScopes);
18513
+ const renderedComponentRoot = renderRuntimeComponentTemplate(context, componentEntry, renderNodeTree, componentScope, componentScopeId, renderPass);
18514
+ mergeComponentEventRoot(renderedComponentRoot, clonedNode);
17628
18515
  if (renderedComponentRoot.attribs) {
17629
- applyNodeBindings(clonedNode, scope);
17630
18516
  renderedComponentRoot.attribs = {
17631
18517
  ...clonedNode.attribs,
17632
18518
  ...renderedComponentRoot.attribs
@@ -17639,66 +18525,94 @@ function renderNodeTree(node, scope, context, ownerJsonPath, ownerFilePath, inst
17639
18525
  renderedComponentRoot.attribs["data-sim-node"] = instancePath;
17640
18526
  renderedComponentRoot.attribs["data-sim-scope"] = componentScopeId;
17641
18527
  }
18528
+ renderPass.componentRoots.set(componentScopeId, renderedComponentRoot);
17642
18529
  return renderedComponentRoot;
17643
18530
  }
17644
- applyNodeBindings(clonedNode, scope);
18531
+ const richText = clonedNode.name === "rich-text";
18532
+ applyNodeBindings(clonedNode, scope, richText);
17645
18533
  clonedNode.attribs["data-sim-node"] = instancePath;
17646
- clonedNode.children = renderChildren(clonedNode.children ?? [], scope, context, ownerJsonPath, ownerFilePath, `${instancePath}/${clonedNode.name}`, seenComponentScopes, templateRenderState);
18534
+ if (richText) return clonedNode;
18535
+ clonedNode.children = renderChildren(clonedNode.children ?? [], scope, context, ownerJsonPath, ownerFilePath, `${instancePath}/${clonedNode.name}`, renderPass, templateRenderState, clonedNode);
17647
18536
  return clonedNode;
17648
18537
  }
18538
+ function reconcileRuntimePageTree(context, page, bindingsOnly = false) {
18539
+ for (;;) {
18540
+ bindAttachmentBindingScope(context, page);
18541
+ if (page.__lastChangedKeys__) context.changedPageKeys = page.__lastChangedKeys__;
18542
+ const route = page.route.replace(LEADING_SLASH_RE$4, "");
18543
+ const resourcePath = context.project.routes.find((item) => item.route === route)?.resourcePath ?? route;
18544
+ const templatePath = path.resolve(context.project.miniprogramRootPath, `${resourcePath}.wxml`);
18545
+ const document = parseTemplateDocument(readTemplateSource(context.artifactSource, templatePath));
18546
+ const pageScopeId = `page:${route}`;
18547
+ const pageScope = {
18548
+ data: page.data,
18549
+ getMethod: (methodName) => {
18550
+ const method = page[methodName];
18551
+ return typeof method === "function" ? method.bind(page) : void 0;
18552
+ },
18553
+ getScopeId: () => pageScopeId,
18554
+ id: "page-root"
18555
+ };
18556
+ for (const scopeId of context.componentScopes.keys()) if (scopeId === pageScopeId || scopeId.startsWith(`${pageScopeId}/`)) context.componentScopes.delete(scopeId);
18557
+ context.componentScopes.set(pageScopeId, pageScope);
18558
+ const renderPass = createPageRenderPass(page, context.componentCache);
18559
+ const root = (document.children ?? [])[0] ?? document;
18560
+ const templateRenderState = prepareTemplateRenderState(context.artifactSource, root, templatePath, context.project.miniprogramRootPath, getRuntimeWxsLoader(context.moduleLoader, context.artifactSource));
18561
+ const renderedRoot = renderNodeTree(root, pageScope, context, path.resolve(context.project.miniprogramRootPath, `${resourcePath}.json`), `${resourcePath}.js`, pageScopeId, renderPass, templateRenderState);
18562
+ const roots = [renderedRoot];
18563
+ if (hasCustomTabBar(context.project.appConfig, route)) roots.push(renderNodeTree({
18564
+ type: "tag",
18565
+ name: "custom-tab-bar",
18566
+ attribs: {},
18567
+ children: []
18568
+ }, customTabBarHostScope(), context, path.resolve(context.project.miniprogramRootPath, "app.json"), "app.js", pageScopeId, renderPass, templateRenderState));
18569
+ reconcileSlotDeclarations(context, renderPass);
18570
+ if (isRenderPassStale(renderPass)) continue;
18571
+ markPageTreeConstructed(page, context.componentCache);
18572
+ const treeRoot = roots.length > 1 ? {
18573
+ type: "root",
18574
+ children: roots
18575
+ } : renderedRoot;
18576
+ linkRenderedParents(treeRoot);
18577
+ registerInspectionTrees(treeRoot, renderPass, context.componentScopes);
18578
+ if (bindingsOnly) return {
18579
+ root: treeRoot,
18580
+ roots
18581
+ };
18582
+ let bindingsRefreshed = false;
18583
+ const updateBindings = () => {
18584
+ bindingsRefreshed = true;
18585
+ if (hasPendingComponentConstruction(context.componentCache)) return;
18586
+ reconcileRuntimePageTree(context, page, true);
18587
+ };
18588
+ const pageAttached = attachComponentPage(page, updateBindings);
18589
+ if (bindingsRefreshed) continue;
18590
+ const pageAttaching = isComponentPageAttaching(page);
18591
+ const scopeIds = orderComponentAttachmentScopes(renderPass.seenComponentScopes, context.componentScopes);
18592
+ const instances = scopeIds.map((scopeId) => context.componentCache.get(scopeId));
18593
+ const attached = !pageAttaching && runWithAttachmentBindingUpdates(page, updateBindings, (scopes) => flushComponentAttachments(context.componentCache, scopes, (instance) => {
18594
+ runComponentLifecycle(instance, "attached");
18595
+ return bindingsRefreshed;
18596
+ }), scopeIds);
18597
+ if (attached === "restart") continue;
18598
+ const canFinalize = !pageAttaching && !hasPendingComponentAttachments(instances);
18599
+ const relationsChanged = canFinalize && reconcileRemovedComponents(context.componentCache, context.componentScopes, renderPass.seenComponentScopes, pageScopeId);
18600
+ if (pageAttached || attached || relationsChanged) continue;
18601
+ if (canFinalize && !isPageBeforeReady(page)) flushDiscardedComponentReady(context.componentCache, pageScopeId, (instance) => runComponentLifecycle(instance, "ready"));
18602
+ if (canFinalize && !isPageBeforeReady(page) && flushComponentReady(instances, (instance) => {
18603
+ runComponentLifecycle(instance, "ready");
18604
+ if (instance !== context.componentCache.get(customTabBarScopeId(route))) instance.__definition__?.pageLifetimes?.show?.call(instance);
18605
+ })) continue;
18606
+ return {
18607
+ root: treeRoot,
18608
+ roots
18609
+ };
18610
+ }
18611
+ }
17649
18612
  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);
18613
+ const { root, roots } = reconcileRuntimePageTree(context, page);
17700
18614
  return {
17701
- root: treeRoot,
18615
+ root,
17702
18616
  wxml: roots.map(serializeDomNode$1).join("")
17703
18617
  };
17704
18618
  }
@@ -17722,15 +18636,23 @@ function createDuplicateRegistrationError(kind, id) {
17722
18636
  function callDefinitionMethod(instance, method, args) {
17723
18637
  if (typeof method === "function") return method.apply(instance, args);
17724
18638
  }
18639
+ function composeDefinitionMethods(primary, secondary) {
18640
+ if (typeof primary !== "function" || typeof secondary !== "function" || primary === secondary) return primary ?? secondary;
18641
+ return function composedDefinitionMethod(...args) {
18642
+ const result = primary.apply(this, args);
18643
+ secondary.apply(this, args);
18644
+ return result;
18645
+ };
18646
+ }
17725
18647
  function normalizeComponentPageDefinition(definition) {
17726
18648
  const { lifetimes = {}, methods = {}, pageLifetimes = {}, ...rest } = definition;
17727
18649
  const created = lifetimes.created ?? rest.created;
17728
18650
  const attached = lifetimes.attached ?? rest.attached;
17729
- const ready = lifetimes.ready ?? rest.ready ?? methods.onReady ?? rest.onReady;
18651
+ const ready = composeDefinitionMethods(lifetimes.ready ?? rest.ready, methods.onReady ?? rest.onReady);
17730
18652
  const detached = lifetimes.detached ?? rest.detached ?? methods.onUnload ?? rest.onUnload;
17731
18653
  const load = methods.onLoad ?? rest.onLoad;
17732
- const show = pageLifetimes.show ?? methods.onShow ?? rest.onShow;
17733
- const hide = pageLifetimes.hide ?? methods.onHide ?? rest.onHide;
18654
+ const show = typeof pageLifetimes.show === "function" ? composeDefinitionMethods(pageLifetimes.show, methods.onShow) : methods.onShow ?? rest.onShow;
18655
+ const hide = typeof pageLifetimes.hide === "function" ? composeDefinitionMethods(pageLifetimes.hide, methods.onHide) : methods.onHide ?? rest.onHide;
17734
18656
  const resize = pageLifetimes.resize ?? methods.onResize ?? rest.onResize;
17735
18657
  const routeDone = pageLifetimes.routeDone ?? methods.onRouteDone ?? rest.onRouteDone;
17736
18658
  const pageDefinition = {
@@ -17758,10 +18680,7 @@ function normalizeComponentPageDefinition(definition) {
17758
18680
  return callDefinitionMethod(this, routeDone, args);
17759
18681
  }
17760
18682
  };
17761
- registerComponentPageAttachment(pageDefinition, function() {
17762
- callDefinitionMethod(this, created, []);
17763
- callDefinitionMethod(this, attached, []);
17764
- });
18683
+ registerComponentPageAttachment(pageDefinition, (instance) => callDefinitionMethod(instance, created, []), (instance) => callDefinitionMethod(instance, attached, []));
17765
18684
  return pageDefinition;
17766
18685
  }
17767
18686
  function createHostRegistries() {
@@ -17952,7 +18871,7 @@ function resolveCapabilityValue(source, schema) {
17952
18871
  }
17953
18872
  return current;
17954
18873
  }
17955
- function createHeadlessWx(driver, runtimeConsole = console) {
18874
+ function createHeadlessWx(driver, runtimeConsole = console, workers) {
17956
18875
  const eventBus = createHeadlessUniEventBus();
17957
18876
  const rpx2px = (value) => {
17958
18877
  const width = driver.getWindowInfoSync().windowWidth;
@@ -17960,6 +18879,10 @@ function createHeadlessWx(driver, runtimeConsole = console) {
17960
18879
  };
17961
18880
  const capabilityTree = {
17962
18881
  canIUse: true,
18882
+ ...workers ? {
18883
+ createWorker: true,
18884
+ preDownloadSubpackage: true
18885
+ } : {},
17963
18886
  canvasToTempFilePath: true,
17964
18887
  chooseImage: { return: {
17965
18888
  errMsg: true,
@@ -18217,6 +19140,14 @@ function createHeadlessWx(driver, runtimeConsole = console) {
18217
19140
  ...eventBus,
18218
19141
  [WEVU_ROUTE_EVENT_CONTRACT_KEY]: 1,
18219
19142
  [WEVU_PAGE_SCROLL_EVENT_CONTRACT_KEY]: 1,
19143
+ createWorker: (script) => {
19144
+ if (!workers) throw new Error("Worker host is unavailable");
19145
+ return workers.createWorker(script);
19146
+ },
19147
+ preDownloadSubpackage: (option) => {
19148
+ if (!workers) throw new Error("Worker host is unavailable");
19149
+ workers.preDownloadSubpackage(option);
19150
+ },
18220
19151
  canIUse: (schema) => typeof schema === "string" && schema.trim() !== "" && resolveCapabilityValue(capabilityTree, schema.trim()) != null,
18221
19152
  canvasToTempFilePath: (option) => invokeWxApi(() => driver.canvasToTempFilePath(option), option),
18222
19153
  chooseImage: (option) => invokeWxApi(() => driver.chooseImage(option ?? {}), option),
@@ -18340,9 +19271,7 @@ function createHeadlessWx(driver, runtimeConsole = console) {
18340
19271
  navigateBack: (option) => runNavigationApi(driver, option, (deferred) => invokeWxApi(() => {
18341
19272
  driver.navigateBack(deferred);
18342
19273
  }, deferred)),
18343
- navigateTo: (option) => runNavigationApi(driver, option, (deferred) => invokeWxApi(() => {
18344
- driver.navigateTo(deferred);
18345
- }, deferred)),
19274
+ navigateTo: (option) => runNavigationApi(driver, option, (deferred) => driver.navigateTo(deferred)),
18346
19275
  nextTick: (callback) => driver.nextTick(callback),
18347
19276
  offBeforeAppRoute: (listener) => driver.offBeforeAppRoute(listener),
18348
19277
  offBeforePageUnload: (listener) => driver.offBeforePageUnload(listener),
@@ -18453,7 +19382,13 @@ function invokePreparedNavigationApi(prepare, option) {
18453
19382
  //#region ../../mpcore/packages/simulator/src/kernel/artifactSource.ts
18454
19383
  function createFileSystemArtifactSource() {
18455
19384
  return {
18456
- has: (filePath) => fs.existsSync(filePath),
19385
+ has(filePath) {
19386
+ try {
19387
+ return fs.statSync(filePath).isFile();
19388
+ } catch {
19389
+ return false;
19390
+ }
19391
+ },
18457
19392
  readText(filePath) {
18458
19393
  try {
18459
19394
  return fs.readFileSync(filePath, "utf8");
@@ -18598,6 +19533,62 @@ var RuntimeKernel = class {
18598
19533
  }
18599
19534
  };
18600
19535
  //#endregion
19536
+ //#region ../../mpcore/packages/simulator/src/kernel/renderQueue.ts
19537
+ /** 两类宿主共享提交、错误诊断与等待回调边界,失败批次不自动重试。 */
19538
+ var RuntimeRenderQueue = class {
19539
+ options;
19540
+ closed = false;
19541
+ pending = false;
19542
+ running = false;
19543
+ callbacks = [];
19544
+ constructor(options) {
19545
+ this.options = options;
19546
+ }
19547
+ close() {
19548
+ this.closed = true;
19549
+ this.pending = false;
19550
+ this.callbacks.length = 0;
19551
+ }
19552
+ request(callback) {
19553
+ if (this.isClosed()) return;
19554
+ if (callback) this.callbacks.push(callback);
19555
+ if (this.pending) return;
19556
+ this.pending = true;
19557
+ if (this.running) return;
19558
+ Promise.resolve().then(async () => {
19559
+ if (this.isClosed()) return;
19560
+ this.pending = false;
19561
+ this.running = true;
19562
+ const callbacks = this.callbacks.splice(0);
19563
+ try {
19564
+ const rendered = this.options.render();
19565
+ if (rendered && typeof rendered.then === "function") await rendered;
19566
+ } catch (error) {
19567
+ this.options.onError(error);
19568
+ callbacks.push(...this.callbacks.splice(0));
19569
+ this.pending = false;
19570
+ } finally {
19571
+ for (const callback of callbacks) {
19572
+ if (this.isClosed()) break;
19573
+ try {
19574
+ callback();
19575
+ } catch (error) {
19576
+ this.options.onError(error);
19577
+ }
19578
+ }
19579
+ this.running = false;
19580
+ if (this.pending && !this.isClosed()) {
19581
+ this.pending = false;
19582
+ this.request();
19583
+ }
19584
+ }
19585
+ });
19586
+ }
19587
+ isClosed() {
19588
+ return this.closed || this.options.isClosed();
19589
+ }
19590
+ };
19591
+ //#endregion
18601
19592
  //#region ../../mpcore/packages/simulator/src/project/resolveRoutes.ts
18602
19593
  const LEADING_SLASH_RE$3 = /^\/+/;
18603
19594
  const TRAILING_SLASH_RE$1 = /\/+$/;
@@ -18798,6 +19789,22 @@ function loadProject(projectPath) {
18798
19789
  });
18799
19790
  }
18800
19791
  //#endregion
19792
+ //#region ../../mpcore/packages/simulator/src/view/nodeSerialization.ts
19793
+ function escapeText$1(text) {
19794
+ return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
19795
+ }
19796
+ function serializeNode(node) {
19797
+ if (node.type === "text") return escapeText$1(node.data ?? "");
19798
+ if (node.type !== "tag") return (node.children ?? []).map(serializeNode).join("");
19799
+ if (node.name === "page") return (node.children ?? []).filter((child) => !(child.type === "tag" && (child.name === "import" || child.name === "wxs"))).map(serializeNode).join("");
19800
+ const attrs = Object.entries(node.attribs ?? {}).filter(([key]) => !key.startsWith("data-sim-")).map(([key, value]) => ` ${key}="${escapeText$1(value).replaceAll("\"", "&quot;")}"`).join("");
19801
+ return `<${node.name ?? ""}${attrs}>${(node.children ?? []).map(serializeNode).join("")}</${node.name ?? ""}>`;
19802
+ }
19803
+ function collectNodeText$1(node) {
19804
+ if (node.type === "text") return node.data ?? "";
19805
+ return (node.children ?? []).map(collectNodeText$1).join("");
19806
+ }
19807
+ //#endregion
18801
19808
  //#region ../../node_modules/.pnpm/boolbase@2.0.0/node_modules/boolbase/dist/index.js
18802
19809
  function trueFunc() {
18803
19810
  return true;
@@ -20224,23 +21231,31 @@ function appendNextSiblings(element, adapter) {
20224
21231
  const selectAll = getSelectorFunction((query, elements, options) => query === falseFunc || !elements || elements.length === 0 ? [] : findAll(query, elements, options));
20225
21232
  //#endregion
20226
21233
  //#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
- };
21234
+ function createAdapter(getChildren, getParent) {
21235
+ const adapter = {
21236
+ isTag: (node) => node.type === "tag",
21237
+ getAttributeValue: (node, name) => node.attribs?.[name],
21238
+ getChildren,
21239
+ getName: (node) => node.name ?? "",
21240
+ getParent,
21241
+ getSiblings: (node) => {
21242
+ const parent = getParent(node);
21243
+ return parent ? getChildren(parent) : [node];
21244
+ },
21245
+ getText: (node) => node.type === "text" ? node.data ?? "" : getChildren(node).map((child) => adapter.getText(child)).join(""),
21246
+ hasAttrib: (node, name) => Object.hasOwn(node.attribs ?? {}, name),
21247
+ removeSubsets: (nodes) => {
21248
+ const unique = new Set(nodes);
21249
+ return [...unique].filter((node) => {
21250
+ for (let parent = getParent(node); parent; parent = getParent(parent)) if (unique.has(parent)) return false;
21251
+ return true;
21252
+ });
21253
+ }
21254
+ };
21255
+ return adapter;
21256
+ }
21257
+ const renderedAdapter = createAdapter((node) => node.children ?? [], (node) => node.parent ?? null);
21258
+ const inspectionAdapter = createAdapter(getInspectionChildren, getInspectionParent);
20244
21259
  function resolveComponentTags(selectors) {
20245
21260
  return selectors.map((group) => group.map((token) => {
20246
21261
  if (token.type === SelectorType.Pseudo && Array.isArray(token.data)) return {
@@ -20263,7 +21278,7 @@ function resolveComponentTags(selectors) {
20263
21278
  };
20264
21279
  }));
20265
21280
  }
20266
- function querySelectorAll(root, selector) {
21281
+ function queryWithAdapter(root, selector, adapter) {
20267
21282
  const parsed = parse$1(selector);
20268
21283
  const query = resolveComponentTags(parsed);
20269
21284
  const includesPageRoot = root.type === "tag" && root.name === "page" && parsed.some((group) => group.some((token) => token.type === SelectorType.Tag && token.name === "page"));
@@ -20272,12 +21287,18 @@ function querySelectorAll(root, selector) {
20272
21287
  cacheResults: false
20273
21288
  });
20274
21289
  }
21290
+ function querySelectorAll(root, selector) {
21291
+ return queryWithAdapter(root, selector, renderedAdapter);
21292
+ }
21293
+ function queryInspectionSelectorAll(root, selector) {
21294
+ return queryWithAdapter(root, selector, inspectionAdapter);
21295
+ }
20275
21296
  //#endregion
20276
21297
  //#region ../../node_modules/.pnpm/whynot@5.0.0/node_modules/whynot/dist/whynot.umd.cjs
20277
- var require_whynot_umd = /* @__PURE__ */ __commonJSMin(((exports, module) => {
21298
+ var require_whynot_umd = /* @__PURE__ */ __commonJSMin(((exports, module, this$4) => {
20278
21299
  (function(t, s) {
20279
21300
  "object" == typeof exports && "undefined" != typeof module ? s(exports) : "function" == typeof define && define.amd ? define(["exports"], s) : s((t = "undefined" != typeof globalThis ? globalThis : t || self).whynot = {});
20280
- })(exports, (function(t) {
21301
+ })(this$4, (function(t) {
20281
21302
  "use strict";
20282
21303
  function s(t, s, i, r) {
20283
21304
  const n = {
@@ -20625,10 +21646,10 @@ var require_whynot_umd = /* @__PURE__ */ __commonJSMin(((exports, module) => {
20625
21646
  }));
20626
21647
  //#endregion
20627
21648
  //#region ../../node_modules/.pnpm/xspattern@3.1.0/node_modules/xspattern/dist/xspattern.umd.cjs
20628
- var require_xspattern_umd = /* @__PURE__ */ __commonJSMin(((exports, module) => {
21649
+ var require_xspattern_umd = /* @__PURE__ */ __commonJSMin(((exports, module, this$3) => {
20629
21650
  (function(A, B) {
20630
21651
  "object" == typeof exports && "undefined" != typeof module ? B(exports, require_whynot_umd()) : "function" == typeof define && define.amd ? define(["exports", "whynot"], B) : B((A = "undefined" != typeof globalThis ? globalThis : A || self).xspattern = {}, A.whynot);
20631
- })(exports, (function(A, B) {
21652
+ })(this$3, (function(A, B) {
20632
21653
  "use strict";
20633
21654
  function a(A) {
20634
21655
  return (B) => B === A;
@@ -21925,10 +22946,10 @@ var require_xspattern_umd = /* @__PURE__ */ __commonJSMin(((exports, module) =>
21925
22946
  }));
21926
22947
  //#endregion
21927
22948
  //#region ../../node_modules/.pnpm/prsc@4.0.0/node_modules/prsc/dist/prsc.umd.cjs
21928
- var require_prsc_umd = /* @__PURE__ */ __commonJSMin(((exports, module) => {
22949
+ var require_prsc_umd = /* @__PURE__ */ __commonJSMin(((exports, module, this$2) => {
21929
22950
  (function(n, t) {
21930
22951
  "object" == typeof exports && "undefined" != typeof module ? t(exports) : "function" == typeof define && define.amd ? define(["exports"], t) : t((n = "undefined" != typeof globalThis ? globalThis : n || self).prsc = {});
21931
- })(exports, (function(n) {
22952
+ })(this$2, (function(n) {
21932
22953
  "use strict";
21933
22954
  function t(n, t) {
21934
22955
  return {
@@ -22171,12 +23192,12 @@ var require_prsc_umd = /* @__PURE__ */ __commonJSMin(((exports, module) => {
22171
23192
  }));
22172
23193
  //#endregion
22173
23194
  //#region ../../mpcore/packages/simulator/src/view/xpath.ts
22174
- var import_fontoxpath = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
23195
+ var import_fontoxpath = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module, this$1) => {
22175
23196
  (function(root, factory) {
22176
23197
  if (typeof define === "function" && define.amd) define(["xspattern", "prsc"], factory);
22177
23198
  else if (typeof exports === "object") module.exports = factory(require_xspattern_umd(), require_prsc_umd());
22178
23199
  else root.fontoxpath = factory(root.xspattern, root.prsc);
22179
- })(exports, function(xspattern, prsc) {
23200
+ })(this$1, function(xspattern, prsc) {
22180
23201
  return (function(xspattern, prsc) {
22181
23202
  const VERSION = "3.34.0";
22182
23203
  const fontoxpathGlobal = {};
@@ -38019,7 +39040,10 @@ var import_fontoxpath = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin((
38019
39040
  });
38020
39041
  })))(), 1);
38021
39042
  const xpathEngine = Reflect.get(import_fontoxpath, "default") ?? import_fontoxpath;
38022
- function wrapNode(source, parent = null) {
39043
+ function getRenderedChildren(node) {
39044
+ return node.children ?? [];
39045
+ }
39046
+ function wrapNode(source, getChildren, parent = null) {
38023
39047
  const node = {
38024
39048
  attributes: [],
38025
39049
  children: [],
@@ -38042,7 +39066,7 @@ function wrapNode(source, parent = null) {
38042
39066
  prefix: null,
38043
39067
  value
38044
39068
  }));
38045
- node.children = (source.children ?? []).map((child) => wrapNode(child, node));
39069
+ node.children = getChildren(source).map((child) => wrapNode(child, getChildren, node));
38046
39070
  return node;
38047
39071
  }
38048
39072
  function sibling(node, offset) {
@@ -38062,40 +39086,28 @@ const domFacade = {
38062
39086
  getParentNode: (node) => node.parent,
38063
39087
  getPreviousSibling: (node) => sibling(node, -1)
38064
39088
  };
38065
- function queryXPathElements(root, expression) {
39089
+ function queryElements(root, expression, getChildren) {
38066
39090
  if (!expression.trim()) throw new Error("XPath must be a non-empty expression in headless testing runtime.");
38067
39091
  const document = wrapNode(root.type === "root" ? root : {
38068
39092
  type: "root",
38069
39093
  children: [root]
38070
- });
39094
+ }, getChildren);
38071
39095
  return xpathEngine.evaluateXPathToNodes(expression, document, domFacade).filter((node) => node.nodeType === 1).map((node) => node.source);
38072
39096
  }
39097
+ function queryXPathElements(root, expression) {
39098
+ return queryElements(root, expression, getRenderedChildren);
39099
+ }
39100
+ function queryInspectionXPathElements(root, expression) {
39101
+ return queryElements(root, expression, getInspectionChildren);
39102
+ }
38073
39103
  //#endregion
38074
39104
  //#region ../../mpcore/packages/simulator/src/view/nodeHandle.ts
38075
- function escapeText$1(text) {
38076
- return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
38077
- }
38078
39105
  function createValueEventDetail(value, detail) {
38079
39106
  return {
38080
39107
  value,
38081
39108
  ...detail ?? {}
38082
39109
  };
38083
39110
  }
38084
- function escapeAttribute$1(value) {
38085
- return escapeText$1(value).replaceAll("\"", "&quot;");
38086
- }
38087
- function serializeNode(node) {
38088
- if (node.type === "text") return escapeText$1(node.data ?? "");
38089
- if (node.type !== "tag") return (node.children ?? []).map(serializeNode).join("");
38090
- if (node.name === "page") return (node.children ?? []).filter((child) => !(child.type === "tag" && (child.name === "import" || child.name === "wxs"))).map(serializeNode).join("");
38091
- const attrs = Object.entries(node.attribs ?? {}).filter(([key]) => !key.startsWith("data-sim-")).map(([key, value]) => ` ${key}="${escapeAttribute$1(value)}"`).join("");
38092
- const children = (node.children ?? []).map(serializeNode).join("");
38093
- return `<${node.name ?? ""}${attrs}>${children}</${node.name ?? ""}>`;
38094
- }
38095
- function collectText(node) {
38096
- if (node.type === "text") return node.data ?? "";
38097
- return (node.children ?? []).map(collectText).join("");
38098
- }
38099
39111
  function resolvePageScopeId$1(scopeId) {
38100
39112
  if (!scopeId) return null;
38101
39113
  const pagePathIndex = scopeId.indexOf("/page/");
@@ -38116,7 +39128,7 @@ var HeadlessTestingNodeHandle = class HeadlessTestingNodeHandle {
38116
39128
  this.interactions?.assertActive?.();
38117
39129
  }
38118
39130
  query(selector) {
38119
- const nodes = querySelectorAll(this.node, selector);
39131
+ const nodes = (this.interactions?.projectionOnly ? querySelectorAll : queryInspectionSelectorAll)(this.node, selector);
38120
39132
  const scope = this.node.attribs?.["data-sim-scope"];
38121
39133
  if (!scope || !this.interactions) return nodes;
38122
39134
  return nodes.filter((node) => {
@@ -38135,7 +39147,7 @@ var HeadlessTestingNodeHandle = class HeadlessTestingNodeHandle {
38135
39147
  }
38136
39148
  async getElementsByXpath(expression) {
38137
39149
  this.assertActive();
38138
- return queryXPathElements(this.node, expression).map((node) => new HeadlessTestingNodeHandle(node, this.interactions));
39150
+ return (this.interactions?.projectionOnly ? queryXPathElements : queryInspectionXPathElements)(this.node, expression).map((node) => new HeadlessTestingNodeHandle(node, this.interactions));
38139
39151
  }
38140
39152
  async attr(name) {
38141
39153
  this.assertActive();
@@ -38173,7 +39185,7 @@ var HeadlessTestingNodeHandle = class HeadlessTestingNodeHandle {
38173
39185
  }
38174
39186
  async text() {
38175
39187
  this.assertActive();
38176
- return collectText(this.node);
39188
+ return collectNodeText$1(this.node);
38177
39189
  }
38178
39190
  async tagName() {
38179
39191
  this.assertActive();
@@ -38185,10 +39197,23 @@ var HeadlessTestingNodeHandle = class HeadlessTestingNodeHandle {
38185
39197
  const normalizedEventName = eventName.trim();
38186
39198
  if (!normalizedEventName) throw new Error("Event name must be a non-empty string in headless testing runtime.");
38187
39199
  let handlerResults;
38188
- const nativeHandled = this.interactions.dispatchNativeEvent?.(this.node, normalizedEventName, event, normalizedEventName === "tap" ? (result) => {
38189
- handlerResults ??= [];
38190
- handlerResults.push(result);
38191
- } : void 0);
39200
+ let nativeHandled;
39201
+ try {
39202
+ nativeHandled = this.interactions.dispatchNativeEvent?.(this.node, normalizedEventName, event, normalizedEventName === "tap" ? (result) => {
39203
+ handlerResults ??= [];
39204
+ handlerResults.push(result);
39205
+ } : void 0);
39206
+ } catch (error) {
39207
+ let failures;
39208
+ if (handlerResults) {
39209
+ for (const result of await Promise.allSettled(handlerResults)) if (result.status === "rejected") {
39210
+ failures ??= [error];
39211
+ failures.push(result.reason);
39212
+ }
39213
+ }
39214
+ if (failures) throw new AggregateError(failures, "Native interaction handlers failed", { cause: error });
39215
+ throw error;
39216
+ }
38192
39217
  if (normalizedEventName === "tap" && nativeHandled) return handlerResults ? (await Promise.all(handlerResults))[0] : void 0;
38193
39218
  const binding = resolveMiniProgramEventBinding(this.node.attribs, normalizedEventName);
38194
39219
  if (!binding?.method) {
@@ -38200,6 +39225,20 @@ var HeadlessTestingNodeHandle = class HeadlessTestingNodeHandle {
38200
39225
  async tap(event = {}) {
38201
39226
  return await this.trigger("tap", event);
38202
39227
  }
39228
+ async touchstart(event = {}) {
39229
+ return await this.trigger("touchstart", event);
39230
+ }
39231
+ async touchmove(event = {}) {
39232
+ return await this.trigger("touchmove", event);
39233
+ }
39234
+ async touchend(event = {}) {
39235
+ return await this.trigger("touchend", event);
39236
+ }
39237
+ async dispatchEvent({ eventName, $: selector, ...event }) {
39238
+ const target = selector ? await this.$(selector) : this;
39239
+ if (!target) throw new Error(`No node matching "${selector}" was found for dispatchEvent in headless testing runtime.`);
39240
+ return await target.trigger(eventName, event);
39241
+ }
38203
39242
  async input(value, event = {}) {
38204
39243
  return await this.trigger("input", {
38205
39244
  ...event,
@@ -38252,16 +39291,20 @@ function isMustacheOnly(value) {
38252
39291
  return trimmed.startsWith("{{") && trimmed.endsWith("}}") && !trimmed.includes("{{", 2);
38253
39292
  }
38254
39293
  function visitDom(node, visitor) {
38255
- visitor(node);
39294
+ if (visitor(node) === false) return;
38256
39295
  for (const child of node.children ?? []) visitDom(child, visitor);
38257
39296
  }
38258
39297
  function interpolateDomTree(root, data) {
38259
39298
  visitDom(root, (node) => {
39299
+ const richText = node.type === "tag" && node.name === "rich-text";
39300
+ if (richText) node.children = [];
38260
39301
  if (typeof node.data === "string") node.data = interpolateTemplateText(node.data, data);
38261
39302
  if (node.attribs) {
38262
39303
  let dataset;
38263
39304
  for (const [key, value] of Object.entries(node.attribs)) {
38264
39305
  const resolvedValue = isMustacheOnly(value) ? resolveRawValueByPath(data, value.trim().slice(2, -2)) : interpolateTemplate(value, data);
39306
+ if (key === "value") setNativePickerValue(node, resolvedValue);
39307
+ if (richText && key === "nodes") node.children = renderRichTextNodes(resolvedValue);
38265
39308
  node.attribs[key] = String(resolvedValue ?? "");
38266
39309
  if (isDatasetAttribute(key)) {
38267
39310
  dataset ??= {};
@@ -38270,6 +39313,7 @@ function interpolateDomTree(root, data) {
38270
39313
  }
38271
39314
  node.dataset = dataset;
38272
39315
  }
39316
+ if (richText) return false;
38273
39317
  });
38274
39318
  }
38275
39319
  function escapeText(text) {
@@ -38291,11 +39335,12 @@ function renderPageTree(project, page) {
38291
39335
  const templateSource = project.artifactSource.readText(templatePath);
38292
39336
  if (templateSource == null) throw new Error(`Missing template in headless runtime: ${templatePath}`);
38293
39337
  const document = parseDocument(`<page>${templateSource}</page>`, {
38294
- xmlMode: false,
39338
+ xmlMode: true,
38295
39339
  decodeEntities: false,
38296
39340
  recognizeSelfClosing: true
38297
39341
  });
38298
39342
  interpolateDomTree(document, page.data);
39343
+ linkRenderedParents(document);
38299
39344
  return {
38300
39345
  root: document,
38301
39346
  wxml: serializeDomNode((document.children ?? [])[0] ?? document)
@@ -38466,7 +39511,7 @@ function getNativeScrollOffset(page, node) {
38466
39511
  };
38467
39512
  }
38468
39513
  /** 原生交互更新宿主状态,导航仍交给统一 wx 路由入口。 */
38469
- function dispatchNativeNodeEvent(page, node, eventName, event, wx, resolveMethod, onHandlerResult) {
39514
+ function dispatchNativeNodeEvent(page, node, eventName, event, wx, resolveMethod, onHandlerResult, resolveComponent) {
38470
39515
  if (eventName === "scroll" && node.name === "scroll-view") {
38471
39516
  const detail = event.detail;
38472
39517
  const state = pageScrollViews.get(page)?.get(nodeKey(node));
@@ -38480,30 +39525,42 @@ function dispatchNativeNodeEvent(page, node, eventName, event, wx, resolveMethod
38480
39525
  if (eventName !== "tap") return false;
38481
39526
  let current = node;
38482
39527
  let navigator;
39528
+ let button;
39529
+ const origin = getNativeNodeOwner(node)?.target ?? node;
38483
39530
  let handlers;
38484
39531
  let handled = false;
38485
- while (current) {
38486
- if (!navigator && current.name === "navigator") navigator = current;
38487
- const binding = resolveMiniProgramEventBinding(current.attribs, "tap");
38488
- if (binding?.method) {
38489
- const call = resolveMethod(current.attribs?.["data-sim-scope"] ?? null, binding.method);
39532
+ const collectHandler = (scopeId, target, binding) => {
39533
+ if (binding.method) {
39534
+ const call = resolveMethod(scopeId, binding.method);
38490
39535
  handlers ??= [];
38491
39536
  handlers.push({
38492
- node: current,
39537
+ event: createMiniProgramEventPayload(target, "tap", event, origin),
38493
39538
  call
38494
39539
  });
38495
39540
  handled = true;
38496
39541
  }
38497
- if (binding?.stopAfter) {
39542
+ };
39543
+ while (current) {
39544
+ if (!navigator && current.name === "navigator") navigator = current;
39545
+ if (!button && current.name === "button") button = current;
39546
+ if (visitNativeNodeEventBindings(current, "tap", collectHandler)) {
38498
39547
  handled = true;
38499
39548
  break;
38500
39549
  }
38501
39550
  current = current.parent;
38502
39551
  }
39552
+ const submit = button ? prepareNativeFormSubmit(button, `page:${page.route}`, event, resolveMethod, resolveComponent) : void 0;
38503
39553
  if (handlers) for (const handler of handlers) {
38504
- const result = handler.call(createMiniProgramEventPayload(handler.node, "tap", event, node));
39554
+ const result = handler.call(handler.event);
38505
39555
  onHandlerResult?.(result);
38506
39556
  }
39557
+ if (submit) {
39558
+ handled = true;
39559
+ if (submit.call) {
39560
+ const result = submit.call(submit.event);
39561
+ onHandlerResult?.(result);
39562
+ }
39563
+ }
38507
39564
  if (!navigator) return handled;
38508
39565
  const openType = navigator.attribs?.["open-type"] ?? "navigate";
38509
39566
  const url = navigator.attribs?.url ?? "";
@@ -39288,6 +40345,64 @@ function createHeadlessMediaQueryObserver(driver, onDisconnect) {
39288
40345
  };
39289
40346
  }
39290
40347
  //#endregion
40348
+ //#region ../../mpcore/packages/simulator/src/view/templateRegistration.ts
40349
+ /** 编译模板及递归依赖共用全局注册图,图外原生模板继续实时读取。 */
40350
+ function createTemplateRegistration(source, projectRoot, compiledTemplatePaths = []) {
40351
+ const root = normalize(projectRoot);
40352
+ const normalizeTemplatePath = (filePath) => {
40353
+ const normalized = normalize(filePath);
40354
+ const local = relative(root, normalized);
40355
+ if (!normalized.endsWith(".wxml") || isAbsolute(local) || local === ".." || local.startsWith("../")) throw new TypeError("Compiled template paths must stay inside the mini-program root and end in .wxml.");
40356
+ return normalized;
40357
+ };
40358
+ const resolveRelativeTemplatePath = (filePath) => {
40359
+ const local = normalize(filePath);
40360
+ if (isAbsolute(local) || local === ".." || local.startsWith("../")) throw new TypeError("Compiled template paths must stay inside the mini-program root and end in .wxml.");
40361
+ return normalizeTemplatePath(join(root, local));
40362
+ };
40363
+ const entries = [...new Set(compiledTemplatePaths.map((filePath) => {
40364
+ if (typeof filePath !== "string" || !filePath || isAbsolute(filePath)) throw new TypeError("Compiled template paths must be project-relative WXML paths.");
40365
+ return resolveRelativeTemplatePath(filePath);
40366
+ }))];
40367
+ let registered = /* @__PURE__ */ new Map();
40368
+ function readSnapshot() {
40369
+ const snapshot = /* @__PURE__ */ new Map();
40370
+ const collect = (filePath) => {
40371
+ filePath = normalizeTemplatePath(filePath);
40372
+ if (snapshot.has(filePath)) return;
40373
+ const content = source.readText(filePath);
40374
+ if (content === void 0) throw new Error(`Missing compiled template during host registration: ${relative(root, filePath)}`);
40375
+ snapshot.set(filePath, content);
40376
+ const visit = (node) => {
40377
+ const request = node.attribs?.src;
40378
+ if ((node.name === "include" || node.name === "import") && request) {
40379
+ const target = (request.endsWith(".wxml") ? request : `${request}.wxml`).replaceAll("\\", "/");
40380
+ if (isAbsolute(target) && !target.startsWith("/")) throw new TypeError("Compiled template paths must stay inside the mini-program root and end in .wxml.");
40381
+ collect(resolveRelativeTemplatePath(target.startsWith("/") ? target.slice(1) : join(relative(root, dirname(filePath)), target)));
40382
+ }
40383
+ for (const child of node.children ?? []) visit(child);
40384
+ };
40385
+ visit(parseWxsTemplateDocument(content));
40386
+ };
40387
+ for (const filePath of entries) collect(filePath);
40388
+ return snapshot;
40389
+ }
40390
+ function reload() {
40391
+ const snapshot = readSnapshot();
40392
+ const changed = snapshot.size !== registered.size || [...snapshot].some(([filePath, content]) => registered.get(filePath) !== content);
40393
+ registered = snapshot;
40394
+ return changed;
40395
+ }
40396
+ reload();
40397
+ return {
40398
+ artifactSource: {
40399
+ has: (filePath) => registered.has(normalize(filePath)) || source.has(filePath),
40400
+ readText: (filePath) => registered.get(normalize(filePath)) ?? source.readText(filePath)
40401
+ },
40402
+ reload
40403
+ };
40404
+ }
40405
+ //#endregion
39291
40406
  //#region ../../mpcore/packages/simulator/src/view/videoContext.ts
39292
40407
  function createEventPayload(node, eventName, detail) {
39293
40408
  const dataset = collectNodeDataset(node);
@@ -39416,6 +40531,490 @@ var HeadlessAppLifecycle = class {
39416
40531
  }
39417
40532
  };
39418
40533
  //#endregion
40534
+ //#region ../../mpcore/packages/simulator/src/runtime/workerHost.ts
40535
+ /** 两种 simulator 共用消息与生命周期;每个 worker 独立模块缓存、全局对象及计时器。 */
40536
+ function createWorkerHost(options) {
40537
+ const active = /* @__PURE__ */ new Set();
40538
+ let closed = false;
40539
+ const configuredRoot = () => {
40540
+ return JSON.parse(options.read(join(options.root, "app.json")) ?? "{}").workers;
40541
+ };
40542
+ return {
40543
+ apis: {
40544
+ preDownloadSubpackage(option) {
40545
+ const config = configuredRoot();
40546
+ if (option.packageType !== "workers" || typeof config !== "object" || !config.isSubpackage) {
40547
+ const error = /* @__PURE__ */ new Error("preDownloadSubpackage:fail workers subpackage is not configured");
40548
+ option.fail?.(error);
40549
+ option.complete?.();
40550
+ return;
40551
+ }
40552
+ const result = { errMsg: "preDownloadSubpackage:ok" };
40553
+ option.success?.(result);
40554
+ option.complete?.(result);
40555
+ },
40556
+ createWorker(scriptPath) {
40557
+ if (closed) throw new Error("createWorker:fail runtime has closed");
40558
+ const config = configuredRoot();
40559
+ const directory = typeof config === "string" ? config : config?.path;
40560
+ if (!directory || typeof scriptPath !== "string" || !scriptPath.endsWith(".js")) throw new Error("createWorker:fail invalid worker path");
40561
+ const root = normalize(join(options.root, directory));
40562
+ const entry = normalize(join(options.root, scriptPath));
40563
+ const inside = (file) => file.startsWith(`${root}/`);
40564
+ if (!inside(entry) || options.read(entry) === void 0) throw new Error("createWorker:fail worker entry is outside the configured directory or missing");
40565
+ const messages = /* @__PURE__ */ new Set();
40566
+ const incoming = /* @__PURE__ */ new Set();
40567
+ const errors = /* @__PURE__ */ new Set();
40568
+ const cache = /* @__PURE__ */ new Map();
40569
+ let terminated = false;
40570
+ const scheduler = new RuntimeScheduler((error) => {
40571
+ const value = { message: error instanceof Error ? error.message : String(error) };
40572
+ if (errors.size) for (const listener of errors) listener(value);
40573
+ else options.console.error(value.message);
40574
+ });
40575
+ const post = (listeners, message) => {
40576
+ if (terminated) return;
40577
+ const copy = structuredClone(message);
40578
+ scheduler.setTimeout(() => {
40579
+ for (const listener of [...listeners]) {
40580
+ if (terminated) break;
40581
+ listener(copy);
40582
+ }
40583
+ }, 0);
40584
+ };
40585
+ const globals = {
40586
+ wx: void 0,
40587
+ App: void 0,
40588
+ Page: void 0,
40589
+ Component: void 0,
40590
+ getApp: void 0,
40591
+ console: options.console,
40592
+ worker: {
40593
+ onMessage: (listener) => incoming.add(listener),
40594
+ postMessage: (message) => post(messages, message)
40595
+ },
40596
+ setTimeout: scheduler.setTimeout.bind(scheduler),
40597
+ clearTimeout: scheduler.clearTimeout.bind(scheduler),
40598
+ setInterval: scheduler.setInterval.bind(scheduler),
40599
+ clearInterval: scheduler.clearInterval.bind(scheduler)
40600
+ };
40601
+ globals.globalThis = globals;
40602
+ const execute = options.createExecutor(globals);
40603
+ const load = (file) => {
40604
+ if (terminated) return;
40605
+ if (!inside(file)) throw new Error("Worker require cannot leave its configured directory");
40606
+ if (cache.has(file)) return cache.get(file).exports;
40607
+ const source = options.read(file);
40608
+ if (source === void 0) throw new Error(`Missing worker module: ${file}`);
40609
+ if (file.endsWith(".json")) return JSON.parse(source);
40610
+ const module = { exports: {} };
40611
+ cache.set(file, module);
40612
+ const require = Object.assign((request) => {
40613
+ const target = resolveMiniProgramModule(file, request, root, (candidate) => options.read(candidate) !== void 0);
40614
+ if (!target) throw new Error(`Cannot resolve worker module: ${request}`);
40615
+ return load(target);
40616
+ }, { async: (request) => Promise.resolve().then(() => require(request)) });
40617
+ try {
40618
+ execute(source, file, module, require);
40619
+ } catch (error) {
40620
+ cache.delete(file);
40621
+ throw error;
40622
+ }
40623
+ return module.exports;
40624
+ };
40625
+ const worker = {
40626
+ onMessage: (listener) => {
40627
+ messages.add(listener);
40628
+ },
40629
+ offMessage: (listener) => {
40630
+ if (listener) messages.delete(listener);
40631
+ else messages.clear();
40632
+ },
40633
+ postMessage: (message) => post(incoming, message),
40634
+ onError: (listener) => {
40635
+ errors.add(listener);
40636
+ },
40637
+ offError: (listener) => {
40638
+ if (listener) errors.delete(listener);
40639
+ else errors.clear();
40640
+ },
40641
+ terminate() {
40642
+ if (terminated) return;
40643
+ terminated = true;
40644
+ scheduler.close();
40645
+ messages.clear();
40646
+ incoming.clear();
40647
+ errors.clear();
40648
+ cache.clear();
40649
+ active.delete(worker);
40650
+ }
40651
+ };
40652
+ active.add(worker);
40653
+ scheduler.setTimeout(() => load(entry), 0);
40654
+ return worker;
40655
+ }
40656
+ },
40657
+ close() {
40658
+ closed = true;
40659
+ for (const worker of active) worker.terminate();
40660
+ }
40661
+ };
40662
+ }
40663
+ //#endregion
40664
+ //#region ../../mpcore/packages/simulator/src/runtime/moduleSyntax/ast.ts
40665
+ function isSyntaxNode(value) {
40666
+ return value !== null && typeof value === "object" && "type" in value && typeof value.type === "string";
40667
+ }
40668
+ function childNodes(node) {
40669
+ const children = [];
40670
+ for (const [key, value] of Object.entries(node)) for (const child of Array.isArray(value) ? value : [value]) if (isSyntaxNode(child)) children.push({
40671
+ key,
40672
+ node: child
40673
+ });
40674
+ return children;
40675
+ }
40676
+ function patternIdentifiers(node) {
40677
+ if (node.type === "Identifier") return [node];
40678
+ const keys = node.type === "ObjectPattern" ? ["properties"] : node.type === "ObjectProperty" ? ["value"] : node.type === "AssignmentPattern" ? ["left"] : node.type === "RestElement" ? ["argument"] : ["elements"];
40679
+ return childNodes(node).filter((child) => keys.includes(child.key)).flatMap((child) => patternIdentifiers(child.node));
40680
+ }
40681
+ function createUniqueName(root) {
40682
+ const used = /* @__PURE__ */ new Set();
40683
+ const visit = (node) => {
40684
+ if (node.type === "Identifier" && typeof node.name === "string") used.add(node.name);
40685
+ childNodes(node).forEach((child) => visit(child.node));
40686
+ };
40687
+ visit(root);
40688
+ let index = 0;
40689
+ return () => {
40690
+ let name;
40691
+ do
40692
+ name = `__mpcoreEsm${index++}`;
40693
+ while (used.has(name));
40694
+ used.add(name);
40695
+ return name;
40696
+ };
40697
+ }
40698
+ function applySourceEdits(source, edits) {
40699
+ for (const edit of edits.toSorted((left, right) => right.start - left.start || right.end - left.end)) source = source.slice(0, edit.start) + edit.content + source.slice(edit.end);
40700
+ return source;
40701
+ }
40702
+ //#endregion
40703
+ //#region ../../mpcore/packages/simulator/src/runtime/moduleSyntax/bindings.ts
40704
+ const functionTypes = /* @__PURE__ */ new Set([
40705
+ "FunctionDeclaration",
40706
+ "FunctionExpression",
40707
+ "ArrowFunctionExpression",
40708
+ "ObjectMethod",
40709
+ "ClassMethod",
40710
+ "ClassPrivateMethod"
40711
+ ]);
40712
+ const blockTypes = /* @__PURE__ */ new Set([
40713
+ "BlockStatement",
40714
+ "CatchClause",
40715
+ "ForStatement",
40716
+ "ForInStatement",
40717
+ "ForOfStatement",
40718
+ "SwitchStatement",
40719
+ "StaticBlock"
40720
+ ]);
40721
+ /** 按词法作用域改写导入引用;声明、键名及同名局部变量保持原样。 */
40722
+ function rewriteImportReferences(root, imports, bindingName) {
40723
+ const scopes = /* @__PURE__ */ new WeakMap();
40724
+ const declarations = /* @__PURE__ */ new WeakSet();
40725
+ const moduleScope = {
40726
+ bindings: /* @__PURE__ */ new Set(),
40727
+ variable: true,
40728
+ functionDepth: 0
40729
+ };
40730
+ const declare = (pattern, scope) => {
40731
+ if (!isSyntaxNode(pattern)) return;
40732
+ for (const identifier of patternIdentifiers(pattern)) {
40733
+ scope.bindings.add(String(identifier.name));
40734
+ declarations.add(identifier);
40735
+ }
40736
+ };
40737
+ const visit = (node, inherited, parent, key) => {
40738
+ let scope = inherited;
40739
+ if (functionTypes.has(node.type)) {
40740
+ if (node.type === "FunctionDeclaration") declare(node.id, inherited);
40741
+ scope = {
40742
+ parent: inherited,
40743
+ bindings: /* @__PURE__ */ new Set(),
40744
+ variable: false,
40745
+ functionDepth: inherited.functionDepth + 1
40746
+ };
40747
+ declare(node.id, scope);
40748
+ if (Array.isArray(node.params)) node.params.forEach((parameter) => declare(parameter, scope));
40749
+ } else if (node.type === "ClassDeclaration" || node.type === "ClassExpression") {
40750
+ if (node.type === "ClassDeclaration") declare(node.id, inherited);
40751
+ scope = {
40752
+ parent: inherited,
40753
+ bindings: /* @__PURE__ */ new Set(),
40754
+ variable: false,
40755
+ functionDepth: inherited.functionDepth
40756
+ };
40757
+ declare(node.id, scope);
40758
+ } else if (blockTypes.has(node.type)) {
40759
+ scope = {
40760
+ parent: inherited,
40761
+ bindings: /* @__PURE__ */ new Set(),
40762
+ variable: node.type === "StaticBlock" || key === "body" && !!parent && functionTypes.has(parent.type),
40763
+ functionDepth: inherited.functionDepth
40764
+ };
40765
+ if (node.type === "CatchClause") declare(node.param, scope);
40766
+ }
40767
+ scopes.set(node, scope);
40768
+ if (node.type === "VariableDeclaration") {
40769
+ let owner = scope;
40770
+ while (node.kind === "var" && !owner.variable && owner.parent) owner = owner.parent;
40771
+ for (const child of childNodes(node)) declare(child.node.id, owner);
40772
+ }
40773
+ if (node.type === "ImportDeclaration") {
40774
+ for (const child of childNodes(node)) if (child.key === "specifiers") declare(child.node.local, moduleScope);
40775
+ return;
40776
+ }
40777
+ if (node.type === "ImportExpression" || node.type === "CallExpression" && isSyntaxNode(node.callee) && node.callee.type === "Import") throw new SyntaxError("Dynamic import is not supported by the synchronous mini-program module loader.");
40778
+ if (node.type === "MetaProperty" && isSyntaxNode(node.meta) && node.meta.name === "import") throw new SyntaxError("import.meta is not supported by the synchronous mini-program module loader.");
40779
+ if (scope.functionDepth === 0 && (node.type === "AwaitExpression" || node.type === "ForOfStatement" && node.await)) throw new SyntaxError("Top-level await is not supported by the synchronous mini-program module loader.");
40780
+ if (scope.functionDepth === 0 && node.type === "ReturnStatement") throw new SyntaxError("Top-level return is not valid in a mini-program ES module.");
40781
+ for (const child of childNodes(node)) {
40782
+ const computedMethodKey = functionTypes.has(node.type) && child.key === "key";
40783
+ const switchDiscriminant = node.type === "SwitchStatement" && child.key === "discriminant";
40784
+ visit(child.node, computedMethodKey || switchDiscriminant ? inherited : scope, node, child.key);
40785
+ }
40786
+ };
40787
+ visit(root, moduleScope);
40788
+ const edits = [];
40789
+ const rewrite = (node, parent, key) => {
40790
+ if (node.type === "ImportDeclaration" || node.type === "ExportAllDeclaration" || node.type === "ExportNamedDeclaration" && !node.declaration) return;
40791
+ if (node.type === "Identifier" && !declarations.has(node) && imports.has(String(node.name))) {
40792
+ const property = parent && key === "key" && !parent.computed && [
40793
+ "ObjectProperty",
40794
+ "ObjectMethod",
40795
+ "ClassMethod",
40796
+ "ClassPrivateMethod",
40797
+ "ClassProperty"
40798
+ ].includes(parent.type);
40799
+ const member = parent && key === "property" && !parent.computed && ["MemberExpression", "OptionalMemberExpression"].includes(parent.type);
40800
+ const label = key === "label" || parent?.type === "PrivateName" || parent?.type === "MetaProperty";
40801
+ let owner = scopes.get(node);
40802
+ while (owner && !owner.bindings.has(String(node.name))) owner = owner.parent;
40803
+ if (!property && !member && !label && owner === moduleScope) {
40804
+ let content = `${bindingName}[${JSON.stringify(node.name)}]`;
40805
+ if (parent && (key === "callee" && ["CallExpression", "OptionalCallExpression"].includes(parent.type) || key === "tag" && parent.type === "TaggedTemplateExpression")) content = `(0, ${content})`;
40806
+ if (parent?.type === "ObjectProperty" && parent.shorthand && key === "value") content = `${node.name}: ${content}`;
40807
+ edits.push({
40808
+ start: node.start,
40809
+ end: node.end,
40810
+ content
40811
+ });
40812
+ }
40813
+ }
40814
+ childNodes(node).forEach((child) => rewrite(child.node, node, child.key));
40815
+ };
40816
+ rewrite(root);
40817
+ return edits;
40818
+ }
40819
+ //#endregion
40820
+ //#region ../../mpcore/packages/simulator/src/runtime/moduleSyntax/index.ts
40821
+ function position(node, edge) {
40822
+ const value = node[edge];
40823
+ if (typeof value !== "number") throw new SyntaxError("Missing source range in mini-program module syntax.");
40824
+ return value;
40825
+ }
40826
+ function identifierName(node) {
40827
+ return node.type === "Identifier" ? String(node.name) : String(node.value);
40828
+ }
40829
+ /** 同步转换静态 ESM;CJS 保留原字节,Node 与浏览器共享导入和导出语义。 */
40830
+ function transformMiniProgramModule(source) {
40831
+ if (!/\b(?:import|export)\b/.test(source)) return source;
40832
+ const ast = parse$2(source, {
40833
+ sourceType: "unambiguous",
40834
+ allowReturnOutsideFunction: true,
40835
+ tokens: true
40836
+ });
40837
+ const moduleNodes = ast.program.body.filter((node) => [
40838
+ "ImportDeclaration",
40839
+ "ExportNamedDeclaration",
40840
+ "ExportDefaultDeclaration",
40841
+ "ExportAllDeclaration"
40842
+ ].includes(node.type));
40843
+ if (moduleNodes.length === 0) return source;
40844
+ const root = ast.program;
40845
+ const fresh = createUniqueName(root);
40846
+ const moduleName = fresh();
40847
+ const requireName = fresh();
40848
+ const objectName = fresh();
40849
+ const bindingsName = fresh();
40850
+ const dependencies = /* @__PURE__ */ new Map();
40851
+ const imports = /* @__PURE__ */ new Map();
40852
+ const exports = /* @__PURE__ */ new Map();
40853
+ const hoistedDefaultNames = [];
40854
+ const starExports = [];
40855
+ const edits = [];
40856
+ const dependency = (request) => {
40857
+ let name = dependencies.get(request);
40858
+ if (!name) {
40859
+ name = fresh();
40860
+ dependencies.set(request, name);
40861
+ }
40862
+ return name;
40863
+ };
40864
+ const remove = (node) => edits.push({
40865
+ start: position(node, "start"),
40866
+ end: position(node, "end"),
40867
+ content: ""
40868
+ });
40869
+ const own = (object, key) => `${objectName}.prototype.hasOwnProperty.call(${object}, ${key})`;
40870
+ for (const node of moduleNodes) {
40871
+ if ("attributes" in node && node.attributes?.length) throw new SyntaxError("Import attributes are not supported by the synchronous mini-program module loader.");
40872
+ if (node.type === "ImportDeclaration") {
40873
+ if (node.phase) throw new SyntaxError("Import phases are not supported by the synchronous mini-program module loader.");
40874
+ const namespace = dependency(node.source.value);
40875
+ for (const specifier of node.specifiers) {
40876
+ const binding = specifier.type === "ImportNamespaceSpecifier" ? namespace : specifier.type === "ImportDefaultSpecifier" ? `${namespace} != null && ${own(namespace, "\"default\"")} ? ${namespace}.default : ${namespace}` : `${namespace}[${JSON.stringify(identifierName(specifier.imported))}]`;
40877
+ imports.set(specifier.local.name, binding);
40878
+ }
40879
+ remove(node);
40880
+ } else if (node.type === "ExportNamedDeclaration") {
40881
+ if (node.declaration) {
40882
+ edits.push({
40883
+ start: position(node, "start"),
40884
+ end: position(node.declaration, "start"),
40885
+ content: ""
40886
+ });
40887
+ const patterns = node.declaration.type === "VariableDeclaration" ? node.declaration.declarations.map((declaration) => declaration.id) : "id" in node.declaration && node.declaration.id ? [node.declaration.id] : [];
40888
+ for (const pattern of patterns) for (const identifier of patternIdentifiers(pattern)) exports.set(String(identifier.name), String(identifier.name));
40889
+ } else {
40890
+ const namespace = node.source ? dependency(node.source.value) : void 0;
40891
+ for (const specifier of node.specifiers) {
40892
+ const exported = identifierName(specifier.exported);
40893
+ if (specifier.type === "ExportNamespaceSpecifier" && namespace) exports.set(exported, namespace);
40894
+ else if (specifier.type === "ExportSpecifier") {
40895
+ const local = identifierName(specifier.local);
40896
+ exports.set(exported, namespace ? `${namespace}[${JSON.stringify(local)}]` : local);
40897
+ } else throw new SyntaxError(`Unsupported mini-program export specifier: ${specifier.type}.`);
40898
+ }
40899
+ remove(node);
40900
+ }
40901
+ } else if (node.type === "ExportAllDeclaration") {
40902
+ starExports.push(dependency(node.source.value));
40903
+ remove(node);
40904
+ } else if (node.type === "ExportDefaultDeclaration") {
40905
+ const declaration = node.declaration;
40906
+ const defaultToken = ast.tokens?.find((token) => token.start > position(node, "start") && token.end <= position(declaration, "start") && token.type.label === "default");
40907
+ if (!defaultToken) throw new SyntaxError("Missing default export token in mini-program module syntax.");
40908
+ if (declaration.type === "FunctionDeclaration" && !declaration.id) {
40909
+ const name = fresh();
40910
+ const keyword = ast.tokens?.find((token) => token.start >= position(declaration, "start") && token.type.label === "function");
40911
+ const nameToken = declaration.generator ? ast.tokens?.find((token) => token.start >= (keyword?.end ?? position(declaration, "start")) && token.type.label === "*") : keyword;
40912
+ if (!nameToken) throw new SyntaxError("Missing default function token in mini-program module syntax.");
40913
+ exports.set("default", name);
40914
+ hoistedDefaultNames.push(name);
40915
+ edits.push({
40916
+ start: position(node, "start"),
40917
+ end: defaultToken.end,
40918
+ content: ""
40919
+ });
40920
+ edits.push({
40921
+ start: nameToken.end,
40922
+ end: nameToken.end,
40923
+ content: ` ${name}`
40924
+ });
40925
+ } else if ((declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") && declaration.id) {
40926
+ exports.set("default", declaration.id.name);
40927
+ edits.push({
40928
+ start: position(node, "start"),
40929
+ end: defaultToken.end,
40930
+ content: ""
40931
+ });
40932
+ } else {
40933
+ const name = fresh();
40934
+ exports.set("default", name);
40935
+ edits.push({
40936
+ start: position(node, "start"),
40937
+ end: defaultToken.end,
40938
+ content: `const ${name} = `
40939
+ });
40940
+ edits.push({
40941
+ start: position(node, "end"),
40942
+ end: position(node, "end"),
40943
+ content: ";"
40944
+ });
40945
+ }
40946
+ }
40947
+ }
40948
+ edits.push(...rewriteImportReferences(root, new Set(imports.keys()), bindingsName));
40949
+ const declarations = [
40950
+ dependencies.size ? `let ${[...dependencies.values()].join(", ")};` : "",
40951
+ `const ${bindingsName} = ${objectName}.create(null);`,
40952
+ ...[...imports].map(([name, expression]) => `${objectName}.defineProperty(${bindingsName}, ${JSON.stringify(name)}, { get: () => (${expression}) });`),
40953
+ ...[...exports].map(([name, expression]) => {
40954
+ const value = imports.has(expression) ? `${bindingsName}[${JSON.stringify(expression)}]` : expression;
40955
+ return `${objectName}.defineProperty(${moduleName}.exports, ${JSON.stringify(name)}, { enumerable: true, get: () => ${value} });`;
40956
+ }),
40957
+ ...hoistedDefaultNames.map((name) => `${objectName}.defineProperty(${name}, 'name', { value: 'default', configurable: true });`),
40958
+ ...[...dependencies].map(([request, namespace]) => `${namespace} = ${requireName}(${JSON.stringify(request)});`)
40959
+ ];
40960
+ if (starExports.length) {
40961
+ const owners = fresh();
40962
+ const namespace = fresh();
40963
+ const key = fresh();
40964
+ declarations.push(`
40965
+ const ${owners} = ${objectName}.create(null);
40966
+ for (const ${namespace} of [${starExports.join(", ")}]) {
40967
+ for (const ${key} of ${objectName}.keys(${namespace})) {
40968
+ if (${key} === 'default' || ${own(`${moduleName}.exports`, key)}) continue;
40969
+ ${owners}[${key}] = ${own(owners, key)} && ${owners}[${key}] !== ${namespace} ? null : ${namespace};
40970
+ }
40971
+ }
40972
+ for (const ${key} of ${objectName}.keys(${owners})) {
40973
+ if (${owners}[${key}] !== null) ${objectName}.defineProperty(${moduleName}.exports, ${key}, { enumerable: true, get: () => ${owners}[${key}][${key}] });
40974
+ }
40975
+ `);
40976
+ }
40977
+ return `(function(${moduleName}, ${requireName}, ${objectName}) {\n"use strict";\n${declarations.join("\n")}\n${applySourceEdits(source, edits)}\n${objectName}.preventExtensions(${moduleName}.exports);\n}).call(undefined, module, require, Object);`;
40978
+ }
40979
+ //#endregion
40980
+ //#region ../../mpcore/packages/simulator/src/runtime/pageDefinitionReload.ts
40981
+ /** 定义更新只解析已有路由,不触发普通导航的启动或 page-not-found 生命周期。 */
40982
+ function resolvePageDefinitionReloadRoute(project, route) {
40983
+ if (typeof route !== "string" || !route.trim() || /[?#]/.test(route)) throw new TypeError("Page definition reload requires a route path without query or hash.");
40984
+ const normalized = route.trim().replace(/^\/+/, "");
40985
+ const record = project.routes.find((record) => record.route === normalized);
40986
+ if (!record) throw new Error(`Unknown page definition reload route: ${route}`);
40987
+ return record;
40988
+ }
40989
+ function restoreMap(target, snapshot) {
40990
+ target.clear();
40991
+ for (const [key, value] of snapshot) target.set(key, value);
40992
+ }
40993
+ /** 仅替换目标 entry;失败恢复缓存和注册表,保留未变依赖的对象身份。 */
40994
+ function reloadPageModuleDefinition(options) {
40995
+ const { filePath, route, source, cache, registries } = options;
40996
+ const previous = cache.get(filePath);
40997
+ if (!previous || !registries.pages.has(route)) throw new Error(`Cannot reload a page definition before it is loaded: ${route}`);
40998
+ if (source === void 0) throw new Error(`Missing page script for definition reload: ${route}`);
40999
+ if (source === previous.source) return false;
41000
+ const modules = new Map(cache);
41001
+ const pages = new Map(registries.pages);
41002
+ const components = new Map(registries.components);
41003
+ const app = registries.appDefinition;
41004
+ cache.delete(filePath);
41005
+ registries.pages.delete(route);
41006
+ try {
41007
+ options.execute();
41008
+ return true;
41009
+ } catch (error) {
41010
+ restoreMap(cache, modules);
41011
+ restoreMap(registries.pages, pages);
41012
+ restoreMap(registries.components, components);
41013
+ registries.appDefinition = app;
41014
+ throw error;
41015
+ }
41016
+ }
41017
+ //#endregion
39419
41018
  //#region ../../mpcore/packages/simulator/src/runtime/runtimeGlobals.ts
39420
41019
  const MINI_PROGRAM_UNAVAILABLE_GLOBAL_NAMES = [
39421
41020
  "window",
@@ -39459,108 +41058,12 @@ function createMiniProgramRuntimeGlobals(availableGlobals, overrides) {
39459
41058
  }
39460
41059
  //#endregion
39461
41060
  //#region ../../mpcore/packages/simulator/src/runtime/moduleLoader.ts
39462
- const ESM_IMPORT_LINE_RE = /^[ \t]*import[^\n]*$/gm;
39463
- const ESM_EXPORT_LIST_RE = /^[ \t]*export[ \t]*\{([^}\n]+)\}[ \t]*;?$/gm;
39464
- const ESM_EXPORT_DECLARATION_RE = /\bexport\s+(const|let|var|function|class)\s+([A-Za-z_$][\w$]*)/g;
39465
- const ESM_EXPORT_DEFAULT_DECLARATION_RE = /\bexport\s+default\s+(function|class)\s+([A-Za-z_$][\w$]*)/g;
39466
- function isImportWhitespace(value) {
39467
- return value === " " || value === " ";
39468
- }
39469
- function parseEsmImportLine(line) {
39470
- let statement = line.trim().slice(6).trim();
39471
- if (statement.endsWith(";")) statement = statement.slice(0, -1).trimEnd();
39472
- if (statement.startsWith("\"") || statement.startsWith("'")) {
39473
- const quote = statement[0];
39474
- if (statement.endsWith(quote) && statement.length > 1) return {
39475
- request: statement.slice(1, -1),
39476
- sideEffect: true
39477
- };
39478
- return;
39479
- }
39480
- let fromIndex = statement.indexOf("from");
39481
- while (fromIndex >= 0) {
39482
- if (isImportWhitespace(statement[fromIndex - 1]) && isImportWhitespace(statement[fromIndex + 4])) break;
39483
- fromIndex = statement.indexOf("from", fromIndex + 4);
39484
- }
39485
- if (fromIndex < 0) return;
39486
- const specifier = statement.slice(0, fromIndex).trim();
39487
- const requestLiteral = statement.slice(fromIndex + 4).trim();
39488
- const quote = requestLiteral[0];
39489
- if (!specifier || quote !== "\"" && quote !== "'" || requestLiteral.length < 2 || !requestLiteral.endsWith(quote)) return;
39490
- return {
39491
- request: requestLiteral.slice(1, -1),
39492
- sideEffect: false,
39493
- specifier
39494
- };
39495
- }
39496
- function createEsmImportBindings(specifier, request, index) {
39497
- const namespaceName = `__headlessEsmImport${index}`;
39498
- const statements = [`const ${namespaceName} = require(${JSON.stringify(request)});`];
39499
- const normalizedSpecifier = specifier.trim();
39500
- if (normalizedSpecifier.startsWith("{") && normalizedSpecifier.endsWith("}")) {
39501
- const entries = normalizedSpecifier.slice(1, -1).split(",");
39502
- for (const entry of entries) {
39503
- const [importedName, localName] = entry.trim().split(/\s+as\s+/);
39504
- if (!importedName) continue;
39505
- const bindingName = localName || importedName;
39506
- statements.push(`const ${bindingName} = ${namespaceName}[${JSON.stringify(importedName)}];`);
39507
- }
39508
- return statements.join("\n");
39509
- }
39510
- if (normalizedSpecifier.startsWith("* as ")) {
39511
- const bindingName = normalizedSpecifier.slice(5).trim();
39512
- statements.push(`const ${bindingName} = ${namespaceName};`);
39513
- return statements.join("\n");
39514
- }
39515
- const [defaultName, namedPart] = normalizedSpecifier.split(/\s*,\s*/, 2);
39516
- if (defaultName) statements.push(`const ${defaultName.trim()} = ${namespaceName}.default ?? ${namespaceName};`);
39517
- if (namedPart?.startsWith("{") && namedPart.endsWith("}")) statements.push(createEsmImportBindings(namedPart, request, index).split("\n").slice(1).join("\n"));
39518
- return statements.join("\n");
39519
- }
39520
- function transformEsmToCommonJs(source) {
39521
- if (!/^\s*(?:import|export)\b/m.test(source)) return source;
39522
- let importIndex = 0;
39523
- const exportedNames = [];
39524
- let transformed = source.replace(ESM_IMPORT_LINE_RE, (line) => {
39525
- const parsed = parseEsmImportLine(line);
39526
- if (!parsed) return line;
39527
- if (parsed.sideEffect) return `require(${JSON.stringify(parsed.request)});`;
39528
- return createEsmImportBindings(parsed.specifier, parsed.request, importIndex++);
39529
- });
39530
- transformed = transformed.replace(ESM_EXPORT_LIST_RE, (_match, entries) => {
39531
- for (const entry of entries.split(",")) {
39532
- const [local, exported] = entry.trim().split(/\s+as\s+/);
39533
- if (local) exportedNames.push({
39534
- exported: exported || local,
39535
- local
39536
- });
39537
- }
39538
- return "";
39539
- });
39540
- transformed = transformed.replace(ESM_EXPORT_DEFAULT_DECLARATION_RE, (_match, kind, local) => {
39541
- exportedNames.push({
39542
- exported: "default",
39543
- local
39544
- });
39545
- return `${kind} ${local}`;
39546
- });
39547
- transformed = transformed.replace(ESM_EXPORT_DECLARATION_RE, (_match, kind, local) => {
39548
- exportedNames.push({
39549
- exported: local,
39550
- local
39551
- });
39552
- return `${kind} ${local}`;
39553
- });
39554
- transformed = transformed.replace(/\bexport\s+default\s+/g, "module.exports.default = ");
39555
- if (exportedNames.length > 0) transformed += `\n${exportedNames.map(({ exported, local }) => `module.exports[${JSON.stringify(exported)}] = ${local};`).join("\n")}`;
39556
- return transformed;
39557
- }
39558
41061
  function createRequireNotFoundError(request, importer) {
39559
41062
  return /* @__PURE__ */ new Error(`Cannot resolve require("${request}") from ${normalize(importer)} in headless runtime.`);
39560
41063
  }
39561
- function createExecutionContext(registries, getCurrentPages, getApp, wxDriver, kernel, globals, onConsole) {
41064
+ function createExecutionContext(registries, getCurrentPages, getApp, wxDriver, kernel, globals, onConsole, workers) {
39562
41065
  const runtimeConsole = kernel.diagnostics.createConsole(console, onConsole);
39563
- const wx = createHeadlessWx(wxDriver, runtimeConsole);
41066
+ const wx = createHeadlessWx(wxDriver, runtimeConsole, workers);
39564
41067
  return createMiniProgramRuntimeGlobals({
39565
41068
  App(definition) {
39566
41069
  return registerAppDefinition(registries, definition);
@@ -39593,8 +41096,19 @@ function createExecutionContext(registries, getCurrentPages, getApp, wxDriver, k
39593
41096
  }, globals);
39594
41097
  }
39595
41098
  function createModuleLoader(registries, getCurrentPages, getApp, wxDriver, options) {
41099
+ const workers = createWorkerHost({
41100
+ root: options.miniprogramRootPath,
41101
+ read: (file) => options.artifactSource.readText(file),
41102
+ console: options.kernel.diagnostics.createConsole(console, options.onConsole),
41103
+ createExecutor(globals) {
41104
+ const realm = vm.createContext(globals);
41105
+ return (source, file, module, require) => {
41106
+ new vm.Script(`(function(module, exports, require) {${source}\n})`, { filename: file }).runInContext(realm)(module, module.exports, require);
41107
+ };
41108
+ }
41109
+ });
39596
41110
  const moduleCache = /* @__PURE__ */ new Map();
39597
- const executionContext = createExecutionContext(registries, getCurrentPages, getApp, wxDriver, options.kernel, options.globals ?? {}, options.onConsole);
41111
+ const executionContext = createExecutionContext(registries, getCurrentPages, getApp, wxDriver, options.kernel, options.globals ?? {}, options.onConsole, workers.apis);
39598
41112
  executionContext.globalThis = executionContext;
39599
41113
  function executeModule(filePath, loadContext) {
39600
41114
  const resolvedPath = path.resolve(filePath);
@@ -39603,6 +41117,7 @@ function createModuleLoader(registries, getCurrentPages, getApp, wxDriver, optio
39603
41117
  const source = options.artifactSource.readText(resolvedPath);
39604
41118
  if (source == null) throw new TypeError(`Missing module in headless runtime: ${normalize(resolvedPath)}`);
39605
41119
  const module = {
41120
+ source,
39606
41121
  componentDefinitions: [],
39607
41122
  exports: {}
39608
41123
  };
@@ -39612,7 +41127,7 @@ function createModuleLoader(registries, getCurrentPages, getApp, wxDriver, optio
39612
41127
  const previousLoadContext = registries.currentLoadContext;
39613
41128
  registries.currentLoadContext = loadContext;
39614
41129
  const localRequire = ((request) => {
39615
- const requiredPath = resolveMiniProgramModule(resolvedPath, request, options.miniprogramRootPath, (candidate) => options.artifactSource.readText(candidate) !== void 0);
41130
+ const requiredPath = resolveMiniProgramModule(resolvedPath, request, options.miniprogramRootPath, (candidate) => options.artifactSource.has(candidate));
39616
41131
  if (!requiredPath) throw createRequireNotFoundError(request, resolvedPath);
39617
41132
  if (requiredPath.endsWith(".json")) {
39618
41133
  const content = options.artifactSource.readText(requiredPath);
@@ -39625,7 +41140,7 @@ function createModuleLoader(registries, getCurrentPages, getApp, wxDriver, optio
39625
41140
  });
39626
41141
  localRequire.async = (request) => Promise.resolve().then(() => localRequire(request));
39627
41142
  try {
39628
- new vm.Script(`(function (exports, module, require, __filename, __dirname) { ${transformEsmToCommonJs(source)}\n})`, { filename: resolvedPath }).runInNewContext(executionContext)(module.exports, module, localRequire, resolvedPath, path.dirname(resolvedPath));
41143
+ new vm.Script(`(function (exports, module, require, __filename, __dirname) { ${transformMiniProgramModule(source)}\n})`, { filename: resolvedPath }).runInNewContext(executionContext)(module.exports, module, localRequire, resolvedPath, path.dirname(resolvedPath));
39629
41144
  const registeredDefinitions = [...registries.components.values()].filter((definition) => !registeredDefinitionsBefore.has(definition));
39630
41145
  module.componentDefinitions = [.../* @__PURE__ */ new Set([
39631
41146
  ...requiredComponentDefinitions,
@@ -39653,6 +41168,7 @@ function createModuleLoader(registries, getCurrentPages, getApp, wxDriver, optio
39653
41168
  return callback(...args);
39654
41169
  },
39655
41170
  close() {
41171
+ workers.close();
39656
41172
  moduleCache.clear();
39657
41173
  closeRuntimeWxsLoader(loader);
39658
41174
  },
@@ -39671,6 +41187,17 @@ function createModuleLoader(registries, getCurrentPages, getApp, wxDriver, optio
39671
41187
  if (!definition) throw new Error(`Page() was not registered for route "${route}" while executing ${normalize(filePath)}.`);
39672
41188
  return definition;
39673
41189
  },
41190
+ reloadPageModule(filePath, route) {
41191
+ const resolvedPath = path.resolve(filePath);
41192
+ return reloadPageModuleDefinition({
41193
+ filePath: resolvedPath,
41194
+ route,
41195
+ source: options.artifactSource.readText(resolvedPath),
41196
+ cache: moduleCache,
41197
+ registries,
41198
+ execute: () => loader.executePageModule(resolvedPath, route)
41199
+ });
41200
+ },
39674
41201
  executeComponentModule(filePath, id) {
39675
41202
  const loadedModule = executeModule(filePath, {
39676
41203
  kind: "component",
@@ -39707,6 +41234,7 @@ var HeadlessRouteEvents = class {
39707
41234
  nextRouteId = 0;
39708
41235
  nextWebviewId = 0;
39709
41236
  activeEvent;
41237
+ activeState;
39710
41238
  constructor(requestRender, isCurrentPage) {
39711
41239
  this.requestRender = requestRender;
39712
41240
  this.isCurrentPage = isCurrentPage;
@@ -39718,6 +41246,7 @@ var HeadlessRouteEvents = class {
39718
41246
  this.bus.$off(name, listener);
39719
41247
  }
39720
41248
  begin(path, query, openType, renderer, page) {
41249
+ this.cancel(this.activeState, /* @__PURE__ */ new Error("navigateTo:fail navigation was superseded"));
39721
41250
  const previous = page ? this.pages.get(page) : void 0;
39722
41251
  const event = {
39723
41252
  path,
@@ -39734,12 +41263,28 @@ var HeadlessRouteEvents = class {
39734
41263
  }
39735
41264
  attach(page, event) {
39736
41265
  const state = this.pages.get(page);
39737
- this.pages.set(page, {
41266
+ const next = {
39738
41267
  webviewId: event.webviewId,
39739
41268
  ready: state?.ready ?? false,
39740
41269
  event,
39741
- committed: false
39742
- });
41270
+ committed: false,
41271
+ completed: false
41272
+ };
41273
+ this.pages.set(page, next);
41274
+ if (this.activeEvent === event) this.activeState = next;
41275
+ }
41276
+ /** 原生 navigateTo 的回调属于本次路由记录,不能以当前页面或提交栈代替完成。 */
41277
+ navigateTo(operation, callbacks) {
41278
+ let state;
41279
+ try {
41280
+ state = this.pages.get(operation());
41281
+ } catch (error) {
41282
+ this.settle(callbacks, error);
41283
+ return;
41284
+ }
41285
+ if (!state || state.canceled) this.settle(callbacks, state?.canceled ?? /* @__PURE__ */ new Error("navigateTo:fail target page was unloaded"));
41286
+ else if (state.completed) this.settle(callbacks);
41287
+ else state.callbacks = callbacks;
39743
41288
  }
39744
41289
  commit(page, event) {
39745
41290
  const state = this.pages.get(page);
@@ -39756,10 +41301,40 @@ var HeadlessRouteEvents = class {
39756
41301
  }
39757
41302
  complete(page, state) {
39758
41303
  this.requestRender(() => {
39759
- if (this.pages.get(page) !== state || this.activeEvent !== state.event || !this.isCurrentPage(page)) return;
39760
- this.bus.$emit("appRouteDone", state.event);
41304
+ if (state.completed || state.canceled || this.pages.get(page) !== state || this.activeEvent !== state.event || !this.isCurrentPage(page)) return;
41305
+ state.completed = true;
41306
+ const callbacks = state.callbacks;
41307
+ state.callbacks = void 0;
41308
+ try {
41309
+ const event = state.event;
41310
+ this.bus.$emit("appRouteDone", {
41311
+ path: event.path,
41312
+ query: { ...event.query },
41313
+ openType: event.openType,
41314
+ webviewId: event.webviewId,
41315
+ routeEventId: event.routeEventId,
41316
+ timeStamp: event.timeStamp
41317
+ });
41318
+ } finally {
41319
+ this.settle(callbacks);
41320
+ }
39761
41321
  });
39762
41322
  }
41323
+ settle(callbacks, error) {
41324
+ try {
41325
+ if (error) callbacks?.fail?.(error);
41326
+ else callbacks?.success?.();
41327
+ } finally {
41328
+ callbacks?.complete?.();
41329
+ }
41330
+ }
41331
+ cancel(state, error) {
41332
+ if (!state || state.completed || state.canceled) return;
41333
+ state.canceled = error;
41334
+ const callbacks = state.callbacks;
41335
+ state.callbacks = void 0;
41336
+ this.settle(callbacks, error);
41337
+ }
39763
41338
  beforeUnload(page, event) {
39764
41339
  const state = this.pages.get(page);
39765
41340
  if (event && state) this.bus.$emit("beforePageUnload", {
@@ -39770,9 +41345,12 @@ var HeadlessRouteEvents = class {
39770
41345
  webviewId: state.webviewId,
39771
41346
  page
39772
41347
  });
41348
+ this.cancel(state, /* @__PURE__ */ new Error("navigateTo:fail target page was unloaded"));
39773
41349
  this.pages.delete(page);
39774
41350
  }
39775
41351
  close() {
41352
+ this.cancel(this.activeState, /* @__PURE__ */ new Error("navigateTo:fail runtime session closed"));
41353
+ this.activeState = void 0;
39776
41354
  this.activeEvent = void 0;
39777
41355
  this.bus.$off();
39778
41356
  }
@@ -41620,6 +43198,7 @@ var HeadlessSession = class {
41620
43198
  appInstance = null;
41621
43199
  appLifecycle = new HeadlessAppLifecycle();
41622
43200
  moduleLoader;
43201
+ templateRegistration;
41623
43202
  routeEvents = new HeadlessRouteEvents((callback) => this.requestRender(callback), (page) => this.currentPageInstance === page && !this.kernel.isClosed);
41624
43203
  eventListeners = /* @__PURE__ */ new Map();
41625
43204
  registries;
@@ -41628,8 +43207,13 @@ var HeadlessSession = class {
41628
43207
  componentCache = /* @__PURE__ */ new Map();
41629
43208
  componentScopes = /* @__PURE__ */ new Map();
41630
43209
  selectorQueryScopeSnapshots = /* @__PURE__ */ new WeakMap();
41631
- renderRequestPending = false;
41632
- renderRequestCallbacks = [];
43210
+ renderQueue = new RuntimeRenderQueue({
43211
+ isClosed: () => this.kernel.isClosed,
43212
+ onError: (error) => this.kernel.diagnostics.record("exception", [error]),
43213
+ render: () => {
43214
+ if (this.currentPageInstance) this.renderCurrentPage();
43215
+ }
43216
+ });
41633
43217
  tabBarRoutes;
41634
43218
  tabPages = /* @__PURE__ */ new Map();
41635
43219
  tabBarItems = /* @__PURE__ */ new Map();
@@ -41648,6 +43232,7 @@ var HeadlessSession = class {
41648
43232
  constructor(options) {
41649
43233
  if (!options.project && !options.projectPath) throw new Error("Headless runtime requires projectPath or project.");
41650
43234
  this.project = options.project ?? loadProject(options.projectPath);
43235
+ this.templateRegistration = createTemplateRegistration(this.project.artifactSource, this.project.miniprogramRootPath, options.compiledTemplatePaths);
41651
43236
  this.wxState = createHeadlessWxState(this.kernel.scheduler, {
41652
43237
  strictMocks: options.strictHostMocks,
41653
43238
  readPackageFile: createPackageAssetReader((relative) => this.project.artifactSource.readText(path.join(this.project.miniprogramRootPath, relative)))
@@ -41701,7 +43286,7 @@ var HeadlessSession = class {
41701
43286
  getMenuButtonBoundingClientRect: () => deriveMenuButtonBoundingClientRect(this.systemInfo),
41702
43287
  getNetworkType: () => this.wxState.getNetworkType(),
41703
43288
  navigateBack: (option) => this.navigateBack(option?.delta),
41704
- navigateTo: (option) => this.navigateTo(option.url),
43289
+ navigateTo: (option) => this.routeEvents.navigateTo(() => this.navigateTo(option.url), option),
41705
43290
  pageScrollTo: (option) => this.pageScrollTo(option),
41706
43291
  openDocument: (option) => this.wxState.openDocument(option),
41707
43292
  clearStorageSync: () => this.wxState.clearStorageSync(),
@@ -41785,8 +43370,7 @@ var HeadlessSession = class {
41785
43370
  failure = { error };
41786
43371
  }
41787
43372
  this.canvasContexts.clear();
41788
- this.renderRequestCallbacks.length = 0;
41789
- this.renderRequestPending = false;
43373
+ this.renderQueue.close();
41790
43374
  this.appLifecycle.close();
41791
43375
  this.routeEvents.close();
41792
43376
  this.wxState.close();
@@ -41836,6 +43420,23 @@ var HeadlessSession = class {
41836
43420
  this.assertActive();
41837
43421
  return this.pages.slice();
41838
43422
  }
43423
+ /** 显式重载已变更的页面 entry,保留 App 与依赖缓存;调用者随后自行导航。 */
43424
+ reloadPageDefinition(route) {
43425
+ this.assertActive();
43426
+ const record = resolvePageDefinitionReloadRoute(this.project, route);
43427
+ const filePath = path.resolve(this.project.miniprogramRootPath, `${record.resourcePath ?? record.route}.js`);
43428
+ return this.moduleLoader.reloadPageModule(filePath, record.route);
43429
+ }
43430
+ /** 显式提交编译模板及其 import/include 依赖;不重载脚本、不导航或替换实例。 */
43431
+ reloadTemplates() {
43432
+ this.assertActive();
43433
+ const changed = this.templateRegistration.reload();
43434
+ if (changed) {
43435
+ closeRuntimeWxsLoader(this.moduleLoader);
43436
+ this.requestRender();
43437
+ }
43438
+ return changed;
43439
+ }
41839
43440
  getWx() {
41840
43441
  this.assertActive();
41841
43442
  return this.moduleLoader.wx;
@@ -41891,7 +43492,7 @@ var HeadlessSession = class {
41891
43492
  const current = this.requireCurrentPage("renderCurrentPage()");
41892
43493
  const rendered = renderRuntimePageTree({
41893
43494
  changedPageKeys: current.__lastChangedKeys__ ?? [],
41894
- artifactSource: this.project.artifactSource,
43495
+ artifactSource: this.templateRegistration.artifactSource,
41895
43496
  componentCache: this.componentCache,
41896
43497
  componentScopes: this.componentScopes,
41897
43498
  moduleLoader: this.moduleLoader,
@@ -41921,20 +43522,18 @@ var HeadlessSession = class {
41921
43522
  dispatchNativeNodeEvent(node, eventName, event, onHandlerResult) {
41922
43523
  const current = this.requireCurrentPage("dispatchNativeNodeEvent()");
41923
43524
  if (eventName === "tap") this.renderCurrentPage();
41924
- return dispatchNativeNodeEvent(current, node, eventName, event, this.moduleLoader.wx, (scopeId, method) => this.resolveScopeMethod(current, scopeId, method), onHandlerResult);
43525
+ return dispatchNativeNodeEvent(current, node, eventName, event, this.moduleLoader.wx, (scopeId, method) => this.resolveScopeMethod(current, scopeId, method), onHandlerResult, (scopeId) => {
43526
+ const instance = this.componentCache.get(scopeId);
43527
+ const scope = this.componentScopes.get(scopeId);
43528
+ return instance?.__definition__ && scope ? {
43529
+ definition: instance.__definition__,
43530
+ ownerScopeId: scope.ownerScopeId ?? null
43531
+ } : void 0;
43532
+ });
41925
43533
  }
41926
43534
  requestRender(callback) {
41927
43535
  this.assertActive();
41928
- if (callback) this.renderRequestCallbacks.push(callback);
41929
- if (this.renderRequestPending) return;
41930
- this.renderRequestPending = true;
41931
- Promise.resolve().then(() => {
41932
- if (this.kernel.isClosed) return;
41933
- this.renderRequestPending = false;
41934
- const callbacks = this.renderRequestCallbacks.splice(0);
41935
- if (this.currentPageInstance) this.renderCurrentPage();
41936
- callbacks.forEach((cb) => cb());
41937
- });
43536
+ this.renderQueue.request(callback);
41938
43537
  }
41939
43538
  getScopeSnapshot(scopeId) {
41940
43539
  const scope = this.componentScopes.get(scopeId);
@@ -42016,19 +43615,19 @@ var HeadlessSession = class {
42016
43615
  return this.wxState.getShareMenu();
42017
43616
  }
42018
43617
  selectComponent(selector) {
42019
- this.renderCurrentPage();
43618
+ if (!hasPendingComponentConstruction(this.componentCache)) this.renderCurrentPage();
42020
43619
  return this.selectComponentsWithin(null, selector)[0] ?? null;
42021
43620
  }
42022
43621
  selectAllComponents(selector) {
42023
- this.renderCurrentPage();
43622
+ if (!hasPendingComponentConstruction(this.componentCache)) this.renderCurrentPage();
42024
43623
  return this.selectComponentsWithin(null, selector);
42025
43624
  }
42026
43625
  selectComponentWithin(scopeId, selector) {
42027
- this.renderCurrentPage();
43626
+ if (!hasPendingComponentConstruction(this.componentCache)) this.renderCurrentPage();
42028
43627
  return this.selectComponentsWithin(scopeId, selector)[0] ?? null;
42029
43628
  }
42030
43629
  selectAllComponentsWithin(scopeId, selector) {
42031
- this.renderCurrentPage();
43630
+ if (!hasPendingComponentConstruction(this.componentCache)) this.renderCurrentPage();
42032
43631
  return this.selectComponentsWithin(scopeId, selector);
42033
43632
  }
42034
43633
  selectOwnerComponent(scopeId) {
@@ -42768,6 +44367,7 @@ function createPageRootNodeHandle(options) {
42768
44367
  const tree = session?.getCurrentPages().includes(page) ? session.renderCurrentPage() : renderPageTree(project, page);
42769
44368
  return new HeadlessTestingNodeHandle(tree.root.type === "root" && !options.includeHostRoots ? tree.root.children?.[0] ?? tree.root : tree.root, {
42770
44369
  assertActive: options.assertActive,
44370
+ projectionOnly: options.projectionOnly,
42771
44371
  dispatchNativeEvent: (node, eventName, event, onHandlerResult) => session?.getCurrentPages().at(-1) === page ? session.dispatchNativeNodeEvent(node, eventName, event, onHandlerResult) : false,
42772
44372
  callMethod: (scopeId, methodName, event) => {
42773
44373
  if (session?.getCurrentPages().includes(page)) return session.callScopeMethod(scopeId, methodName, event);
@@ -43130,7 +44730,18 @@ var HeadlessTestingPageHandle = class {
43130
44730
  renderedAccess() {
43131
44731
  return {
43132
44732
  assertActive: () => this.assertActive(),
43133
- findAll: async (selector) => await this.$$(selector),
44733
+ findAll: async (selector) => {
44734
+ this.assertActive();
44735
+ const root = createPageRootNodeHandle({
44736
+ assertActive: () => this.assertActive(),
44737
+ createPageHandle: () => this,
44738
+ page: this.page,
44739
+ project: this.project,
44740
+ projectionOnly: true,
44741
+ session: this.session
44742
+ });
44743
+ return selector === "page" ? [root] : await root.$$(selector);
44744
+ },
43134
44745
  waitFor: async (ms) => await this.waitFor(ms),
43135
44746
  wxml: async () => await this.wxml()
43136
44747
  };
@@ -43147,6 +44758,133 @@ var HeadlessTestingPageHandle = class {
43147
44758
  }
43148
44759
  };
43149
44760
  //#endregion
44761
+ //#region ../../mpcore/packages/simulator/src/testing/sessionHandle/protocol.ts
44762
+ /** 无头协议只公布已有运行时语义;不把宿主缺失能力伪装为调试器结果。 */
44763
+ var HeadlessTestingProtocolUnsupportedError = class extends Error {
44764
+ method;
44765
+ code = HEADLESS_PROTOCOL_UNSUPPORTED;
44766
+ constructor(method) {
44767
+ super(`Protocol method ${method} is not supported by the headless testing runtime.`);
44768
+ this.method = method;
44769
+ this.name = "HeadlessTestingProtocolUnsupportedError";
44770
+ }
44771
+ };
44772
+ function requireString(params, name) {
44773
+ const value = params[name];
44774
+ if (typeof value !== "string" || !value.trim()) throw new TypeError(`Protocol parameter ${name} must be a non-empty string.`);
44775
+ return value;
44776
+ }
44777
+ function readArgs(params) {
44778
+ if (params.args === void 0) return [];
44779
+ if (!Array.isArray(params.args)) throw new TypeError("Protocol parameter args must be an array.");
44780
+ return cloneProtocolValue(params.args);
44781
+ }
44782
+ function pageSnapshot(page) {
44783
+ return {
44784
+ pageId: page.pageId,
44785
+ path: page.path,
44786
+ query: page.query
44787
+ };
44788
+ }
44789
+ async function dispatch(target, method, params) {
44790
+ switch (method) {
44791
+ case "Tool.getInfo": return await target.toolInfo();
44792
+ case "App.getCurrentPage": {
44793
+ const page = await target.currentPage();
44794
+ return page ? pageSnapshot(page) : null;
44795
+ }
44796
+ case "App.getPageStack": return { pageStack: (await target.getCurrentPages()).map(pageSnapshot) };
44797
+ case "App.callFunction": return { result: cloneProtocolValue(await target.evaluateWithOptions(requireString(params, "functionDeclaration"), {}, ...readArgs(params))) };
44798
+ case "App.callWxMethod": return { result: cloneProtocolValue(await target.callWxMethodWithOptions(requireString(params, "method"), {}, ...readArgs(params))) };
44799
+ default: throw new HeadlessTestingProtocolUnsupportedError(method);
44800
+ }
44801
+ }
44802
+ async function sendHeadlessTestingProtocol(target, method, params, options) {
44803
+ let rejectClosed;
44804
+ const closed = new Promise((_resolve, reject) => {
44805
+ rejectClosed = reject;
44806
+ });
44807
+ const onClose = () => rejectClosed(/* @__PURE__ */ new Error(`The headless testing session closed before protocol method ${method} completed.`));
44808
+ target.on("close", onClose);
44809
+ try {
44810
+ return await runWithTimeout(() => Promise.race([dispatch(target, method, params), closed]), options.timeout, `Timed out executing protocol method ${method} in headless testing runtime.`);
44811
+ } finally {
44812
+ target.off("close", onClose);
44813
+ }
44814
+ }
44815
+ //#endregion
44816
+ //#region ../../mpcore/packages/simulator/src/testing/sessionHandle/cdp.ts
44817
+ /** 保留官方调用形状;当前后端的 enable 会明确报告不支持。 */
44818
+ function createHeadlessTestingCDP(target, assertActive) {
44819
+ let enabling;
44820
+ const enable = (options) => {
44821
+ enabling ??= target.send("App.CDPEnable", {}, options).catch((error) => {
44822
+ enabling = void 0;
44823
+ throw error;
44824
+ });
44825
+ return enabling;
44826
+ };
44827
+ return {
44828
+ enable,
44829
+ async sendCommand(domain, method, params = {}, options) {
44830
+ await enable(options);
44831
+ return await target.send("App.CDPCommand", {
44832
+ domain,
44833
+ method,
44834
+ params
44835
+ }, options);
44836
+ },
44837
+ async listProtocol(options) {
44838
+ await enable(options);
44839
+ return await target.send("App.CDPListProtocol", {}, options);
44840
+ },
44841
+ onEvent() {
44842
+ assertActive();
44843
+ throw new HeadlessTestingProtocolUnsupportedError("App.CDPEvent");
44844
+ }
44845
+ };
44846
+ }
44847
+ //#endregion
44848
+ //#region ../../mpcore/packages/simulator/src/testing/sessionHandle/navigation.ts
44849
+ /** 所有测试句柄通过同一个 wx 导航边界等待成功、失败或关闭通知。 */
44850
+ var HeadlessTestingNavigation = class {
44851
+ session;
44852
+ pending = /* @__PURE__ */ new Set();
44853
+ constructor(session) {
44854
+ this.session = session;
44855
+ session.on("close", () => {
44856
+ for (const reject of this.pending) reject(/* @__PURE__ */ new Error("The headless testing session closed before navigation completed."));
44857
+ this.pending.clear();
44858
+ });
44859
+ }
44860
+ invoke(methodName, options) {
44861
+ return new Promise((resolve, reject) => {
44862
+ const rejectNavigation = (error) => {
44863
+ this.pending.delete(rejectNavigation);
44864
+ reject(error);
44865
+ };
44866
+ this.pending.add(rejectNavigation);
44867
+ try {
44868
+ this.session.callWxMethod(methodName, {
44869
+ ...options,
44870
+ success: () => {
44871
+ this.pending.delete(rejectNavigation);
44872
+ resolve();
44873
+ },
44874
+ fail: (error) => {
44875
+ const details = error && typeof error === "object" ? error : void 0;
44876
+ const reason = typeof details?.errMsg === "string" ? details.errMsg : typeof details?.message === "string" ? details.message : String(error);
44877
+ const target = options.url ? ` route "${options.url}"` : "";
44878
+ rejectNavigation(new Error(`Failed to ${methodName}${target} through the headless wx runtime: ${reason}`, { cause: error }));
44879
+ }
44880
+ });
44881
+ } catch (error) {
44882
+ rejectNavigation(error);
44883
+ }
44884
+ });
44885
+ }
44886
+ };
44887
+ //#endregion
43150
44888
  //#region ../../mpcore/packages/simulator/src/testing/sessionHandle/scope.ts
43151
44889
  var HeadlessTestingScopeHandle = class HeadlessTestingScopeHandle {
43152
44890
  scopeId;
@@ -43208,16 +44946,41 @@ var HeadlessTestingScopeHandle = class HeadlessTestingScopeHandle {
43208
44946
  var HeadlessTestingSessionHandle = class {
43209
44947
  project;
43210
44948
  session;
43211
- pendingNavigations = /* @__PURE__ */ new Set();
44949
+ navigation;
44950
+ disconnection;
44951
+ cdp;
43212
44952
  constructor(project, session) {
43213
44953
  this.project = project;
43214
44954
  this.session = session;
43215
- this.session.on("close", () => {
43216
- for (const reject of this.pendingNavigations) reject(/* @__PURE__ */ new Error("The headless testing session closed before navigation completed."));
43217
- });
44955
+ this.navigation = new HeadlessTestingNavigation(session);
43218
44956
  }
43219
- async close() {
43220
- this.session.close();
44957
+ close() {
44958
+ return this.disconnect();
44959
+ }
44960
+ /** 先保存共享结果,再同步开始关闭,覆盖关闭监听器内的重入调用。 */
44961
+ disconnect() {
44962
+ if (!this.disconnection) {
44963
+ let resolve;
44964
+ let reject;
44965
+ this.disconnection = new Promise((accept, fail) => {
44966
+ resolve = accept;
44967
+ reject = fail;
44968
+ });
44969
+ try {
44970
+ this.session.close();
44971
+ resolve();
44972
+ } catch (error) {
44973
+ reject(error);
44974
+ }
44975
+ }
44976
+ return this.disconnection;
44977
+ }
44978
+ async send(method, params = {}, options = {}) {
44979
+ this.session.assertActive();
44980
+ return await sendHeadlessTestingProtocol(this, method, params, options);
44981
+ }
44982
+ CDP() {
44983
+ return this.cdp ??= createHeadlessTestingCDP(this, () => this.session.assertActive());
43221
44984
  }
43222
44985
  async toolInfo() {
43223
44986
  return { version: "mpcore-simulator" };
@@ -43256,6 +45019,10 @@ var HeadlessTestingSessionHandle = class {
43256
45019
  const normalizedMethodName = normalizeNonEmptyInput(methodName, "wx method name");
43257
45020
  return await runWithTimeout(() => this.session.callWxMethod(normalizedMethodName, ...args), options.timeout, `Timed out calling wx.${normalizedMethodName} in headless testing runtime.`);
43258
45021
  }
45022
+ /** 返回与 DevTools automator 兼容的运行时系统信息。 */
45023
+ async systemInfo() {
45024
+ return await this.callWxMethod("getSystemInfoSync");
45025
+ }
43259
45026
  async scopeSnapshot(scopeId) {
43260
45027
  const normalizedScopeId = normalizeNonEmptyInput(scopeId, "Scope id");
43261
45028
  return this.session.getScopeSnapshot(normalizedScopeId);
@@ -43283,23 +45050,31 @@ var HeadlessTestingSessionHandle = class {
43283
45050
  }, `Timed out waiting for ${normalizedCount} component(s) matching "${normalizedSelector}" in headless testing runtime.`, options);
43284
45051
  }
43285
45052
  async reLaunch(route) {
43286
- await this.invokeNavigation("reLaunch", { url: route });
45053
+ await this.navigation.invoke("reLaunch", { url: route });
43287
45054
  return await this.requireCurrentPage("reLaunch", route);
43288
45055
  }
45056
+ /** 只更新页面定义;不隐式导航,也不替换当前页面或应用实例。 */
45057
+ async reloadPageDefinition(route) {
45058
+ return this.session.reloadPageDefinition(route);
45059
+ }
45060
+ /** 显式提交宿主编译模板;不隐式重载脚本或导航。 */
45061
+ async reloadTemplates() {
45062
+ return this.session.reloadTemplates();
45063
+ }
43289
45064
  async navigateTo(route) {
43290
- await this.invokeNavigation("navigateTo", { url: route });
45065
+ await this.navigation.invoke("navigateTo", { url: route });
43291
45066
  return await this.requireCurrentPage("navigateTo", route);
43292
45067
  }
43293
45068
  async redirectTo(route) {
43294
- await this.invokeNavigation("redirectTo", { url: route });
45069
+ await this.navigation.invoke("redirectTo", { url: route });
43295
45070
  return await this.requireCurrentPage("redirectTo", route);
43296
45071
  }
43297
45072
  async navigateBack(delta = 1) {
43298
- await this.invokeNavigation("navigateBack", { delta });
45073
+ await this.navigation.invoke("navigateBack", { delta });
43299
45074
  return await this.currentPage();
43300
45075
  }
43301
45076
  async switchTab(route) {
43302
- await this.invokeNavigation("switchTab", { url: route });
45077
+ await this.navigation.invoke("switchTab", { url: route });
43303
45078
  return await this.requireCurrentPage("switchTab", route);
43304
45079
  }
43305
45080
  async waitForCurrentPage(route, options = {}) {
@@ -43335,32 +45110,6 @@ var HeadlessTestingSessionHandle = class {
43335
45110
  async triggerRouteDone(options) {
43336
45111
  this.session.triggerRouteDone(options);
43337
45112
  }
43338
- invokeNavigation(methodName, options) {
43339
- return new Promise((resolve, reject) => {
43340
- const rejectNavigation = (error) => {
43341
- this.pendingNavigations.delete(rejectNavigation);
43342
- reject(error);
43343
- };
43344
- this.pendingNavigations.add(rejectNavigation);
43345
- try {
43346
- this.session.callWxMethod(methodName, {
43347
- ...options,
43348
- success: () => {
43349
- this.pendingNavigations.delete(rejectNavigation);
43350
- resolve();
43351
- },
43352
- fail: (error) => {
43353
- const details = error && typeof error === "object" ? error : void 0;
43354
- const reason = typeof details?.errMsg === "string" ? details.errMsg : typeof details?.message === "string" ? details.message : String(error);
43355
- const target = options.url ? ` route "${options.url}"` : "";
43356
- rejectNavigation(new Error(`Failed to ${methodName}${target} through the headless wx runtime: ${reason}`, { cause: error }));
43357
- }
43358
- });
43359
- } catch (error) {
43360
- rejectNavigation(error);
43361
- }
43362
- });
43363
- }
43364
45113
  async requireCurrentPage(methodName, route) {
43365
45114
  const page = await this.currentPage();
43366
45115
  if (!page) throw new Error(`Failed to ${methodName} route "${route}" through the headless wx runtime.`);
@@ -43379,7 +45128,10 @@ function resolveInitialRoute(session) {
43379
45128
  return session.project.routes[0]?.route ?? null;
43380
45129
  }
43381
45130
  async function launch(options) {
43382
- const session = createHeadlessSession({ projectPath: options.projectPath });
45131
+ const session = createHeadlessSession({
45132
+ compiledTemplatePaths: options.compiledTemplatePaths,
45133
+ projectPath: options.projectPath
45134
+ });
43383
45135
  const handle = new HeadlessTestingSessionHandle(session.project, session);
43384
45136
  try {
43385
45137
  await options.onSessionCreated?.(handle);