@weapp-vite/miniprogram-automator 1.3.0 → 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, {
@@ -163,11 +163,14 @@ function runPropertyObservers(definition, instance, changedKeys, previousPropert
163
163
  const changedRoots = new Set(changedKeys.map((key) => parseDataPath$1(key)[0]));
164
164
  for (const changedKey of Object.keys(propOptions)) {
165
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;
166
168
  const option = propOptions[changedKey];
167
169
  if (!option || typeof option !== "object" || Array.isArray(option)) continue;
168
170
  const observer = typeof option.observer === "string" ? instance[option.observer] : option.observer;
169
171
  if (typeof observer !== "function") continue;
170
- 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);
171
174
  }
172
175
  }
173
176
  function normalizeObserverPattern(pattern) {
@@ -378,7 +381,6 @@ function resolveInitialProperties(definition, properties) {
378
381
  if (option && typeof option === "object" && !Array.isArray(option) && "value" in option) resolved[key] = resolvePropertyDefaultValue(option, definition);
379
382
  else resolved[key] = cloneValue$1(resolveDefaultValueByType(normalizePropertyType(option)));
380
383
  }
381
- for (const [key, value] of Object.entries(properties)) if (!(key in resolved)) resolved[key] = value;
382
384
  return resolved;
383
385
  }
384
386
  //#endregion
@@ -778,7 +780,7 @@ const basename = function(p, extension) {
778
780
  }
779
781
  return extension && lastSegment.endsWith(extension) ? lastSegment.slice(0, -extension.length) : lastSegment;
780
782
  };
781
- const parse$2 = function(p) {
783
+ const parse$3 = function(p) {
782
784
  const root = _PATH_ROOT_RE.exec(p)?.[0]?.replace(/\\/g, "/") || "";
783
785
  const base = basename(p);
784
786
  const extension = extname(base);
@@ -804,7 +806,7 @@ const _path = {
804
806
  matchesGlob,
805
807
  normalize,
806
808
  normalizeString,
807
- parse: parse$2,
809
+ parse: parse$3,
808
810
  relative,
809
811
  resolve,
810
812
  sep: "/",
@@ -839,11 +841,11 @@ const opposite = {
839
841
  function definitions(instance) {
840
842
  return Object.entries(instance.__definition__?.relations ?? {});
841
843
  }
842
- function includesBehavior(behaviors, target) {
843
- 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));
844
846
  }
845
847
  function matches(source, key, relation, target) {
846
- if (relation.target) return includesBehavior(target.__definition__?.behaviors, relation.target);
848
+ if (relation.target) return includesBehavior$1(target.__definition__?.behaviors, relation.target);
847
849
  const targetPath = key.startsWith("/") ? key.slice(1) : posix.join(posix.dirname(String(source.is)), key);
848
850
  return posix.normalize(targetPath) === target.is;
849
851
  }
@@ -917,29 +919,23 @@ function detachComponentRelations(instances) {
917
919
  //#region ../../mpcore/packages/simulator/src/runtime/componentInstance/index.ts
918
920
  function createComponentInstance(options) {
919
921
  const definition = normalizeComponentDefinition(options.definition);
920
- const inputProperties = resolveInitialProperties(definition, options.properties ?? {});
921
922
  const data = {
922
923
  ...resolveInitialData$1(definition),
923
- ...Object.fromEntries(Object.keys(definition.properties ?? {}).map((key) => [key, inputProperties[key]]))
924
+ ...resolveInitialProperties(definition, options.properties ?? {})
924
925
  };
925
926
  const instance = {
926
927
  __definition__: definition,
927
928
  data,
928
929
  __propertySnapshots__: {},
929
- properties: {
930
- ...data,
931
- ...inputProperties
932
- },
930
+ properties: data,
933
931
  getRelationNodes(key) {
934
932
  return getComponentRelationNodes(instance, key);
935
933
  },
936
934
  setData(patch, callback) {
937
935
  const changedKeys = Object.keys(patch);
938
- const dataKeys = [...new Set(changedKeys.map((key) => parseDataPath$1(key)[0]))];
939
- 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));
940
937
  const previousProperties = Object.fromEntries(propertyKeys.map((key) => [key, cloneValue$1(instance.properties[key])]));
941
938
  for (const [key, value] of Object.entries(patch)) assignByPath$1(instance.data, key, value);
942
- for (const key of dataKeys) instance.properties[key] = instance.data[key];
943
939
  synchronizeAttachmentBindings(instance);
944
940
  runComponentObservers(definition, instance, changedKeys, previousProperties);
945
941
  if (options.requestRender) options.requestRender(callback);
@@ -1292,6 +1288,31 @@ function collectNodeDataset(node) {
1292
1288
  //#endregion
1293
1289
  //#region ../../mpcore/packages/simulator/src/view/componentEvent.ts
1294
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
+ }
1295
1316
  /** 在构造子树之前建立父链,created/observer 中的事件也能访问声明树。 */
1296
1317
  function registerComponentEventNode(node, scope, parent) {
1297
1318
  node.parent = parent ?? null;
@@ -13001,6 +13022,29 @@ var Parser$1 = class extends StatementParser {
13001
13022
  return result;
13002
13023
  }
13003
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
+ }
13004
13048
  function parseExpression(input, options) {
13005
13049
  const parser = getParser(options, input);
13006
13050
  if (parser.options.strictMode) parser.state.strict = true;
@@ -13424,6 +13468,7 @@ function syncComponentProperties(instance, definition, nextProperties, bindingEx
13424
13468
  const changedRootKeys = [];
13425
13469
  const previousProperties = {};
13426
13470
  for (const [key, value] of Object.entries(nextProperties)) {
13471
+ if (!Object.hasOwn(definition.properties ?? {}, key)) continue;
13427
13472
  const nextValue = normalizeComponentPropertyValue(definition, key, value);
13428
13473
  const bindingExpression = bindingExpressions[key];
13429
13474
  const bindingAffected = freshPageChanges && !!bindingExpression && changedPageKeys.some((changedKey) => {
@@ -13434,7 +13479,6 @@ function syncComponentProperties(instance, definition, nextProperties, bindingEx
13434
13479
  previousProperties[key] = instance.properties[key];
13435
13480
  const deliveredValue = cloneValue$1(nextValue);
13436
13481
  instance.properties[key] = deliveredValue;
13437
- if (Object.hasOwn(definition.properties ?? {}, key)) instance.data[key] = deliveredValue;
13438
13482
  changedRootKeys.push(key);
13439
13483
  }
13440
13484
  instance.__propertySnapshots ??= {};
@@ -13445,6 +13489,32 @@ function syncComponentProperties(instance, definition, nextProperties, bindingEx
13445
13489
  runComponentObservers(definition, instance, changedRootKeys, previousProperties);
13446
13490
  }
13447
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
13448
13518
  //#region ../../mpcore/packages/simulator/src/runtime/pageLifecycle.ts
13449
13519
  const loadingPages = /* @__PURE__ */ new WeakSet();
13450
13520
  function isPageBeforeReady(page) {
@@ -13533,6 +13603,10 @@ function createPluginArtifactSource(fallback, miniprogramRootPath, plugins) {
13533
13603
  readText: (filePath) => fallback.readText(resolvePhysicalPath(filePath))
13534
13604
  };
13535
13605
  }
13606
+ //#endregion
13607
+ //#region ../../@weapp-core/constants/dist/index.js
13608
+ /** 无头测试后端不支持所请求的自动化协议。 */
13609
+ const HEADLESS_PROTOCOL_UNSUPPORTED = "HEADLESS_PROTOCOL_UNSUPPORTED";
13536
13610
  const WEVU_NATIVE_INSTANCE_KEY = "__wevuNativeInstance";
13537
13611
  /** 宿主值为 1 时,实现带 routeEventId 的路由阶段契约;不代表微信 SDK 版本。 */
13538
13612
  const WEVU_ROUTE_EVENT_CONTRACT_KEY = "__wevuRouteEventContract";
@@ -13646,66 +13720,155 @@ function resolveMiniProgramComponent(ownerFilePath, request, miniprogramRootPath
13646
13720
  return npmPath ? relative(miniprogramRootPath, npmPath).slice(0, -3) : localPath;
13647
13721
  }
13648
13722
  //#endregion
13649
- //#region ../../mpcore/packages/simulator/src/view/templateImports.ts
13650
- function createImportedTemplateState(root, filePath, loadImport, loadWxs) {
13651
- const documents = /* @__PURE__ */ new Map();
13652
- const definitionScopes = /* @__PURE__ */ new Map();
13653
- const definitionWxsScopes = /* @__PURE__ */ new Map();
13654
- const collect = (node, owner) => {
13655
- const existing = documents.get(owner);
13656
- if (existing) return existing;
13657
- const state = createTemplateRenderState(node);
13658
- const wxsModules = Object.create(null);
13659
- const ownDefinitions = new Map(state.definitions);
13660
- const document = {
13661
- state,
13662
- exports: ownDefinitions
13663
- };
13664
- documents.set(owner, document);
13665
- const visit = (child) => {
13666
- if (child.name === "wxs" && loadWxs) {
13667
- const name = child.attribs?.module;
13668
- if (!name || !/^[a-z_$][\w$]*$/i.test(name) || [
13669
- "__proto__",
13670
- "constructor",
13671
- "prototype"
13672
- ].includes(name)) throw new Error(`Invalid WXS module name in ${owner}: ${String(name)}`);
13673
- if (Object.hasOwn(wxsModules, name)) throw new Error(`Duplicate WXS module in ${owner}: ${name}`);
13674
- wxsModules[name] = loadWxs(owner, child);
13675
- return;
13676
- }
13677
- if (child.name === "import" && child.attribs?.src) {
13678
- const source = child.attribs.src;
13679
- const imported = loadImport(owner, source.endsWith(".wxml") ? source : `${source}.wxml`);
13680
- const importedDocument = collect(imported.root, imported.filePath);
13681
- for (const [name, definition] of importedDocument.exports) state.definitions.set(name, definition);
13682
- }
13683
- for (const nested of child.children ?? []) visit(nested);
13684
- };
13685
- visit(node);
13686
- if (Object.keys(wxsModules).length) state.wxsModules = wxsModules;
13687
- for (const [name, definition] of ownDefinitions) {
13688
- state.definitions.set(name, definition);
13689
- definitionScopes.set(definition, state.definitions);
13690
- if (state.wxsModules) definitionWxsScopes.set(definition, state.wxsModules);
13691
- }
13692
- state.definitionScopes = definitionScopes;
13693
- state.definitionWxsScopes = definitionWxsScopes;
13694
- 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 })) }
13695
13730
  };
13696
- return collect(root, filePath).state;
13697
13731
  }
13698
13732
  //#endregion
13699
- //#region ../../mpcore/packages/simulator/src/view/templateText.ts
13700
- function interpolateTemplateText(input, data, emptyNull = false) {
13701
- let output = "";
13702
- let offset = 0;
13703
- for (const range of templateInterpolations(input)) {
13704
- const value = resolveTemplateExpression(data, range.expression);
13705
- output += input.slice(offset, range.start) + (value === void 0 || emptyNull && value === null ? "" : String(value));
13706
- offset = range.end;
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;
13707
13785
  }
13708
- 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
+ };
13709
13872
  }
13710
13873
  //#endregion
13711
13874
  //#region ../../node_modules/.pnpm/entities@8.1.0/node_modules/entities/dist/decode-codepoint.js
@@ -16754,7 +16917,7 @@ const attributeEscapeRegex = /["&\u{A0}]/gu;
16754
16917
  * following {@link https://html.spec.whatwg.org/multipage/parsing.html#escapingString}.
16755
16918
  * @param data String to escape.
16756
16919
  */
16757
- function escapeAttribute$3(data) {
16920
+ function escapeAttribute$2(data) {
16758
16921
  return escapeWithRegex(attributeEscapeRegex, data);
16759
16922
  }
16760
16923
  const textEscapeRegex = /[&<>\u{A0}]/gu;
@@ -16866,7 +17029,7 @@ function replaceQuotes(value) {
16866
17029
  */
16867
17030
  function formatAttributes(attributes, options, xmlMode) {
16868
17031
  if (!attributes) return "";
16869
- 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;
16870
17033
  const isForeign = xmlMode === "foreign";
16871
17034
  const showEmpty = !!(options.emptyAttrs ?? xmlMode);
16872
17035
  let result = "";
@@ -17481,6 +17644,344 @@ function parseDocument(data, options) {
17481
17644
  return handler.root;
17482
17645
  }
17483
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
17484
17985
  //#region ../../mpcore/packages/simulator/src/view/templateAttributes.ts
17485
17986
  function decodeAttribute(raw, quote) {
17486
17987
  const source = raw.replaceAll(`\\${quote}`, quote);
@@ -17540,7 +18041,7 @@ function parseWxsTemplateDocument(templateSource) {
17540
18041
  });
17541
18042
  const masked = maskTemplateAttributes(source);
17542
18043
  const document = parseDocument(`<page>${masked.source}</page>`, {
17543
- xmlMode: false,
18044
+ xmlMode: true,
17544
18045
  decodeEntities: false,
17545
18046
  lowerCaseAttributeNames: false,
17546
18047
  lowerCaseTags: false,
@@ -17598,7 +18099,7 @@ function collectDataset(node, source) {
17598
18099
  function escapeText$2(text) {
17599
18100
  return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
17600
18101
  }
17601
- function escapeAttribute$2(value) {
18102
+ function escapeAttribute$1(value) {
17602
18103
  return escapeText$2(value).replaceAll("\"", "&quot;");
17603
18104
  }
17604
18105
  function cloneNode(node) {
@@ -17643,7 +18144,7 @@ function serializeDomNode$1(node) {
17643
18144
  if (node.type === "root") return (node.children ?? []).map(serializeDomNode$1).join("");
17644
18145
  const tagName = node.name ?? "";
17645
18146
  if (!tagName) return (node.children ?? []).map(serializeDomNode$1).join("");
17646
- 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}>`;
17647
18148
  }
17648
18149
  function isTagNode(node) {
17649
18150
  return node.type === "tag" && typeof node.name === "string";
@@ -17662,7 +18163,7 @@ function resolveComponentAttributeValue(value, scope) {
17662
18163
  }
17663
18164
  return interpolateTemplate$1(value, wxsScopeData(scope));
17664
18165
  }
17665
- function applyNodeBindings(node, scope) {
18166
+ function applyNodeBindings(node, scope, richText = false) {
17666
18167
  if (!isTagNode(node)) {
17667
18168
  if (node.type === "text" && typeof node.data === "string") node.data = interpolateTemplateText(node.data, wxsScopeData(scope));
17668
18169
  return;
@@ -17670,6 +18171,7 @@ function applyNodeBindings(node, scope) {
17670
18171
  node.attribs ??= {};
17671
18172
  node.attribs["data-sim-scope"] = scope.getScopeId();
17672
18173
  for (const key of STRUCTURAL_ATTRS) delete node.attribs[key];
18174
+ if (richText) node.children = [];
17673
18175
  let dataset;
17674
18176
  for (const [key, value] of Object.entries({ ...node.attribs })) {
17675
18177
  if (EVENT_BINDING_ATTRS.includes(key)) {
@@ -17677,6 +18179,8 @@ function applyNodeBindings(node, scope) {
17677
18179
  continue;
17678
18180
  }
17679
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);
17680
18184
  node.attribs[key] = String(resolvedValue ?? "");
17681
18185
  if (isDatasetAttribute(key)) {
17682
18186
  dataset ??= {};
@@ -17703,8 +18207,7 @@ function createLoopScope(scope, itemName, indexName, item, index) {
17703
18207
  //#endregion
17704
18208
  //#region ../../mpcore/packages/simulator/src/runtime/render/component.ts
17705
18209
  function resolveComponentRegistryEntry(context, ownerJsonPath, ownerFilePath, alias, genericComponentBasePath) {
17706
- const usingComponents = resolveUsingComponents(context, ownerJsonPath, ownerFilePath);
17707
- const componentBasePath = genericComponentBasePath ?? usingComponents.get(alias);
18210
+ const componentBasePath = genericComponentBasePath ?? resolveUsingComponent(context, ownerJsonPath, ownerFilePath, alias);
17708
18211
  if (!componentBasePath) return null;
17709
18212
  const filePath = `${componentBasePath}.js`;
17710
18213
  const templatePath = `${componentBasePath}.wxml`;
@@ -17725,30 +18228,25 @@ function readComponentConfig(artifactSource, jsonPath) {
17725
18228
  return {};
17726
18229
  }
17727
18230
  }
17728
- function resolveUsingComponents(context, ownerJsonPath, ownerFilePath) {
18231
+ function resolveUsingComponent(context, ownerJsonPath, ownerFilePath, alias) {
17729
18232
  try {
17730
18233
  const usingComponents = readComponentConfig(context.artifactSource, ownerJsonPath).usingComponents;
17731
- if (!usingComponents || typeof usingComponents !== "object" || Array.isArray(usingComponents)) return /* @__PURE__ */ new Map();
17732
- const resolved = /* @__PURE__ */ new Map();
17733
- for (const [alias, rawPath] of Object.entries(usingComponents)) {
17734
- if (typeof rawPath !== "string") continue;
17735
- const basePath = resolvePluginRequest(context.project.plugins, rawPath, "publicComponent")?.resourcePath ?? resolveMiniProgramComponent(ownerFilePath, rawPath, context.project.miniprogramRootPath, (candidate) => context.artifactSource.readText(candidate) !== void 0);
17736
- resolved.set(alias, basePath.replace(LEADING_SLASH_RE$4, ""));
17737
- }
17738
- 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, "");
17739
18238
  } catch {
17740
- return /* @__PURE__ */ new Map();
18239
+ return;
17741
18240
  }
17742
18241
  }
17743
18242
  function resolveComponentGenerics(context, hostNode, ownerJsonPath, ownerFilePath, componentFilePath, ownerGenerics) {
17744
18243
  const componentJsonPath = path.resolve(context.project.miniprogramRootPath, `${componentFilePath.replace(JS_FILE_RE, "")}.json`);
17745
18244
  const componentGenerics = readComponentConfig(context.artifactSource, componentJsonPath).componentGenerics;
17746
18245
  if (!componentGenerics || typeof componentGenerics !== "object" || Array.isArray(componentGenerics)) return;
17747
- const ownerComponents = resolveUsingComponents(context, ownerJsonPath, ownerFilePath);
17748
18246
  const resolved = /* @__PURE__ */ new Map();
17749
18247
  for (const [genericName, definition] of Object.entries(componentGenerics)) {
17750
18248
  const selectedAlias = hostNode.attribs?.[`generic:${genericName}`];
17751
- 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;
17752
18250
  if (selectedPath) {
17753
18251
  resolved.set(genericName, selectedPath);
17754
18252
  continue;
@@ -17799,7 +18297,6 @@ function resolveComponentProperties(clonedNode, scope, definition) {
17799
18297
  };
17800
18298
  }
17801
18299
  function createRuntimeComponentInstance(componentScopeId, context, componentEntry, nextProperties, ownerScopeId, renderTemplate, bindEventsBeforeProperties = false) {
17802
- const componentProperties = Object.keys(componentEntry.definition.methods ?? {}).some((key) => key.startsWith("__weapp_vite_")) || Object.hasOwn(componentEntry.definition.properties ?? {}, "__wvSlotOwnerId") ? Object.fromEntries(Object.keys(componentEntry.definition.properties ?? {}).filter((key) => Object.hasOwn(nextProperties, key)).map((key) => [key, nextProperties[key]])) : nextProperties;
17803
18300
  const instanceOptions = {
17804
18301
  definition: componentEntry.definition,
17805
18302
  requestRender: (callback) => context.session.requestRender(callback)
@@ -17821,7 +18318,7 @@ function createRuntimeComponentInstance(componentScopeId, context, componentEntr
17821
18318
  runComponentLifecycle(componentInstance, "created");
17822
18319
  if (bindEventsBeforeProperties) instanceOptions.triggerEvent = buildComponentTrigger(componentScopeId, context);
17823
18320
  componentInstance.__propertySnapshots = Object.fromEntries(Object.entries(componentInstance.properties).map(([key, propertyValue]) => [key, cloneValue$1(propertyValue)]));
17824
- syncComponentProperties(componentInstance, componentInstance.__definition__ ?? componentEntry.definition, componentProperties, {}, [], renderTemplate);
18321
+ syncComponentProperties(componentInstance, componentInstance.__definition__ ?? componentEntry.definition, nextProperties, {}, [], renderTemplate);
17825
18322
  if (!bindEventsBeforeProperties) instanceOptions.triggerEvent = buildComponentTrigger(componentScopeId, context);
17826
18323
  return componentInstance;
17827
18324
  } catch (error) {
@@ -18031,8 +18528,10 @@ function renderNodeTree(node, scope, context, ownerJsonPath, ownerFilePath, inst
18031
18528
  renderPass.componentRoots.set(componentScopeId, renderedComponentRoot);
18032
18529
  return renderedComponentRoot;
18033
18530
  }
18034
- applyNodeBindings(clonedNode, scope);
18531
+ const richText = clonedNode.name === "rich-text";
18532
+ applyNodeBindings(clonedNode, scope, richText);
18035
18533
  clonedNode.attribs["data-sim-node"] = instancePath;
18534
+ if (richText) return clonedNode;
18036
18535
  clonedNode.children = renderChildren(clonedNode.children ?? [], scope, context, ownerJsonPath, ownerFilePath, `${instancePath}/${clonedNode.name}`, renderPass, templateRenderState, clonedNode);
18037
18536
  return clonedNode;
18038
18537
  }
@@ -18097,14 +18596,7 @@ function reconcileRuntimePageTree(context, page, bindingsOnly = false) {
18097
18596
  }), scopeIds);
18098
18597
  if (attached === "restart") continue;
18099
18598
  const canFinalize = !pageAttaching && !hasPendingComponentAttachments(instances);
18100
- const removed = canFinalize ? [...context.componentCache].filter(([scopeId]) => scopeId.startsWith(`${pageScopeId}/`) && !renderPass.seenComponentScopes.has(scopeId)) : [];
18101
- for (const [scopeId, instance] of removed) if (isComponentAttached(instance)) runComponentLifecycle(instance, "detached");
18102
- else scheduleDiscardedComponentReady(context.componentCache, scopeId, instance);
18103
- const relationsChanged = canFinalize && syncComponentRelations(context.componentCache, renderPass.seenComponentScopes, pageScopeId);
18104
- for (const [scopeId] of removed) {
18105
- context.componentCache.delete(scopeId);
18106
- context.componentScopes.delete(scopeId);
18107
- }
18599
+ const relationsChanged = canFinalize && reconcileRemovedComponents(context.componentCache, context.componentScopes, renderPass.seenComponentScopes, pageScopeId);
18108
18600
  if (pageAttached || attached || relationsChanged) continue;
18109
18601
  if (canFinalize && !isPageBeforeReady(page)) flushDiscardedComponentReady(context.componentCache, pageScopeId, (instance) => runComponentLifecycle(instance, "ready"));
18110
18602
  if (canFinalize && !isPageBeforeReady(page) && flushComponentReady(instances, (instance) => {
@@ -18890,7 +19382,13 @@ function invokePreparedNavigationApi(prepare, option) {
18890
19382
  //#region ../../mpcore/packages/simulator/src/kernel/artifactSource.ts
18891
19383
  function createFileSystemArtifactSource() {
18892
19384
  return {
18893
- has: (filePath) => fs.existsSync(filePath),
19385
+ has(filePath) {
19386
+ try {
19387
+ return fs.statSync(filePath).isFile();
19388
+ } catch {
19389
+ return false;
19390
+ }
19391
+ },
18894
19392
  readText(filePath) {
18895
19393
  try {
18896
19394
  return fs.readFileSync(filePath, "utf8");
@@ -19035,6 +19533,62 @@ var RuntimeKernel = class {
19035
19533
  }
19036
19534
  };
19037
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
19038
19592
  //#region ../../mpcore/packages/simulator/src/project/resolveRoutes.ts
19039
19593
  const LEADING_SLASH_RE$3 = /^\/+/;
19040
19594
  const TRAILING_SLASH_RE$1 = /\/+$/;
@@ -19235,32 +19789,20 @@ function loadProject(projectPath) {
19235
19789
  });
19236
19790
  }
19237
19791
  //#endregion
19238
- //#region ../../mpcore/packages/simulator/src/view/eventBinding.ts
19239
- function resolveMiniProgramEventBinding(attributes, eventName) {
19240
- return resolveEventBindingPhases(attributes, eventName, false)?.bubble ?? null;
19792
+ //#region ../../mpcore/packages/simulator/src/view/nodeSerialization.ts
19793
+ function escapeText$1(text) {
19794
+ return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
19241
19795
  }
19242
- /** 冒泡时保留原始 target,currentTarget 则属于当前执行绑定的节点。 */
19243
- function createMiniProgramEventPayload(node, eventName, event, origin = node) {
19244
- const dataset = collectNodeDataset(node);
19245
- const nodeId = node.attribs?.id ?? "";
19246
- const isOrigin = node === origin;
19247
- return {
19248
- bubbles: false,
19249
- capturePhase: false,
19250
- composed: false,
19251
- currentTarget: {
19252
- dataset: isOrigin ? event.currentTarget?.dataset ?? event.dataset ?? dataset : dataset,
19253
- id: isOrigin ? event.currentTarget?.id ?? event.id ?? nodeId : nodeId
19254
- },
19255
- detail: event.detail,
19256
- mark: event.mark,
19257
- target: {
19258
- dataset: event.target?.dataset ?? event.dataset ?? (isOrigin ? dataset : collectNodeDataset(origin)),
19259
- id: event.target?.id ?? event.id ?? origin.attribs?.id ?? ""
19260
- },
19261
- timeStamp: Date.now(),
19262
- type: eventName
19263
- };
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("");
19264
19806
  }
19265
19807
  //#endregion
19266
19808
  //#region ../../node_modules/.pnpm/boolbase@2.0.0/node_modules/boolbase/dist/index.js
@@ -20753,10 +21295,10 @@ function queryInspectionSelectorAll(root, selector) {
20753
21295
  }
20754
21296
  //#endregion
20755
21297
  //#region ../../node_modules/.pnpm/whynot@5.0.0/node_modules/whynot/dist/whynot.umd.cjs
20756
- var require_whynot_umd = /* @__PURE__ */ __commonJSMin(((exports, module) => {
21298
+ var require_whynot_umd = /* @__PURE__ */ __commonJSMin(((exports, module, this$4) => {
20757
21299
  (function(t, s) {
20758
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 = {});
20759
- })(exports, (function(t) {
21301
+ })(this$4, (function(t) {
20760
21302
  "use strict";
20761
21303
  function s(t, s, i, r) {
20762
21304
  const n = {
@@ -21104,10 +21646,10 @@ var require_whynot_umd = /* @__PURE__ */ __commonJSMin(((exports, module) => {
21104
21646
  }));
21105
21647
  //#endregion
21106
21648
  //#region ../../node_modules/.pnpm/xspattern@3.1.0/node_modules/xspattern/dist/xspattern.umd.cjs
21107
- var require_xspattern_umd = /* @__PURE__ */ __commonJSMin(((exports, module) => {
21649
+ var require_xspattern_umd = /* @__PURE__ */ __commonJSMin(((exports, module, this$3) => {
21108
21650
  (function(A, B) {
21109
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);
21110
- })(exports, (function(A, B) {
21652
+ })(this$3, (function(A, B) {
21111
21653
  "use strict";
21112
21654
  function a(A) {
21113
21655
  return (B) => B === A;
@@ -22404,10 +22946,10 @@ var require_xspattern_umd = /* @__PURE__ */ __commonJSMin(((exports, module) =>
22404
22946
  }));
22405
22947
  //#endregion
22406
22948
  //#region ../../node_modules/.pnpm/prsc@4.0.0/node_modules/prsc/dist/prsc.umd.cjs
22407
- var require_prsc_umd = /* @__PURE__ */ __commonJSMin(((exports, module) => {
22949
+ var require_prsc_umd = /* @__PURE__ */ __commonJSMin(((exports, module, this$2) => {
22408
22950
  (function(n, t) {
22409
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 = {});
22410
- })(exports, (function(n) {
22952
+ })(this$2, (function(n) {
22411
22953
  "use strict";
22412
22954
  function t(n, t) {
22413
22955
  return {
@@ -22650,12 +23192,12 @@ var require_prsc_umd = /* @__PURE__ */ __commonJSMin(((exports, module) => {
22650
23192
  }));
22651
23193
  //#endregion
22652
23194
  //#region ../../mpcore/packages/simulator/src/view/xpath.ts
22653
- var import_fontoxpath = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
23195
+ var import_fontoxpath = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module, this$1) => {
22654
23196
  (function(root, factory) {
22655
23197
  if (typeof define === "function" && define.amd) define(["xspattern", "prsc"], factory);
22656
23198
  else if (typeof exports === "object") module.exports = factory(require_xspattern_umd(), require_prsc_umd());
22657
23199
  else root.fontoxpath = factory(root.xspattern, root.prsc);
22658
- })(exports, function(xspattern, prsc) {
23200
+ })(this$1, function(xspattern, prsc) {
22659
23201
  return (function(xspattern, prsc) {
22660
23202
  const VERSION = "3.34.0";
22661
23203
  const fontoxpathGlobal = {};
@@ -38560,30 +39102,12 @@ function queryInspectionXPathElements(root, expression) {
38560
39102
  }
38561
39103
  //#endregion
38562
39104
  //#region ../../mpcore/packages/simulator/src/view/nodeHandle.ts
38563
- function escapeText$1(text) {
38564
- return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
38565
- }
38566
39105
  function createValueEventDetail(value, detail) {
38567
39106
  return {
38568
39107
  value,
38569
39108
  ...detail ?? {}
38570
39109
  };
38571
39110
  }
38572
- function escapeAttribute$1(value) {
38573
- return escapeText$1(value).replaceAll("\"", "&quot;");
38574
- }
38575
- function serializeNode(node) {
38576
- if (node.type === "text") return escapeText$1(node.data ?? "");
38577
- if (node.type !== "tag") return (node.children ?? []).map(serializeNode).join("");
38578
- if (node.name === "page") return (node.children ?? []).filter((child) => !(child.type === "tag" && (child.name === "import" || child.name === "wxs"))).map(serializeNode).join("");
38579
- const attrs = Object.entries(node.attribs ?? {}).filter(([key]) => !key.startsWith("data-sim-")).map(([key, value]) => ` ${key}="${escapeAttribute$1(value)}"`).join("");
38580
- const children = (node.children ?? []).map(serializeNode).join("");
38581
- return `<${node.name ?? ""}${attrs}>${children}</${node.name ?? ""}>`;
38582
- }
38583
- function collectText(node) {
38584
- if (node.type === "text") return node.data ?? "";
38585
- return (node.children ?? []).map(collectText).join("");
38586
- }
38587
39111
  function resolvePageScopeId$1(scopeId) {
38588
39112
  if (!scopeId) return null;
38589
39113
  const pagePathIndex = scopeId.indexOf("/page/");
@@ -38661,7 +39185,7 @@ var HeadlessTestingNodeHandle = class HeadlessTestingNodeHandle {
38661
39185
  }
38662
39186
  async text() {
38663
39187
  this.assertActive();
38664
- return collectText(this.node);
39188
+ return collectNodeText$1(this.node);
38665
39189
  }
38666
39190
  async tagName() {
38667
39191
  this.assertActive();
@@ -38673,10 +39197,23 @@ var HeadlessTestingNodeHandle = class HeadlessTestingNodeHandle {
38673
39197
  const normalizedEventName = eventName.trim();
38674
39198
  if (!normalizedEventName) throw new Error("Event name must be a non-empty string in headless testing runtime.");
38675
39199
  let handlerResults;
38676
- const nativeHandled = this.interactions.dispatchNativeEvent?.(this.node, normalizedEventName, event, normalizedEventName === "tap" ? (result) => {
38677
- handlerResults ??= [];
38678
- handlerResults.push(result);
38679
- } : 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
+ }
38680
39217
  if (normalizedEventName === "tap" && nativeHandled) return handlerResults ? (await Promise.all(handlerResults))[0] : void 0;
38681
39218
  const binding = resolveMiniProgramEventBinding(this.node.attribs, normalizedEventName);
38682
39219
  if (!binding?.method) {
@@ -38688,6 +39225,20 @@ var HeadlessTestingNodeHandle = class HeadlessTestingNodeHandle {
38688
39225
  async tap(event = {}) {
38689
39226
  return await this.trigger("tap", event);
38690
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
+ }
38691
39242
  async input(value, event = {}) {
38692
39243
  return await this.trigger("input", {
38693
39244
  ...event,
@@ -38740,16 +39291,20 @@ function isMustacheOnly(value) {
38740
39291
  return trimmed.startsWith("{{") && trimmed.endsWith("}}") && !trimmed.includes("{{", 2);
38741
39292
  }
38742
39293
  function visitDom(node, visitor) {
38743
- visitor(node);
39294
+ if (visitor(node) === false) return;
38744
39295
  for (const child of node.children ?? []) visitDom(child, visitor);
38745
39296
  }
38746
39297
  function interpolateDomTree(root, data) {
38747
39298
  visitDom(root, (node) => {
39299
+ const richText = node.type === "tag" && node.name === "rich-text";
39300
+ if (richText) node.children = [];
38748
39301
  if (typeof node.data === "string") node.data = interpolateTemplateText(node.data, data);
38749
39302
  if (node.attribs) {
38750
39303
  let dataset;
38751
39304
  for (const [key, value] of Object.entries(node.attribs)) {
38752
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);
38753
39308
  node.attribs[key] = String(resolvedValue ?? "");
38754
39309
  if (isDatasetAttribute(key)) {
38755
39310
  dataset ??= {};
@@ -38758,6 +39313,7 @@ function interpolateDomTree(root, data) {
38758
39313
  }
38759
39314
  node.dataset = dataset;
38760
39315
  }
39316
+ if (richText) return false;
38761
39317
  });
38762
39318
  }
38763
39319
  function escapeText(text) {
@@ -38779,11 +39335,12 @@ function renderPageTree(project, page) {
38779
39335
  const templateSource = project.artifactSource.readText(templatePath);
38780
39336
  if (templateSource == null) throw new Error(`Missing template in headless runtime: ${templatePath}`);
38781
39337
  const document = parseDocument(`<page>${templateSource}</page>`, {
38782
- xmlMode: false,
39338
+ xmlMode: true,
38783
39339
  decodeEntities: false,
38784
39340
  recognizeSelfClosing: true
38785
39341
  });
38786
39342
  interpolateDomTree(document, page.data);
39343
+ linkRenderedParents(document);
38787
39344
  return {
38788
39345
  root: document,
38789
39346
  wxml: serializeDomNode((document.children ?? [])[0] ?? document)
@@ -38954,7 +39511,7 @@ function getNativeScrollOffset(page, node) {
38954
39511
  };
38955
39512
  }
38956
39513
  /** 原生交互更新宿主状态,导航仍交给统一 wx 路由入口。 */
38957
- function dispatchNativeNodeEvent(page, node, eventName, event, wx, resolveMethod, onHandlerResult) {
39514
+ function dispatchNativeNodeEvent(page, node, eventName, event, wx, resolveMethod, onHandlerResult, resolveComponent) {
38958
39515
  if (eventName === "scroll" && node.name === "scroll-view") {
38959
39516
  const detail = event.detail;
38960
39517
  const state = pageScrollViews.get(page)?.get(nodeKey(node));
@@ -38968,30 +39525,42 @@ function dispatchNativeNodeEvent(page, node, eventName, event, wx, resolveMethod
38968
39525
  if (eventName !== "tap") return false;
38969
39526
  let current = node;
38970
39527
  let navigator;
39528
+ let button;
39529
+ const origin = getNativeNodeOwner(node)?.target ?? node;
38971
39530
  let handlers;
38972
39531
  let handled = false;
38973
- while (current) {
38974
- if (!navigator && current.name === "navigator") navigator = current;
38975
- const binding = resolveMiniProgramEventBinding(current.attribs, "tap");
38976
- if (binding?.method) {
38977
- const call = resolveMethod(current.attribs?.["data-sim-scope"] ?? null, binding.method);
39532
+ const collectHandler = (scopeId, target, binding) => {
39533
+ if (binding.method) {
39534
+ const call = resolveMethod(scopeId, binding.method);
38978
39535
  handlers ??= [];
38979
39536
  handlers.push({
38980
- node: current,
39537
+ event: createMiniProgramEventPayload(target, "tap", event, origin),
38981
39538
  call
38982
39539
  });
38983
39540
  handled = true;
38984
39541
  }
38985
- 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)) {
38986
39547
  handled = true;
38987
39548
  break;
38988
39549
  }
38989
39550
  current = current.parent;
38990
39551
  }
39552
+ const submit = button ? prepareNativeFormSubmit(button, `page:${page.route}`, event, resolveMethod, resolveComponent) : void 0;
38991
39553
  if (handlers) for (const handler of handlers) {
38992
- const result = handler.call(createMiniProgramEventPayload(handler.node, "tap", event, node));
39554
+ const result = handler.call(handler.event);
38993
39555
  onHandlerResult?.(result);
38994
39556
  }
39557
+ if (submit) {
39558
+ handled = true;
39559
+ if (submit.call) {
39560
+ const result = submit.call(submit.event);
39561
+ onHandlerResult?.(result);
39562
+ }
39563
+ }
38995
39564
  if (!navigator) return handled;
38996
39565
  const openType = navigator.attribs?.["open-type"] ?? "navigate";
38997
39566
  const url = navigator.attribs?.url ?? "";
@@ -39776,6 +40345,64 @@ function createHeadlessMediaQueryObserver(driver, onDisconnect) {
39776
40345
  };
39777
40346
  }
39778
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
39779
40406
  //#region ../../mpcore/packages/simulator/src/view/videoContext.ts
39780
40407
  function createEventPayload(node, eventName, detail) {
39781
40408
  const dataset = collectNodeDataset(node);
@@ -40034,6 +40661,360 @@ function createWorkerHost(options) {
40034
40661
  };
40035
40662
  }
40036
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
40037
41018
  //#region ../../mpcore/packages/simulator/src/runtime/runtimeGlobals.ts
40038
41019
  const MINI_PROGRAM_UNAVAILABLE_GLOBAL_NAMES = [
40039
41020
  "window",
@@ -40077,102 +41058,6 @@ function createMiniProgramRuntimeGlobals(availableGlobals, overrides) {
40077
41058
  }
40078
41059
  //#endregion
40079
41060
  //#region ../../mpcore/packages/simulator/src/runtime/moduleLoader.ts
40080
- const ESM_IMPORT_LINE_RE = /^[ \t]*import[^\n]*$/gm;
40081
- const ESM_EXPORT_LIST_RE = /^[ \t]*export[ \t]*\{([^}\n]+)\}[ \t]*;?$/gm;
40082
- const ESM_EXPORT_DECLARATION_RE = /\bexport\s+(const|let|var|function|class)\s+([A-Za-z_$][\w$]*)/g;
40083
- const ESM_EXPORT_DEFAULT_DECLARATION_RE = /\bexport\s+default\s+(function|class)\s+([A-Za-z_$][\w$]*)/g;
40084
- function isImportWhitespace(value) {
40085
- return value === " " || value === " ";
40086
- }
40087
- function parseEsmImportLine(line) {
40088
- let statement = line.trim().slice(6).trim();
40089
- if (statement.endsWith(";")) statement = statement.slice(0, -1).trimEnd();
40090
- if (statement.startsWith("\"") || statement.startsWith("'")) {
40091
- const quote = statement[0];
40092
- if (statement.endsWith(quote) && statement.length > 1) return {
40093
- request: statement.slice(1, -1),
40094
- sideEffect: true
40095
- };
40096
- return;
40097
- }
40098
- let fromIndex = statement.indexOf("from");
40099
- while (fromIndex >= 0) {
40100
- if (isImportWhitespace(statement[fromIndex - 1]) && isImportWhitespace(statement[fromIndex + 4])) break;
40101
- fromIndex = statement.indexOf("from", fromIndex + 4);
40102
- }
40103
- if (fromIndex < 0) return;
40104
- const specifier = statement.slice(0, fromIndex).trim();
40105
- const requestLiteral = statement.slice(fromIndex + 4).trim();
40106
- const quote = requestLiteral[0];
40107
- if (!specifier || quote !== "\"" && quote !== "'" || requestLiteral.length < 2 || !requestLiteral.endsWith(quote)) return;
40108
- return {
40109
- request: requestLiteral.slice(1, -1),
40110
- sideEffect: false,
40111
- specifier
40112
- };
40113
- }
40114
- function createEsmImportBindings(specifier, request, index) {
40115
- const namespaceName = `__headlessEsmImport${index}`;
40116
- const statements = [`const ${namespaceName} = require(${JSON.stringify(request)});`];
40117
- const normalizedSpecifier = specifier.trim();
40118
- if (normalizedSpecifier.startsWith("{") && normalizedSpecifier.endsWith("}")) {
40119
- const entries = normalizedSpecifier.slice(1, -1).split(",");
40120
- for (const entry of entries) {
40121
- const [importedName, localName] = entry.trim().split(/\s+as\s+/);
40122
- if (!importedName) continue;
40123
- const bindingName = localName || importedName;
40124
- statements.push(`const ${bindingName} = ${namespaceName}[${JSON.stringify(importedName)}];`);
40125
- }
40126
- return statements.join("\n");
40127
- }
40128
- if (normalizedSpecifier.startsWith("* as ")) {
40129
- const bindingName = normalizedSpecifier.slice(5).trim();
40130
- statements.push(`const ${bindingName} = ${namespaceName};`);
40131
- return statements.join("\n");
40132
- }
40133
- const [defaultName, namedPart] = normalizedSpecifier.split(/\s*,\s*/, 2);
40134
- if (defaultName) statements.push(`const ${defaultName.trim()} = ${namespaceName}.default ?? ${namespaceName};`);
40135
- if (namedPart?.startsWith("{") && namedPart.endsWith("}")) statements.push(createEsmImportBindings(namedPart, request, index).split("\n").slice(1).join("\n"));
40136
- return statements.join("\n");
40137
- }
40138
- function transformEsmToCommonJs(source) {
40139
- if (!/^\s*(?:import|export)\b/m.test(source)) return source;
40140
- let importIndex = 0;
40141
- const exportedNames = [];
40142
- let transformed = source.replace(ESM_IMPORT_LINE_RE, (line) => {
40143
- const parsed = parseEsmImportLine(line);
40144
- if (!parsed) return line;
40145
- if (parsed.sideEffect) return `require(${JSON.stringify(parsed.request)});`;
40146
- return createEsmImportBindings(parsed.specifier, parsed.request, importIndex++);
40147
- });
40148
- transformed = transformed.replace(ESM_EXPORT_LIST_RE, (_match, entries) => {
40149
- for (const entry of entries.split(",")) {
40150
- const [local, exported] = entry.trim().split(/\s+as\s+/);
40151
- if (local) exportedNames.push({
40152
- exported: exported || local,
40153
- local
40154
- });
40155
- }
40156
- return "";
40157
- });
40158
- transformed = transformed.replace(ESM_EXPORT_DEFAULT_DECLARATION_RE, (_match, kind, local) => {
40159
- exportedNames.push({
40160
- exported: "default",
40161
- local
40162
- });
40163
- return `${kind} ${local}`;
40164
- });
40165
- transformed = transformed.replace(ESM_EXPORT_DECLARATION_RE, (_match, kind, local) => {
40166
- exportedNames.push({
40167
- exported: local,
40168
- local
40169
- });
40170
- return `${kind} ${local}`;
40171
- });
40172
- transformed = transformed.replace(/\bexport\s+default\s+/g, "module.exports.default = ");
40173
- if (exportedNames.length > 0) transformed += `\n${exportedNames.map(({ exported, local }) => `module.exports[${JSON.stringify(exported)}] = ${local};`).join("\n")}`;
40174
- return transformed;
40175
- }
40176
41061
  function createRequireNotFoundError(request, importer) {
40177
41062
  return /* @__PURE__ */ new Error(`Cannot resolve require("${request}") from ${normalize(importer)} in headless runtime.`);
40178
41063
  }
@@ -40232,6 +41117,7 @@ function createModuleLoader(registries, getCurrentPages, getApp, wxDriver, optio
40232
41117
  const source = options.artifactSource.readText(resolvedPath);
40233
41118
  if (source == null) throw new TypeError(`Missing module in headless runtime: ${normalize(resolvedPath)}`);
40234
41119
  const module = {
41120
+ source,
40235
41121
  componentDefinitions: [],
40236
41122
  exports: {}
40237
41123
  };
@@ -40241,7 +41127,7 @@ function createModuleLoader(registries, getCurrentPages, getApp, wxDriver, optio
40241
41127
  const previousLoadContext = registries.currentLoadContext;
40242
41128
  registries.currentLoadContext = loadContext;
40243
41129
  const localRequire = ((request) => {
40244
- 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));
40245
41131
  if (!requiredPath) throw createRequireNotFoundError(request, resolvedPath);
40246
41132
  if (requiredPath.endsWith(".json")) {
40247
41133
  const content = options.artifactSource.readText(requiredPath);
@@ -40254,7 +41140,7 @@ function createModuleLoader(registries, getCurrentPages, getApp, wxDriver, optio
40254
41140
  });
40255
41141
  localRequire.async = (request) => Promise.resolve().then(() => localRequire(request));
40256
41142
  try {
40257
- 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));
40258
41144
  const registeredDefinitions = [...registries.components.values()].filter((definition) => !registeredDefinitionsBefore.has(definition));
40259
41145
  module.componentDefinitions = [.../* @__PURE__ */ new Set([
40260
41146
  ...requiredComponentDefinitions,
@@ -40301,6 +41187,17 @@ function createModuleLoader(registries, getCurrentPages, getApp, wxDriver, optio
40301
41187
  if (!definition) throw new Error(`Page() was not registered for route "${route}" while executing ${normalize(filePath)}.`);
40302
41188
  return definition;
40303
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
+ },
40304
41201
  executeComponentModule(filePath, id) {
40305
41202
  const loadedModule = executeModule(filePath, {
40306
41203
  kind: "component",
@@ -40409,7 +41306,15 @@ var HeadlessRouteEvents = class {
40409
41306
  const callbacks = state.callbacks;
40410
41307
  state.callbacks = void 0;
40411
41308
  try {
40412
- this.bus.$emit("appRouteDone", state.event);
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
+ });
40413
41318
  } finally {
40414
41319
  this.settle(callbacks);
40415
41320
  }
@@ -42293,6 +43198,7 @@ var HeadlessSession = class {
42293
43198
  appInstance = null;
42294
43199
  appLifecycle = new HeadlessAppLifecycle();
42295
43200
  moduleLoader;
43201
+ templateRegistration;
42296
43202
  routeEvents = new HeadlessRouteEvents((callback) => this.requestRender(callback), (page) => this.currentPageInstance === page && !this.kernel.isClosed);
42297
43203
  eventListeners = /* @__PURE__ */ new Map();
42298
43204
  registries;
@@ -42301,8 +43207,13 @@ var HeadlessSession = class {
42301
43207
  componentCache = /* @__PURE__ */ new Map();
42302
43208
  componentScopes = /* @__PURE__ */ new Map();
42303
43209
  selectorQueryScopeSnapshots = /* @__PURE__ */ new WeakMap();
42304
- renderRequestPending = false;
42305
- 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
+ });
42306
43217
  tabBarRoutes;
42307
43218
  tabPages = /* @__PURE__ */ new Map();
42308
43219
  tabBarItems = /* @__PURE__ */ new Map();
@@ -42321,6 +43232,7 @@ var HeadlessSession = class {
42321
43232
  constructor(options) {
42322
43233
  if (!options.project && !options.projectPath) throw new Error("Headless runtime requires projectPath or project.");
42323
43234
  this.project = options.project ?? loadProject(options.projectPath);
43235
+ this.templateRegistration = createTemplateRegistration(this.project.artifactSource, this.project.miniprogramRootPath, options.compiledTemplatePaths);
42324
43236
  this.wxState = createHeadlessWxState(this.kernel.scheduler, {
42325
43237
  strictMocks: options.strictHostMocks,
42326
43238
  readPackageFile: createPackageAssetReader((relative) => this.project.artifactSource.readText(path.join(this.project.miniprogramRootPath, relative)))
@@ -42458,8 +43370,7 @@ var HeadlessSession = class {
42458
43370
  failure = { error };
42459
43371
  }
42460
43372
  this.canvasContexts.clear();
42461
- this.renderRequestCallbacks.length = 0;
42462
- this.renderRequestPending = false;
43373
+ this.renderQueue.close();
42463
43374
  this.appLifecycle.close();
42464
43375
  this.routeEvents.close();
42465
43376
  this.wxState.close();
@@ -42509,6 +43420,23 @@ var HeadlessSession = class {
42509
43420
  this.assertActive();
42510
43421
  return this.pages.slice();
42511
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
+ }
42512
43440
  getWx() {
42513
43441
  this.assertActive();
42514
43442
  return this.moduleLoader.wx;
@@ -42564,7 +43492,7 @@ var HeadlessSession = class {
42564
43492
  const current = this.requireCurrentPage("renderCurrentPage()");
42565
43493
  const rendered = renderRuntimePageTree({
42566
43494
  changedPageKeys: current.__lastChangedKeys__ ?? [],
42567
- artifactSource: this.project.artifactSource,
43495
+ artifactSource: this.templateRegistration.artifactSource,
42568
43496
  componentCache: this.componentCache,
42569
43497
  componentScopes: this.componentScopes,
42570
43498
  moduleLoader: this.moduleLoader,
@@ -42594,20 +43522,18 @@ var HeadlessSession = class {
42594
43522
  dispatchNativeNodeEvent(node, eventName, event, onHandlerResult) {
42595
43523
  const current = this.requireCurrentPage("dispatchNativeNodeEvent()");
42596
43524
  if (eventName === "tap") this.renderCurrentPage();
42597
- 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
+ });
42598
43533
  }
42599
43534
  requestRender(callback) {
42600
43535
  this.assertActive();
42601
- if (callback) this.renderRequestCallbacks.push(callback);
42602
- if (this.renderRequestPending) return;
42603
- this.renderRequestPending = true;
42604
- Promise.resolve().then(() => {
42605
- if (this.kernel.isClosed) return;
42606
- this.renderRequestPending = false;
42607
- const callbacks = this.renderRequestCallbacks.splice(0);
42608
- if (this.currentPageInstance) this.renderCurrentPage();
42609
- callbacks.forEach((cb) => cb());
42610
- });
43536
+ this.renderQueue.request(callback);
42611
43537
  }
42612
43538
  getScopeSnapshot(scopeId) {
42613
43539
  const scope = this.componentScopes.get(scopeId);
@@ -43832,6 +44758,133 @@ var HeadlessTestingPageHandle = class {
43832
44758
  }
43833
44759
  };
43834
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
43835
44888
  //#region ../../mpcore/packages/simulator/src/testing/sessionHandle/scope.ts
43836
44889
  var HeadlessTestingScopeHandle = class HeadlessTestingScopeHandle {
43837
44890
  scopeId;
@@ -43893,20 +44946,41 @@ var HeadlessTestingScopeHandle = class HeadlessTestingScopeHandle {
43893
44946
  var HeadlessTestingSessionHandle = class {
43894
44947
  project;
43895
44948
  session;
43896
- pendingNavigations = /* @__PURE__ */ new Set();
44949
+ navigation;
44950
+ disconnection;
44951
+ cdp;
43897
44952
  constructor(project, session) {
43898
44953
  this.project = project;
43899
44954
  this.session = session;
43900
- this.session.on("close", () => {
43901
- for (const reject of this.pendingNavigations) reject(/* @__PURE__ */ new Error("The headless testing session closed before navigation completed."));
43902
- });
44955
+ this.navigation = new HeadlessTestingNavigation(session);
43903
44956
  }
43904
- async close() {
43905
- this.disconnect();
44957
+ close() {
44958
+ return this.disconnect();
43906
44959
  }
43907
- /** 无头会话独占本地 runtime;同步释放与 automator 的连接清理契约一致。 */
44960
+ /** 先保存共享结果,再同步开始关闭,覆盖关闭监听器内的重入调用。 */
43908
44961
  disconnect() {
43909
- this.session.close();
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());
43910
44984
  }
43911
44985
  async toolInfo() {
43912
44986
  return { version: "mpcore-simulator" };
@@ -43976,23 +45050,31 @@ var HeadlessTestingSessionHandle = class {
43976
45050
  }, `Timed out waiting for ${normalizedCount} component(s) matching "${normalizedSelector}" in headless testing runtime.`, options);
43977
45051
  }
43978
45052
  async reLaunch(route) {
43979
- await this.invokeNavigation("reLaunch", { url: route });
45053
+ await this.navigation.invoke("reLaunch", { url: route });
43980
45054
  return await this.requireCurrentPage("reLaunch", route);
43981
45055
  }
45056
+ /** 只更新页面定义;不隐式导航,也不替换当前页面或应用实例。 */
45057
+ async reloadPageDefinition(route) {
45058
+ return this.session.reloadPageDefinition(route);
45059
+ }
45060
+ /** 显式提交宿主编译模板;不隐式重载脚本或导航。 */
45061
+ async reloadTemplates() {
45062
+ return this.session.reloadTemplates();
45063
+ }
43982
45064
  async navigateTo(route) {
43983
- await this.invokeNavigation("navigateTo", { url: route });
45065
+ await this.navigation.invoke("navigateTo", { url: route });
43984
45066
  return await this.requireCurrentPage("navigateTo", route);
43985
45067
  }
43986
45068
  async redirectTo(route) {
43987
- await this.invokeNavigation("redirectTo", { url: route });
45069
+ await this.navigation.invoke("redirectTo", { url: route });
43988
45070
  return await this.requireCurrentPage("redirectTo", route);
43989
45071
  }
43990
45072
  async navigateBack(delta = 1) {
43991
- await this.invokeNavigation("navigateBack", { delta });
45073
+ await this.navigation.invoke("navigateBack", { delta });
43992
45074
  return await this.currentPage();
43993
45075
  }
43994
45076
  async switchTab(route) {
43995
- await this.invokeNavigation("switchTab", { url: route });
45077
+ await this.navigation.invoke("switchTab", { url: route });
43996
45078
  return await this.requireCurrentPage("switchTab", route);
43997
45079
  }
43998
45080
  async waitForCurrentPage(route, options = {}) {
@@ -44028,32 +45110,6 @@ var HeadlessTestingSessionHandle = class {
44028
45110
  async triggerRouteDone(options) {
44029
45111
  this.session.triggerRouteDone(options);
44030
45112
  }
44031
- invokeNavigation(methodName, options) {
44032
- return new Promise((resolve, reject) => {
44033
- const rejectNavigation = (error) => {
44034
- this.pendingNavigations.delete(rejectNavigation);
44035
- reject(error);
44036
- };
44037
- this.pendingNavigations.add(rejectNavigation);
44038
- try {
44039
- this.session.callWxMethod(methodName, {
44040
- ...options,
44041
- success: () => {
44042
- this.pendingNavigations.delete(rejectNavigation);
44043
- resolve();
44044
- },
44045
- fail: (error) => {
44046
- const details = error && typeof error === "object" ? error : void 0;
44047
- const reason = typeof details?.errMsg === "string" ? details.errMsg : typeof details?.message === "string" ? details.message : String(error);
44048
- const target = options.url ? ` route "${options.url}"` : "";
44049
- rejectNavigation(new Error(`Failed to ${methodName}${target} through the headless wx runtime: ${reason}`, { cause: error }));
44050
- }
44051
- });
44052
- } catch (error) {
44053
- rejectNavigation(error);
44054
- }
44055
- });
44056
- }
44057
45113
  async requireCurrentPage(methodName, route) {
44058
45114
  const page = await this.currentPage();
44059
45115
  if (!page) throw new Error(`Failed to ${methodName} route "${route}" through the headless wx runtime.`);
@@ -44072,7 +45128,10 @@ function resolveInitialRoute(session) {
44072
45128
  return session.project.routes[0]?.route ?? null;
44073
45129
  }
44074
45130
  async function launch(options) {
44075
- const session = createHeadlessSession({ projectPath: options.projectPath });
45131
+ const session = createHeadlessSession({
45132
+ compiledTemplatePaths: options.compiledTemplatePaths,
45133
+ projectPath: options.projectPath
45134
+ });
44076
45135
  const handle = new HeadlessTestingSessionHandle(session.project, session);
44077
45136
  try {
44078
45137
  await options.onSessionCreated?.(handle);