@weapp-vite/miniprogram-automator 1.2.18 → 1.2.20

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-BNQZTcKo.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.20";
1102
1102
  //#endregion
1103
1103
  //#region src/Native.ts
1104
1104
  /** Native 的实现。 */
@@ -1194,7 +1194,7 @@ function linkRenderedParents(root, parent = null) {
1194
1194
  for (const child of root.children ?? []) linkRenderedParents(child, root);
1195
1195
  }
1196
1196
  //#endregion
1197
- //#region ../../node_modules/.pnpm/@babel+parser@8.0.5/node_modules/@babel/parser/lib/index.js
1197
+ //#region ../../node_modules/.pnpm/@babel+parser@8.0.6/node_modules/@babel/parser/lib/index.js
1198
1198
  var Position = class {
1199
1199
  constructor(line, col, index) {
1200
1200
  this.line = void 0;
@@ -2209,16 +2209,30 @@ const types = {
2209
2209
  j_cTag: new TokContext("</tag"),
2210
2210
  j_expr: new TokContext("<tag>...</tag>", true)
2211
2211
  };
2212
- const bmpIdentifierStart = /[\p{ID_Start}\u088f\u0c5c\u0cdc\ua7ce\ua7cf\ua7d2\ua7d4\ua7f1]/u;
2213
- const bmpIdentifier = /[\p{ID_Continue}\u088f\u0c5c\u0cdc\ua7ce\ua7cf\ua7d2\ua7d4\ua7f1\u1acf-\u1add\u1ae0-\u1aeb]/u;
2212
+ const bmpIdentifierStart = /[\p{ID_Start}\u0558\u058b\u058c\u088f\u0c5c\u0cdc\u208f\u209d-\u209f\ua7ce\ua7cf\ua7d2\ua7d4\ua7dd\ua7e2\ua7f1\uab6c\uab6d]/u;
2213
+ const bmpIdentifier = /[\p{ID_Continue}\u0558\u058b\u058c\u088f\u0c5c\u0cdc\u208f\u209d-\u209f\ua7ce\ua7cf\ua7d2\ua7d4\ua7dd\ua7e2\ua7f1\uab6c\uab6d\u05c8\u05c9\u0b53\u0b54\u1acf-\u1af0]/u;
2214
2214
  const supplementaryIdentifierStartCodes = [
2215
- 2368,
2215
+ 1979,
2216
+ 4,
2217
+ 385,
2216
2218
  25,
2217
2219
  1388,
2218
2220
  2,
2219
- 3817,
2221
+ 18,
2222
+ 21,
2223
+ 3100,
2224
+ 0,
2225
+ 678,
2220
2226
  43,
2221
- 20677,
2227
+ 22,
2228
+ 0,
2229
+ 1662,
2230
+ 0,
2231
+ 6,
2232
+ 10,
2233
+ 209,
2234
+ 310,
2235
+ 18458,
2222
2236
  24,
2223
2237
  3,
2224
2238
  24,
@@ -2226,11 +2240,31 @@ const supplementaryIdentifierStartCodes = [
2226
2240
  4,
2227
2241
  6146,
2228
2242
  7,
2229
- 1290,
2230
- 21,
2231
- 98,
2243
+ 1239,
2244
+ 4,
2245
+ 47,
2246
+ 23,
2247
+ 96,
2232
2248
  114,
2233
- 22734,
2249
+ 14,
2250
+ 913,
2251
+ 15,
2252
+ 50,
2253
+ 8017,
2254
+ 5,
2255
+ 64,
2256
+ 0,
2257
+ 9534,
2258
+ 0,
2259
+ 2169,
2260
+ 5,
2261
+ 7,
2262
+ 86,
2263
+ 15,
2264
+ 6,
2265
+ 55,
2266
+ 50,
2267
+ 1729,
2234
2268
  30,
2235
2269
  2,
2236
2270
  2,
@@ -2244,19 +2278,37 @@ const supplementaryIdentifierStartCodes = [
2244
2278
  1,
2245
2279
  53307,
2246
2280
  5,
2247
- 5987,
2281
+ 223,
2282
+ 0,
2283
+ 5764,
2248
2284
  11,
2249
2285
  21763,
2250
- 4297
2286
+ 4297,
2287
+ 39815,
2288
+ 11327
2251
2289
  ];
2252
2290
  const supplementaryIdentifierCodes = [
2253
- 3834,
2254
- 1,
2291
+ 3787,
2292
+ 4,
2293
+ 33,
2294
+ 11,
2255
2295
  3173,
2256
2296
  7,
2257
2297
  633,
2258
2298
  9,
2259
- 51450,
2299
+ 7,
2300
+ 0,
2301
+ 45879,
2302
+ 1,
2303
+ 296,
2304
+ 2,
2305
+ 9,
2306
+ 1,
2307
+ 3,
2308
+ 0,
2309
+ 33,
2310
+ 1,
2311
+ 5218,
2260
2312
  0,
2261
2313
  3,
2262
2314
  0,
@@ -3649,10 +3701,10 @@ const flow = (superClass) => class FlowParserMixin extends superClass {
3649
3701
  const arrows = [];
3650
3702
  while (stack.length !== 0) {
3651
3703
  const node = stack.pop();
3652
- if (node.type === "ArrowFunctionExpression" && node.body.type !== "BlockStatement") {
3704
+ if (node.type === "ArrowFunctionExpression") {
3653
3705
  if (node.typeParameters || !node.returnType) this.finishArrowValidation(node);
3654
3706
  else arrows.push(node);
3655
- stack.push(node.body);
3707
+ if (node.body.type !== "BlockStatement") stack.push(node.body);
3656
3708
  } else if (node.type === "ConditionalExpression") {
3657
3709
  stack.push(node.consequent);
3658
3710
  stack.push(node.alternate);
@@ -4720,7 +4772,7 @@ const entities = {
4720
4772
  hearts: "♥",
4721
4773
  diams: "♦"
4722
4774
  };
4723
- const lineBreakG = new RegExp(/\r\n|[\r\n\u2028\u2029]/.source, "g");
4775
+ const lineBreakG = /\r\n|[\r\n\u2028\u2029]/g;
4724
4776
  function isNewLine(code) {
4725
4777
  switch (code) {
4726
4778
  case 10:
@@ -5685,12 +5737,14 @@ function readInt(input, pos, lineStart, curLine, radix, len, forceLen, allowNumS
5685
5737
  else if (_isDigit(code)) val = code - 48;
5686
5738
  else val = Infinity;
5687
5739
  if (val >= radix) {
5688
- if (val <= 9 && bailOnError) return {
5689
- n: null,
5690
- pos
5691
- };
5692
- else if (val <= 9 && errors.invalidDigit(pos, lineStart, curLine, radix)) val = 0;
5693
- else if (forceLen) {
5740
+ if (val <= 9) {
5741
+ if (bailOnError) return {
5742
+ n: null,
5743
+ pos
5744
+ };
5745
+ errors.invalidDigit(pos, lineStart, curLine, radix);
5746
+ val = 0;
5747
+ } else if (forceLen) {
5694
5748
  val = 0;
5695
5749
  invalid = true;
5696
5750
  } else break;
@@ -6520,9 +6574,7 @@ var Tokenizer$1 = class extends CommentsParser {
6520
6574
  }
6521
6575
  errorHandlers_readInt = {
6522
6576
  invalidDigit: (pos, lineStart, curLine, radix) => {
6523
- if (!(this.optionFlags & 4096)) return false;
6524
6577
  this.raise(Errors.InvalidDigit, buildPosition(pos, lineStart, curLine), { radix });
6525
- return true;
6526
6578
  },
6527
6579
  numericSeparatorInEscapeSequence: this.errorBuilder(Errors.NumericSeparatorInEscapeSequence),
6528
6580
  unexpectedNumericSeparator: this.errorBuilder(Errors.UnexpectedNumericSeparator)
@@ -12103,6 +12155,9 @@ const typescript = (superClass) => class TypeScriptParserMixin extends superClas
12103
12155
  }
12104
12156
  fillOptionalPropertiesForTSESLint(node) {
12105
12157
  switch (node.type) {
12158
+ case "ImportDeclaration":
12159
+ node.phase ??= null;
12160
+ return;
12106
12161
  case "ExpressionStatement":
12107
12162
  node.directive ??= void 0;
12108
12163
  return;
@@ -12634,13 +12689,13 @@ const BLOCKED_MEMBER_KEYS = /* @__PURE__ */ new Set([
12634
12689
  "prototype"
12635
12690
  ]);
12636
12691
  const NUMERIC_DOT_PATH_RE = /\.(\d+)(?=[.[\s,}\]]|$)/g;
12637
- function isMustacheOnly$1(value) {
12692
+ function isMustacheOnly$2(value) {
12638
12693
  const trimmed = value.trim();
12639
12694
  return trimmed.startsWith("{{") && trimmed.endsWith("}}") && !trimmed.includes("{{", 2);
12640
12695
  }
12641
12696
  function unwrapMustacheExpression(expression) {
12642
12697
  const normalized = expression.trim();
12643
- if (isMustacheOnly$1(normalized)) return normalized.slice(2, -2).trim();
12698
+ if (isMustacheOnly$2(normalized)) return normalized.slice(2, -2).trim();
12644
12699
  return normalized;
12645
12700
  }
12646
12701
  function parseTemplateExpression(expression) {
@@ -12845,6 +12900,26 @@ function flushComponentAttachments(instances, attach) {
12845
12900
  return pending.length > 0;
12846
12901
  }
12847
12902
  //#endregion
12903
+ //#region ../../mpcore/packages/simulator/src/runtime/componentInstance/discardedReady.ts
12904
+ const discardedReady = /* @__PURE__ */ new WeakMap();
12905
+ /** 初始模板分支被 props 替换时,微信仍发送 ready,但不发送 attached 或 detached。 */
12906
+ function scheduleDiscardedComponentReady(cache, scopeId, instance) {
12907
+ const pending = discardedReady.get(cache) ?? /* @__PURE__ */ new Map();
12908
+ pending.set(scopeId, instance);
12909
+ discardedReady.set(cache, pending);
12910
+ }
12911
+ function flushDiscardedComponentReady(cache, pageScopeId, ready) {
12912
+ const pending = discardedReady.get(cache);
12913
+ if (!pending) return;
12914
+ const current = [...pending].filter(([scopeId]) => scopeId.startsWith(`${pageScopeId}/`));
12915
+ for (const [scopeId, instance] of current) {
12916
+ pending.delete(scopeId);
12917
+ instance.__ready__ = true;
12918
+ }
12919
+ for (const [, instance] of current) ready(instance);
12920
+ if (!pending.size) discardedReady.delete(cache);
12921
+ }
12922
+ //#endregion
12848
12923
  //#region ../../mpcore/packages/simulator/src/runtime/pageLifecycle.ts
12849
12924
  const loadingPages = /* @__PURE__ */ new WeakSet();
12850
12925
  function isPageBeforeReady(page) {
@@ -13045,6 +13120,20 @@ function resolveSelectorQueryScopeSnapshot(scope, snapshots) {
13045
13120
  return null;
13046
13121
  }
13047
13122
  //#endregion
13123
+ //#region ../../mpcore/packages/simulator/src/runtime/componentInstance/selection.ts
13124
+ function hasExportBehavior(definition, visited = /* @__PURE__ */ new Set()) {
13125
+ if (visited.has(definition)) return false;
13126
+ visited.add(definition);
13127
+ return (Array.isArray(definition.behaviors) ? definition.behaviors : []).some((behavior) => behavior === "wx://component-export" || behavior !== null && typeof behavior === "object" && hasExportBehavior(behavior, visited));
13128
+ }
13129
+ /** 原生选择器尊重组件导出行为;测试桥接继续保留完整组件实例。 */
13130
+ function resolveNativeComponentSelection(instance) {
13131
+ if (!instance) return null;
13132
+ const definition = instance.__definition__;
13133
+ if (!definition || !hasExportBehavior(definition) || typeof instance.export !== "function") return instance;
13134
+ return instance.export();
13135
+ }
13136
+ //#endregion
13048
13137
  //#region ../../mpcore/packages/simulator/src/runtime/moduleResolution.ts
13049
13138
  function resolveMiniProgramModule(importer, request, miniprogramRootPath, hasFile) {
13050
13139
  function resolveFile(basePath) {
@@ -13079,6 +13168,21 @@ function resolveMiniProgramComponent(ownerFilePath, request, miniprogramRootPath
13079
13168
  return npmPath ? relative(miniprogramRootPath, npmPath).slice(0, -3) : localPath;
13080
13169
  }
13081
13170
  //#endregion
13171
+ //#region ../../mpcore/packages/simulator/src/view/nodeDataset.ts
13172
+ const DATASET_NAME_RE = /-([a-z])/g;
13173
+ function isDatasetAttribute(attributeName) {
13174
+ return attributeName.startsWith("data-") && !attributeName.startsWith("data-sim-");
13175
+ }
13176
+ function toDatasetKey(attributeName) {
13177
+ return attributeName.slice(5).replace(DATASET_NAME_RE, (_match, char) => char.toUpperCase());
13178
+ }
13179
+ function collectNodeDataset(node) {
13180
+ if (node.dataset) return { ...node.dataset };
13181
+ const dataset = {};
13182
+ for (const [key, value] of Object.entries(node.attribs ?? {})) if (isDatasetAttribute(key)) dataset[toDatasetKey(key)] = value;
13183
+ return dataset;
13184
+ }
13185
+ //#endregion
13082
13186
  //#region ../../mpcore/packages/simulator/src/view/templateImports.ts
13083
13187
  function createImportedTemplateState(root, filePath, loadImport, loadWxs) {
13084
13188
  const documents = /* @__PURE__ */ new Map();
@@ -13108,7 +13212,8 @@ function createImportedTemplateState(root, filePath, loadImport, loadWxs) {
13108
13212
  return;
13109
13213
  }
13110
13214
  if (child.name === "import" && child.attribs?.src) {
13111
- const imported = loadImport(owner, child.attribs.src);
13215
+ const source = child.attribs.src;
13216
+ const imported = loadImport(owner, source.endsWith(".wxml") ? source : `${source}.wxml`);
13112
13217
  const importedDocument = collect(imported.root, imported.filePath);
13113
13218
  for (const [name, definition] of importedDocument.exports) state.definitions.set(name, definition);
13114
13219
  }
@@ -16936,7 +17041,6 @@ function parseWxsTemplateDocument(templateSource) {
16936
17041
  //#endregion
16937
17042
  //#region ../../mpcore/packages/simulator/src/runtime/render/shared.ts
16938
17043
  const TEMPLATE_INTERPOLATION_RE$1 = /\{\{([^{}]+)\}\}/g;
16939
- const DATASET_NAME_RE$3 = /-([a-z])/g;
16940
17044
  const LEADING_SLASH_RE$4 = /^\/+/;
16941
17045
  const EVENT_BINDING_ATTRS = [
16942
17046
  "bindtap",
@@ -16955,19 +17059,16 @@ const STRUCTURAL_ATTRS = [
16955
17059
  ];
16956
17060
  const CLASS_SPLIT_RE = /\s+/;
16957
17061
  const JS_FILE_RE = /\.js$/;
16958
- function isMustacheOnly(value) {
17062
+ function isMustacheOnly$1(value) {
16959
17063
  const trimmed = value.trim();
16960
17064
  return trimmed.startsWith("{{") && trimmed.endsWith("}}") && !trimmed.includes("{{", 2);
16961
17065
  }
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) {
17066
+ function collectDataset(node, source) {
17067
+ if (node.dataset) return collectNodeDataset(node);
16966
17068
  const dataset = {};
16967
17069
  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 ?? "");
17070
+ if (!isDatasetAttribute(key)) continue;
17071
+ dataset[toDatasetKey(key)] = source && isMustacheOnly$1(value) ? resolveTemplateExpression(source, value) : value;
16971
17072
  }
16972
17073
  return dataset;
16973
17074
  }
@@ -16981,13 +17082,14 @@ function cloneNode(node) {
16981
17082
  return {
16982
17083
  ...node,
16983
17084
  attribs: node.attribs ? { ...node.attribs } : void 0,
17085
+ dataset: node.dataset ? { ...node.dataset } : void 0,
16984
17086
  children: node.children?.map((child) => cloneNode(child))
16985
17087
  };
16986
17088
  }
16987
17089
  function resolveValueByPath$1(source, expression) {
16988
17090
  return resolveTemplateExpression(source, expression) ?? "";
16989
17091
  }
16990
- function resolveRawValueByPath(source, expression) {
17092
+ function resolveRawValueByPath$1(source, expression) {
16991
17093
  return resolveTemplateExpression(source, expression);
16992
17094
  }
16993
17095
  function interpolateTemplate$1(input, data) {
@@ -17030,16 +17132,16 @@ function isTagNode(node) {
17030
17132
  return node.type === "tag" && typeof node.name === "string";
17031
17133
  }
17032
17134
  function resolveAttributeValue(value, scope) {
17033
- if (isMustacheOnly(value)) {
17135
+ if (isMustacheOnly$1(value)) {
17034
17136
  const expression = value.trim().slice(2, -2);
17035
- return resolveValueByPath$1(wxsScopeData(scope), expression);
17137
+ return resolveRawValueByPath$1(wxsScopeData(scope), expression);
17036
17138
  }
17037
17139
  return interpolateTemplate$1(value, wxsScopeData(scope));
17038
17140
  }
17039
17141
  function resolveComponentAttributeValue(value, scope) {
17040
- if (isMustacheOnly(value)) {
17142
+ if (isMustacheOnly$1(value)) {
17041
17143
  const expression = value.trim().slice(2, -2);
17042
- return resolveRawValueByPath(wxsScopeData(scope), expression) ?? null;
17144
+ return resolveRawValueByPath$1(wxsScopeData(scope), expression) ?? null;
17043
17145
  }
17044
17146
  return interpolateTemplate$1(value, wxsScopeData(scope));
17045
17147
  }
@@ -17051,18 +17153,25 @@ function applyNodeBindings(node, scope) {
17051
17153
  node.attribs ??= {};
17052
17154
  node.attribs["data-sim-scope"] = scope.getScopeId();
17053
17155
  for (const key of STRUCTURAL_ATTRS) delete node.attribs[key];
17156
+ let dataset;
17054
17157
  for (const [key, value] of Object.entries({ ...node.attribs })) {
17055
17158
  if (EVENT_BINDING_ATTRS.includes(key)) {
17056
17159
  node.attribs["data-sim-tap"] = value;
17057
17160
  continue;
17058
17161
  }
17059
- node.attribs[key] = typeof value === "string" ? String(resolveAttributeValue(value, scope)) : String(value);
17162
+ const resolvedValue = typeof value === "string" ? resolveAttributeValue(value, scope) : value;
17163
+ node.attribs[key] = String(resolvedValue ?? "");
17164
+ if (isDatasetAttribute(key)) {
17165
+ dataset ??= {};
17166
+ dataset[toDatasetKey(key)] = resolvedValue;
17167
+ }
17060
17168
  }
17169
+ node.dataset = dataset;
17061
17170
  }
17062
17171
  function evaluateConditionalBranch(node, scope) {
17063
17172
  const condition = node.attribs?.["wx:if"] ?? node.attribs?.["wx:elif"];
17064
17173
  if (condition == null) return true;
17065
- return Boolean(resolveRawValueByPath(wxsScopeData(scope), condition));
17174
+ return Boolean(resolveRawValueByPath$1(wxsScopeData(scope), condition));
17066
17175
  }
17067
17176
  function createLoopScope(scope, itemName, indexName, item, index) {
17068
17177
  return {
@@ -17138,7 +17247,7 @@ function collectComponentEventBindings(hostNode) {
17138
17247
  return collectMiniProgramEventBindings(hostNode.attribs);
17139
17248
  }
17140
17249
  function buildComponentTrigger(componentScopeId, context, hostNode) {
17141
- const hostDataset = collectDataset$3(hostNode);
17250
+ const hostDataset = collectDataset(hostNode);
17142
17251
  const hostId = hostNode.attribs?.id ?? "";
17143
17252
  return (instance, eventName, detail, triggerOptions) => {
17144
17253
  const originScope = context.componentScopes.get(componentScopeId);
@@ -17200,7 +17309,7 @@ function createComponentScope(clonedNode, scope, componentScopeId, componentInst
17200
17309
  alias: clonedNode.name,
17201
17310
  classList: String(clonedNode.attribs?.class ?? "").split(CLASS_SPLIT_RE).map((item) => item.trim()).filter(Boolean),
17202
17311
  data: { ...componentInstance.data },
17203
- dataset: collectDataset$3(clonedNode, wxsScopeData(scope)),
17312
+ dataset: collectDataset(clonedNode, wxsScopeData(scope)),
17204
17313
  eventBindings: collectComponentEventBindings(clonedNode),
17205
17314
  getMethod: (methodName) => {
17206
17315
  const method = componentInstance?.[methodName];
@@ -17223,7 +17332,7 @@ function resolveComponentProperties(clonedNode, scope, definition) {
17223
17332
  if (key.startsWith("bind") || key.startsWith("generic:")) continue;
17224
17333
  const camelizedKey = key.replace(/-([a-z])/g, (_match, char) => char.toUpperCase());
17225
17334
  const propertyKey = key in declaredProperties || !(camelizedKey in declaredProperties) ? key : camelizedKey;
17226
- if (isMustacheOnly(String(value))) bindingExpressions[propertyKey] = String(value).trim().slice(2, -2).trim();
17335
+ if (isMustacheOnly$1(String(value))) bindingExpressions[propertyKey] = String(value).trim().slice(2, -2).trim();
17227
17336
  nextProperties[propertyKey] = resolveComponentAttributeValue(String(value), scope);
17228
17337
  }
17229
17338
  return {
@@ -17231,7 +17340,7 @@ function resolveComponentProperties(clonedNode, scope, definition) {
17231
17340
  bindingExpressions
17232
17341
  };
17233
17342
  }
17234
- function createRuntimeComponentInstance(componentScopeId, context, clonedNode, componentEntry, nextProperties, ownerScopeId) {
17343
+ function createRuntimeComponentInstance(componentScopeId, context, clonedNode, componentEntry, nextProperties, ownerScopeId, beforeCreated) {
17235
17344
  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
17345
  const componentInstance = createComponentInstance({
17237
17346
  definition: componentEntry.definition,
@@ -17243,10 +17352,11 @@ function createRuntimeComponentInstance(componentScopeId, context, clonedNode, c
17243
17352
  componentInstance.createIntersectionObserver = (options) => context.session.createIntersectionObserver(componentInstance, options);
17244
17353
  componentInstance.createMediaQueryObserver = () => context.session.createMediaQueryObserver(componentInstance);
17245
17354
  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);
17355
+ componentInstance.selectComponent = (selector) => resolveNativeComponentSelection(context.session.selectComponentWithin(componentScopeId, selector));
17356
+ componentInstance.selectAllComponents = (selector) => context.session.selectAllComponentsWithin(componentScopeId, selector).map(resolveNativeComponentSelection);
17248
17357
  componentInstance.selectOwnerComponent = () => ownerScopeId ? context.componentCache.get(ownerScopeId) ?? null : null;
17249
17358
  context.componentCache.set(componentScopeId, componentInstance);
17359
+ beforeCreated?.(componentInstance);
17250
17360
  runComponentLifecycle(componentInstance, "created");
17251
17361
  componentInstance.__propertySnapshots = Object.fromEntries(Object.entries(componentInstance.properties).map(([key, propertyValue]) => [key, cloneValue$1(propertyValue)]));
17252
17362
  syncComponentProperties(componentInstance, componentInstance.__definition__ ?? componentEntry.definition, componentProperties, {}, []);
@@ -17267,7 +17377,7 @@ function expandNodeByFor(node, scope) {
17267
17377
  scope,
17268
17378
  instanceSuffix: ""
17269
17379
  }];
17270
- const items = resolveLoopEntries(resolveRawValueByPath(wxsScopeData(scope), forExpression));
17380
+ const items = resolveLoopEntries(resolveRawValueByPath$1(wxsScopeData(scope), forExpression));
17271
17381
  const itemName = node.attribs?.["wx:for-item"]?.trim() || "item";
17272
17382
  const indexName = node.attribs?.["wx:for-index"]?.trim() || "index";
17273
17383
  return items.map(([index, item]) => ({
@@ -17382,12 +17492,16 @@ function renderNodeTree(node, scope, context, ownerJsonPath, ownerFilePath, inst
17382
17492
  const componentScopeId = `${instancePath}/${clonedNode.name}`;
17383
17493
  const ownerScopeId = scope.getScopeId().includes("/") ? scope.getScopeId() : void 0;
17384
17494
  const { nextProperties, bindingExpressions } = resolveComponentProperties(clonedNode, scope, componentEntry.definition);
17495
+ const genericComponents = resolveComponentGenerics(context, clonedNode, ownerJsonPath, ownerFilePath, componentEntry.filePath);
17496
+ const slots = collectComponentSlots(clonedNode, scope, ownerJsonPath, ownerFilePath, componentScopeId, templateRenderState);
17385
17497
  let componentInstance = context.componentCache.get(componentScopeId);
17386
- if (!componentInstance) componentInstance = createRuntimeComponentInstance(componentScopeId, context, clonedNode, componentEntry, nextProperties, ownerScopeId);
17498
+ if (!componentInstance) componentInstance = createRuntimeComponentInstance(componentScopeId, context, clonedNode, componentEntry, nextProperties, ownerScopeId, (instance) => {
17499
+ const initialScope = createComponentScope(clonedNode, scope, componentScopeId, instance, genericComponents, slots);
17500
+ context.componentScopes.set(componentScopeId, initialScope);
17501
+ renderRuntimeComponentTemplate(context, componentEntry, renderNodeTree, initialScope, componentScopeId, /* @__PURE__ */ new Set());
17502
+ });
17387
17503
  else syncComponentProperties(componentInstance, componentInstance.__definition__ ?? componentEntry.definition, nextProperties, bindingExpressions, context.changedPageKeys);
17388
17504
  seenComponentScopes.add(componentScopeId);
17389
- const genericComponents = resolveComponentGenerics(context, clonedNode, ownerJsonPath, ownerFilePath, componentEntry.filePath);
17390
- const slots = collectComponentSlots(clonedNode, scope, ownerJsonPath, ownerFilePath, componentScopeId, templateRenderState);
17391
17505
  const componentScope = createComponentScope(clonedNode, scope, componentScopeId, componentInstance, genericComponents, slots);
17392
17506
  context.componentScopes.set(componentScopeId, componentScope);
17393
17507
  const renderedComponentRoot = renderRuntimeComponentTemplate(context, componentEntry, renderNodeTree, componentScope, componentScopeId, seenComponentScopes);
@@ -17397,6 +17511,10 @@ function renderNodeTree(node, scope, context, ownerJsonPath, ownerFilePath, inst
17397
17511
  ...clonedNode.attribs,
17398
17512
  ...renderedComponentRoot.attribs
17399
17513
  };
17514
+ if (clonedNode.dataset || renderedComponentRoot.dataset) renderedComponentRoot.dataset = {
17515
+ ...clonedNode.dataset,
17516
+ ...renderedComponentRoot.dataset
17517
+ };
17400
17518
  renderedComponentRoot.attribs["data-sim-component"] = clonedNode.name;
17401
17519
  renderedComponentRoot.attribs["data-sim-node"] = instancePath;
17402
17520
  renderedComponentRoot.attribs["data-sim-scope"] = componentScopeId;
@@ -17440,7 +17558,8 @@ function renderRuntimePageTree(context, page) {
17440
17558
  const pageAttaching = isComponentPageAttaching(page);
17441
17559
  const attached = !pageAttaching && flushComponentAttachments([...seenComponentScopes].map((scopeId) => context.componentCache.get(scopeId)), (instance) => runComponentLifecycle(instance, "attached"));
17442
17560
  const removed = [...context.componentCache].filter(([scopeId]) => scopeId.startsWith(`${pageScopeId}/`) && !seenComponentScopes.has(scopeId));
17443
- for (const [, instance] of removed) runComponentLifecycle(instance, "detached");
17561
+ for (const [scopeId, instance] of removed) if (isComponentAttached(instance)) runComponentLifecycle(instance, "detached");
17562
+ else scheduleDiscardedComponentReady(context.componentCache, scopeId, instance);
17444
17563
  const instances = [...seenComponentScopes].map((scopeId) => context.componentCache.get(scopeId));
17445
17564
  const relationsChanged = !pageAttaching && !hasPendingComponentAttachments(instances) && syncComponentRelations(context.componentCache, seenComponentScopes, pageScopeId);
17446
17565
  for (const [scopeId] of removed) {
@@ -17448,6 +17567,7 @@ function renderRuntimePageTree(context, page) {
17448
17567
  context.componentScopes.delete(scopeId);
17449
17568
  }
17450
17569
  if (pageAttached || attached || relationsChanged) return renderRuntimePageTree(context, page);
17570
+ if (!isPageBeforeReady(page)) flushDiscardedComponentReady(context.componentCache, pageScopeId, (instance) => runComponentLifecycle(instance, "ready"));
17451
17571
  if (!isPageBeforeReady(page) && flushComponentReady(instances, (instance) => {
17452
17572
  runComponentLifecycle(instance, "ready");
17453
17573
  if (instance !== context.componentCache.get(customTabBarScopeId(route))) instance.__definition__?.pageLifetimes?.show?.call(instance);
@@ -17633,6 +17753,57 @@ function createHeadlessLogManager(runtimeConsole) {
17633
17753
  };
17634
17754
  }
17635
17755
  //#endregion
17756
+ //#region ../../mpcore/packages/simulator/src/host/wx/startupNavigation.ts
17757
+ const queues = /* @__PURE__ */ new WeakMap();
17758
+ /** 冷启动先完成 App 与首屏装载,再顺序提交启动钩子请求的导航。 */
17759
+ var StartupNavigationQueue = class {
17760
+ scheduler;
17761
+ collecting = false;
17762
+ pending = [];
17763
+ constructor(scheduler) {
17764
+ this.scheduler = scheduler;
17765
+ }
17766
+ capture(operation) {
17767
+ this.collecting = true;
17768
+ try {
17769
+ operation();
17770
+ } catch (error) {
17771
+ this.pending.length = 0;
17772
+ throw error;
17773
+ } finally {
17774
+ this.collecting = false;
17775
+ }
17776
+ }
17777
+ run(option, operation) {
17778
+ if (!this.collecting) return operation(option);
17779
+ const defer = (callback) => callback ? (...args) => {
17780
+ this.scheduler.setTimeout(() => callback(...args), 0);
17781
+ } : void 0;
17782
+ const deferred = option && {
17783
+ ...option,
17784
+ success: defer(option.success),
17785
+ fail: defer(option.fail),
17786
+ complete: defer(option.complete)
17787
+ };
17788
+ this.pending.push(() => {
17789
+ operation(deferred);
17790
+ });
17791
+ }
17792
+ flush() {
17793
+ const pending = this.pending.splice(0);
17794
+ for (const operation of pending) operation();
17795
+ }
17796
+ };
17797
+ /** 内部绑定保持 driver 的公开形状不变,两种 runtime 共享启动调度。 */
17798
+ function bindStartupNavigation(driver, queue) {
17799
+ queues.set(driver, queue);
17800
+ return driver;
17801
+ }
17802
+ function runNavigationApi(driver, option, operation) {
17803
+ const queue = queues.get(driver);
17804
+ return queue ? queue.run(option, operation) : operation(option);
17805
+ }
17806
+ //#endregion
17636
17807
  //#region ../../mpcore/packages/simulator/src/host/wx/index.ts
17637
17808
  function invokeWxApi(operation, option) {
17638
17809
  try {
@@ -18030,12 +18201,12 @@ function createHeadlessWx(driver, runtimeConsole = console) {
18030
18201
  hideLoading: (option) => invokeWxApi(() => driver.hideLoading(), option),
18031
18202
  hideToast: () => driver.hideToast(),
18032
18203
  downloadFile: (option) => driver.downloadFile(option),
18033
- navigateBack: (option) => invokeWxApi(() => {
18034
- driver.navigateBack(option);
18035
- }, option),
18036
- navigateTo: (option) => invokeWxApi(() => {
18037
- driver.navigateTo(option);
18038
- }, option),
18204
+ navigateBack: (option) => runNavigationApi(driver, option, (deferred) => invokeWxApi(() => {
18205
+ driver.navigateBack(deferred);
18206
+ }, deferred)),
18207
+ navigateTo: (option) => runNavigationApi(driver, option, (deferred) => invokeWxApi(() => {
18208
+ driver.navigateTo(deferred);
18209
+ }, deferred)),
18039
18210
  nextTick: (callback) => driver.nextTick(callback),
18040
18211
  offAppHide: (callback) => driver.offAppHide(callback),
18041
18212
  offAppShow: (callback) => driver.offAppShow(callback),
@@ -18048,12 +18219,12 @@ function createHeadlessWx(driver, runtimeConsole = console) {
18048
18219
  driver.pageScrollTo(option);
18049
18220
  }, option),
18050
18221
  previewImage: (option) => invokeWxApi(() => driver.previewImage(option), option),
18051
- reLaunch: (option) => invokeWxApi(() => {
18052
- driver.reLaunch(option);
18053
- }, option),
18054
- redirectTo: (option) => invokeWxApi(() => {
18055
- driver.redirectTo(option);
18056
- }, option),
18222
+ reLaunch: (option) => runNavigationApi(driver, option, (deferred) => invokeWxApi(() => {
18223
+ driver.reLaunch(deferred);
18224
+ }, deferred)),
18225
+ redirectTo: (option) => runNavigationApi(driver, option, (deferred) => invokeWxApi(() => {
18226
+ driver.redirectTo(deferred);
18227
+ }, deferred)),
18057
18228
  removeSavedFile: (option) => invokeWxApi(() => driver.removeSavedFile(option), option),
18058
18229
  saveImageToPhotosAlbum: (option) => invokeWxApi(() => driver.saveImageToPhotosAlbum(option), option),
18059
18230
  saveVideoToPhotosAlbum: (option) => invokeWxApi(() => driver.saveVideoToPhotosAlbum(option), option),
@@ -18089,7 +18260,7 @@ function createHeadlessWx(driver, runtimeConsole = console) {
18089
18260
  showToast: (option) => invokeWxApi(() => driver.showToast(option), option),
18090
18261
  startPullDownRefresh: (option) => invokeWxApi(() => driver.startPullDownRefresh(), option),
18091
18262
  stopPullDownRefresh: () => driver.stopPullDownRefresh(),
18092
- switchTab: (option) => driver.switchTab(option),
18263
+ switchTab: (option) => runNavigationApi(driver, option, (deferred) => driver.switchTab(deferred)),
18093
18264
  uploadFile: (option) => driver.uploadFile(option),
18094
18265
  removeTabBarBadge: (option) => invokeWxApi(() => driver.removeTabBarBadge(option), option),
18095
18266
  setTabBarBadge: (option) => invokeWxApi(() => driver.setTabBarBadge(option), option),
@@ -18347,6 +18518,40 @@ function createProjectDescriptor(options) {
18347
18518
  };
18348
18519
  }
18349
18520
  //#endregion
18521
+ //#region ../../mpcore/packages/simulator/src/project/hostConfig.ts
18522
+ /**
18523
+ * @description 根据构建后的 app.json 创建模拟器提供给小程序脚本读取的宿主配置。
18524
+ * 微信开发者工具的 tabBar 页面路径通常带有 `.html` 后缀,保留该形态以覆盖路由归一化边界。
18525
+ */
18526
+ function createMiniProgramHostConfig(appConfig, override) {
18527
+ const appTabBar = appConfig.tabBar && typeof appConfig.tabBar === "object" ? appConfig.tabBar : void 0;
18528
+ const tabBar = appTabBar ? {
18529
+ ...appTabBar,
18530
+ ...Array.isArray(appTabBar.list) ? { list: appTabBar.list.map((item) => {
18531
+ if (!item || typeof item !== "object") return item;
18532
+ const pagePath = typeof item.pagePath === "string" ? item.pagePath.replace(/^\/+/, "").replace(/\.html$/, "") : void 0;
18533
+ return pagePath ? {
18534
+ ...item,
18535
+ pagePath: `${pagePath}.html`
18536
+ } : { ...item };
18537
+ }) } : {}
18538
+ } : void 0;
18539
+ const generated = {
18540
+ ...appConfig,
18541
+ ...tabBar ? { tabBar } : {}
18542
+ };
18543
+ if (!override || typeof override !== "object" || Array.isArray(override)) return generated;
18544
+ const overrideRecord = override;
18545
+ return {
18546
+ ...generated,
18547
+ ...overrideRecord,
18548
+ ...generated.tabBar || overrideRecord.tabBar ? { tabBar: {
18549
+ ...generated.tabBar ?? {},
18550
+ ...overrideRecord.tabBar ?? {}
18551
+ } } : {}
18552
+ };
18553
+ }
18554
+ //#endregion
18350
18555
  //#region ../../mpcore/packages/simulator/src/project/loadProject.ts
18351
18556
  function readJsonObject$1(filePath) {
18352
18557
  try {
@@ -37714,23 +37919,11 @@ function queryXPathElements(root, expression) {
37714
37919
  }
37715
37920
  //#endregion
37716
37921
  //#region ../../mpcore/packages/simulator/src/view/nodeHandle.ts
37717
- const DATASET_NAME_RE$2 = /-([a-z])/g;
37718
37922
  function escapeText$1(text) {
37719
37923
  return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
37720
37924
  }
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
37925
  function createEventPayload$1(node, eventName, event) {
37733
- const dataset = collectDataset$2(node);
37926
+ const dataset = collectNodeDataset(node);
37734
37927
  const nodeId = node.attribs?.id ?? "";
37735
37928
  return {
37736
37929
  bubbles: false,
@@ -37818,7 +38011,7 @@ var HeadlessTestingNodeHandle = class HeadlessTestingNodeHandle {
37818
38011
  }
37819
38012
  async dataset() {
37820
38013
  this.assertActive();
37821
- return collectDataset$2(this.node);
38014
+ return collectNodeDataset(this.node);
37822
38015
  }
37823
38016
  async scope() {
37824
38017
  this.assertActive();
@@ -37893,13 +38086,19 @@ var HeadlessTestingNodeHandle = class HeadlessTestingNodeHandle {
37893
38086
  //#region ../../mpcore/packages/simulator/src/view/render.ts
37894
38087
  const LEADING_SLASH_RE$2 = /^\/+/;
37895
38088
  const TEMPLATE_INTERPOLATION_RE = /\{\{([^{}]+)\}\}/g;
37896
- function resolveValueByPath(source, expression) {
38089
+ function resolveRawValueByPath(source, expression) {
37897
38090
  const normalized = expression.trim();
37898
- if (!normalized) return "";
38091
+ if (!normalized) return;
37899
38092
  const segments = normalized.split(".").filter(Boolean);
37900
38093
  let current = source;
37901
- for (const segment of segments) current = current?.[segment];
37902
- return current ?? "";
38094
+ for (const segment of segments) {
38095
+ if (current == null || typeof current !== "object" && typeof current !== "function") return;
38096
+ current = Reflect.get(current, segment);
38097
+ }
38098
+ return current;
38099
+ }
38100
+ function resolveValueByPath(source, expression) {
38101
+ return resolveRawValueByPath(source, expression) ?? "";
37903
38102
  }
37904
38103
  function interpolateTemplate(input, data) {
37905
38104
  return input.replace(TEMPLATE_INTERPOLATION_RE, (_match, expression) => {
@@ -37907,6 +38106,10 @@ function interpolateTemplate(input, data) {
37907
38106
  return typeof value === "string" ? value : String(value);
37908
38107
  });
37909
38108
  }
38109
+ function isMustacheOnly(value) {
38110
+ const trimmed = value.trim();
38111
+ return trimmed.startsWith("{{") && trimmed.endsWith("}}") && !trimmed.includes("{{", 2);
38112
+ }
37910
38113
  function visitDom(node, visitor) {
37911
38114
  visitor(node);
37912
38115
  for (const child of node.children ?? []) visitDom(child, visitor);
@@ -37914,7 +38117,18 @@ function visitDom(node, visitor) {
37914
38117
  function interpolateDomTree(root, data) {
37915
38118
  visitDom(root, (node) => {
37916
38119
  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);
38120
+ if (node.attribs) {
38121
+ let dataset;
38122
+ for (const [key, value] of Object.entries(node.attribs)) {
38123
+ const resolvedValue = isMustacheOnly(value) ? resolveRawValueByPath(data, value.trim().slice(2, -2)) : interpolateTemplate(value, data);
38124
+ node.attribs[key] = String(resolvedValue ?? "");
38125
+ if (isDatasetAttribute(key)) {
38126
+ dataset ??= {};
38127
+ dataset[toDatasetKey(key)] = resolvedValue;
38128
+ }
38129
+ }
38130
+ node.dataset = dataset;
38131
+ }
37918
38132
  });
37919
38133
  }
37920
38134
  function escapeText(text) {
@@ -37948,21 +38162,9 @@ function renderPageTree(project, page) {
37948
38162
  }
37949
38163
  //#endregion
37950
38164
  //#region ../../mpcore/packages/simulator/src/view/selectorQuery.ts
37951
- const DATASET_NAME_RE$1 = /-([a-z])/g;
37952
38165
  const LEADING_MARK_PREFIX_RE = /^mark[:\-]?/;
37953
38166
  const MARK_NAME_RE = /[:\-]([a-z])/g;
37954
38167
  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
38168
  function toMarkKey(attributeName) {
37967
38169
  return attributeName.replace(LEADING_MARK_PREFIX_RE, "").replace(MARK_NAME_RE, (_match, char) => char.toUpperCase());
37968
38170
  }
@@ -38039,7 +38241,7 @@ function resolvePropertyValue(node, propertyName) {
38039
38241
  if (!normalizedPropertyName) return;
38040
38242
  if (normalizedPropertyName === "id") return node.attribs?.id ?? "";
38041
38243
  if (normalizedPropertyName === "class") return node.attribs?.class ?? "";
38042
- if (normalizedPropertyName === "dataset") return collectDataset$1(node);
38244
+ if (normalizedPropertyName === "dataset") return collectNodeDataset(node);
38043
38245
  return node.attribs?.[normalizedPropertyName];
38044
38246
  }
38045
38247
  function pickProperties(node, propertyNames) {
@@ -38056,7 +38258,7 @@ function pickComputedStyle(node, propertyNames) {
38056
38258
  function resolveFieldsResult(node, fields, options) {
38057
38259
  const result = {};
38058
38260
  if (fields.id) result.id = node.attribs?.id ?? "";
38059
- if (fields.dataset) result.dataset = collectDataset$1(node);
38261
+ if (fields.dataset) result.dataset = collectNodeDataset(node);
38060
38262
  if (fields.mark) result.mark = collectMark(node);
38061
38263
  if (fields.rect || fields.size) {
38062
38264
  const rect = resolveRect$1(node, options.root.name === "page" ? options.windowInfo : void 0);
@@ -38761,20 +38963,8 @@ function createHeadlessMediaQueryObserver(driver, onDisconnect) {
38761
38963
  }
38762
38964
  //#endregion
38763
38965
  //#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
38966
  function createEventPayload(node, eventName, detail) {
38777
- const dataset = collectDataset(node);
38967
+ const dataset = collectNodeDataset(node);
38778
38968
  const nodeId = node.attribs?.id ?? "";
38779
38969
  return {
38780
38970
  bubbles: false,
@@ -39290,6 +39480,110 @@ function deriveMenuButtonBoundingClientRect(systemInfo) {
39290
39480
  };
39291
39481
  }
39292
39482
  //#endregion
39483
+ //#region ../../mpcore/packages/simulator/src/runtime/request/task.ts
39484
+ /** mock 与显式网络桥接共用终止状态;终止后不再分发 headers/chunk。 */
39485
+ function createRequestTask(option, onAbort = () => {}) {
39486
+ const headers = /* @__PURE__ */ new Set();
39487
+ const chunks = /* @__PURE__ */ new Set();
39488
+ let settled = false;
39489
+ function finish(result, success) {
39490
+ if (settled) return;
39491
+ settled = true;
39492
+ headers.clear();
39493
+ chunks.clear();
39494
+ try {
39495
+ if (success) option.success?.(result);
39496
+ else option.fail?.(result);
39497
+ } finally {
39498
+ option.complete?.(result);
39499
+ }
39500
+ }
39501
+ return {
39502
+ task: {
39503
+ abort() {
39504
+ if (settled) return;
39505
+ const error = Object.assign(/* @__PURE__ */ new Error("request:fail abort"), { errMsg: "request:fail abort" });
39506
+ try {
39507
+ finish(error, false);
39508
+ } finally {
39509
+ onAbort();
39510
+ }
39511
+ },
39512
+ onHeadersReceived(callback) {
39513
+ headers.add(callback);
39514
+ },
39515
+ offHeadersReceived(callback) {
39516
+ if (callback) headers.delete(callback);
39517
+ else headers.clear();
39518
+ },
39519
+ onChunkReceived(callback) {
39520
+ chunks.add(callback);
39521
+ },
39522
+ offChunkReceived(callback) {
39523
+ if (callback) chunks.delete(callback);
39524
+ else chunks.clear();
39525
+ }
39526
+ },
39527
+ get settled() {
39528
+ return settled;
39529
+ },
39530
+ headers(result) {
39531
+ if (settled) return;
39532
+ for (const callback of Array.from(headers)) if (!settled && headers.has(callback)) callback(result);
39533
+ },
39534
+ chunk(data) {
39535
+ if (settled || !option.enableChunked) return;
39536
+ for (const callback of Array.from(chunks)) if (!settled && chunks.has(callback)) callback({ data: data.slice(0) });
39537
+ },
39538
+ succeed(result) {
39539
+ finish(result, true);
39540
+ },
39541
+ fail(error) {
39542
+ finish(Object.assign(error, { errMsg: "errMsg" in error ? error.errMsg : error.message }), false);
39543
+ }
39544
+ };
39545
+ }
39546
+ //#endregion
39547
+ //#region ../../mpcore/packages/simulator/src/runtime/request/mock.ts
39548
+ /** 仅模拟已声明的分块;不进行网络访问,也不把完整 response 拆成假分块。 */
39549
+ function createChunkedMockRequest(option, mock, response, scheduler, onSuccess) {
39550
+ let timer;
39551
+ const request = createRequestTask(option, () => scheduler.clearTimeout(timer));
39552
+ const chunks = (mock.chunks ?? []).map((chunk) => ({
39553
+ ...chunk,
39554
+ data: chunk.data.slice(0)
39555
+ }));
39556
+ let index = 0;
39557
+ function next() {
39558
+ if (request.settled) return;
39559
+ const chunk = chunks[index++];
39560
+ if (chunk) timer = scheduler.setTimeout(() => {
39561
+ request.chunk(chunk.data);
39562
+ next();
39563
+ }, Math.max(0, chunk.delay ?? 0));
39564
+ else timer = scheduler.setTimeout(() => {
39565
+ if (request.settled) return;
39566
+ if (mock.error) request.fail(new Error(mock.error));
39567
+ else {
39568
+ onSuccess();
39569
+ request.succeed({
39570
+ ...response,
39571
+ data: ""
39572
+ });
39573
+ }
39574
+ }, 0);
39575
+ }
39576
+ timer = scheduler.setTimeout(() => {
39577
+ request.headers({
39578
+ cookies: response.cookies,
39579
+ header: response.header,
39580
+ statusCode: response.statusCode
39581
+ });
39582
+ next();
39583
+ }, Math.max(0, mock.delay ?? 0));
39584
+ return request.task;
39585
+ }
39586
+ //#endregion
39293
39587
  //#region ../../mpcore/packages/simulator/src/runtime/wxState.ts
39294
39588
  const STORAGE_LIMIT_SIZE = 10240;
39295
39589
  const DEFAULT_MODAL_CANCEL_COLOR = "#000000";
@@ -40611,6 +40905,7 @@ function createHeadlessWxState(scheduler = nativeScheduler, options = {}) {
40611
40905
  request(option) {
40612
40906
  const matchedMock = requestMocks.find((mock) => matchesRequestMock(mock, option));
40613
40907
  if (!matchedMock) {
40908
+ const request = createRequestTask(option);
40614
40909
  const logEntry = {
40615
40910
  data: cloneValue(option.data),
40616
40911
  header: { ...option.header ?? {} },
@@ -40621,9 +40916,8 @@ function createHeadlessWxState(scheduler = nativeScheduler, options = {}) {
40621
40916
  requestLogs.push(logEntry);
40622
40917
  const error = /* @__PURE__ */ new Error(`No request mock matched in headless runtime: ${normalizeMethod(option.method)} ${option.url}`);
40623
40918
  if (options.strictMocks) throw error;
40624
- option.fail?.(error);
40625
- option.complete?.();
40626
- return createNoopTask();
40919
+ request.fail(error);
40920
+ return request.task;
40627
40921
  }
40628
40922
  const response = resolveRequestResponse(matchedMock, option);
40629
40923
  const requestLogEntry = {
@@ -40635,20 +40929,31 @@ function createHeadlessWxState(scheduler = nativeScheduler, options = {}) {
40635
40929
  url: option.url
40636
40930
  };
40637
40931
  const delay = Number.isFinite(matchedMock.delay) ? Math.max(0, Math.trunc(matchedMock.delay ?? 0)) : 0;
40932
+ if (option.enableChunked) return createChunkedMockRequest(option, matchedMock, response, scheduler, () => {
40933
+ requestLogs.push({
40934
+ ...requestLogEntry,
40935
+ response: {
40936
+ ...response,
40937
+ data: ""
40938
+ }
40939
+ });
40940
+ });
40941
+ const request = createRequestTask(option);
40638
40942
  if (delay <= 0) {
40639
40943
  requestLogs.push(requestLogEntry);
40640
- option.success?.(response);
40641
- option.complete?.(response);
40642
- return createNoopTask();
40944
+ request.succeed(response);
40945
+ return request.task;
40643
40946
  }
40644
- return runDelayedTask(delay, () => {
40947
+ const delayed = runDelayedTask(delay, () => {
40645
40948
  requestLogs.push(requestLogEntry);
40646
- option.success?.(response);
40647
- option.complete?.(response);
40949
+ request.succeed(response);
40648
40950
  }, (error) => {
40649
- option.fail?.(error);
40650
- option.complete?.();
40951
+ request.fail(error);
40651
40952
  }, "request:fail abort");
40953
+ return {
40954
+ ...request.task,
40955
+ abort: delayed.abort
40956
+ };
40652
40957
  },
40653
40958
  removeSavedFile(option) {
40654
40959
  return removeSavedFile(option.filePath);
@@ -40865,7 +41170,7 @@ function matchesComponentSelector(scope, selector) {
40865
41170
  if (dataAttrMatch) {
40866
41171
  const [, key, value] = dataAttrMatch;
40867
41172
  const datasetKey = key.replace(DATASET_KEY_RE, (_match, char) => char.toUpperCase());
40868
- return scope.dataset?.[datasetKey] === value;
41173
+ return scope.dataset != null && Object.hasOwn(scope.dataset, datasetKey) && String(scope.dataset[datasetKey] ?? "") === value;
40869
41174
  }
40870
41175
  return scope.alias === part;
40871
41176
  });
@@ -40891,6 +41196,7 @@ function resolveNavigationPath(targetPath, baseRoute) {
40891
41196
  var HeadlessSession = class {
40892
41197
  project;
40893
41198
  kernel = new RuntimeKernel();
41199
+ startupNavigation = new StartupNavigationQueue(this.kernel.scheduler);
40894
41200
  appDefinition = null;
40895
41201
  appInstance = null;
40896
41202
  appLifecycle = new HeadlessAppLifecycle();
@@ -40945,7 +41251,7 @@ var HeadlessSession = class {
40945
41251
  });
40946
41252
  });
40947
41253
  this.tabBarVisible = this.tabBarRoutes.size > 0;
40948
- this.moduleLoader = createModuleLoader(this.registries, () => this.pages.slice(), () => this.getApp(), {
41254
+ this.moduleLoader = createModuleLoader(this.registries, () => this.pages.slice(), () => this.getApp(), bindStartupNavigation({
40949
41255
  chooseImage: (option) => this.wxState.chooseImage(option ?? {}),
40950
41256
  chooseMessageFile: (option) => this.wxState.chooseMessageFile(option ?? {}),
40951
41257
  chooseMedia: (option) => this.wxState.chooseMedia(option ?? {}),
@@ -41024,9 +41330,12 @@ var HeadlessSession = class {
41024
41330
  removeTabBarBadge: (option) => this.removeTabBarBadge(option.index),
41025
41331
  setTabBarBadge: (option) => this.setTabBarBadge(option.index, option.text),
41026
41332
  updateShareMenu: (option) => this.wxState.updateShareMenu(option)
41027
- }, {
41333
+ }, this.startupNavigation), {
41028
41334
  artifactSource: this.project.artifactSource,
41029
- globals: options.globals,
41335
+ globals: {
41336
+ ...options.globals,
41337
+ __wxConfig: createMiniProgramHostConfig(this.project.appConfig, options.globals?.__wxConfig)
41338
+ },
41030
41339
  kernel: this.kernel,
41031
41340
  miniprogramRootPath: this.project.miniprogramRootPath,
41032
41341
  onConsole: (entry) => this.emit("console", entry),
@@ -41480,9 +41789,16 @@ var HeadlessSession = class {
41480
41789
  if (Object.hasOwn(runtimeApp, "routes")) this.appInstance.routes = runtimeApp.routes;
41481
41790
  }
41482
41791
  bootstrapNavigation(launchOptions = createAppLaunchOptions("", {})) {
41483
- const isLaunching = !this.appInstance;
41484
- this.bootstrap(launchOptions);
41485
- return isLaunching ? this.currentPageInstance : null;
41792
+ if (this.appInstance) return null;
41793
+ this.startupNavigation.capture(() => {
41794
+ this.bootstrap(launchOptions);
41795
+ });
41796
+ if (launchOptions.path) {
41797
+ const query = new URLSearchParams(launchOptions.query).toString();
41798
+ this.reLaunch(`/${launchOptions.path}${query ? `?${query}` : ""}`);
41799
+ }
41800
+ this.startupNavigation.flush();
41801
+ return this.currentPageInstance;
41486
41802
  }
41487
41803
  reLaunch(url) {
41488
41804
  const target = this.resolveNavigationTarget(url);
@@ -41683,8 +41999,8 @@ var HeadlessSession = class {
41683
41999
  });
41684
42000
  pageInstance.createIntersectionObserver = (options) => this.createIntersectionObserver(pageInstance, options);
41685
42001
  pageInstance.createMediaQueryObserver = () => this.createMediaQueryObserver(pageInstance);
41686
- pageInstance.selectComponent = (selector) => this.selectComponent(selector);
41687
- pageInstance.selectAllComponents = (selector) => this.selectAllComponents(selector);
42002
+ pageInstance.selectComponent = (selector) => resolveNativeComponentSelection(this.selectComponent(selector));
42003
+ pageInstance.selectAllComponents = (selector) => this.selectAllComponents(selector).map(resolveNativeComponentSelection);
41688
42004
  pageInstance.getTabBar = () => {
41689
42005
  if (this.currentPageInstance === pageInstance) this.renderCurrentPage();
41690
42006
  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.20",
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.5",
48
48
  "obug": "^3.0.0",
49
49
  "ws": "^8.21.3"
50
50
  },