@weapp-vite/miniprogram-automator 1.2.18 → 1.2.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -943,7 +943,7 @@ var Connection = class Connection extends EventEmitter {
943
943
  //#endregion
944
944
  //#region src/headless.ts
945
945
  async function launchHeadlessAutomator(options) {
946
- return await (await import("./launch-Bvji0XA8.mjs")).launch({ projectPath: options.projectPath });
946
+ return await (await import("./launch-CMN_emd-.mjs")).launch({ projectPath: options.projectPath });
947
947
  }
948
948
  //#endregion
949
949
  //#region src/launcher/portLease.ts
@@ -1098,7 +1098,7 @@ function resolveWechatDevtoolsBootstrapArgs(args) {
1098
1098
  }
1099
1099
  //#endregion
1100
1100
  //#region package.json
1101
- var version = "1.2.18";
1101
+ var version = "1.2.19";
1102
1102
  //#endregion
1103
1103
  //#region src/Native.ts
1104
1104
  /** Native 的实现。 */
@@ -12634,13 +12634,13 @@ const BLOCKED_MEMBER_KEYS = /* @__PURE__ */ new Set([
12634
12634
  "prototype"
12635
12635
  ]);
12636
12636
  const NUMERIC_DOT_PATH_RE = /\.(\d+)(?=[.[\s,}\]]|$)/g;
12637
- function isMustacheOnly$1(value) {
12637
+ function isMustacheOnly$2(value) {
12638
12638
  const trimmed = value.trim();
12639
12639
  return trimmed.startsWith("{{") && trimmed.endsWith("}}") && !trimmed.includes("{{", 2);
12640
12640
  }
12641
12641
  function unwrapMustacheExpression(expression) {
12642
12642
  const normalized = expression.trim();
12643
- if (isMustacheOnly$1(normalized)) return normalized.slice(2, -2).trim();
12643
+ if (isMustacheOnly$2(normalized)) return normalized.slice(2, -2).trim();
12644
12644
  return normalized;
12645
12645
  }
12646
12646
  function parseTemplateExpression(expression) {
@@ -12845,6 +12845,26 @@ function flushComponentAttachments(instances, attach) {
12845
12845
  return pending.length > 0;
12846
12846
  }
12847
12847
  //#endregion
12848
+ //#region ../../mpcore/packages/simulator/src/runtime/componentInstance/discardedReady.ts
12849
+ const discardedReady = /* @__PURE__ */ new WeakMap();
12850
+ /** 初始模板分支被 props 替换时,微信仍发送 ready,但不发送 attached 或 detached。 */
12851
+ function scheduleDiscardedComponentReady(cache, scopeId, instance) {
12852
+ const pending = discardedReady.get(cache) ?? /* @__PURE__ */ new Map();
12853
+ pending.set(scopeId, instance);
12854
+ discardedReady.set(cache, pending);
12855
+ }
12856
+ function flushDiscardedComponentReady(cache, pageScopeId, ready) {
12857
+ const pending = discardedReady.get(cache);
12858
+ if (!pending) return;
12859
+ const current = [...pending].filter(([scopeId]) => scopeId.startsWith(`${pageScopeId}/`));
12860
+ for (const [scopeId, instance] of current) {
12861
+ pending.delete(scopeId);
12862
+ instance.__ready__ = true;
12863
+ }
12864
+ for (const [, instance] of current) ready(instance);
12865
+ if (!pending.size) discardedReady.delete(cache);
12866
+ }
12867
+ //#endregion
12848
12868
  //#region ../../mpcore/packages/simulator/src/runtime/pageLifecycle.ts
12849
12869
  const loadingPages = /* @__PURE__ */ new WeakSet();
12850
12870
  function isPageBeforeReady(page) {
@@ -13045,6 +13065,20 @@ function resolveSelectorQueryScopeSnapshot(scope, snapshots) {
13045
13065
  return null;
13046
13066
  }
13047
13067
  //#endregion
13068
+ //#region ../../mpcore/packages/simulator/src/runtime/componentInstance/selection.ts
13069
+ function hasExportBehavior(definition, visited = /* @__PURE__ */ new Set()) {
13070
+ if (visited.has(definition)) return false;
13071
+ visited.add(definition);
13072
+ return (Array.isArray(definition.behaviors) ? definition.behaviors : []).some((behavior) => behavior === "wx://component-export" || behavior !== null && typeof behavior === "object" && hasExportBehavior(behavior, visited));
13073
+ }
13074
+ /** 原生选择器尊重组件导出行为;测试桥接继续保留完整组件实例。 */
13075
+ function resolveNativeComponentSelection(instance) {
13076
+ if (!instance) return null;
13077
+ const definition = instance.__definition__;
13078
+ if (!definition || !hasExportBehavior(definition) || typeof instance.export !== "function") return instance;
13079
+ return instance.export();
13080
+ }
13081
+ //#endregion
13048
13082
  //#region ../../mpcore/packages/simulator/src/runtime/moduleResolution.ts
13049
13083
  function resolveMiniProgramModule(importer, request, miniprogramRootPath, hasFile) {
13050
13084
  function resolveFile(basePath) {
@@ -13079,6 +13113,21 @@ function resolveMiniProgramComponent(ownerFilePath, request, miniprogramRootPath
13079
13113
  return npmPath ? relative(miniprogramRootPath, npmPath).slice(0, -3) : localPath;
13080
13114
  }
13081
13115
  //#endregion
13116
+ //#region ../../mpcore/packages/simulator/src/view/nodeDataset.ts
13117
+ const DATASET_NAME_RE = /-([a-z])/g;
13118
+ function isDatasetAttribute(attributeName) {
13119
+ return attributeName.startsWith("data-") && !attributeName.startsWith("data-sim-");
13120
+ }
13121
+ function toDatasetKey(attributeName) {
13122
+ return attributeName.slice(5).replace(DATASET_NAME_RE, (_match, char) => char.toUpperCase());
13123
+ }
13124
+ function collectNodeDataset(node) {
13125
+ if (node.dataset) return { ...node.dataset };
13126
+ const dataset = {};
13127
+ for (const [key, value] of Object.entries(node.attribs ?? {})) if (isDatasetAttribute(key)) dataset[toDatasetKey(key)] = value;
13128
+ return dataset;
13129
+ }
13130
+ //#endregion
13082
13131
  //#region ../../mpcore/packages/simulator/src/view/templateImports.ts
13083
13132
  function createImportedTemplateState(root, filePath, loadImport, loadWxs) {
13084
13133
  const documents = /* @__PURE__ */ new Map();
@@ -13108,7 +13157,8 @@ function createImportedTemplateState(root, filePath, loadImport, loadWxs) {
13108
13157
  return;
13109
13158
  }
13110
13159
  if (child.name === "import" && child.attribs?.src) {
13111
- const imported = loadImport(owner, child.attribs.src);
13160
+ const source = child.attribs.src;
13161
+ const imported = loadImport(owner, source.endsWith(".wxml") ? source : `${source}.wxml`);
13112
13162
  const importedDocument = collect(imported.root, imported.filePath);
13113
13163
  for (const [name, definition] of importedDocument.exports) state.definitions.set(name, definition);
13114
13164
  }
@@ -16936,7 +16986,6 @@ function parseWxsTemplateDocument(templateSource) {
16936
16986
  //#endregion
16937
16987
  //#region ../../mpcore/packages/simulator/src/runtime/render/shared.ts
16938
16988
  const TEMPLATE_INTERPOLATION_RE$1 = /\{\{([^{}]+)\}\}/g;
16939
- const DATASET_NAME_RE$3 = /-([a-z])/g;
16940
16989
  const LEADING_SLASH_RE$4 = /^\/+/;
16941
16990
  const EVENT_BINDING_ATTRS = [
16942
16991
  "bindtap",
@@ -16955,19 +17004,16 @@ const STRUCTURAL_ATTRS = [
16955
17004
  ];
16956
17005
  const CLASS_SPLIT_RE = /\s+/;
16957
17006
  const JS_FILE_RE = /\.js$/;
16958
- function isMustacheOnly(value) {
17007
+ function isMustacheOnly$1(value) {
16959
17008
  const trimmed = value.trim();
16960
17009
  return trimmed.startsWith("{{") && trimmed.endsWith("}}") && !trimmed.includes("{{", 2);
16961
17010
  }
16962
- function toDatasetKey$3(attributeName) {
16963
- return attributeName.slice(5).replace(DATASET_NAME_RE$3, (_match, char) => char.toUpperCase());
16964
- }
16965
- function collectDataset$3(node, source) {
17011
+ function collectDataset(node, source) {
17012
+ if (node.dataset) return collectNodeDataset(node);
16966
17013
  const dataset = {};
16967
17014
  for (const [key, value] of Object.entries(node.attribs ?? {})) {
16968
- if (!key.startsWith("data-") || key === "data-sim-scope" || key === "data-sim-tap" || key === "data-sim-component") continue;
16969
- const resolvedValue = source && isMustacheOnly(value) ? resolveTemplateExpression(source, value) : value;
16970
- dataset[toDatasetKey$3(key)] = String(resolvedValue ?? "");
17015
+ if (!isDatasetAttribute(key)) continue;
17016
+ dataset[toDatasetKey(key)] = source && isMustacheOnly$1(value) ? resolveTemplateExpression(source, value) : value;
16971
17017
  }
16972
17018
  return dataset;
16973
17019
  }
@@ -16981,13 +17027,14 @@ function cloneNode(node) {
16981
17027
  return {
16982
17028
  ...node,
16983
17029
  attribs: node.attribs ? { ...node.attribs } : void 0,
17030
+ dataset: node.dataset ? { ...node.dataset } : void 0,
16984
17031
  children: node.children?.map((child) => cloneNode(child))
16985
17032
  };
16986
17033
  }
16987
17034
  function resolveValueByPath$1(source, expression) {
16988
17035
  return resolveTemplateExpression(source, expression) ?? "";
16989
17036
  }
16990
- function resolveRawValueByPath(source, expression) {
17037
+ function resolveRawValueByPath$1(source, expression) {
16991
17038
  return resolveTemplateExpression(source, expression);
16992
17039
  }
16993
17040
  function interpolateTemplate$1(input, data) {
@@ -17030,16 +17077,16 @@ function isTagNode(node) {
17030
17077
  return node.type === "tag" && typeof node.name === "string";
17031
17078
  }
17032
17079
  function resolveAttributeValue(value, scope) {
17033
- if (isMustacheOnly(value)) {
17080
+ if (isMustacheOnly$1(value)) {
17034
17081
  const expression = value.trim().slice(2, -2);
17035
- return resolveValueByPath$1(wxsScopeData(scope), expression);
17082
+ return resolveRawValueByPath$1(wxsScopeData(scope), expression);
17036
17083
  }
17037
17084
  return interpolateTemplate$1(value, wxsScopeData(scope));
17038
17085
  }
17039
17086
  function resolveComponentAttributeValue(value, scope) {
17040
- if (isMustacheOnly(value)) {
17087
+ if (isMustacheOnly$1(value)) {
17041
17088
  const expression = value.trim().slice(2, -2);
17042
- return resolveRawValueByPath(wxsScopeData(scope), expression) ?? null;
17089
+ return resolveRawValueByPath$1(wxsScopeData(scope), expression) ?? null;
17043
17090
  }
17044
17091
  return interpolateTemplate$1(value, wxsScopeData(scope));
17045
17092
  }
@@ -17051,18 +17098,25 @@ function applyNodeBindings(node, scope) {
17051
17098
  node.attribs ??= {};
17052
17099
  node.attribs["data-sim-scope"] = scope.getScopeId();
17053
17100
  for (const key of STRUCTURAL_ATTRS) delete node.attribs[key];
17101
+ let dataset;
17054
17102
  for (const [key, value] of Object.entries({ ...node.attribs })) {
17055
17103
  if (EVENT_BINDING_ATTRS.includes(key)) {
17056
17104
  node.attribs["data-sim-tap"] = value;
17057
17105
  continue;
17058
17106
  }
17059
- node.attribs[key] = typeof value === "string" ? String(resolveAttributeValue(value, scope)) : String(value);
17107
+ const resolvedValue = typeof value === "string" ? resolveAttributeValue(value, scope) : value;
17108
+ node.attribs[key] = String(resolvedValue ?? "");
17109
+ if (isDatasetAttribute(key)) {
17110
+ dataset ??= {};
17111
+ dataset[toDatasetKey(key)] = resolvedValue;
17112
+ }
17060
17113
  }
17114
+ node.dataset = dataset;
17061
17115
  }
17062
17116
  function evaluateConditionalBranch(node, scope) {
17063
17117
  const condition = node.attribs?.["wx:if"] ?? node.attribs?.["wx:elif"];
17064
17118
  if (condition == null) return true;
17065
- return Boolean(resolveRawValueByPath(wxsScopeData(scope), condition));
17119
+ return Boolean(resolveRawValueByPath$1(wxsScopeData(scope), condition));
17066
17120
  }
17067
17121
  function createLoopScope(scope, itemName, indexName, item, index) {
17068
17122
  return {
@@ -17138,7 +17192,7 @@ function collectComponentEventBindings(hostNode) {
17138
17192
  return collectMiniProgramEventBindings(hostNode.attribs);
17139
17193
  }
17140
17194
  function buildComponentTrigger(componentScopeId, context, hostNode) {
17141
- const hostDataset = collectDataset$3(hostNode);
17195
+ const hostDataset = collectDataset(hostNode);
17142
17196
  const hostId = hostNode.attribs?.id ?? "";
17143
17197
  return (instance, eventName, detail, triggerOptions) => {
17144
17198
  const originScope = context.componentScopes.get(componentScopeId);
@@ -17200,7 +17254,7 @@ function createComponentScope(clonedNode, scope, componentScopeId, componentInst
17200
17254
  alias: clonedNode.name,
17201
17255
  classList: String(clonedNode.attribs?.class ?? "").split(CLASS_SPLIT_RE).map((item) => item.trim()).filter(Boolean),
17202
17256
  data: { ...componentInstance.data },
17203
- dataset: collectDataset$3(clonedNode, wxsScopeData(scope)),
17257
+ dataset: collectDataset(clonedNode, wxsScopeData(scope)),
17204
17258
  eventBindings: collectComponentEventBindings(clonedNode),
17205
17259
  getMethod: (methodName) => {
17206
17260
  const method = componentInstance?.[methodName];
@@ -17223,7 +17277,7 @@ function resolveComponentProperties(clonedNode, scope, definition) {
17223
17277
  if (key.startsWith("bind") || key.startsWith("generic:")) continue;
17224
17278
  const camelizedKey = key.replace(/-([a-z])/g, (_match, char) => char.toUpperCase());
17225
17279
  const propertyKey = key in declaredProperties || !(camelizedKey in declaredProperties) ? key : camelizedKey;
17226
- if (isMustacheOnly(String(value))) bindingExpressions[propertyKey] = String(value).trim().slice(2, -2).trim();
17280
+ if (isMustacheOnly$1(String(value))) bindingExpressions[propertyKey] = String(value).trim().slice(2, -2).trim();
17227
17281
  nextProperties[propertyKey] = resolveComponentAttributeValue(String(value), scope);
17228
17282
  }
17229
17283
  return {
@@ -17231,7 +17285,7 @@ function resolveComponentProperties(clonedNode, scope, definition) {
17231
17285
  bindingExpressions
17232
17286
  };
17233
17287
  }
17234
- function createRuntimeComponentInstance(componentScopeId, context, clonedNode, componentEntry, nextProperties, ownerScopeId) {
17288
+ function createRuntimeComponentInstance(componentScopeId, context, clonedNode, componentEntry, nextProperties, ownerScopeId, beforeCreated) {
17235
17289
  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;
17236
17290
  const componentInstance = createComponentInstance({
17237
17291
  definition: componentEntry.definition,
@@ -17243,10 +17297,11 @@ function createRuntimeComponentInstance(componentScopeId, context, clonedNode, c
17243
17297
  componentInstance.createIntersectionObserver = (options) => context.session.createIntersectionObserver(componentInstance, options);
17244
17298
  componentInstance.createMediaQueryObserver = () => context.session.createMediaQueryObserver(componentInstance);
17245
17299
  componentInstance.createSelectorQuery = () => context.moduleLoader.wx.createSelectorQuery().in(componentInstance);
17246
- componentInstance.selectComponent = (selector) => context.session.selectComponentWithin(componentScopeId, selector);
17247
- componentInstance.selectAllComponents = (selector) => context.session.selectAllComponentsWithin(componentScopeId, selector);
17300
+ componentInstance.selectComponent = (selector) => resolveNativeComponentSelection(context.session.selectComponentWithin(componentScopeId, selector));
17301
+ componentInstance.selectAllComponents = (selector) => context.session.selectAllComponentsWithin(componentScopeId, selector).map(resolveNativeComponentSelection);
17248
17302
  componentInstance.selectOwnerComponent = () => ownerScopeId ? context.componentCache.get(ownerScopeId) ?? null : null;
17249
17303
  context.componentCache.set(componentScopeId, componentInstance);
17304
+ beforeCreated?.(componentInstance);
17250
17305
  runComponentLifecycle(componentInstance, "created");
17251
17306
  componentInstance.__propertySnapshots = Object.fromEntries(Object.entries(componentInstance.properties).map(([key, propertyValue]) => [key, cloneValue$1(propertyValue)]));
17252
17307
  syncComponentProperties(componentInstance, componentInstance.__definition__ ?? componentEntry.definition, componentProperties, {}, []);
@@ -17267,7 +17322,7 @@ function expandNodeByFor(node, scope) {
17267
17322
  scope,
17268
17323
  instanceSuffix: ""
17269
17324
  }];
17270
- const items = resolveLoopEntries(resolveRawValueByPath(wxsScopeData(scope), forExpression));
17325
+ const items = resolveLoopEntries(resolveRawValueByPath$1(wxsScopeData(scope), forExpression));
17271
17326
  const itemName = node.attribs?.["wx:for-item"]?.trim() || "item";
17272
17327
  const indexName = node.attribs?.["wx:for-index"]?.trim() || "index";
17273
17328
  return items.map(([index, item]) => ({
@@ -17382,12 +17437,16 @@ function renderNodeTree(node, scope, context, ownerJsonPath, ownerFilePath, inst
17382
17437
  const componentScopeId = `${instancePath}/${clonedNode.name}`;
17383
17438
  const ownerScopeId = scope.getScopeId().includes("/") ? scope.getScopeId() : void 0;
17384
17439
  const { nextProperties, bindingExpressions } = resolveComponentProperties(clonedNode, scope, componentEntry.definition);
17440
+ const genericComponents = resolveComponentGenerics(context, clonedNode, ownerJsonPath, ownerFilePath, componentEntry.filePath);
17441
+ const slots = collectComponentSlots(clonedNode, scope, ownerJsonPath, ownerFilePath, componentScopeId, templateRenderState);
17385
17442
  let componentInstance = context.componentCache.get(componentScopeId);
17386
- if (!componentInstance) componentInstance = createRuntimeComponentInstance(componentScopeId, context, clonedNode, componentEntry, nextProperties, ownerScopeId);
17443
+ if (!componentInstance) componentInstance = createRuntimeComponentInstance(componentScopeId, context, clonedNode, componentEntry, nextProperties, ownerScopeId, (instance) => {
17444
+ const initialScope = createComponentScope(clonedNode, scope, componentScopeId, instance, genericComponents, slots);
17445
+ context.componentScopes.set(componentScopeId, initialScope);
17446
+ renderRuntimeComponentTemplate(context, componentEntry, renderNodeTree, initialScope, componentScopeId, /* @__PURE__ */ new Set());
17447
+ });
17387
17448
  else syncComponentProperties(componentInstance, componentInstance.__definition__ ?? componentEntry.definition, nextProperties, bindingExpressions, context.changedPageKeys);
17388
17449
  seenComponentScopes.add(componentScopeId);
17389
- const genericComponents = resolveComponentGenerics(context, clonedNode, ownerJsonPath, ownerFilePath, componentEntry.filePath);
17390
- const slots = collectComponentSlots(clonedNode, scope, ownerJsonPath, ownerFilePath, componentScopeId, templateRenderState);
17391
17450
  const componentScope = createComponentScope(clonedNode, scope, componentScopeId, componentInstance, genericComponents, slots);
17392
17451
  context.componentScopes.set(componentScopeId, componentScope);
17393
17452
  const renderedComponentRoot = renderRuntimeComponentTemplate(context, componentEntry, renderNodeTree, componentScope, componentScopeId, seenComponentScopes);
@@ -17397,6 +17456,10 @@ function renderNodeTree(node, scope, context, ownerJsonPath, ownerFilePath, inst
17397
17456
  ...clonedNode.attribs,
17398
17457
  ...renderedComponentRoot.attribs
17399
17458
  };
17459
+ if (clonedNode.dataset || renderedComponentRoot.dataset) renderedComponentRoot.dataset = {
17460
+ ...clonedNode.dataset,
17461
+ ...renderedComponentRoot.dataset
17462
+ };
17400
17463
  renderedComponentRoot.attribs["data-sim-component"] = clonedNode.name;
17401
17464
  renderedComponentRoot.attribs["data-sim-node"] = instancePath;
17402
17465
  renderedComponentRoot.attribs["data-sim-scope"] = componentScopeId;
@@ -17440,7 +17503,8 @@ function renderRuntimePageTree(context, page) {
17440
17503
  const pageAttaching = isComponentPageAttaching(page);
17441
17504
  const attached = !pageAttaching && flushComponentAttachments([...seenComponentScopes].map((scopeId) => context.componentCache.get(scopeId)), (instance) => runComponentLifecycle(instance, "attached"));
17442
17505
  const removed = [...context.componentCache].filter(([scopeId]) => scopeId.startsWith(`${pageScopeId}/`) && !seenComponentScopes.has(scopeId));
17443
- for (const [, instance] of removed) runComponentLifecycle(instance, "detached");
17506
+ for (const [scopeId, instance] of removed) if (isComponentAttached(instance)) runComponentLifecycle(instance, "detached");
17507
+ else scheduleDiscardedComponentReady(context.componentCache, scopeId, instance);
17444
17508
  const instances = [...seenComponentScopes].map((scopeId) => context.componentCache.get(scopeId));
17445
17509
  const relationsChanged = !pageAttaching && !hasPendingComponentAttachments(instances) && syncComponentRelations(context.componentCache, seenComponentScopes, pageScopeId);
17446
17510
  for (const [scopeId] of removed) {
@@ -17448,6 +17512,7 @@ function renderRuntimePageTree(context, page) {
17448
17512
  context.componentScopes.delete(scopeId);
17449
17513
  }
17450
17514
  if (pageAttached || attached || relationsChanged) return renderRuntimePageTree(context, page);
17515
+ if (!isPageBeforeReady(page)) flushDiscardedComponentReady(context.componentCache, pageScopeId, (instance) => runComponentLifecycle(instance, "ready"));
17451
17516
  if (!isPageBeforeReady(page) && flushComponentReady(instances, (instance) => {
17452
17517
  runComponentLifecycle(instance, "ready");
17453
17518
  if (instance !== context.componentCache.get(customTabBarScopeId(route))) instance.__definition__?.pageLifetimes?.show?.call(instance);
@@ -37714,23 +37779,11 @@ function queryXPathElements(root, expression) {
37714
37779
  }
37715
37780
  //#endregion
37716
37781
  //#region ../../mpcore/packages/simulator/src/view/nodeHandle.ts
37717
- const DATASET_NAME_RE$2 = /-([a-z])/g;
37718
37782
  function escapeText$1(text) {
37719
37783
  return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
37720
37784
  }
37721
- function toDatasetKey$2(attributeName) {
37722
- return attributeName.slice(5).replace(DATASET_NAME_RE$2, (_match, char) => char.toUpperCase());
37723
- }
37724
- function collectDataset$2(node) {
37725
- const dataset = {};
37726
- for (const [key, value] of Object.entries(node.attribs ?? {})) {
37727
- if (!key.startsWith("data-") || key.startsWith("data-sim-")) continue;
37728
- dataset[toDatasetKey$2(key)] = value;
37729
- }
37730
- return dataset;
37731
- }
37732
37785
  function createEventPayload$1(node, eventName, event) {
37733
- const dataset = collectDataset$2(node);
37786
+ const dataset = collectNodeDataset(node);
37734
37787
  const nodeId = node.attribs?.id ?? "";
37735
37788
  return {
37736
37789
  bubbles: false,
@@ -37818,7 +37871,7 @@ var HeadlessTestingNodeHandle = class HeadlessTestingNodeHandle {
37818
37871
  }
37819
37872
  async dataset() {
37820
37873
  this.assertActive();
37821
- return collectDataset$2(this.node);
37874
+ return collectNodeDataset(this.node);
37822
37875
  }
37823
37876
  async scope() {
37824
37877
  this.assertActive();
@@ -37893,13 +37946,19 @@ var HeadlessTestingNodeHandle = class HeadlessTestingNodeHandle {
37893
37946
  //#region ../../mpcore/packages/simulator/src/view/render.ts
37894
37947
  const LEADING_SLASH_RE$2 = /^\/+/;
37895
37948
  const TEMPLATE_INTERPOLATION_RE = /\{\{([^{}]+)\}\}/g;
37896
- function resolveValueByPath(source, expression) {
37949
+ function resolveRawValueByPath(source, expression) {
37897
37950
  const normalized = expression.trim();
37898
- if (!normalized) return "";
37951
+ if (!normalized) return;
37899
37952
  const segments = normalized.split(".").filter(Boolean);
37900
37953
  let current = source;
37901
- for (const segment of segments) current = current?.[segment];
37902
- return current ?? "";
37954
+ for (const segment of segments) {
37955
+ if (current == null || typeof current !== "object" && typeof current !== "function") return;
37956
+ current = Reflect.get(current, segment);
37957
+ }
37958
+ return current;
37959
+ }
37960
+ function resolveValueByPath(source, expression) {
37961
+ return resolveRawValueByPath(source, expression) ?? "";
37903
37962
  }
37904
37963
  function interpolateTemplate(input, data) {
37905
37964
  return input.replace(TEMPLATE_INTERPOLATION_RE, (_match, expression) => {
@@ -37907,6 +37966,10 @@ function interpolateTemplate(input, data) {
37907
37966
  return typeof value === "string" ? value : String(value);
37908
37967
  });
37909
37968
  }
37969
+ function isMustacheOnly(value) {
37970
+ const trimmed = value.trim();
37971
+ return trimmed.startsWith("{{") && trimmed.endsWith("}}") && !trimmed.includes("{{", 2);
37972
+ }
37910
37973
  function visitDom(node, visitor) {
37911
37974
  visitor(node);
37912
37975
  for (const child of node.children ?? []) visitDom(child, visitor);
@@ -37914,7 +37977,18 @@ function visitDom(node, visitor) {
37914
37977
  function interpolateDomTree(root, data) {
37915
37978
  visitDom(root, (node) => {
37916
37979
  if (typeof node.data === "string") node.data = interpolateTemplateText(node.data, data);
37917
- if (node.attribs) for (const [key, value] of Object.entries(node.attribs)) node.attribs[key] = interpolateTemplate(value, data);
37980
+ if (node.attribs) {
37981
+ let dataset;
37982
+ for (const [key, value] of Object.entries(node.attribs)) {
37983
+ const resolvedValue = isMustacheOnly(value) ? resolveRawValueByPath(data, value.trim().slice(2, -2)) : interpolateTemplate(value, data);
37984
+ node.attribs[key] = String(resolvedValue ?? "");
37985
+ if (isDatasetAttribute(key)) {
37986
+ dataset ??= {};
37987
+ dataset[toDatasetKey(key)] = resolvedValue;
37988
+ }
37989
+ }
37990
+ node.dataset = dataset;
37991
+ }
37918
37992
  });
37919
37993
  }
37920
37994
  function escapeText(text) {
@@ -37948,21 +38022,9 @@ function renderPageTree(project, page) {
37948
38022
  }
37949
38023
  //#endregion
37950
38024
  //#region ../../mpcore/packages/simulator/src/view/selectorQuery.ts
37951
- const DATASET_NAME_RE$1 = /-([a-z])/g;
37952
38025
  const LEADING_MARK_PREFIX_RE = /^mark[:\-]?/;
37953
38026
  const MARK_NAME_RE = /[:\-]([a-z])/g;
37954
38027
  const NUMERIC_LIKE_VALUE_RE$1 = /-?\d+(?:\.\d+)?/;
37955
- function toDatasetKey$1(attributeName) {
37956
- return attributeName.slice(5).replace(DATASET_NAME_RE$1, (_match, char) => char.toUpperCase());
37957
- }
37958
- function collectDataset$1(node) {
37959
- const dataset = {};
37960
- for (const [key, value] of Object.entries(node.attribs ?? {})) {
37961
- if (!key.startsWith("data-") || key.startsWith("data-sim-")) continue;
37962
- dataset[toDatasetKey$1(key)] = value;
37963
- }
37964
- return dataset;
37965
- }
37966
38028
  function toMarkKey(attributeName) {
37967
38029
  return attributeName.replace(LEADING_MARK_PREFIX_RE, "").replace(MARK_NAME_RE, (_match, char) => char.toUpperCase());
37968
38030
  }
@@ -38039,7 +38101,7 @@ function resolvePropertyValue(node, propertyName) {
38039
38101
  if (!normalizedPropertyName) return;
38040
38102
  if (normalizedPropertyName === "id") return node.attribs?.id ?? "";
38041
38103
  if (normalizedPropertyName === "class") return node.attribs?.class ?? "";
38042
- if (normalizedPropertyName === "dataset") return collectDataset$1(node);
38104
+ if (normalizedPropertyName === "dataset") return collectNodeDataset(node);
38043
38105
  return node.attribs?.[normalizedPropertyName];
38044
38106
  }
38045
38107
  function pickProperties(node, propertyNames) {
@@ -38056,7 +38118,7 @@ function pickComputedStyle(node, propertyNames) {
38056
38118
  function resolveFieldsResult(node, fields, options) {
38057
38119
  const result = {};
38058
38120
  if (fields.id) result.id = node.attribs?.id ?? "";
38059
- if (fields.dataset) result.dataset = collectDataset$1(node);
38121
+ if (fields.dataset) result.dataset = collectNodeDataset(node);
38060
38122
  if (fields.mark) result.mark = collectMark(node);
38061
38123
  if (fields.rect || fields.size) {
38062
38124
  const rect = resolveRect$1(node, options.root.name === "page" ? options.windowInfo : void 0);
@@ -38761,20 +38823,8 @@ function createHeadlessMediaQueryObserver(driver, onDisconnect) {
38761
38823
  }
38762
38824
  //#endregion
38763
38825
  //#region ../../mpcore/packages/simulator/src/view/videoContext.ts
38764
- const DATASET_NAME_RE = /-([a-z])/g;
38765
- function toDatasetKey(attributeName) {
38766
- return attributeName.slice(5).replace(DATASET_NAME_RE, (_match, char) => char.toUpperCase());
38767
- }
38768
- function collectDataset(node) {
38769
- const dataset = {};
38770
- for (const [key, value] of Object.entries(node.attribs ?? {})) {
38771
- if (!key.startsWith("data-") || key.startsWith("data-sim-")) continue;
38772
- dataset[toDatasetKey(key)] = value;
38773
- }
38774
- return dataset;
38775
- }
38776
38826
  function createEventPayload(node, eventName, detail) {
38777
- const dataset = collectDataset(node);
38827
+ const dataset = collectNodeDataset(node);
38778
38828
  const nodeId = node.attribs?.id ?? "";
38779
38829
  return {
38780
38830
  bubbles: false,
@@ -39290,6 +39340,110 @@ function deriveMenuButtonBoundingClientRect(systemInfo) {
39290
39340
  };
39291
39341
  }
39292
39342
  //#endregion
39343
+ //#region ../../mpcore/packages/simulator/src/runtime/request/task.ts
39344
+ /** mock 与显式网络桥接共用终止状态;终止后不再分发 headers/chunk。 */
39345
+ function createRequestTask(option, onAbort = () => {}) {
39346
+ const headers = /* @__PURE__ */ new Set();
39347
+ const chunks = /* @__PURE__ */ new Set();
39348
+ let settled = false;
39349
+ function finish(result, success) {
39350
+ if (settled) return;
39351
+ settled = true;
39352
+ headers.clear();
39353
+ chunks.clear();
39354
+ try {
39355
+ if (success) option.success?.(result);
39356
+ else option.fail?.(result);
39357
+ } finally {
39358
+ option.complete?.(result);
39359
+ }
39360
+ }
39361
+ return {
39362
+ task: {
39363
+ abort() {
39364
+ if (settled) return;
39365
+ const error = Object.assign(/* @__PURE__ */ new Error("request:fail abort"), { errMsg: "request:fail abort" });
39366
+ try {
39367
+ finish(error, false);
39368
+ } finally {
39369
+ onAbort();
39370
+ }
39371
+ },
39372
+ onHeadersReceived(callback) {
39373
+ headers.add(callback);
39374
+ },
39375
+ offHeadersReceived(callback) {
39376
+ if (callback) headers.delete(callback);
39377
+ else headers.clear();
39378
+ },
39379
+ onChunkReceived(callback) {
39380
+ chunks.add(callback);
39381
+ },
39382
+ offChunkReceived(callback) {
39383
+ if (callback) chunks.delete(callback);
39384
+ else chunks.clear();
39385
+ }
39386
+ },
39387
+ get settled() {
39388
+ return settled;
39389
+ },
39390
+ headers(result) {
39391
+ if (settled) return;
39392
+ for (const callback of Array.from(headers)) if (!settled && headers.has(callback)) callback(result);
39393
+ },
39394
+ chunk(data) {
39395
+ if (settled || !option.enableChunked) return;
39396
+ for (const callback of Array.from(chunks)) if (!settled && chunks.has(callback)) callback({ data: data.slice(0) });
39397
+ },
39398
+ succeed(result) {
39399
+ finish(result, true);
39400
+ },
39401
+ fail(error) {
39402
+ finish(Object.assign(error, { errMsg: "errMsg" in error ? error.errMsg : error.message }), false);
39403
+ }
39404
+ };
39405
+ }
39406
+ //#endregion
39407
+ //#region ../../mpcore/packages/simulator/src/runtime/request/mock.ts
39408
+ /** 仅模拟已声明的分块;不进行网络访问,也不把完整 response 拆成假分块。 */
39409
+ function createChunkedMockRequest(option, mock, response, scheduler, onSuccess) {
39410
+ let timer;
39411
+ const request = createRequestTask(option, () => scheduler.clearTimeout(timer));
39412
+ const chunks = (mock.chunks ?? []).map((chunk) => ({
39413
+ ...chunk,
39414
+ data: chunk.data.slice(0)
39415
+ }));
39416
+ let index = 0;
39417
+ function next() {
39418
+ if (request.settled) return;
39419
+ const chunk = chunks[index++];
39420
+ if (chunk) timer = scheduler.setTimeout(() => {
39421
+ request.chunk(chunk.data);
39422
+ next();
39423
+ }, Math.max(0, chunk.delay ?? 0));
39424
+ else timer = scheduler.setTimeout(() => {
39425
+ if (request.settled) return;
39426
+ if (mock.error) request.fail(new Error(mock.error));
39427
+ else {
39428
+ onSuccess();
39429
+ request.succeed({
39430
+ ...response,
39431
+ data: ""
39432
+ });
39433
+ }
39434
+ }, 0);
39435
+ }
39436
+ timer = scheduler.setTimeout(() => {
39437
+ request.headers({
39438
+ cookies: response.cookies,
39439
+ header: response.header,
39440
+ statusCode: response.statusCode
39441
+ });
39442
+ next();
39443
+ }, Math.max(0, mock.delay ?? 0));
39444
+ return request.task;
39445
+ }
39446
+ //#endregion
39293
39447
  //#region ../../mpcore/packages/simulator/src/runtime/wxState.ts
39294
39448
  const STORAGE_LIMIT_SIZE = 10240;
39295
39449
  const DEFAULT_MODAL_CANCEL_COLOR = "#000000";
@@ -40611,6 +40765,7 @@ function createHeadlessWxState(scheduler = nativeScheduler, options = {}) {
40611
40765
  request(option) {
40612
40766
  const matchedMock = requestMocks.find((mock) => matchesRequestMock(mock, option));
40613
40767
  if (!matchedMock) {
40768
+ const request = createRequestTask(option);
40614
40769
  const logEntry = {
40615
40770
  data: cloneValue(option.data),
40616
40771
  header: { ...option.header ?? {} },
@@ -40621,9 +40776,8 @@ function createHeadlessWxState(scheduler = nativeScheduler, options = {}) {
40621
40776
  requestLogs.push(logEntry);
40622
40777
  const error = /* @__PURE__ */ new Error(`No request mock matched in headless runtime: ${normalizeMethod(option.method)} ${option.url}`);
40623
40778
  if (options.strictMocks) throw error;
40624
- option.fail?.(error);
40625
- option.complete?.();
40626
- return createNoopTask();
40779
+ request.fail(error);
40780
+ return request.task;
40627
40781
  }
40628
40782
  const response = resolveRequestResponse(matchedMock, option);
40629
40783
  const requestLogEntry = {
@@ -40635,20 +40789,31 @@ function createHeadlessWxState(scheduler = nativeScheduler, options = {}) {
40635
40789
  url: option.url
40636
40790
  };
40637
40791
  const delay = Number.isFinite(matchedMock.delay) ? Math.max(0, Math.trunc(matchedMock.delay ?? 0)) : 0;
40792
+ if (option.enableChunked) return createChunkedMockRequest(option, matchedMock, response, scheduler, () => {
40793
+ requestLogs.push({
40794
+ ...requestLogEntry,
40795
+ response: {
40796
+ ...response,
40797
+ data: ""
40798
+ }
40799
+ });
40800
+ });
40801
+ const request = createRequestTask(option);
40638
40802
  if (delay <= 0) {
40639
40803
  requestLogs.push(requestLogEntry);
40640
- option.success?.(response);
40641
- option.complete?.(response);
40642
- return createNoopTask();
40804
+ request.succeed(response);
40805
+ return request.task;
40643
40806
  }
40644
- return runDelayedTask(delay, () => {
40807
+ const delayed = runDelayedTask(delay, () => {
40645
40808
  requestLogs.push(requestLogEntry);
40646
- option.success?.(response);
40647
- option.complete?.(response);
40809
+ request.succeed(response);
40648
40810
  }, (error) => {
40649
- option.fail?.(error);
40650
- option.complete?.();
40811
+ request.fail(error);
40651
40812
  }, "request:fail abort");
40813
+ return {
40814
+ ...request.task,
40815
+ abort: delayed.abort
40816
+ };
40652
40817
  },
40653
40818
  removeSavedFile(option) {
40654
40819
  return removeSavedFile(option.filePath);
@@ -40865,7 +41030,7 @@ function matchesComponentSelector(scope, selector) {
40865
41030
  if (dataAttrMatch) {
40866
41031
  const [, key, value] = dataAttrMatch;
40867
41032
  const datasetKey = key.replace(DATASET_KEY_RE, (_match, char) => char.toUpperCase());
40868
- return scope.dataset?.[datasetKey] === value;
41033
+ return scope.dataset != null && Object.hasOwn(scope.dataset, datasetKey) && String(scope.dataset[datasetKey] ?? "") === value;
40869
41034
  }
40870
41035
  return scope.alias === part;
40871
41036
  });
@@ -41683,8 +41848,8 @@ var HeadlessSession = class {
41683
41848
  });
41684
41849
  pageInstance.createIntersectionObserver = (options) => this.createIntersectionObserver(pageInstance, options);
41685
41850
  pageInstance.createMediaQueryObserver = () => this.createMediaQueryObserver(pageInstance);
41686
- pageInstance.selectComponent = (selector) => this.selectComponent(selector);
41687
- pageInstance.selectAllComponents = (selector) => this.selectAllComponents(selector);
41851
+ pageInstance.selectComponent = (selector) => resolveNativeComponentSelection(this.selectComponent(selector));
41852
+ pageInstance.selectAllComponents = (selector) => this.selectAllComponents(selector).map(resolveNativeComponentSelection);
41688
41853
  pageInstance.getTabBar = () => {
41689
41854
  if (this.currentPageInstance === pageInstance) this.renderCurrentPage();
41690
41855
  return this.componentCache.get(customTabBarScopeId(pageInstance.route)) ?? null;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@weapp-vite/miniprogram-automator",
3
3
  "type": "module",
4
- "version": "1.2.18",
4
+ "version": "1.2.19",
5
5
  "description": "完全兼容微信 miniprogram-automator 的现代化替代实现",
6
6
  "author": "ice breaker <1324318532@qq.com>",
7
7
  "license": "MIT",
@@ -44,7 +44,7 @@
44
44
  "node": "^20.19.0 || >=22.12.0"
45
45
  },
46
46
  "dependencies": {
47
- "@weapp-vite/qr": "1.1.3",
47
+ "@weapp-vite/qr": "1.1.4",
48
48
  "obug": "^3.0.0",
49
49
  "ws": "^8.21.3"
50
50
  },