@weapp-vite/miniprogram-automator 1.2.21 → 1.2.23
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.d.mts +4 -0
- package/dist/index.mjs +24 -6
- package/dist/{launch-BNQZTcKo.mjs → launch-CSjvON-f.mjs} +693 -162
- package/package.json +4 -4
|
@@ -12636,6 +12636,48 @@ function getParserClass(pluginsMap) {
|
|
|
12636
12636
|
return cls;
|
|
12637
12637
|
}
|
|
12638
12638
|
//#endregion
|
|
12639
|
+
//#region ../../mpcore/packages/simulator/src/view/templateInterpolation.ts
|
|
12640
|
+
/** 按表达式词法寻找插值边界,字符串和对象内部的大括号不结束绑定。 */
|
|
12641
|
+
function templateInterpolations(source) {
|
|
12642
|
+
const ranges = [];
|
|
12643
|
+
let offset = 0;
|
|
12644
|
+
while (offset < source.length) {
|
|
12645
|
+
const start = source.indexOf("{{", offset);
|
|
12646
|
+
if (start < 0) break;
|
|
12647
|
+
let quote;
|
|
12648
|
+
let depth = 0;
|
|
12649
|
+
let closed = false;
|
|
12650
|
+
for (let i = start + 2; i < source.length; i++) {
|
|
12651
|
+
const char = source[i];
|
|
12652
|
+
if (quote) {
|
|
12653
|
+
if (char === "\\") i++;
|
|
12654
|
+
else if (char === quote) quote = void 0;
|
|
12655
|
+
} else if (char === "'" || char === "\"") quote = char;
|
|
12656
|
+
else if (char === "{") depth++;
|
|
12657
|
+
else if (char === "}") {
|
|
12658
|
+
if (depth === 0 && source[i + 1] === "}") {
|
|
12659
|
+
ranges.push({
|
|
12660
|
+
start,
|
|
12661
|
+
end: i + 2,
|
|
12662
|
+
expression: source.slice(start + 2, i)
|
|
12663
|
+
});
|
|
12664
|
+
offset = i + 2;
|
|
12665
|
+
closed = true;
|
|
12666
|
+
break;
|
|
12667
|
+
}
|
|
12668
|
+
depth--;
|
|
12669
|
+
}
|
|
12670
|
+
}
|
|
12671
|
+
if (!closed) break;
|
|
12672
|
+
}
|
|
12673
|
+
return ranges;
|
|
12674
|
+
}
|
|
12675
|
+
function isTemplateExpression(value) {
|
|
12676
|
+
const trimmed = value.trim();
|
|
12677
|
+
const ranges = templateInterpolations(trimmed);
|
|
12678
|
+
return ranges.length === 1 && ranges[0].start === 0 && ranges[0].end === trimmed.length;
|
|
12679
|
+
}
|
|
12680
|
+
//#endregion
|
|
12639
12681
|
//#region ../../mpcore/packages/simulator/src/view/wxs.ts
|
|
12640
12682
|
const wxsFunctions = /* @__PURE__ */ new WeakSet();
|
|
12641
12683
|
function registerWxsExports(value, visited = /* @__PURE__ */ new Set()) {
|
|
@@ -12689,13 +12731,9 @@ const BLOCKED_MEMBER_KEYS = /* @__PURE__ */ new Set([
|
|
|
12689
12731
|
"prototype"
|
|
12690
12732
|
]);
|
|
12691
12733
|
const NUMERIC_DOT_PATH_RE = /\.(\d+)(?=[.[\s,}\]]|$)/g;
|
|
12692
|
-
function isMustacheOnly$2(value) {
|
|
12693
|
-
const trimmed = value.trim();
|
|
12694
|
-
return trimmed.startsWith("{{") && trimmed.endsWith("}}") && !trimmed.includes("{{", 2);
|
|
12695
|
-
}
|
|
12696
12734
|
function unwrapMustacheExpression(expression) {
|
|
12697
12735
|
const normalized = expression.trim();
|
|
12698
|
-
if (
|
|
12736
|
+
if (isTemplateExpression(normalized)) return normalized.slice(2, -2).trim();
|
|
12699
12737
|
return normalized;
|
|
12700
12738
|
}
|
|
12701
12739
|
function parseTemplateExpression(expression) {
|
|
@@ -12925,7 +12963,7 @@ const loadingPages = /* @__PURE__ */ new WeakSet();
|
|
|
12925
12963
|
function isPageBeforeReady(page) {
|
|
12926
12964
|
return loadingPages.has(page);
|
|
12927
12965
|
}
|
|
12928
|
-
function runInitialPageLifecycles(page, query, scheduler, isAlive, renderComponents) {
|
|
12966
|
+
function runInitialPageLifecycles(page, query, scheduler, isAlive, renderComponents, onReady) {
|
|
12929
12967
|
loadingPages.add(page);
|
|
12930
12968
|
try {
|
|
12931
12969
|
renderComponents?.();
|
|
@@ -12940,7 +12978,10 @@ function runInitialPageLifecycles(page, query, scheduler, isAlive, renderCompone
|
|
|
12940
12978
|
scheduler.setTimeout(() => {
|
|
12941
12979
|
if (!isAlive()) return;
|
|
12942
12980
|
page.onReady?.();
|
|
12943
|
-
if (isAlive())
|
|
12981
|
+
if (isAlive()) {
|
|
12982
|
+
page.onRouteDone?.({});
|
|
12983
|
+
onReady?.();
|
|
12984
|
+
}
|
|
12944
12985
|
}, 0);
|
|
12945
12986
|
}
|
|
12946
12987
|
//#endregion
|
|
@@ -13006,6 +13047,21 @@ function createPluginArtifactSource(fallback, miniprogramRootPath, plugins) {
|
|
|
13006
13047
|
};
|
|
13007
13048
|
}
|
|
13008
13049
|
//#endregion
|
|
13050
|
+
//#region ../../mpcore/packages/simulator/src/view/nodeDataset.ts
|
|
13051
|
+
const DATASET_NAME_RE = /-([a-z])/g;
|
|
13052
|
+
function isDatasetAttribute(attributeName) {
|
|
13053
|
+
return attributeName.startsWith("data-") && !attributeName.startsWith("data-sim-");
|
|
13054
|
+
}
|
|
13055
|
+
function toDatasetKey(attributeName) {
|
|
13056
|
+
return attributeName.slice(5).replace(DATASET_NAME_RE, (_match, char) => char.toUpperCase());
|
|
13057
|
+
}
|
|
13058
|
+
function collectNodeDataset(node) {
|
|
13059
|
+
if (node.dataset) return { ...node.dataset };
|
|
13060
|
+
const dataset = {};
|
|
13061
|
+
for (const [key, value] of Object.entries(node.attribs ?? {})) if (isDatasetAttribute(key)) dataset[toDatasetKey(key)] = value;
|
|
13062
|
+
return dataset;
|
|
13063
|
+
}
|
|
13064
|
+
//#endregion
|
|
13009
13065
|
//#region ../../mpcore/packages/simulator/src/view/eventBinding.ts
|
|
13010
13066
|
const LEGACY_EVENT_NAME_RE = /^\w+$/;
|
|
13011
13067
|
const EVENT_BINDING_PREFIXES = [
|
|
@@ -13059,7 +13115,34 @@ function collectMiniProgramEventBindings(attributes = {}) {
|
|
|
13059
13115
|
function resolveMiniProgramEventBinding(attributes, eventName) {
|
|
13060
13116
|
return collectMiniProgramEventBindings(attributes).get(eventName) ?? null;
|
|
13061
13117
|
}
|
|
13118
|
+
/** 冒泡时保留原始 target,currentTarget 则属于当前执行绑定的节点。 */
|
|
13119
|
+
function createMiniProgramEventPayload(node, eventName, event, origin = node) {
|
|
13120
|
+
const dataset = collectNodeDataset(node);
|
|
13121
|
+
const nodeId = node.attribs?.id ?? "";
|
|
13122
|
+
const isOrigin = node === origin;
|
|
13123
|
+
return {
|
|
13124
|
+
bubbles: false,
|
|
13125
|
+
capturePhase: false,
|
|
13126
|
+
composed: false,
|
|
13127
|
+
currentTarget: {
|
|
13128
|
+
dataset: isOrigin ? event.currentTarget?.dataset ?? event.dataset ?? dataset : dataset,
|
|
13129
|
+
id: isOrigin ? event.currentTarget?.id ?? event.id ?? nodeId : nodeId
|
|
13130
|
+
},
|
|
13131
|
+
detail: event.detail,
|
|
13132
|
+
mark: event.mark,
|
|
13133
|
+
target: {
|
|
13134
|
+
dataset: event.target?.dataset ?? event.dataset ?? (isOrigin ? dataset : collectNodeDataset(origin)),
|
|
13135
|
+
id: event.target?.id ?? event.id ?? origin.attribs?.id ?? ""
|
|
13136
|
+
},
|
|
13137
|
+
timeStamp: Date.now(),
|
|
13138
|
+
type: eventName
|
|
13139
|
+
};
|
|
13140
|
+
}
|
|
13062
13141
|
const WEVU_NATIVE_INSTANCE_KEY = "__wevuNativeInstance";
|
|
13142
|
+
/** 宿主值为 1 时,实现带 routeEventId 的路由阶段契约;不代表微信 SDK 版本。 */
|
|
13143
|
+
const WEVU_ROUTE_EVENT_CONTRACT_KEY = "__wevuRouteEventContract";
|
|
13144
|
+
/** 宿主负责 pageScrollTo 的实际滚动事件,框架不再合成第二份通知。 */
|
|
13145
|
+
const WEVU_PAGE_SCROLL_EVENT_CONTRACT_KEY = "__wevuPageScrollEventContract";
|
|
13063
13146
|
//#endregion
|
|
13064
13147
|
//#region ../../mpcore/packages/simulator/src/view/selectorQueryScope.ts
|
|
13065
13148
|
const MPCORE_COMPONENT_SCOPE_ID = "__mpcoreComponentScopeId";
|
|
@@ -13168,21 +13251,6 @@ function resolveMiniProgramComponent(ownerFilePath, request, miniprogramRootPath
|
|
|
13168
13251
|
return npmPath ? relative(miniprogramRootPath, npmPath).slice(0, -3) : localPath;
|
|
13169
13252
|
}
|
|
13170
13253
|
//#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
|
|
13186
13254
|
//#region ../../mpcore/packages/simulator/src/view/templateImports.ts
|
|
13187
13255
|
function createImportedTemplateState(root, filePath, loadImport, loadWxs) {
|
|
13188
13256
|
const documents = /* @__PURE__ */ new Map();
|
|
@@ -13234,12 +13302,15 @@ function createImportedTemplateState(root, filePath, loadImport, loadWxs) {
|
|
|
13234
13302
|
}
|
|
13235
13303
|
//#endregion
|
|
13236
13304
|
//#region ../../mpcore/packages/simulator/src/view/templateText.ts
|
|
13237
|
-
|
|
13238
|
-
|
|
13239
|
-
|
|
13240
|
-
|
|
13241
|
-
|
|
13242
|
-
|
|
13305
|
+
function interpolateTemplateText(input, data, emptyNull = false) {
|
|
13306
|
+
let output = "";
|
|
13307
|
+
let offset = 0;
|
|
13308
|
+
for (const range of templateInterpolations(input)) {
|
|
13309
|
+
const value = resolveTemplateExpression(data, range.expression);
|
|
13310
|
+
output += input.slice(offset, range.start) + (value === void 0 || emptyNull && value === null ? "" : String(value));
|
|
13311
|
+
offset = range.end;
|
|
13312
|
+
}
|
|
13313
|
+
return output + input.slice(offset);
|
|
13243
13314
|
}
|
|
13244
13315
|
//#endregion
|
|
13245
13316
|
//#region ../../node_modules/.pnpm/entities@8.1.0/node_modules/entities/dist/decode-codepoint.js
|
|
@@ -17015,20 +17086,76 @@ function parseDocument(data, options) {
|
|
|
17015
17086
|
return handler.root;
|
|
17016
17087
|
}
|
|
17017
17088
|
//#endregion
|
|
17089
|
+
//#region ../../mpcore/packages/simulator/src/view/templateAttributes.ts
|
|
17090
|
+
function decodeAttribute(raw, quote) {
|
|
17091
|
+
const source = raw.replaceAll(`\\${quote}`, quote);
|
|
17092
|
+
let value = "";
|
|
17093
|
+
let offset = 0;
|
|
17094
|
+
for (const range of templateInterpolations(source)) {
|
|
17095
|
+
value += source.slice(offset, range.start).replace(/\\([\s\S])/g, "$1");
|
|
17096
|
+
value += source.slice(range.start, range.end);
|
|
17097
|
+
offset = range.end;
|
|
17098
|
+
}
|
|
17099
|
+
return value + source.slice(offset).replace(/\\([\s\S])/g, "$1");
|
|
17100
|
+
}
|
|
17101
|
+
/** 先保存 WXML 属性词法值,再交给 HTML parser 构建标签树,避免反斜杠引号提前截断属性。 */
|
|
17102
|
+
function maskTemplateAttributes(source) {
|
|
17103
|
+
const attributes = /* @__PURE__ */ new Map();
|
|
17104
|
+
if (!source.includes("\\")) return {
|
|
17105
|
+
source,
|
|
17106
|
+
attributes
|
|
17107
|
+
};
|
|
17108
|
+
let prefix = "__mpcore_quoted_attribute_";
|
|
17109
|
+
while (source.includes(prefix)) prefix += "_";
|
|
17110
|
+
let output = "";
|
|
17111
|
+
let offset = 0;
|
|
17112
|
+
for (let i = 0; i < source.length; i++) {
|
|
17113
|
+
if (source.startsWith("<!--", i)) {
|
|
17114
|
+
const end = source.indexOf("-->", i + 4);
|
|
17115
|
+
i = end < 0 ? source.length : end + 2;
|
|
17116
|
+
continue;
|
|
17117
|
+
}
|
|
17118
|
+
if (source[i] !== "<" || !/[a-z_]/i.test(source[i + 1] ?? "")) continue;
|
|
17119
|
+
for (i++; i < source.length && source[i] !== ">"; i++) {
|
|
17120
|
+
if (source[i] !== "\"" && source[i] !== "'") continue;
|
|
17121
|
+
const quote = source[i];
|
|
17122
|
+
const start = i + 1;
|
|
17123
|
+
for (i++; i < source.length && source[i] !== quote; i++) if (source[i] === "\\") i++;
|
|
17124
|
+
if (i >= source.length) break;
|
|
17125
|
+
const raw = source.slice(start, i);
|
|
17126
|
+
if (!raw.includes("\\")) continue;
|
|
17127
|
+
const token = `${prefix}${attributes.size}`;
|
|
17128
|
+
attributes.set(token, decodeAttribute(raw, quote));
|
|
17129
|
+
output += source.slice(offset, start) + token;
|
|
17130
|
+
offset = i;
|
|
17131
|
+
}
|
|
17132
|
+
}
|
|
17133
|
+
return {
|
|
17134
|
+
source: output + source.slice(offset),
|
|
17135
|
+
attributes
|
|
17136
|
+
};
|
|
17137
|
+
}
|
|
17138
|
+
//#endregion
|
|
17018
17139
|
//#region ../../mpcore/packages/simulator/src/view/wxsDocument.ts
|
|
17019
17140
|
const INLINE_WXS_RE = /<wxs\b([^>]*)(?<!\/)>([\s\S]*?)<\/wxs\s*>/g;
|
|
17020
17141
|
function parseWxsTemplateDocument(templateSource) {
|
|
17021
17142
|
const scripts = [];
|
|
17022
|
-
const
|
|
17143
|
+
const masked = maskTemplateAttributes(templateSource.replace(INLINE_WXS_RE, (_match, attributes, script) => {
|
|
17023
17144
|
return `<wxs data-sim-wxs="${scripts.push(script) - 1}"${attributes}>wxs-source</wxs>`;
|
|
17024
|
-
})
|
|
17145
|
+
}));
|
|
17146
|
+
const document = parseDocument(`<page>${masked.source}</page>`, {
|
|
17025
17147
|
xmlMode: false,
|
|
17026
17148
|
decodeEntities: false,
|
|
17027
17149
|
lowerCaseAttributeNames: false,
|
|
17028
17150
|
lowerCaseTags: false,
|
|
17029
17151
|
recognizeSelfClosing: true
|
|
17030
17152
|
});
|
|
17153
|
+
if (!scripts.length && !masked.attributes.size) return document;
|
|
17154
|
+
const hasAttributes = masked.attributes.size > 0;
|
|
17031
17155
|
const restore = (node) => {
|
|
17156
|
+
if (hasAttributes && node.attribs) {
|
|
17157
|
+
for (const [name, token] of Object.entries(node.attribs)) if (masked.attributes.has(token)) node.attribs[name] = masked.attributes.get(token);
|
|
17158
|
+
}
|
|
17032
17159
|
if (node.name === "wxs" && Object.hasOwn(node.attribs ?? {}, "data-sim-wxs")) {
|
|
17033
17160
|
node.children[0].data = scripts[Number(node.attribs["data-sim-wxs"])];
|
|
17034
17161
|
delete node.attribs["data-sim-wxs"];
|
|
@@ -17040,7 +17167,6 @@ function parseWxsTemplateDocument(templateSource) {
|
|
|
17040
17167
|
}
|
|
17041
17168
|
//#endregion
|
|
17042
17169
|
//#region ../../mpcore/packages/simulator/src/runtime/render/shared.ts
|
|
17043
|
-
const TEMPLATE_INTERPOLATION_RE$1 = /\{\{([^{}]+)\}\}/g;
|
|
17044
17170
|
const LEADING_SLASH_RE$4 = /^\/+/;
|
|
17045
17171
|
const EVENT_BINDING_ATTRS = [
|
|
17046
17172
|
"bindtap",
|
|
@@ -17060,8 +17186,7 @@ const STRUCTURAL_ATTRS = [
|
|
|
17060
17186
|
const CLASS_SPLIT_RE = /\s+/;
|
|
17061
17187
|
const JS_FILE_RE = /\.js$/;
|
|
17062
17188
|
function isMustacheOnly$1(value) {
|
|
17063
|
-
|
|
17064
|
-
return trimmed.startsWith("{{") && trimmed.endsWith("}}") && !trimmed.includes("{{", 2);
|
|
17189
|
+
return isTemplateExpression(value);
|
|
17065
17190
|
}
|
|
17066
17191
|
function collectDataset(node, source) {
|
|
17067
17192
|
if (node.dataset) return collectNodeDataset(node);
|
|
@@ -17086,17 +17211,11 @@ function cloneNode(node) {
|
|
|
17086
17211
|
children: node.children?.map((child) => cloneNode(child))
|
|
17087
17212
|
};
|
|
17088
17213
|
}
|
|
17089
|
-
function resolveValueByPath$1(source, expression) {
|
|
17090
|
-
return resolveTemplateExpression(source, expression) ?? "";
|
|
17091
|
-
}
|
|
17092
17214
|
function resolveRawValueByPath$1(source, expression) {
|
|
17093
17215
|
return resolveTemplateExpression(source, expression);
|
|
17094
17216
|
}
|
|
17095
17217
|
function interpolateTemplate$1(input, data) {
|
|
17096
|
-
return input
|
|
17097
|
-
const value = resolveValueByPath$1(data, expression);
|
|
17098
|
-
return typeof value === "string" ? value : String(value);
|
|
17099
|
-
});
|
|
17218
|
+
return interpolateTemplateText(input, data, true);
|
|
17100
17219
|
}
|
|
17101
17220
|
function readTemplateSource(artifactSource, filePath) {
|
|
17102
17221
|
const source = artifactSource.readText(filePath);
|
|
@@ -17223,7 +17342,7 @@ function resolveUsingComponents(context, ownerJsonPath, ownerFilePath) {
|
|
|
17223
17342
|
return /* @__PURE__ */ new Map();
|
|
17224
17343
|
}
|
|
17225
17344
|
}
|
|
17226
|
-
function resolveComponentGenerics(context, hostNode, ownerJsonPath, ownerFilePath, componentFilePath) {
|
|
17345
|
+
function resolveComponentGenerics(context, hostNode, ownerJsonPath, ownerFilePath, componentFilePath, ownerGenerics) {
|
|
17227
17346
|
const componentJsonPath = path.resolve(context.project.miniprogramRootPath, `${componentFilePath.replace(JS_FILE_RE, "")}.json`);
|
|
17228
17347
|
const componentGenerics = readComponentConfig(context.artifactSource, componentJsonPath).componentGenerics;
|
|
17229
17348
|
if (!componentGenerics || typeof componentGenerics !== "object" || Array.isArray(componentGenerics)) return;
|
|
@@ -17231,7 +17350,7 @@ function resolveComponentGenerics(context, hostNode, ownerJsonPath, ownerFilePat
|
|
|
17231
17350
|
const resolved = /* @__PURE__ */ new Map();
|
|
17232
17351
|
for (const [genericName, definition] of Object.entries(componentGenerics)) {
|
|
17233
17352
|
const selectedAlias = hostNode.attribs?.[`generic:${genericName}`];
|
|
17234
|
-
const selectedPath = selectedAlias ? ownerComponents.get(selectedAlias) : void 0;
|
|
17353
|
+
const selectedPath = selectedAlias ? ownerGenerics?.get(selectedAlias) ?? ownerComponents.get(selectedAlias) : void 0;
|
|
17235
17354
|
if (selectedPath) {
|
|
17236
17355
|
resolved.set(genericName, selectedPath);
|
|
17237
17356
|
continue;
|
|
@@ -17293,8 +17412,9 @@ function syncComponentProperties(instance, definition, nextProperties, bindingEx
|
|
|
17293
17412
|
const previousSnapshot = instance.__propertySnapshots?.[key];
|
|
17294
17413
|
if (hasComponentPropertyValueChanged(instance.properties[key], previousSnapshot, nextValue, bindingAffected)) {
|
|
17295
17414
|
previousProperties[key] = instance.properties[key];
|
|
17296
|
-
|
|
17297
|
-
|
|
17415
|
+
const deliveredValue = cloneValue$1(nextValue);
|
|
17416
|
+
instance.properties[key] = deliveredValue;
|
|
17417
|
+
if (Object.hasOwn(definition.properties ?? {}, key)) instance.data[key] = deliveredValue;
|
|
17298
17418
|
changedRootKeys.push(key);
|
|
17299
17419
|
}
|
|
17300
17420
|
instance.__propertySnapshots ??= {};
|
|
@@ -17492,7 +17612,7 @@ function renderNodeTree(node, scope, context, ownerJsonPath, ownerFilePath, inst
|
|
|
17492
17612
|
const componentScopeId = `${instancePath}/${clonedNode.name}`;
|
|
17493
17613
|
const ownerScopeId = scope.getScopeId().includes("/") ? scope.getScopeId() : void 0;
|
|
17494
17614
|
const { nextProperties, bindingExpressions } = resolveComponentProperties(clonedNode, scope, componentEntry.definition);
|
|
17495
|
-
const genericComponents = resolveComponentGenerics(context, clonedNode, ownerJsonPath, ownerFilePath, componentEntry.filePath);
|
|
17615
|
+
const genericComponents = resolveComponentGenerics(context, clonedNode, ownerJsonPath, ownerFilePath, componentEntry.filePath, scope.genericComponents);
|
|
17496
17616
|
const slots = collectComponentSlots(clonedNode, scope, ownerJsonPath, ownerFilePath, componentScopeId, templateRenderState);
|
|
17497
17617
|
let componentInstance = context.componentCache.get(componentScopeId);
|
|
17498
17618
|
if (!componentInstance) componentInstance = createRuntimeComponentInstance(componentScopeId, context, clonedNode, componentEntry, nextProperties, ownerScopeId, (instance) => {
|
|
@@ -17775,7 +17895,13 @@ var StartupNavigationQueue = class {
|
|
|
17775
17895
|
}
|
|
17776
17896
|
}
|
|
17777
17897
|
run(option, operation) {
|
|
17778
|
-
if (!this.collecting)
|
|
17898
|
+
if (!this.collecting) {
|
|
17899
|
+
const submitted = option && { ...option };
|
|
17900
|
+
this.scheduler.queueMicrotask(() => {
|
|
17901
|
+
this.scheduler.setTimeout(() => operation(submitted), 0);
|
|
17902
|
+
});
|
|
17903
|
+
return;
|
|
17904
|
+
}
|
|
17779
17905
|
const defer = (callback) => callback ? (...args) => {
|
|
17780
17906
|
this.scheduler.setTimeout(() => callback(...args), 0);
|
|
17781
17907
|
} : void 0;
|
|
@@ -18034,6 +18160,14 @@ function createHeadlessWx(driver, runtimeConsole = console) {
|
|
|
18034
18160
|
navigateBack: true,
|
|
18035
18161
|
navigateTo: true,
|
|
18036
18162
|
nextTick: true,
|
|
18163
|
+
offBeforeAppRoute: true,
|
|
18164
|
+
offBeforePageUnload: true,
|
|
18165
|
+
offAppRoute: true,
|
|
18166
|
+
offAppRouteDone: true,
|
|
18167
|
+
onBeforeAppRoute: true,
|
|
18168
|
+
onBeforePageUnload: true,
|
|
18169
|
+
onAppRoute: true,
|
|
18170
|
+
onAppRouteDone: true,
|
|
18037
18171
|
offAppHide: true,
|
|
18038
18172
|
offAppShow: true,
|
|
18039
18173
|
offNetworkStatusChange: true,
|
|
@@ -18081,6 +18215,8 @@ function createHeadlessWx(driver, runtimeConsole = console) {
|
|
|
18081
18215
|
};
|
|
18082
18216
|
return {
|
|
18083
18217
|
...eventBus,
|
|
18218
|
+
[WEVU_ROUTE_EVENT_CONTRACT_KEY]: 1,
|
|
18219
|
+
[WEVU_PAGE_SCROLL_EVENT_CONTRACT_KEY]: 1,
|
|
18084
18220
|
canIUse: (schema) => typeof schema === "string" && schema.trim() !== "" && resolveCapabilityValue(capabilityTree, schema.trim()) != null,
|
|
18085
18221
|
canvasToTempFilePath: (option) => invokeWxApi(() => driver.canvasToTempFilePath(option), option),
|
|
18086
18222
|
chooseImage: (option) => invokeWxApi(() => driver.chooseImage(option ?? {}), option),
|
|
@@ -18208,6 +18344,14 @@ function createHeadlessWx(driver, runtimeConsole = console) {
|
|
|
18208
18344
|
driver.navigateTo(deferred);
|
|
18209
18345
|
}, deferred)),
|
|
18210
18346
|
nextTick: (callback) => driver.nextTick(callback),
|
|
18347
|
+
offBeforeAppRoute: (listener) => driver.offBeforeAppRoute(listener),
|
|
18348
|
+
offBeforePageUnload: (listener) => driver.offBeforePageUnload(listener),
|
|
18349
|
+
offAppRoute: (listener) => driver.offAppRoute(listener),
|
|
18350
|
+
offAppRouteDone: (listener) => driver.offAppRouteDone(listener),
|
|
18351
|
+
onBeforeAppRoute: (listener) => driver.onBeforeAppRoute(listener),
|
|
18352
|
+
onBeforePageUnload: (listener) => driver.onBeforePageUnload(listener),
|
|
18353
|
+
onAppRoute: (listener) => driver.onAppRoute(listener),
|
|
18354
|
+
onAppRouteDone: (listener) => driver.onAppRouteDone(listener),
|
|
18211
18355
|
offAppHide: (callback) => driver.offAppHide(callback),
|
|
18212
18356
|
offAppShow: (callback) => driver.offAppShow(callback),
|
|
18213
18357
|
offNetworkStatusChange: (callback) => driver.offNetworkStatusChange(callback),
|
|
@@ -18367,6 +18511,7 @@ var RuntimeScheduler = class {
|
|
|
18367
18511
|
intervals = /* @__PURE__ */ new Set();
|
|
18368
18512
|
timeouts = /* @__PURE__ */ new Set();
|
|
18369
18513
|
closed = false;
|
|
18514
|
+
canceledHandle;
|
|
18370
18515
|
onError;
|
|
18371
18516
|
constructor(onError) {
|
|
18372
18517
|
this.onError = onError;
|
|
@@ -18388,7 +18533,7 @@ var RuntimeScheduler = class {
|
|
|
18388
18533
|
this.timeouts.clear();
|
|
18389
18534
|
}
|
|
18390
18535
|
queueMicrotask(handler) {
|
|
18391
|
-
if (this.closed)
|
|
18536
|
+
if (this.closed) return;
|
|
18392
18537
|
queueMicrotask(() => {
|
|
18393
18538
|
if (this.closed) return;
|
|
18394
18539
|
try {
|
|
@@ -18399,7 +18544,7 @@ var RuntimeScheduler = class {
|
|
|
18399
18544
|
});
|
|
18400
18545
|
}
|
|
18401
18546
|
setInterval(handler, timeout, ...args) {
|
|
18402
|
-
if (this.closed)
|
|
18547
|
+
if (this.closed) return this.getCanceledHandle();
|
|
18403
18548
|
const handle = setInterval(() => {
|
|
18404
18549
|
try {
|
|
18405
18550
|
handler(...args);
|
|
@@ -18411,7 +18556,7 @@ var RuntimeScheduler = class {
|
|
|
18411
18556
|
return handle;
|
|
18412
18557
|
}
|
|
18413
18558
|
setTimeout(handler, timeout, ...args) {
|
|
18414
|
-
if (this.closed)
|
|
18559
|
+
if (this.closed) return this.getCanceledHandle();
|
|
18415
18560
|
const handle = setTimeout(() => {
|
|
18416
18561
|
this.timeouts.delete(handle);
|
|
18417
18562
|
try {
|
|
@@ -18423,6 +18568,14 @@ var RuntimeScheduler = class {
|
|
|
18423
18568
|
this.timeouts.add(handle);
|
|
18424
18569
|
return handle;
|
|
18425
18570
|
}
|
|
18571
|
+
/** 已销毁 realm 的 Promise 回调不能复活任务,也不能向外泄漏异步拒绝。 */
|
|
18572
|
+
getCanceledHandle() {
|
|
18573
|
+
if (!this.canceledHandle) {
|
|
18574
|
+
this.canceledHandle = setTimeout(() => {}, 0);
|
|
18575
|
+
clearTimeout(this.canceledHandle);
|
|
18576
|
+
}
|
|
18577
|
+
return this.canceledHandle;
|
|
18578
|
+
}
|
|
18426
18579
|
};
|
|
18427
18580
|
//#endregion
|
|
18428
18581
|
//#region ../../mpcore/packages/simulator/src/kernel/runtimeKernel.ts
|
|
@@ -37922,27 +38075,6 @@ function queryXPathElements(root, expression) {
|
|
|
37922
38075
|
function escapeText$1(text) {
|
|
37923
38076
|
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
37924
38077
|
}
|
|
37925
|
-
function createEventPayload$1(node, eventName, event) {
|
|
37926
|
-
const dataset = collectNodeDataset(node);
|
|
37927
|
-
const nodeId = node.attribs?.id ?? "";
|
|
37928
|
-
return {
|
|
37929
|
-
bubbles: false,
|
|
37930
|
-
capturePhase: false,
|
|
37931
|
-
composed: false,
|
|
37932
|
-
currentTarget: {
|
|
37933
|
-
dataset: event.currentTarget?.dataset ?? event.dataset ?? dataset,
|
|
37934
|
-
id: event.currentTarget?.id ?? event.id ?? nodeId
|
|
37935
|
-
},
|
|
37936
|
-
detail: event.detail,
|
|
37937
|
-
mark: event.mark,
|
|
37938
|
-
target: {
|
|
37939
|
-
dataset: event.target?.dataset ?? event.dataset ?? dataset,
|
|
37940
|
-
id: event.target?.id ?? event.id ?? nodeId
|
|
37941
|
-
},
|
|
37942
|
-
timeStamp: Date.now(),
|
|
37943
|
-
type: eventName
|
|
37944
|
-
};
|
|
37945
|
-
}
|
|
37946
38078
|
function createValueEventDetail(value, detail) {
|
|
37947
38079
|
return {
|
|
37948
38080
|
value,
|
|
@@ -38052,9 +38184,18 @@ var HeadlessTestingNodeHandle = class HeadlessTestingNodeHandle {
|
|
|
38052
38184
|
if (!this.interactions) throw new Error("Node interactions are not available for this headless testing node.");
|
|
38053
38185
|
const normalizedEventName = eventName.trim();
|
|
38054
38186
|
if (!normalizedEventName) throw new Error("Event name must be a non-empty string in headless testing runtime.");
|
|
38187
|
+
let handlerResults;
|
|
38188
|
+
const nativeHandled = this.interactions.dispatchNativeEvent?.(this.node, normalizedEventName, event, normalizedEventName === "tap" ? (result) => {
|
|
38189
|
+
handlerResults ??= [];
|
|
38190
|
+
handlerResults.push(result);
|
|
38191
|
+
} : void 0);
|
|
38192
|
+
if (normalizedEventName === "tap" && nativeHandled) return handlerResults ? (await Promise.all(handlerResults))[0] : void 0;
|
|
38055
38193
|
const binding = resolveMiniProgramEventBinding(this.node.attribs, normalizedEventName);
|
|
38056
|
-
if (!binding?.method)
|
|
38057
|
-
|
|
38194
|
+
if (!binding?.method) {
|
|
38195
|
+
if (nativeHandled) return;
|
|
38196
|
+
throw new Error(`No ${normalizedEventName} binding was found on <${this.node.name ?? "unknown"}> in headless testing runtime.`);
|
|
38197
|
+
}
|
|
38198
|
+
return await this.interactions.callMethod(this.node.attribs?.["data-sim-scope"] ?? null, binding.method, createMiniProgramEventPayload(this.node, normalizedEventName, event));
|
|
38058
38199
|
}
|
|
38059
38200
|
async tap(event = {}) {
|
|
38060
38201
|
return await this.trigger("tap", event);
|
|
@@ -38161,35 +38302,9 @@ function renderPageTree(project, page) {
|
|
|
38161
38302
|
};
|
|
38162
38303
|
}
|
|
38163
38304
|
//#endregion
|
|
38164
|
-
//#region ../../mpcore/packages/simulator/src/view/
|
|
38165
|
-
const LEADING_MARK_PREFIX_RE = /^mark[:\-]?/;
|
|
38166
|
-
const MARK_NAME_RE = /[:\-]([a-z])/g;
|
|
38305
|
+
//#region ../../mpcore/packages/simulator/src/view/nodeGeometry.ts
|
|
38167
38306
|
const NUMERIC_LIKE_VALUE_RE$1 = /-?\d+(?:\.\d+)?/;
|
|
38168
|
-
|
|
38169
|
-
return attributeName.replace(LEADING_MARK_PREFIX_RE, "").replace(MARK_NAME_RE, (_match, char) => char.toUpperCase());
|
|
38170
|
-
}
|
|
38171
|
-
function collectMark(node) {
|
|
38172
|
-
const mark = {};
|
|
38173
|
-
for (const [key, value] of Object.entries(node.attribs ?? {})) {
|
|
38174
|
-
if (!(key.startsWith("mark:") || key.startsWith("mark-"))) continue;
|
|
38175
|
-
mark[toMarkKey(key)] = value;
|
|
38176
|
-
}
|
|
38177
|
-
return mark;
|
|
38178
|
-
}
|
|
38179
|
-
function createScopedRoot(node) {
|
|
38180
|
-
return {
|
|
38181
|
-
children: [node],
|
|
38182
|
-
type: "root"
|
|
38183
|
-
};
|
|
38184
|
-
}
|
|
38185
|
-
function findNodeByScopeId(root, scopeId) {
|
|
38186
|
-
if (root.attribs?.["data-sim-scope"] === scopeId) return root;
|
|
38187
|
-
for (const child of root.children ?? []) {
|
|
38188
|
-
const match = findNodeByScopeId(child, scopeId);
|
|
38189
|
-
if (match) return match;
|
|
38190
|
-
}
|
|
38191
|
-
return null;
|
|
38192
|
-
}
|
|
38307
|
+
const PIXEL_LENGTH_RE = /^\d+(?:\.\d+)?px$|^0$/;
|
|
38193
38308
|
function parseStyleDeclarations$1(styleValue) {
|
|
38194
38309
|
const declarations = {};
|
|
38195
38310
|
if (!styleValue) return declarations;
|
|
@@ -38224,18 +38339,229 @@ function resolveRect$1(node, windowInfo) {
|
|
|
38224
38339
|
width
|
|
38225
38340
|
};
|
|
38226
38341
|
}
|
|
38342
|
+
/** 仅支持显式 px 视口和单个 view 内容盒;未知样式布局不作为已测量滚动范围。 */
|
|
38343
|
+
function resolveNativeScrollBounds(node) {
|
|
38344
|
+
const viewport = parseStyleDeclarations$1(node.attribs?.style);
|
|
38345
|
+
let content;
|
|
38346
|
+
const visit = (parent) => {
|
|
38347
|
+
for (const child of parent.children ?? []) {
|
|
38348
|
+
if (child.type === "comment" || child.type === "text" && !child.data?.trim()) continue;
|
|
38349
|
+
if (child.name === "block") {
|
|
38350
|
+
if (!visit(child)) return false;
|
|
38351
|
+
} else {
|
|
38352
|
+
if (content || child.name !== "view") return false;
|
|
38353
|
+
content = child;
|
|
38354
|
+
}
|
|
38355
|
+
}
|
|
38356
|
+
return true;
|
|
38357
|
+
};
|
|
38358
|
+
if (!visit(node)) return {
|
|
38359
|
+
maxTop: 0,
|
|
38360
|
+
maxLeft: 0
|
|
38361
|
+
};
|
|
38362
|
+
const box = parseStyleDeclarations$1(content?.attribs?.style);
|
|
38363
|
+
const supported = content && !viewport.display && !box.display && !box.position && !box.transform;
|
|
38364
|
+
const extent = (dimension) => {
|
|
38365
|
+
const viewportValue = viewport[dimension];
|
|
38366
|
+
const contentValue = box[dimension];
|
|
38367
|
+
if (!supported || !viewportValue || !contentValue || !PIXEL_LENGTH_RE.test(viewportValue) || !PIXEL_LENGTH_RE.test(contentValue)) return 0;
|
|
38368
|
+
const size = parseNumericLikeValue$1(viewportValue);
|
|
38369
|
+
const contentSize = parseNumericLikeValue$1(contentValue);
|
|
38370
|
+
return size > 0 && Number.isFinite(size) && Number.isFinite(contentSize) ? Math.max(0, contentSize - size) : 0;
|
|
38371
|
+
};
|
|
38372
|
+
return {
|
|
38373
|
+
maxTop: extent("height"),
|
|
38374
|
+
maxLeft: extent("width")
|
|
38375
|
+
};
|
|
38376
|
+
}
|
|
38377
|
+
//#endregion
|
|
38378
|
+
//#region ../../mpcore/packages/simulator/src/view/nativeNode.ts
|
|
38379
|
+
const pageScrollViews = /* @__PURE__ */ new WeakMap();
|
|
38380
|
+
function coordinate(value) {
|
|
38381
|
+
const number = Number(value);
|
|
38382
|
+
return Number.isFinite(number) ? Math.max(0, number) : 0;
|
|
38383
|
+
}
|
|
38384
|
+
function nodeKey(node) {
|
|
38385
|
+
return node.attribs?.["data-sim-node"] ?? "";
|
|
38386
|
+
}
|
|
38387
|
+
/** 原生滚动位置独立于绑定值;未改变的绑定不能覆盖用户滚动。 */
|
|
38388
|
+
function syncNativeScrollViews(page, root, emit) {
|
|
38389
|
+
let states = pageScrollViews.get(page);
|
|
38390
|
+
let present;
|
|
38391
|
+
const visit = (node) => {
|
|
38392
|
+
if (node.name === "scroll-view") {
|
|
38393
|
+
const key = nodeKey(node);
|
|
38394
|
+
present ??= /* @__PURE__ */ new Set();
|
|
38395
|
+
present.add(key);
|
|
38396
|
+
if (!states) {
|
|
38397
|
+
states = /* @__PURE__ */ new Map();
|
|
38398
|
+
pageScrollViews.set(page, states);
|
|
38399
|
+
}
|
|
38400
|
+
const boundTop = coordinate(node.attribs?.["scroll-top"]);
|
|
38401
|
+
const boundLeft = coordinate(node.attribs?.["scroll-left"]);
|
|
38402
|
+
const bounds = resolveNativeScrollBounds(node);
|
|
38403
|
+
const scrollY = node.attribs?.["scroll-y"];
|
|
38404
|
+
const scrollX = node.attribs?.["scroll-x"];
|
|
38405
|
+
const maxTop = scrollY !== void 0 && scrollY !== "false" && scrollY !== "0" ? bounds.maxTop : 0;
|
|
38406
|
+
const maxLeft = scrollX !== void 0 && scrollX !== "false" && scrollX !== "0" ? bounds.maxLeft : 0;
|
|
38407
|
+
let state = states.get(key);
|
|
38408
|
+
const previousTop = state?.scrollTop ?? 0;
|
|
38409
|
+
const previousLeft = state?.scrollLeft ?? 0;
|
|
38410
|
+
if (!state) {
|
|
38411
|
+
state = {
|
|
38412
|
+
boundTop,
|
|
38413
|
+
boundLeft,
|
|
38414
|
+
scrollTop: boundTop,
|
|
38415
|
+
scrollLeft: boundLeft,
|
|
38416
|
+
maxTop,
|
|
38417
|
+
maxLeft
|
|
38418
|
+
};
|
|
38419
|
+
states.set(key, state);
|
|
38420
|
+
} else {
|
|
38421
|
+
if (state.boundTop !== boundTop) {
|
|
38422
|
+
state.scrollTop = boundTop;
|
|
38423
|
+
state.boundTop = boundTop;
|
|
38424
|
+
}
|
|
38425
|
+
if (state.boundLeft !== boundLeft) {
|
|
38426
|
+
state.scrollLeft = boundLeft;
|
|
38427
|
+
state.boundLeft = boundLeft;
|
|
38428
|
+
}
|
|
38429
|
+
}
|
|
38430
|
+
state.maxTop = maxTop;
|
|
38431
|
+
state.maxLeft = maxLeft;
|
|
38432
|
+
state.scrollTop = Math.min(state.scrollTop, maxTop);
|
|
38433
|
+
state.scrollLeft = Math.min(state.scrollLeft, maxLeft);
|
|
38434
|
+
if (state.scrollTop !== previousTop || state.scrollLeft !== previousLeft) {
|
|
38435
|
+
const binding = resolveMiniProgramEventBinding(node.attribs, "scroll");
|
|
38436
|
+
if (binding?.method) {
|
|
38437
|
+
const target = {
|
|
38438
|
+
id: node.attribs?.id ?? "",
|
|
38439
|
+
dataset: collectNodeDataset(node)
|
|
38440
|
+
};
|
|
38441
|
+
emit(node.attribs?.["data-sim-scope"] ?? null, binding.method, {
|
|
38442
|
+
type: "scroll",
|
|
38443
|
+
timeStamp: Date.now(),
|
|
38444
|
+
detail: {
|
|
38445
|
+
scrollTop: state.scrollTop,
|
|
38446
|
+
scrollLeft: state.scrollLeft
|
|
38447
|
+
},
|
|
38448
|
+
target,
|
|
38449
|
+
currentTarget: target
|
|
38450
|
+
});
|
|
38451
|
+
}
|
|
38452
|
+
}
|
|
38453
|
+
}
|
|
38454
|
+
for (const child of node.children ?? []) visit(child);
|
|
38455
|
+
};
|
|
38456
|
+
visit(root);
|
|
38457
|
+
if (states) {
|
|
38458
|
+
for (const key of states.keys()) if (!present?.has(key)) states.delete(key);
|
|
38459
|
+
}
|
|
38460
|
+
}
|
|
38461
|
+
function getNativeScrollOffset(page, node) {
|
|
38462
|
+
const state = pageScrollViews.get(page)?.get(nodeKey(node));
|
|
38463
|
+
return {
|
|
38464
|
+
scrollTop: state?.scrollTop ?? 0,
|
|
38465
|
+
scrollLeft: state?.scrollLeft ?? 0
|
|
38466
|
+
};
|
|
38467
|
+
}
|
|
38468
|
+
/** 原生交互更新宿主状态,导航仍交给统一 wx 路由入口。 */
|
|
38469
|
+
function dispatchNativeNodeEvent(page, node, eventName, event, wx, resolveMethod, onHandlerResult) {
|
|
38470
|
+
if (eventName === "scroll" && node.name === "scroll-view") {
|
|
38471
|
+
const detail = event.detail;
|
|
38472
|
+
const state = pageScrollViews.get(page)?.get(nodeKey(node));
|
|
38473
|
+
if (state) {
|
|
38474
|
+
if (detail?.scrollTop !== void 0) state.scrollTop = Math.min(coordinate(detail.scrollTop), state.maxTop);
|
|
38475
|
+
if (detail?.scrollLeft !== void 0) state.scrollLeft = Math.min(coordinate(detail.scrollLeft), state.maxLeft);
|
|
38476
|
+
}
|
|
38477
|
+
event.detail = Object.assign(event.detail && typeof event.detail === "object" ? event.detail : {}, getNativeScrollOffset(page, node));
|
|
38478
|
+
return true;
|
|
38479
|
+
}
|
|
38480
|
+
if (eventName !== "tap") return false;
|
|
38481
|
+
let current = node;
|
|
38482
|
+
let navigator;
|
|
38483
|
+
let handlers;
|
|
38484
|
+
let handled = false;
|
|
38485
|
+
while (current) {
|
|
38486
|
+
if (!navigator && current.name === "navigator") navigator = current;
|
|
38487
|
+
const binding = resolveMiniProgramEventBinding(current.attribs, "tap");
|
|
38488
|
+
if (binding?.method) {
|
|
38489
|
+
const call = resolveMethod(current.attribs?.["data-sim-scope"] ?? null, binding.method);
|
|
38490
|
+
handlers ??= [];
|
|
38491
|
+
handlers.push({
|
|
38492
|
+
node: current,
|
|
38493
|
+
call
|
|
38494
|
+
});
|
|
38495
|
+
handled = true;
|
|
38496
|
+
}
|
|
38497
|
+
if (binding?.stopAfter) {
|
|
38498
|
+
handled = true;
|
|
38499
|
+
break;
|
|
38500
|
+
}
|
|
38501
|
+
current = current.parent;
|
|
38502
|
+
}
|
|
38503
|
+
if (handlers) for (const handler of handlers) {
|
|
38504
|
+
const result = handler.call(createMiniProgramEventPayload(handler.node, "tap", event, node));
|
|
38505
|
+
onHandlerResult?.(result);
|
|
38506
|
+
}
|
|
38507
|
+
if (!navigator) return handled;
|
|
38508
|
+
const openType = navigator.attribs?.["open-type"] ?? "navigate";
|
|
38509
|
+
const url = navigator.attribs?.url ?? "";
|
|
38510
|
+
switch (openType) {
|
|
38511
|
+
case "navigate":
|
|
38512
|
+
wx.navigateTo({ url });
|
|
38513
|
+
break;
|
|
38514
|
+
case "redirect":
|
|
38515
|
+
wx.redirectTo({ url });
|
|
38516
|
+
break;
|
|
38517
|
+
case "switchTab":
|
|
38518
|
+
wx.switchTab({ url });
|
|
38519
|
+
break;
|
|
38520
|
+
case "reLaunch":
|
|
38521
|
+
wx.reLaunch({ url });
|
|
38522
|
+
break;
|
|
38523
|
+
case "navigateBack":
|
|
38524
|
+
wx.navigateBack({ delta: Number(navigator.attribs?.delta ?? 1) });
|
|
38525
|
+
break;
|
|
38526
|
+
default: return handled;
|
|
38527
|
+
}
|
|
38528
|
+
return true;
|
|
38529
|
+
}
|
|
38530
|
+
//#endregion
|
|
38531
|
+
//#region ../../mpcore/packages/simulator/src/view/selectorQuery.ts
|
|
38532
|
+
const LEADING_MARK_PREFIX_RE = /^mark[:\-]?/;
|
|
38533
|
+
const MARK_NAME_RE = /[:\-]([a-z])/g;
|
|
38534
|
+
function toMarkKey(attributeName) {
|
|
38535
|
+
return attributeName.replace(LEADING_MARK_PREFIX_RE, "").replace(MARK_NAME_RE, (_match, char) => char.toUpperCase());
|
|
38536
|
+
}
|
|
38537
|
+
function collectMark(node) {
|
|
38538
|
+
const mark = {};
|
|
38539
|
+
for (const [key, value] of Object.entries(node.attribs ?? {})) {
|
|
38540
|
+
if (!(key.startsWith("mark:") || key.startsWith("mark-"))) continue;
|
|
38541
|
+
mark[toMarkKey(key)] = value;
|
|
38542
|
+
}
|
|
38543
|
+
return mark;
|
|
38544
|
+
}
|
|
38545
|
+
function createScopedRoot(node) {
|
|
38546
|
+
return {
|
|
38547
|
+
children: [node],
|
|
38548
|
+
type: "root"
|
|
38549
|
+
};
|
|
38550
|
+
}
|
|
38551
|
+
function findNodeByScopeId(root, scopeId) {
|
|
38552
|
+
if (root.attribs?.["data-sim-scope"] === scopeId) return root;
|
|
38553
|
+
for (const child of root.children ?? []) {
|
|
38554
|
+
const match = findNodeByScopeId(child, scopeId);
|
|
38555
|
+
if (match) return match;
|
|
38556
|
+
}
|
|
38557
|
+
return null;
|
|
38558
|
+
}
|
|
38227
38559
|
function resolveSelectorScrollTop(root, selector) {
|
|
38228
38560
|
const normalizedSelector = selector?.trim();
|
|
38229
38561
|
if (!normalizedSelector) return null;
|
|
38230
38562
|
const match = querySelectorAll(root, normalizedSelector)[0];
|
|
38231
38563
|
return match ? resolveRect$1(match).top : null;
|
|
38232
38564
|
}
|
|
38233
|
-
function resolveScrollOffset(node) {
|
|
38234
|
-
return {
|
|
38235
|
-
scrollLeft: parseNumericLikeValue$1(node.attribs?.["data-sim-scroll-left"]),
|
|
38236
|
-
scrollTop: parseNumericLikeValue$1(node.attribs?.["data-sim-scroll-top"])
|
|
38237
|
-
};
|
|
38238
|
-
}
|
|
38239
38565
|
function resolvePropertyValue(node, propertyName) {
|
|
38240
38566
|
const normalizedPropertyName = propertyName.trim();
|
|
38241
38567
|
if (!normalizedPropertyName) return;
|
|
@@ -38268,7 +38594,7 @@ function resolveFieldsResult(node, fields, options) {
|
|
|
38268
38594
|
result.height = rect.height;
|
|
38269
38595
|
}
|
|
38270
38596
|
}
|
|
38271
|
-
if (fields.scrollOffset) Object.assign(result,
|
|
38597
|
+
if (fields.scrollOffset) Object.assign(result, getNativeScrollOffset(options.page, node));
|
|
38272
38598
|
if (Array.isArray(fields.properties) && fields.properties.length > 0) Object.assign(result, pickProperties(node, fields.properties));
|
|
38273
38599
|
if (Array.isArray(fields.computedStyle) && fields.computedStyle.length > 0) Object.assign(result, pickComputedStyle(node, fields.computedStyle));
|
|
38274
38600
|
if (fields.context) result.context = options.resolveContext?.(node) ?? { type: "unsupported-context" };
|
|
@@ -39359,6 +39685,99 @@ function createModuleLoader(registries, getCurrentPages, getApp, wxDriver, optio
|
|
|
39359
39685
|
return loader;
|
|
39360
39686
|
}
|
|
39361
39687
|
//#endregion
|
|
39688
|
+
//#region ../../mpcore/packages/simulator/src/runtime/packageAsset.ts
|
|
39689
|
+
const assetExtension = /\.(?:png|jpe?g|gif|svg|cer|mp3|aac|m4a|mp4|wav|ogg|silk|wasm|br|cur|ico|skel|crt|cert|gltf|glb|bin|ktx|hdr|exr|xnet|pem)$/i;
|
|
39690
|
+
/** 包内静态资源只读且每次访问宿主当前产物,不缓存为用户文件,也不允许越过包根目录。 */
|
|
39691
|
+
function createPackageAssetReader(read) {
|
|
39692
|
+
return (filePath) => {
|
|
39693
|
+
if (filePath.includes("\\") || filePath.includes(":") || filePath.includes("\0")) return;
|
|
39694
|
+
const relative = posix.normalize(filePath.replace(/^\/+/, ""));
|
|
39695
|
+
if (relative === ".." || relative.startsWith("../") || !assetExtension.test(relative)) return;
|
|
39696
|
+
return read(relative);
|
|
39697
|
+
};
|
|
39698
|
+
}
|
|
39699
|
+
//#endregion
|
|
39700
|
+
//#region ../../mpcore/packages/simulator/src/runtime/routeEvents.ts
|
|
39701
|
+
/** 两种模拟器共用路由身份与监听状态,完成事件由页面就绪和宿主提交驱动。 */
|
|
39702
|
+
var HeadlessRouteEvents = class {
|
|
39703
|
+
requestRender;
|
|
39704
|
+
isCurrentPage;
|
|
39705
|
+
bus = createHeadlessUniEventBus();
|
|
39706
|
+
pages = /* @__PURE__ */ new WeakMap();
|
|
39707
|
+
nextRouteId = 0;
|
|
39708
|
+
nextWebviewId = 0;
|
|
39709
|
+
activeEvent;
|
|
39710
|
+
constructor(requestRender, isCurrentPage) {
|
|
39711
|
+
this.requestRender = requestRender;
|
|
39712
|
+
this.isCurrentPage = isCurrentPage;
|
|
39713
|
+
}
|
|
39714
|
+
on(name, listener) {
|
|
39715
|
+
this.bus.$on(name, listener);
|
|
39716
|
+
}
|
|
39717
|
+
off(name, listener) {
|
|
39718
|
+
this.bus.$off(name, listener);
|
|
39719
|
+
}
|
|
39720
|
+
begin(path, query, openType, renderer, page) {
|
|
39721
|
+
const previous = page ? this.pages.get(page) : void 0;
|
|
39722
|
+
const event = {
|
|
39723
|
+
path,
|
|
39724
|
+
query: { ...query },
|
|
39725
|
+
openType: this.nextRouteId === 0 ? "appLaunch" : openType,
|
|
39726
|
+
renderer: previous?.event.renderer ?? (renderer === "skyline" || renderer === "xr-frame" ? renderer : "webview"),
|
|
39727
|
+
routeEventId: String(++this.nextRouteId),
|
|
39728
|
+
webviewId: previous?.webviewId ?? ++this.nextWebviewId,
|
|
39729
|
+
timeStamp: Date.now()
|
|
39730
|
+
};
|
|
39731
|
+
this.activeEvent = event;
|
|
39732
|
+
this.bus.$emit("beforeAppRoute", event);
|
|
39733
|
+
return event;
|
|
39734
|
+
}
|
|
39735
|
+
attach(page, event) {
|
|
39736
|
+
const state = this.pages.get(page);
|
|
39737
|
+
this.pages.set(page, {
|
|
39738
|
+
webviewId: event.webviewId,
|
|
39739
|
+
ready: state?.ready ?? false,
|
|
39740
|
+
event,
|
|
39741
|
+
committed: false
|
|
39742
|
+
});
|
|
39743
|
+
}
|
|
39744
|
+
commit(page, event) {
|
|
39745
|
+
const state = this.pages.get(page);
|
|
39746
|
+
if (!state || state.event !== event || this.activeEvent !== event || !this.isCurrentPage(page)) return;
|
|
39747
|
+
state.committed = true;
|
|
39748
|
+
this.bus.$emit("appRoute", event);
|
|
39749
|
+
if (state.ready) this.complete(page, state);
|
|
39750
|
+
}
|
|
39751
|
+
ready(page) {
|
|
39752
|
+
const state = this.pages.get(page);
|
|
39753
|
+
if (!state) return;
|
|
39754
|
+
state.ready = true;
|
|
39755
|
+
if (state.committed) this.complete(page, state);
|
|
39756
|
+
}
|
|
39757
|
+
complete(page, state) {
|
|
39758
|
+
this.requestRender(() => {
|
|
39759
|
+
if (this.pages.get(page) !== state || this.activeEvent !== state.event || !this.isCurrentPage(page)) return;
|
|
39760
|
+
this.bus.$emit("appRouteDone", state.event);
|
|
39761
|
+
});
|
|
39762
|
+
}
|
|
39763
|
+
beforeUnload(page, event) {
|
|
39764
|
+
const state = this.pages.get(page);
|
|
39765
|
+
if (event && state) this.bus.$emit("beforePageUnload", {
|
|
39766
|
+
...event,
|
|
39767
|
+
path: page.route,
|
|
39768
|
+
query: { ...page.options },
|
|
39769
|
+
renderer: state.event.renderer,
|
|
39770
|
+
webviewId: state.webviewId,
|
|
39771
|
+
page
|
|
39772
|
+
});
|
|
39773
|
+
this.pages.delete(page);
|
|
39774
|
+
}
|
|
39775
|
+
close() {
|
|
39776
|
+
this.activeEvent = void 0;
|
|
39777
|
+
this.bus.$off();
|
|
39778
|
+
}
|
|
39779
|
+
};
|
|
39780
|
+
//#endregion
|
|
39362
39781
|
//#region ../../mpcore/packages/simulator/src/runtime/systemInfo.ts
|
|
39363
39782
|
function createDefaultLocationResult() {
|
|
39364
39783
|
return {
|
|
@@ -40153,7 +40572,7 @@ function createHeadlessWxState(scheduler = nativeScheduler, options = {}) {
|
|
|
40153
40572
|
const readFile = (filePath, encoding) => {
|
|
40154
40573
|
normalizeEncoding(encoding);
|
|
40155
40574
|
const normalizedPath = normalizeFsPath(filePath);
|
|
40156
|
-
const fileContent = files.get(normalizedPath);
|
|
40575
|
+
const fileContent = files.get(normalizedPath) ?? options.readPackageFile?.(normalizedPath);
|
|
40157
40576
|
if (fileContent == null) throw new Error(`readFile:fail no such file or directory, open '${normalizedPath}'`);
|
|
40158
40577
|
return {
|
|
40159
40578
|
data: fileContent,
|
|
@@ -41201,6 +41620,7 @@ var HeadlessSession = class {
|
|
|
41201
41620
|
appInstance = null;
|
|
41202
41621
|
appLifecycle = new HeadlessAppLifecycle();
|
|
41203
41622
|
moduleLoader;
|
|
41623
|
+
routeEvents = new HeadlessRouteEvents((callback) => this.requestRender(callback), (page) => this.currentPageInstance === page && !this.kernel.isClosed);
|
|
41204
41624
|
eventListeners = /* @__PURE__ */ new Map();
|
|
41205
41625
|
registries;
|
|
41206
41626
|
currentPageInstance = null;
|
|
@@ -41228,7 +41648,10 @@ var HeadlessSession = class {
|
|
|
41228
41648
|
constructor(options) {
|
|
41229
41649
|
if (!options.project && !options.projectPath) throw new Error("Headless runtime requires projectPath or project.");
|
|
41230
41650
|
this.project = options.project ?? loadProject(options.projectPath);
|
|
41231
|
-
this.wxState = createHeadlessWxState(this.kernel.scheduler, {
|
|
41651
|
+
this.wxState = createHeadlessWxState(this.kernel.scheduler, {
|
|
41652
|
+
strictMocks: options.strictHostMocks,
|
|
41653
|
+
readPackageFile: createPackageAssetReader((relative) => this.project.artifactSource.readText(path.join(this.project.miniprogramRootPath, relative)))
|
|
41654
|
+
});
|
|
41232
41655
|
this.registries = createHostRegistries();
|
|
41233
41656
|
const rawTabBarList = Array.isArray(this.project.appConfig.tabBar?.list) ? this.project.appConfig.tabBar.list : [];
|
|
41234
41657
|
this.tabBarRoutes = /* @__PURE__ */ new Set();
|
|
@@ -41295,6 +41718,14 @@ var HeadlessSession = class {
|
|
|
41295
41718
|
saveImageToPhotosAlbum: (option) => this.wxState.saveImageToPhotosAlbum(option),
|
|
41296
41719
|
saveVideoToPhotosAlbum: (option) => this.wxState.saveVideoToPhotosAlbum(option),
|
|
41297
41720
|
nextTick: (callback) => this.kernel.scheduler.queueMicrotask(() => callback?.()),
|
|
41721
|
+
offBeforeAppRoute: (listener) => this.routeEvents.off("beforeAppRoute", listener),
|
|
41722
|
+
offBeforePageUnload: (listener) => this.routeEvents.off("beforePageUnload", listener),
|
|
41723
|
+
offAppRoute: (listener) => this.routeEvents.off("appRoute", listener),
|
|
41724
|
+
offAppRouteDone: (listener) => this.routeEvents.off("appRouteDone", listener),
|
|
41725
|
+
onBeforeAppRoute: (listener) => this.routeEvents.on("beforeAppRoute", listener),
|
|
41726
|
+
onBeforePageUnload: (listener) => this.routeEvents.on("beforePageUnload", listener),
|
|
41727
|
+
onAppRoute: (listener) => this.routeEvents.on("appRoute", listener),
|
|
41728
|
+
onAppRouteDone: (listener) => this.routeEvents.on("appRouteDone", listener),
|
|
41298
41729
|
offAppHide: (callback) => this.appLifecycle.offAppHide(callback),
|
|
41299
41730
|
offAppShow: (callback) => this.appLifecycle.offAppShow(callback),
|
|
41300
41731
|
offNetworkStatusChange: (callback) => this.wxState.offNetworkStatusChange(callback),
|
|
@@ -41347,15 +41778,28 @@ var HeadlessSession = class {
|
|
|
41347
41778
|
}
|
|
41348
41779
|
close() {
|
|
41349
41780
|
if (this.kernel.isClosed) return;
|
|
41350
|
-
|
|
41781
|
+
let failure;
|
|
41782
|
+
try {
|
|
41783
|
+
this.unloadAllPages(void 0, true);
|
|
41784
|
+
} catch (error) {
|
|
41785
|
+
failure = { error };
|
|
41786
|
+
}
|
|
41351
41787
|
this.canvasContexts.clear();
|
|
41352
41788
|
this.renderRequestCallbacks.length = 0;
|
|
41353
41789
|
this.renderRequestPending = false;
|
|
41354
41790
|
this.appLifecycle.close();
|
|
41791
|
+
this.routeEvents.close();
|
|
41355
41792
|
this.wxState.close();
|
|
41356
41793
|
this.moduleLoader.close();
|
|
41357
|
-
this.eventListeners.clear();
|
|
41358
41794
|
this.kernel.close();
|
|
41795
|
+
try {
|
|
41796
|
+
this.emit("close");
|
|
41797
|
+
} catch (error) {
|
|
41798
|
+
failure ??= { error };
|
|
41799
|
+
} finally {
|
|
41800
|
+
this.eventListeners.clear();
|
|
41801
|
+
}
|
|
41802
|
+
if (failure) throw failure.error;
|
|
41359
41803
|
}
|
|
41360
41804
|
getDiagnostics() {
|
|
41361
41805
|
return this.kernel.diagnostics.getEntries();
|
|
@@ -41372,7 +41816,14 @@ var HeadlessSession = class {
|
|
|
41372
41816
|
if (listeners?.size === 0) this.eventListeners.delete(eventName);
|
|
41373
41817
|
}
|
|
41374
41818
|
emit(eventName, ...args) {
|
|
41375
|
-
|
|
41819
|
+
let failure;
|
|
41820
|
+
for (const handler of [...this.eventListeners.get(eventName) ?? []]) try {
|
|
41821
|
+
handler(...args);
|
|
41822
|
+
} catch (error) {
|
|
41823
|
+
if (eventName !== "close") throw error;
|
|
41824
|
+
failure ??= { error };
|
|
41825
|
+
}
|
|
41826
|
+
if (failure) throw failure.error;
|
|
41376
41827
|
}
|
|
41377
41828
|
get isClosed() {
|
|
41378
41829
|
return this.kernel.isClosed;
|
|
@@ -41454,6 +41905,11 @@ var HeadlessSession = class {
|
|
|
41454
41905
|
selectOwnerComponent: (scopeId) => this.selectOwnerComponent(scopeId)
|
|
41455
41906
|
}
|
|
41456
41907
|
}, current);
|
|
41908
|
+
syncNativeScrollViews(current, rendered.root, (scopeId, method, event) => {
|
|
41909
|
+
this.kernel.scheduler.queueMicrotask(() => {
|
|
41910
|
+
if (this.currentPageInstance === current) this.callScopeMethod(scopeId, method, event);
|
|
41911
|
+
});
|
|
41912
|
+
});
|
|
41457
41913
|
const componentScopePrefix = `page:${stripLeadingSlash(current.route)}`;
|
|
41458
41914
|
for (const [scopeId, instance] of this.componentCache.entries()) if (scopeId.startsWith(componentScopePrefix)) this.selectorQueryScopeSnapshots.set(instance, {
|
|
41459
41915
|
page: current,
|
|
@@ -41462,6 +41918,11 @@ var HeadlessSession = class {
|
|
|
41462
41918
|
});
|
|
41463
41919
|
return rendered;
|
|
41464
41920
|
}
|
|
41921
|
+
dispatchNativeNodeEvent(node, eventName, event, onHandlerResult) {
|
|
41922
|
+
const current = this.requireCurrentPage("dispatchNativeNodeEvent()");
|
|
41923
|
+
if (eventName === "tap") this.renderCurrentPage();
|
|
41924
|
+
return dispatchNativeNodeEvent(current, node, eventName, event, this.moduleLoader.wx, (scopeId, method) => this.resolveScopeMethod(current, scopeId, method), onHandlerResult);
|
|
41925
|
+
}
|
|
41465
41926
|
requestRender(callback) {
|
|
41466
41927
|
this.assertActive();
|
|
41467
41928
|
if (callback) this.renderRequestCallbacks.push(callback);
|
|
@@ -41509,21 +41970,27 @@ var HeadlessSession = class {
|
|
|
41509
41970
|
callScopeMethod(scopeId, methodName, event) {
|
|
41510
41971
|
const current = this.requireCurrentPage(`scope method "${methodName}"`);
|
|
41511
41972
|
this.renderCurrentPage();
|
|
41512
|
-
|
|
41973
|
+
return this.resolveScopeMethod(current, scopeId, methodName)(event);
|
|
41974
|
+
}
|
|
41975
|
+
/** 解析本次交互的实例,后续回调不随重渲染或导航切换所有者。 */
|
|
41976
|
+
resolveScopeMethod(current, scopeId, methodName) {
|
|
41977
|
+
if (!scopeId || scopeId === `page:${stripLeadingSlash(current.route)}`) return (event) => {
|
|
41513
41978
|
const method = current[methodName];
|
|
41514
41979
|
if (typeof method !== "function") throw new TypeError(`Method "${methodName}" does not exist on headless page ${current.route}.`);
|
|
41515
41980
|
return method.call(current, event);
|
|
41516
|
-
}
|
|
41981
|
+
};
|
|
41517
41982
|
const instance = this.componentCache.get(scopeId);
|
|
41518
41983
|
if (!instance) throw new Error(`Unknown scope "${scopeId}" in headless runtime.`);
|
|
41519
|
-
|
|
41520
|
-
|
|
41521
|
-
|
|
41522
|
-
|
|
41984
|
+
return (event) => {
|
|
41985
|
+
instance.__lastInteractionEvent__ = {
|
|
41986
|
+
currentTarget: event.currentTarget,
|
|
41987
|
+
mark: event.mark,
|
|
41988
|
+
target: event.target
|
|
41989
|
+
};
|
|
41990
|
+
const method = instance[methodName];
|
|
41991
|
+
if (typeof method !== "function") throw new TypeError(`Method "${methodName}" does not exist on headless component scope "${scopeId}".`);
|
|
41992
|
+
return method.call(instance, event);
|
|
41523
41993
|
};
|
|
41524
|
-
const method = instance[methodName];
|
|
41525
|
-
if (typeof method !== "function") throw new TypeError(`Method "${methodName}" does not exist on headless component scope "${scopeId}".`);
|
|
41526
|
-
return method.call(instance, event);
|
|
41527
41994
|
}
|
|
41528
41995
|
callScopeMethodDirect(scopeId, methodName, ...args) {
|
|
41529
41996
|
const normalizedScopeId = scopeId.trim();
|
|
@@ -41804,11 +42271,13 @@ var HeadlessSession = class {
|
|
|
41804
42271
|
const target = this.resolveNavigationTarget(url);
|
|
41805
42272
|
const launchedPage = this.bootstrapNavigation(createAppLaunchOptions(target.normalizedRoute, target.query));
|
|
41806
42273
|
if (launchedPage) return launchedPage;
|
|
41807
|
-
this.
|
|
41808
|
-
|
|
42274
|
+
const event = this.beginRoute(target, "reLaunch");
|
|
42275
|
+
this.unloadAllPages(event);
|
|
42276
|
+
const pageInstance = this.createFreshPage(target, event);
|
|
41809
42277
|
this.pages.push(pageInstance);
|
|
41810
42278
|
this.currentPageInstance = pageInstance;
|
|
41811
42279
|
this.runInitialPageLifecycles(pageInstance, target.query);
|
|
42280
|
+
this.routeEvents.commit(pageInstance, event);
|
|
41812
42281
|
return pageInstance;
|
|
41813
42282
|
}
|
|
41814
42283
|
navigateTo(url) {
|
|
@@ -41817,12 +42286,14 @@ var HeadlessSession = class {
|
|
|
41817
42286
|
if (launchedPage) return launchedPage;
|
|
41818
42287
|
if (this.pages.length >= PAGE_STACK_LIMIT) throw new Error(`Cannot navigateTo() beyond a ${PAGE_STACK_LIMIT}-page stack in headless runtime.`);
|
|
41819
42288
|
if (this.isTabBarRoute(target.routeRecord.route)) throw new Error(`wx.navigateTo() cannot open a tabBar page in headless runtime: ${url}`);
|
|
42289
|
+
const event = this.beginRoute(target, "navigateTo");
|
|
41820
42290
|
this.currentPageInstance?.onHide?.();
|
|
41821
42291
|
if (this.currentPageInstance) this.runPageComponentLifetime(this.currentPageInstance.route, "hide");
|
|
41822
|
-
const pageInstance = this.createFreshPage(target);
|
|
42292
|
+
const pageInstance = this.createFreshPage(target, event);
|
|
41823
42293
|
this.pages.push(pageInstance);
|
|
41824
42294
|
this.currentPageInstance = pageInstance;
|
|
41825
42295
|
this.runInitialPageLifecycles(pageInstance, target.query);
|
|
42296
|
+
this.routeEvents.commit(pageInstance, event);
|
|
41826
42297
|
return pageInstance;
|
|
41827
42298
|
}
|
|
41828
42299
|
redirectTo(url) {
|
|
@@ -41830,15 +42301,17 @@ var HeadlessSession = class {
|
|
|
41830
42301
|
const launchedPage = this.bootstrapNavigation(createAppLaunchOptions(target.normalizedRoute, target.query));
|
|
41831
42302
|
if (launchedPage) return launchedPage;
|
|
41832
42303
|
if (this.isTabBarRoute(target.routeRecord.route)) throw new Error(`wx.redirectTo() cannot open a tabBar page in headless runtime: ${url}`);
|
|
42304
|
+
const event = this.beginRoute(target, "redirectTo");
|
|
41833
42305
|
const current = this.currentPageInstance;
|
|
41834
42306
|
if (current) {
|
|
42307
|
+
this.unloadPage(current, event);
|
|
41835
42308
|
this.pages.pop();
|
|
41836
|
-
this.unloadPage(current);
|
|
41837
42309
|
}
|
|
41838
|
-
const pageInstance = this.createFreshPage(target);
|
|
42310
|
+
const pageInstance = this.createFreshPage(target, event);
|
|
41839
42311
|
this.pages.push(pageInstance);
|
|
41840
42312
|
this.currentPageInstance = pageInstance;
|
|
41841
42313
|
this.runInitialPageLifecycles(pageInstance, target.query);
|
|
42314
|
+
this.routeEvents.commit(pageInstance, event);
|
|
41842
42315
|
return pageInstance;
|
|
41843
42316
|
}
|
|
41844
42317
|
navigateBack(delta = 1) {
|
|
@@ -41846,12 +42319,17 @@ var HeadlessSession = class {
|
|
|
41846
42319
|
if (launchedPage) return launchedPage;
|
|
41847
42320
|
if (this.pages.length <= 1) return this.currentPageInstance;
|
|
41848
42321
|
const removableCount = Math.min(Number.isFinite(delta) ? Math.max(1, Math.trunc(delta)) : 1, this.pages.length - 1);
|
|
41849
|
-
const
|
|
41850
|
-
|
|
41851
|
-
|
|
42322
|
+
const nextPage = this.pages[this.pages.length - removableCount - 1];
|
|
42323
|
+
const event = this.routeEvents.begin(nextPage.route, nextPage.options, "navigateBack", void 0, nextPage);
|
|
42324
|
+
this.routeEvents.attach(nextPage, event);
|
|
42325
|
+
for (let index = 0; index < removableCount; index++) {
|
|
42326
|
+
this.unloadPage(this.pages.at(-1), event);
|
|
42327
|
+
this.pages.pop();
|
|
42328
|
+
}
|
|
41852
42329
|
this.currentPageInstance = nextPage;
|
|
41853
42330
|
nextPage?.onShow?.();
|
|
41854
42331
|
if (nextPage) this.runPageComponentLifetime(nextPage.route, "show");
|
|
42332
|
+
this.routeEvents.commit(nextPage, event);
|
|
41855
42333
|
return nextPage;
|
|
41856
42334
|
}
|
|
41857
42335
|
switchTab(url) {
|
|
@@ -41869,6 +42347,8 @@ var HeadlessSession = class {
|
|
|
41869
42347
|
const current = this.currentPageInstance;
|
|
41870
42348
|
const cachedTarget = this.tabPages.get(target.routeRecord.route) ?? null;
|
|
41871
42349
|
if (current === cachedTarget && current) return () => {};
|
|
42350
|
+
const event = this.beginRoute(target, "switchTab", cachedTarget ?? void 0);
|
|
42351
|
+
if (cachedTarget) this.routeEvents.attach(cachedTarget, event);
|
|
41872
42352
|
if (current && current !== cachedTarget) {
|
|
41873
42353
|
current.onHide?.();
|
|
41874
42354
|
this.runPageComponentLifetime(current.route, "hide");
|
|
@@ -41878,7 +42358,7 @@ var HeadlessSession = class {
|
|
|
41878
42358
|
let nextPage = cachedTarget;
|
|
41879
42359
|
let shouldRunInitialLifecycles = false;
|
|
41880
42360
|
if (!nextPage) {
|
|
41881
|
-
nextPage = this.createFreshPage(target);
|
|
42361
|
+
nextPage = this.createFreshPage(target, event);
|
|
41882
42362
|
this.tabPages.set(target.routeRecord.route, nextPage);
|
|
41883
42363
|
shouldRunInitialLifecycles = true;
|
|
41884
42364
|
}
|
|
@@ -41891,7 +42371,8 @@ var HeadlessSession = class {
|
|
|
41891
42371
|
this.runPageComponentLifetime(nextPage.route, "show");
|
|
41892
42372
|
}
|
|
41893
42373
|
return () => {
|
|
41894
|
-
for (const page of pagesToUnload) this.unloadPage(page);
|
|
42374
|
+
for (const page of pagesToUnload) this.unloadPage(page, event);
|
|
42375
|
+
this.routeEvents.commit(nextPage, event);
|
|
41895
42376
|
};
|
|
41896
42377
|
}
|
|
41897
42378
|
pageScrollTo(option) {
|
|
@@ -41986,17 +42467,20 @@ var HeadlessSession = class {
|
|
|
41986
42467
|
windowInfo: this.getWindowInfo()
|
|
41987
42468
|
});
|
|
41988
42469
|
}
|
|
41989
|
-
|
|
42470
|
+
beginRoute(target, openType, page) {
|
|
42471
|
+
return this.routeEvents.begin(target.normalizedRoute, target.query, openType, target.pageConfig?.renderer ?? this.project.appConfig.renderer, page);
|
|
42472
|
+
}
|
|
42473
|
+
createFreshPage(target, event) {
|
|
41990
42474
|
const resourcePath = target.routeRecord.resourcePath ?? target.routeRecord.route;
|
|
41991
42475
|
const pageModulePath = path.resolve(this.project.miniprogramRootPath, `${resourcePath}.js`);
|
|
41992
|
-
const pageConfigPath = path.resolve(this.project.miniprogramRootPath, `${resourcePath}.json`);
|
|
41993
42476
|
const pageDefinition = this.moduleLoader.executePageModule(pageModulePath, target.routeRecord.route);
|
|
41994
|
-
const pageConfig =
|
|
42477
|
+
const pageConfig = target.pageConfig;
|
|
41995
42478
|
const pageInstance = createPageInstance(`/${target.routeRecord.route}`, pageDefinition, target.query, {
|
|
41996
42479
|
background: resolveBackgroundSnapshot(this.project.appConfig, pageConfig),
|
|
41997
42480
|
navigationBar: resolveNavigationBarSnapshot(this.project.appConfig, pageConfig),
|
|
41998
42481
|
requestRender: (callback) => this.requestRender(callback)
|
|
41999
42482
|
});
|
|
42483
|
+
this.routeEvents.attach(pageInstance, event);
|
|
42000
42484
|
pageInstance.createIntersectionObserver = (options) => this.createIntersectionObserver(pageInstance, options);
|
|
42001
42485
|
pageInstance.createMediaQueryObserver = () => this.createMediaQueryObserver(pageInstance);
|
|
42002
42486
|
pageInstance.selectComponent = (selector) => resolveNativeComponentSelection(this.selectComponent(selector));
|
|
@@ -42009,7 +42493,7 @@ var HeadlessSession = class {
|
|
|
42009
42493
|
return pageInstance;
|
|
42010
42494
|
}
|
|
42011
42495
|
runInitialPageLifecycles(pageInstance, query) {
|
|
42012
|
-
runInitialPageLifecycles(pageInstance, query, this.kernel.scheduler, () => this.pages.includes(pageInstance) || this.tabPages.get(pageInstance.route) === pageInstance, () => this.renderCurrentPage());
|
|
42496
|
+
runInitialPageLifecycles(pageInstance, query, this.kernel.scheduler, () => this.pages.includes(pageInstance) || this.tabPages.get(pageInstance.route) === pageInstance, () => this.renderCurrentPage(), () => this.routeEvents.ready(pageInstance));
|
|
42013
42497
|
}
|
|
42014
42498
|
isTabBarRoute(route) {
|
|
42015
42499
|
return this.tabBarRoutes.has(stripLeadingSlash(route));
|
|
@@ -42038,24 +42522,45 @@ var HeadlessSession = class {
|
|
|
42038
42522
|
return {
|
|
42039
42523
|
normalizedRoute,
|
|
42040
42524
|
query,
|
|
42041
|
-
routeRecord
|
|
42525
|
+
routeRecord,
|
|
42526
|
+
pageConfig: readJsonObject(this.project, path.resolve(this.project.miniprogramRootPath, `${routeRecord.resourcePath ?? routeRecord.route}.json`))
|
|
42042
42527
|
};
|
|
42043
42528
|
}
|
|
42044
|
-
unloadAllPages() {
|
|
42529
|
+
unloadAllPages(event, closing = false) {
|
|
42045
42530
|
const pagesToUnload = /* @__PURE__ */ new Set([...this.pages, ...this.tabPages.values()]);
|
|
42046
|
-
|
|
42531
|
+
let failure;
|
|
42532
|
+
for (const page of [...pagesToUnload].reverse()) try {
|
|
42533
|
+
this.unloadPage(page, event, closing);
|
|
42534
|
+
} catch (error) {
|
|
42535
|
+
if (!closing) throw error;
|
|
42536
|
+
failure ??= { error };
|
|
42537
|
+
}
|
|
42047
42538
|
this.pages.length = 0;
|
|
42048
42539
|
this.tabPages.clear();
|
|
42049
42540
|
this.componentCache.clear();
|
|
42050
42541
|
this.componentScopes.clear();
|
|
42051
42542
|
this.clearMediaQueryObservers();
|
|
42052
42543
|
this.currentPageInstance = null;
|
|
42544
|
+
if (failure) throw failure.error;
|
|
42053
42545
|
}
|
|
42054
|
-
unloadPage(page) {
|
|
42055
|
-
|
|
42546
|
+
unloadPage(page, event, closing = false) {
|
|
42547
|
+
let failure;
|
|
42548
|
+
try {
|
|
42549
|
+
this.routeEvents.beforeUnload(page, event);
|
|
42550
|
+
page.onUnload?.();
|
|
42551
|
+
} catch (error) {
|
|
42552
|
+
if (!closing) throw error;
|
|
42553
|
+
failure = { error };
|
|
42554
|
+
}
|
|
42056
42555
|
this.clearMediaQueryObservers(page);
|
|
42057
|
-
|
|
42556
|
+
try {
|
|
42557
|
+
this.detachPageComponents(page.route, closing);
|
|
42558
|
+
} catch (error) {
|
|
42559
|
+
if (!closing) throw error;
|
|
42560
|
+
failure ??= { error };
|
|
42561
|
+
}
|
|
42058
42562
|
this.tabPages.delete(stripLeadingSlash(page.route));
|
|
42563
|
+
if (failure) throw failure.error;
|
|
42059
42564
|
}
|
|
42060
42565
|
requireCurrentPage(action) {
|
|
42061
42566
|
this.bootstrap();
|
|
@@ -42083,15 +42588,27 @@ var HeadlessSession = class {
|
|
|
42083
42588
|
const directlyOwned = rootScopeId ? matches.filter(([, scope]) => scope.ownerScopeId === rootScopeId) : [];
|
|
42084
42589
|
return (directlyOwned.length > 0 ? directlyOwned : matches).map(([candidateScopeId]) => this.componentCache.get(candidateScopeId)).filter(Boolean);
|
|
42085
42590
|
}
|
|
42086
|
-
detachPageComponents(route) {
|
|
42591
|
+
detachPageComponents(route, closing = false) {
|
|
42087
42592
|
const prefix = `page:${stripLeadingSlash(route)}`;
|
|
42088
42593
|
const removed = [...this.componentCache].filter(([scopeId]) => scopeId.startsWith(prefix));
|
|
42089
|
-
|
|
42090
|
-
|
|
42594
|
+
let failure;
|
|
42595
|
+
for (const [, instance] of removed) try {
|
|
42596
|
+
runComponentLifecycle(instance, "detached");
|
|
42597
|
+
} catch (error) {
|
|
42598
|
+
if (!closing) throw error;
|
|
42599
|
+
failure ??= { error };
|
|
42600
|
+
}
|
|
42601
|
+
try {
|
|
42602
|
+
detachComponentRelations(removed.map(([, instance]) => instance));
|
|
42603
|
+
} catch (error) {
|
|
42604
|
+
if (!closing) throw error;
|
|
42605
|
+
failure ??= { error };
|
|
42606
|
+
}
|
|
42091
42607
|
for (const [scopeId] of removed) {
|
|
42092
42608
|
this.componentCache.delete(scopeId);
|
|
42093
42609
|
this.componentScopes.delete(scopeId);
|
|
42094
42610
|
}
|
|
42611
|
+
if (failure) throw failure.error;
|
|
42095
42612
|
}
|
|
42096
42613
|
notifyMediaQueryObservers() {
|
|
42097
42614
|
for (const entry of [...this.mediaQueryObservers]) {
|
|
@@ -42251,6 +42768,7 @@ function createPageRootNodeHandle(options) {
|
|
|
42251
42768
|
const tree = session?.getCurrentPages().includes(page) ? session.renderCurrentPage() : renderPageTree(project, page);
|
|
42252
42769
|
return new HeadlessTestingNodeHandle(tree.root.type === "root" && !options.includeHostRoots ? tree.root.children?.[0] ?? tree.root : tree.root, {
|
|
42253
42770
|
assertActive: options.assertActive,
|
|
42771
|
+
dispatchNativeEvent: (node, eventName, event, onHandlerResult) => session?.getCurrentPages().at(-1) === page ? session.dispatchNativeNodeEvent(node, eventName, event, onHandlerResult) : false,
|
|
42254
42772
|
callMethod: (scopeId, methodName, event) => {
|
|
42255
42773
|
if (session?.getCurrentPages().includes(page)) return session.callScopeMethod(scopeId, methodName, event);
|
|
42256
42774
|
const method = page[methodName];
|
|
@@ -42690,9 +43208,13 @@ var HeadlessTestingScopeHandle = class HeadlessTestingScopeHandle {
|
|
|
42690
43208
|
var HeadlessTestingSessionHandle = class {
|
|
42691
43209
|
project;
|
|
42692
43210
|
session;
|
|
43211
|
+
pendingNavigations = /* @__PURE__ */ new Set();
|
|
42693
43212
|
constructor(project, session) {
|
|
42694
43213
|
this.project = project;
|
|
42695
43214
|
this.session = session;
|
|
43215
|
+
this.session.on("close", () => {
|
|
43216
|
+
for (const reject of this.pendingNavigations) reject(/* @__PURE__ */ new Error("The headless testing session closed before navigation completed."));
|
|
43217
|
+
});
|
|
42696
43218
|
}
|
|
42697
43219
|
async close() {
|
|
42698
43220
|
this.session.close();
|
|
@@ -42761,23 +43283,23 @@ var HeadlessTestingSessionHandle = class {
|
|
|
42761
43283
|
}, `Timed out waiting for ${normalizedCount} component(s) matching "${normalizedSelector}" in headless testing runtime.`, options);
|
|
42762
43284
|
}
|
|
42763
43285
|
async reLaunch(route) {
|
|
42764
|
-
this.invokeNavigation("reLaunch", { url: route });
|
|
43286
|
+
await this.invokeNavigation("reLaunch", { url: route });
|
|
42765
43287
|
return await this.requireCurrentPage("reLaunch", route);
|
|
42766
43288
|
}
|
|
42767
43289
|
async navigateTo(route) {
|
|
42768
|
-
this.invokeNavigation("navigateTo", { url: route });
|
|
43290
|
+
await this.invokeNavigation("navigateTo", { url: route });
|
|
42769
43291
|
return await this.requireCurrentPage("navigateTo", route);
|
|
42770
43292
|
}
|
|
42771
43293
|
async redirectTo(route) {
|
|
42772
|
-
this.invokeNavigation("redirectTo", { url: route });
|
|
43294
|
+
await this.invokeNavigation("redirectTo", { url: route });
|
|
42773
43295
|
return await this.requireCurrentPage("redirectTo", route);
|
|
42774
43296
|
}
|
|
42775
43297
|
async navigateBack(delta = 1) {
|
|
42776
|
-
this.invokeNavigation("navigateBack", { delta });
|
|
43298
|
+
await this.invokeNavigation("navigateBack", { delta });
|
|
42777
43299
|
return await this.currentPage();
|
|
42778
43300
|
}
|
|
42779
43301
|
async switchTab(route) {
|
|
42780
|
-
this.invokeNavigation("switchTab", { url: route });
|
|
43302
|
+
await this.invokeNavigation("switchTab", { url: route });
|
|
42781
43303
|
return await this.requireCurrentPage("switchTab", route);
|
|
42782
43304
|
}
|
|
42783
43305
|
async waitForCurrentPage(route, options = {}) {
|
|
@@ -42814,21 +43336,30 @@ var HeadlessTestingSessionHandle = class {
|
|
|
42814
43336
|
this.session.triggerRouteDone(options);
|
|
42815
43337
|
}
|
|
42816
43338
|
invokeNavigation(methodName, options) {
|
|
42817
|
-
|
|
42818
|
-
|
|
42819
|
-
|
|
42820
|
-
|
|
42821
|
-
|
|
42822
|
-
|
|
43339
|
+
return new Promise((resolve, reject) => {
|
|
43340
|
+
const rejectNavigation = (error) => {
|
|
43341
|
+
this.pendingNavigations.delete(rejectNavigation);
|
|
43342
|
+
reject(error);
|
|
43343
|
+
};
|
|
43344
|
+
this.pendingNavigations.add(rejectNavigation);
|
|
43345
|
+
try {
|
|
43346
|
+
this.session.callWxMethod(methodName, {
|
|
43347
|
+
...options,
|
|
43348
|
+
success: () => {
|
|
43349
|
+
this.pendingNavigations.delete(rejectNavigation);
|
|
43350
|
+
resolve();
|
|
43351
|
+
},
|
|
43352
|
+
fail: (error) => {
|
|
43353
|
+
const details = error && typeof error === "object" ? error : void 0;
|
|
43354
|
+
const reason = typeof details?.errMsg === "string" ? details.errMsg : typeof details?.message === "string" ? details.message : String(error);
|
|
43355
|
+
const target = options.url ? ` route "${options.url}"` : "";
|
|
43356
|
+
rejectNavigation(new Error(`Failed to ${methodName}${target} through the headless wx runtime: ${reason}`, { cause: error }));
|
|
43357
|
+
}
|
|
43358
|
+
});
|
|
43359
|
+
} catch (error) {
|
|
43360
|
+
rejectNavigation(error);
|
|
42823
43361
|
}
|
|
42824
43362
|
});
|
|
42825
|
-
if (failure.received) {
|
|
42826
|
-
const error = failure.error;
|
|
42827
|
-
const details = error && typeof error === "object" ? error : void 0;
|
|
42828
|
-
const reason = typeof details?.errMsg === "string" ? details.errMsg : typeof details?.message === "string" ? details.message : String(error);
|
|
42829
|
-
const target = options.url ? ` route "${options.url}"` : "";
|
|
42830
|
-
throw new Error(`Failed to ${methodName}${target} through the headless wx runtime: ${reason}`, { cause: error });
|
|
42831
|
-
}
|
|
42832
43363
|
}
|
|
42833
43364
|
async requireCurrentPage(methodName, route) {
|
|
42834
43365
|
const page = await this.currentPage();
|