@weapp-vite/miniprogram-automator 1.2.13 → 1.2.15

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.
@@ -12036,6 +12036,42 @@ function resolveTemplateData(node, source) {
12036
12036
  return resolved && typeof resolved === "object" ? resolved : {};
12037
12037
  }
12038
12038
  //#endregion
12039
+ //#region ../../mpcore/packages/simulator/src/project/plugins.ts
12040
+ const HEADLESS_PLUGIN_VIRTUAL_ROOT = "__plugins__";
12041
+ const PLUGIN_URL_RE = /^plugin:\/\/([^/]+)\/(.+)$/;
12042
+ function createPluginVirtualRoot(alias) {
12043
+ return `${HEADLESS_PLUGIN_VIRTUAL_ROOT}/${alias}`;
12044
+ }
12045
+ function resolvePluginRequest(plugins, request, kind) {
12046
+ const match = PLUGIN_URL_RE.exec(request);
12047
+ if (!match) return;
12048
+ const [, alias, publicName] = match;
12049
+ const plugin = plugins.find((item) => item.alias === alias);
12050
+ const registry = kind === "page" ? plugin?.config.pages : plugin?.config.publicComponents;
12051
+ const resourcePath = registry && typeof registry === "object" && !Array.isArray(registry) ? registry[publicName] : void 0;
12052
+ if (!plugin || typeof resourcePath !== "string" || !resourcePath) return;
12053
+ return {
12054
+ plugin,
12055
+ publicName,
12056
+ resourcePath: `${plugin.virtualRoot}/${resourcePath}`
12057
+ };
12058
+ }
12059
+ function createPluginArtifactSource(fallback, miniprogramRootPath, plugins) {
12060
+ function resolvePhysicalPath(filePath) {
12061
+ const normalizedPath = path.resolve(filePath);
12062
+ for (const plugin of plugins) {
12063
+ const virtualRootPath = path.resolve(miniprogramRootPath, plugin.virtualRoot);
12064
+ const relativePath = path.relative(virtualRootPath, normalizedPath);
12065
+ if (!relativePath.startsWith("..") && !path.isAbsolute(relativePath)) return path.resolve(plugin.rootPath, relativePath);
12066
+ }
12067
+ return normalizedPath;
12068
+ }
12069
+ return {
12070
+ has: (filePath) => fallback.has(resolvePhysicalPath(filePath)),
12071
+ readText: (filePath) => fallback.readText(resolvePhysicalPath(filePath))
12072
+ };
12073
+ }
12074
+ //#endregion
12039
12075
  //#region ../../mpcore/packages/simulator/src/view/eventBinding.ts
12040
12076
  const LEGACY_EVENT_NAME_RE = /^\w+$/;
12041
12077
  const EVENT_BINDING_PREFIXES = [
@@ -14583,11 +14619,12 @@ function isMustacheOnly(value) {
14583
14619
  function toDatasetKey$3(attributeName) {
14584
14620
  return attributeName.slice(5).replace(DATASET_NAME_RE$3, (_match, char) => char.toUpperCase());
14585
14621
  }
14586
- function collectDataset$3(node) {
14622
+ function collectDataset$3(node, source) {
14587
14623
  const dataset = {};
14588
14624
  for (const [key, value] of Object.entries(node.attribs ?? {})) {
14589
14625
  if (!key.startsWith("data-") || key === "data-sim-scope" || key === "data-sim-tap" || key === "data-sim-component") continue;
14590
- dataset[toDatasetKey$3(key)] = String(value);
14626
+ const resolvedValue = source && isMustacheOnly(value) ? resolveTemplateExpression(source, value) : value;
14627
+ dataset[toDatasetKey$3(key)] = String(resolvedValue ?? "");
14591
14628
  }
14592
14629
  return dataset;
14593
14630
  }
@@ -14698,7 +14735,7 @@ function createLoopScope(scope, itemName, indexName, item, index) {
14698
14735
  //#endregion
14699
14736
  //#region ../../mpcore/packages/simulator/src/runtime/render/component.ts
14700
14737
  function resolveComponentRegistryEntry(context, ownerJsonPath, ownerFilePath, alias, genericComponentBasePath) {
14701
- const usingComponents = resolveUsingComponents(context.artifactSource, ownerJsonPath, ownerFilePath);
14738
+ const usingComponents = resolveUsingComponents(context, ownerJsonPath, ownerFilePath);
14702
14739
  const componentBasePath = genericComponentBasePath ?? usingComponents.get(alias);
14703
14740
  if (!componentBasePath) return null;
14704
14741
  const filePath = `${componentBasePath}.js`;
@@ -14720,14 +14757,14 @@ function readComponentConfig(artifactSource, jsonPath) {
14720
14757
  return {};
14721
14758
  }
14722
14759
  }
14723
- function resolveUsingComponents(artifactSource, ownerJsonPath, ownerFilePath) {
14760
+ function resolveUsingComponents(context, ownerJsonPath, ownerFilePath) {
14724
14761
  try {
14725
- const usingComponents = readComponentConfig(artifactSource, ownerJsonPath).usingComponents;
14762
+ const usingComponents = readComponentConfig(context.artifactSource, ownerJsonPath).usingComponents;
14726
14763
  if (!usingComponents || typeof usingComponents !== "object" || Array.isArray(usingComponents)) return /* @__PURE__ */ new Map();
14727
14764
  const resolved = /* @__PURE__ */ new Map();
14728
14765
  for (const [alias, rawPath] of Object.entries(usingComponents)) {
14729
14766
  if (typeof rawPath !== "string") continue;
14730
- const basePath = rawPath.startsWith("/") ? rawPath.replace(LEADING_SLASH_RE$4, "") : path.posix.normalize(path.posix.join(path.posix.dirname(ownerFilePath), rawPath));
14767
+ const basePath = resolvePluginRequest(context.project.plugins, rawPath, "publicComponent")?.resourcePath ?? (rawPath.startsWith("/") ? rawPath.replace(LEADING_SLASH_RE$4, "") : path.posix.normalize(path.posix.join(path.posix.dirname(ownerFilePath), rawPath)));
14731
14768
  resolved.set(alias, basePath.replace(LEADING_SLASH_RE$4, ""));
14732
14769
  }
14733
14770
  return resolved;
@@ -14739,7 +14776,7 @@ function resolveComponentGenerics(context, hostNode, ownerJsonPath, ownerFilePat
14739
14776
  const componentJsonPath = path.resolve(context.project.miniprogramRootPath, `${componentFilePath.replace(JS_FILE_RE, "")}.json`);
14740
14777
  const componentGenerics = readComponentConfig(context.artifactSource, componentJsonPath).componentGenerics;
14741
14778
  if (!componentGenerics || typeof componentGenerics !== "object" || Array.isArray(componentGenerics)) return;
14742
- const ownerComponents = resolveUsingComponents(context.artifactSource, ownerJsonPath, ownerFilePath);
14779
+ const ownerComponents = resolveUsingComponents(context, ownerJsonPath, ownerFilePath);
14743
14780
  const resolved = /* @__PURE__ */ new Map();
14744
14781
  for (const [genericName, definition] of Object.entries(componentGenerics)) {
14745
14782
  const selectedAlias = hostNode.attribs?.[`generic:${genericName}`];
@@ -14764,9 +14801,10 @@ function buildComponentTrigger(componentScopeId, context, hostNode) {
14764
14801
  return (instance, eventName, detail, triggerOptions) => {
14765
14802
  const interactionTarget = instance.__lastInteractionEvent__?.target;
14766
14803
  const interactionCurrentTarget = instance.__lastInteractionEvent__?.currentTarget;
14804
+ const componentDataset = context.componentScopes.get(componentScopeId)?.dataset ?? hostDataset;
14767
14805
  const interactionMark = instance.__lastInteractionEvent__?.mark;
14768
14806
  const target = {
14769
- dataset: interactionTarget?.dataset ?? hostDataset,
14807
+ dataset: interactionTarget?.dataset ?? componentDataset,
14770
14808
  id: interactionTarget?.id ?? hostId
14771
14809
  };
14772
14810
  let currentScopeId = componentScopeId;
@@ -14822,7 +14860,7 @@ function createComponentScope(clonedNode, scope, componentScopeId, componentInst
14822
14860
  alias: clonedNode.name,
14823
14861
  classList: String(clonedNode.attribs?.class ?? "").split(CLASS_SPLIT_RE).map((item) => item.trim()).filter(Boolean),
14824
14862
  data: createMergedScopeData(scope.data, componentInstance.properties, componentInstance.data),
14825
- dataset: collectDataset$3(clonedNode),
14863
+ dataset: collectDataset$3(clonedNode, scope.data),
14826
14864
  eventBindings: collectComponentEventBindings(clonedNode),
14827
14865
  getMethod: (methodName) => {
14828
14866
  const method = componentInstance?.[methodName];
@@ -14854,9 +14892,10 @@ function resolveComponentProperties(clonedNode, scope, definition) {
14854
14892
  };
14855
14893
  }
14856
14894
  function createRuntimeComponentInstance(componentScopeId, context, clonedNode, componentEntry, nextProperties, ownerScopeId) {
14895
+ 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;
14857
14896
  const componentInstance = createComponentInstance({
14858
14897
  definition: componentEntry.definition,
14859
- properties: nextProperties,
14898
+ properties: componentProperties,
14860
14899
  requestRender: (callback) => context.session.requestRender(callback),
14861
14900
  triggerEvent: buildComponentTrigger(componentScopeId, context, clonedNode)
14862
14901
  });
@@ -14869,7 +14908,7 @@ function createRuntimeComponentInstance(componentScopeId, context, clonedNode, c
14869
14908
  componentInstance.selectOwnerComponent = () => ownerScopeId ? context.componentCache.get(ownerScopeId) ?? null : null;
14870
14909
  context.componentCache.set(componentScopeId, componentInstance);
14871
14910
  runComponentLifecycle(componentInstance, "created");
14872
- runComponentObservers(componentInstance.__definition__ ?? componentEntry.definition, componentInstance, Object.keys(nextProperties), {});
14911
+ runComponentObservers(componentInstance.__definition__ ?? componentEntry.definition, componentInstance, Object.keys(componentProperties), {});
14873
14912
  componentInstance.__propertySnapshots = Object.fromEntries(Object.entries(componentInstance.properties).map(([key, propertyValue]) => [key, cloneValue$1(propertyValue)]));
14874
14913
  runComponentLifecycle(componentInstance, "attached");
14875
14914
  return componentInstance;
@@ -15050,7 +15089,8 @@ function renderNodeTree(node, scope, context, ownerJsonPath, ownerFilePath, inst
15050
15089
  }
15051
15090
  function renderRuntimePageTree(context, page) {
15052
15091
  const route = page.route.replace(LEADING_SLASH_RE$4, "");
15053
- const templatePath = path.resolve(context.project.miniprogramRootPath, `${route}.wxml`);
15092
+ const resourcePath = context.project.routes.find((item) => item.route === route)?.resourcePath ?? route;
15093
+ const templatePath = path.resolve(context.project.miniprogramRootPath, `${resourcePath}.wxml`);
15054
15094
  const document = parseTemplateDocument(readTemplateSource(context.artifactSource, templatePath));
15055
15095
  const pageScopeId = `page:${route}`;
15056
15096
  const pageScope = {
@@ -15067,7 +15107,7 @@ function renderRuntimePageTree(context, page) {
15067
15107
  const seenComponentScopes = /* @__PURE__ */ new Set();
15068
15108
  const root = (document.children ?? [])[0] ?? document;
15069
15109
  const templateRenderState = createTemplateRenderState(root);
15070
- const renderedRoot = renderNodeTree(root, pageScope, context, path.resolve(context.project.miniprogramRootPath, `${route}.json`), `${route}.js`, pageScopeId, seenComponentScopes, templateRenderState);
15110
+ const renderedRoot = renderNodeTree(root, pageScope, context, path.resolve(context.project.miniprogramRootPath, `${resourcePath}.json`), `${resourcePath}.js`, pageScopeId, seenComponentScopes, templateRenderState);
15071
15111
  for (const [scopeId, instance] of [...context.componentCache.entries()]) if (!seenComponentScopes.has(scopeId)) {
15072
15112
  runComponentLifecycle(instance, "detached");
15073
15113
  context.componentCache.delete(scopeId);
@@ -15138,15 +15178,21 @@ function registerAppDefinition(registries, definition) {
15138
15178
  registries.appDefinition = definition;
15139
15179
  return definition;
15140
15180
  }
15141
- function registerPageDefinition(registries, definition) {
15142
- const route = registries.currentLoadContext?.route;
15181
+ function registerPageDefinition(registries, definition, explicitRoute) {
15182
+ const route = explicitRoute ?? registries.currentLoadContext?.route;
15143
15183
  if (!route) throw new Error("Cannot register Page() without an active page load context in headless runtime.");
15144
15184
  if (registries.pages.has(route)) throw createDuplicateRegistrationError("page", route);
15145
15185
  registries.pages.set(route, definition);
15146
15186
  return definition;
15147
15187
  }
15148
15188
  function registerComponentDefinition(registries, definition) {
15149
- if (registries.currentLoadContext?.kind === "page") return registerPageDefinition(registries, normalizeComponentPageDefinition(definition));
15189
+ if (registries.currentLoadContext?.kind === "page") {
15190
+ if (registries.currentLoadContext.componentDefinitions) {
15191
+ registries.currentLoadContext.componentDefinitions.push(definition);
15192
+ return definition;
15193
+ }
15194
+ return registerPageDefinition(registries, normalizeComponentPageDefinition(definition));
15195
+ }
15150
15196
  const id = registries.currentLoadContext?.route ?? `anonymous:${registries.components.size}`;
15151
15197
  if (registries.components.has(id)) throw createDuplicateRegistrationError("component", id);
15152
15198
  registries.components.set(id, definition);
@@ -15694,15 +15740,24 @@ var RuntimeDiagnostics = class {
15694
15740
  timestamp: Date.now()
15695
15741
  });
15696
15742
  }
15697
- createConsole(baseConsole = console) {
15743
+ createConsole(baseConsole = console, onConsole) {
15698
15744
  const runtimeConsole = Object.create(baseConsole);
15745
+ const wrap = (level, method) => (...args) => {
15746
+ if (level === "error" || level === "warn") this.record(level, args);
15747
+ onConsole?.({
15748
+ args: [...args],
15749
+ level
15750
+ });
15751
+ method(...args);
15752
+ };
15753
+ runtimeConsole.debug = wrap("debug", baseConsole.debug.bind(baseConsole));
15754
+ runtimeConsole.info = wrap("info", baseConsole.info.bind(baseConsole));
15755
+ runtimeConsole.log = wrap("log", baseConsole.log.bind(baseConsole));
15699
15756
  runtimeConsole.error = (...args) => {
15700
- this.record("error", args);
15701
- baseConsole.error(...args);
15757
+ wrap("error", baseConsole.error.bind(baseConsole))(...args);
15702
15758
  };
15703
15759
  runtimeConsole.warn = (...args) => {
15704
- this.record("warn", args);
15705
- baseConsole.warn(...args);
15760
+ wrap("warn", baseConsole.warn.bind(baseConsole))(...args);
15706
15761
  };
15707
15762
  return runtimeConsole;
15708
15763
  }
@@ -15803,7 +15858,7 @@ function pushRoute(records, seen, record) {
15803
15858
  seen.add(record.route);
15804
15859
  records.push(record);
15805
15860
  }
15806
- function resolveRoutesFromAppConfig(config) {
15861
+ function resolveRoutesFromAppConfig(config, plugins = []) {
15807
15862
  const records = [];
15808
15863
  const seen = /* @__PURE__ */ new Set();
15809
15864
  if (Array.isArray(config.pages)) for (const page of config.pages) {
@@ -15833,6 +15888,19 @@ function resolveRoutesFromAppConfig(config) {
15833
15888
  });
15834
15889
  }
15835
15890
  }
15891
+ for (const plugin of plugins) {
15892
+ const pages = plugin.config.pages;
15893
+ if (!pages || typeof pages !== "object" || Array.isArray(pages)) continue;
15894
+ for (const resourcePath of Object.values(pages)) {
15895
+ if (typeof resourcePath !== "string" || !resourcePath) continue;
15896
+ pushRoute(records, seen, {
15897
+ kind: "page",
15898
+ resourcePath: `${plugin.virtualRoot}/${resourcePath}`,
15899
+ route: `plugin-private://${plugin.provider}/${resourcePath}`,
15900
+ source: "plugin"
15901
+ });
15902
+ }
15903
+ }
15836
15904
  return records;
15837
15905
  }
15838
15906
  //#endregion
@@ -15844,9 +15912,10 @@ function createProjectDescriptor(options) {
15844
15912
  appConfigPath: options.appConfigPath,
15845
15913
  miniprogramRoot: options.miniprogramRoot,
15846
15914
  miniprogramRootPath: options.miniprogramRootPath,
15915
+ plugins: options.plugins ?? [],
15847
15916
  projectPath: options.projectPath,
15848
15917
  projectConfigFiles: options.projectConfigFiles ?? [],
15849
- routes: resolveRoutesFromAppConfig(options.appConfig)
15918
+ routes: resolveRoutesFromAppConfig(options.appConfig, options.plugins ?? [])
15850
15919
  };
15851
15920
  }
15852
15921
  //#endregion
@@ -15963,6 +16032,36 @@ function resolveMiniprogramRoot(projectPath, configFiles) {
15963
16032
  }
15964
16033
  throw new Error(`Failed to resolve miniprogramRoot for headless runtime project: ${projectPath}. Expected project.config.json or project.private.config.json to define miniprogramRoot.`);
15965
16034
  }
16035
+ function resolveConfiguredRoot(configFiles, key) {
16036
+ for (const configFile of [...configFiles].reverse()) {
16037
+ const value = configFile.value[key];
16038
+ if (typeof value === "string" && value.trim()) return value.trim();
16039
+ }
16040
+ }
16041
+ function resolveLocalPlugins(projectPath, projectConfigFiles, appConfig) {
16042
+ const pluginRoot = resolveConfiguredRoot(projectConfigFiles, "pluginRoot");
16043
+ const configuredPlugins = appConfig.plugins;
16044
+ if (!pluginRoot || !configuredPlugins || typeof configuredPlugins !== "object" || Array.isArray(configuredPlugins)) return [];
16045
+ const rootPath = path.resolve(projectPath, pluginRoot);
16046
+ const pluginConfig = readJsonObject$1(path.resolve(rootPath, "plugin.json"));
16047
+ if (!pluginConfig) return [];
16048
+ const plugins = [];
16049
+ const projectAppId = resolveConfiguredRoot(projectConfigFiles, "appid");
16050
+ for (const [alias, definition] of Object.entries(configuredPlugins)) {
16051
+ if (!definition || typeof definition !== "object" || Array.isArray(definition)) continue;
16052
+ const provider = definition.provider;
16053
+ const isLocalPlugin = definition.version === "dev" || projectAppId && provider === projectAppId;
16054
+ if (typeof provider !== "string" || !provider || !isLocalPlugin) continue;
16055
+ plugins.push({
16056
+ alias,
16057
+ config: pluginConfig,
16058
+ provider,
16059
+ rootPath,
16060
+ virtualRoot: createPluginVirtualRoot(alias)
16061
+ });
16062
+ }
16063
+ return plugins;
16064
+ }
15966
16065
  function ensureFileExists(filePath, label) {
15967
16066
  if (fs.existsSync(filePath)) return;
15968
16067
  throw new Error(`Missing ${label} for headless runtime project: ${normalize(filePath)}`);
@@ -15981,11 +16080,15 @@ function loadProject(projectPath) {
15981
16080
  ensureFileExists(miniprogramRootPath, "miniprogramRoot directory");
15982
16081
  const appConfigPath = path.resolve(miniprogramRootPath, "app.json");
15983
16082
  ensureFileExists(appConfigPath, "built app.json");
16083
+ const appConfig = loadAppConfig(appConfigPath);
16084
+ const plugins = resolveLocalPlugins(normalizedProjectPath, projectConfigFiles, appConfig);
15984
16085
  return createProjectDescriptor({
15985
- appConfig: loadAppConfig(appConfigPath),
16086
+ appConfig,
15986
16087
  appConfigPath,
16088
+ artifactSource: createPluginArtifactSource(createFileSystemArtifactSource(), miniprogramRootPath, plugins),
15987
16089
  miniprogramRoot,
15988
16090
  miniprogramRootPath,
16091
+ plugins,
15989
16092
  projectPath: normalizedProjectPath,
15990
16093
  projectConfigFiles
15991
16094
  });
@@ -16321,12 +16424,15 @@ function parseNumericLikeValue$1(value) {
16321
16424
  const match = value.match(NUMERIC_LIKE_VALUE_RE$1);
16322
16425
  return match ? Number(match[0]) : 0;
16323
16426
  }
16324
- function resolveRect$1(node) {
16427
+ function resolveRect$1(node, windowInfo) {
16325
16428
  const style = parseStyleDeclarations$1(node.attribs?.style);
16326
16429
  const left = parseNumericLikeValue$1(node.attribs?.["data-sim-left"] ?? style.left);
16327
16430
  const top = parseNumericLikeValue$1(node.attribs?.["data-sim-top"] ?? style.top);
16328
- const width = parseNumericLikeValue$1(node.attribs?.["data-sim-width"] ?? style.width);
16329
- const height = parseNumericLikeValue$1(node.attribs?.["data-sim-height"] ?? style.height);
16431
+ const rawWidth = node.attribs?.["data-sim-width"] ?? style.width;
16432
+ const rawHeight = node.attribs?.["data-sim-height"] ?? style.height;
16433
+ const isTopLevelView = node.name === "view" && node.parent?.name === "page" && windowInfo?.windowWidth != null;
16434
+ const width = rawWidth != null ? parseNumericLikeValue$1(rawWidth) : isTopLevelView && windowInfo ? windowInfo.windowWidth : 0;
16435
+ const height = rawHeight != null ? parseNumericLikeValue$1(rawHeight) : isTopLevelView && windowInfo ? windowInfo.windowHeight : 0;
16330
16436
  return {
16331
16437
  bottom: top + height,
16332
16438
  height,
@@ -16373,7 +16479,7 @@ function resolveFieldsResult(node, fields, options) {
16373
16479
  if (fields.dataset) result.dataset = collectDataset$1(node);
16374
16480
  if (fields.mark) result.mark = collectMark(node);
16375
16481
  if (fields.rect || fields.size) {
16376
- const rect = resolveRect$1(node);
16482
+ const rect = resolveRect$1(node, options.root.name === "page" ? options.windowInfo : void 0);
16377
16483
  if (fields.rect) Object.assign(result, rect);
16378
16484
  if (fields.size) {
16379
16485
  result.width = rect.width;
@@ -17161,6 +17267,102 @@ function createHeadlessVideoContext(driver, videoId, scope) {
17161
17267
  }
17162
17268
  //#endregion
17163
17269
  //#region ../../mpcore/packages/simulator/src/runtime/moduleLoader.ts
17270
+ const ESM_IMPORT_LINE_RE = /^[ \t]*import[^\n]*$/gm;
17271
+ const ESM_EXPORT_LIST_RE = /^[ \t]*export[ \t]*\{([^}\n]+)\}[ \t]*;?$/gm;
17272
+ const ESM_EXPORT_DECLARATION_RE = /\bexport\s+(const|let|var|function|class)\s+([A-Za-z_$][\w$]*)/g;
17273
+ const ESM_EXPORT_DEFAULT_DECLARATION_RE = /\bexport\s+default\s+(function|class)\s+([A-Za-z_$][\w$]*)/g;
17274
+ function isImportWhitespace(value) {
17275
+ return value === " " || value === " ";
17276
+ }
17277
+ function parseEsmImportLine(line) {
17278
+ let statement = line.trim().slice(6).trim();
17279
+ if (statement.endsWith(";")) statement = statement.slice(0, -1).trimEnd();
17280
+ if (statement.startsWith("\"") || statement.startsWith("'")) {
17281
+ const quote = statement[0];
17282
+ if (statement.endsWith(quote) && statement.length > 1) return {
17283
+ request: statement.slice(1, -1),
17284
+ sideEffect: true
17285
+ };
17286
+ return;
17287
+ }
17288
+ let fromIndex = statement.indexOf("from");
17289
+ while (fromIndex >= 0) {
17290
+ if (isImportWhitespace(statement[fromIndex - 1]) && isImportWhitespace(statement[fromIndex + 4])) break;
17291
+ fromIndex = statement.indexOf("from", fromIndex + 4);
17292
+ }
17293
+ if (fromIndex < 0) return;
17294
+ const specifier = statement.slice(0, fromIndex).trim();
17295
+ const requestLiteral = statement.slice(fromIndex + 4).trim();
17296
+ const quote = requestLiteral[0];
17297
+ if (!specifier || quote !== "\"" && quote !== "'" || requestLiteral.length < 2 || !requestLiteral.endsWith(quote)) return;
17298
+ return {
17299
+ request: requestLiteral.slice(1, -1),
17300
+ sideEffect: false,
17301
+ specifier
17302
+ };
17303
+ }
17304
+ function createEsmImportBindings(specifier, request, index) {
17305
+ const namespaceName = `__headlessEsmImport${index}`;
17306
+ const statements = [`const ${namespaceName} = require(${JSON.stringify(request)});`];
17307
+ const normalizedSpecifier = specifier.trim();
17308
+ if (normalizedSpecifier.startsWith("{") && normalizedSpecifier.endsWith("}")) {
17309
+ const entries = normalizedSpecifier.slice(1, -1).split(",");
17310
+ for (const entry of entries) {
17311
+ const [importedName, localName] = entry.trim().split(/\s+as\s+/);
17312
+ if (!importedName) continue;
17313
+ const bindingName = localName || importedName;
17314
+ statements.push(`const ${bindingName} = ${namespaceName}[${JSON.stringify(importedName)}];`);
17315
+ }
17316
+ return statements.join("\n");
17317
+ }
17318
+ if (normalizedSpecifier.startsWith("* as ")) {
17319
+ const bindingName = normalizedSpecifier.slice(5).trim();
17320
+ statements.push(`const ${bindingName} = ${namespaceName};`);
17321
+ return statements.join("\n");
17322
+ }
17323
+ const [defaultName, namedPart] = normalizedSpecifier.split(/\s*,\s*/, 2);
17324
+ if (defaultName) statements.push(`const ${defaultName.trim()} = ${namespaceName}.default ?? ${namespaceName};`);
17325
+ if (namedPart?.startsWith("{") && namedPart.endsWith("}")) statements.push(createEsmImportBindings(namedPart, request, index).split("\n").slice(1).join("\n"));
17326
+ return statements.join("\n");
17327
+ }
17328
+ function transformEsmToCommonJs(source) {
17329
+ if (!/^\s*(?:import|export)\b/m.test(source)) return source;
17330
+ let importIndex = 0;
17331
+ const exportedNames = [];
17332
+ let transformed = source.replace(ESM_IMPORT_LINE_RE, (line) => {
17333
+ const parsed = parseEsmImportLine(line);
17334
+ if (!parsed) return line;
17335
+ if (parsed.sideEffect) return `require(${JSON.stringify(parsed.request)});`;
17336
+ return createEsmImportBindings(parsed.specifier, parsed.request, importIndex++);
17337
+ });
17338
+ transformed = transformed.replace(ESM_EXPORT_LIST_RE, (_match, entries) => {
17339
+ for (const entry of entries.split(",")) {
17340
+ const [local, exported] = entry.trim().split(/\s+as\s+/);
17341
+ if (local) exportedNames.push({
17342
+ exported: exported || local,
17343
+ local
17344
+ });
17345
+ }
17346
+ return "";
17347
+ });
17348
+ transformed = transformed.replace(ESM_EXPORT_DEFAULT_DECLARATION_RE, (_match, kind, local) => {
17349
+ exportedNames.push({
17350
+ exported: "default",
17351
+ local
17352
+ });
17353
+ return `${kind} ${local}`;
17354
+ });
17355
+ transformed = transformed.replace(ESM_EXPORT_DECLARATION_RE, (_match, kind, local) => {
17356
+ exportedNames.push({
17357
+ exported: local,
17358
+ local
17359
+ });
17360
+ return `${kind} ${local}`;
17361
+ });
17362
+ transformed = transformed.replace(/\bexport\s+default\s+/g, "module.exports.default = ");
17363
+ if (exportedNames.length > 0) transformed += `\n${exportedNames.map(({ exported, local }) => `module.exports[${JSON.stringify(exported)}] = ${local};`).join("\n")}`;
17364
+ return transformed;
17365
+ }
17164
17366
  function createRequireNotFoundError(request, importer) {
17165
17367
  return /* @__PURE__ */ new Error(`Cannot resolve require("${request}") from ${normalize(importer)} in headless runtime.`);
17166
17368
  }
@@ -17176,7 +17378,7 @@ function resolveRequiredModulePath(artifactSource, importer, request) {
17176
17378
  for (const candidate of candidates) if (artifactSource.has(candidate)) return candidate;
17177
17379
  throw createRequireNotFoundError(request, importer);
17178
17380
  }
17179
- function createExecutionContext(registries, getCurrentPages, getApp, wxDriver, kernel, globals) {
17381
+ function createExecutionContext(registries, getCurrentPages, getApp, wxDriver, kernel, globals, onConsole) {
17180
17382
  const wx = createHeadlessWx(wxDriver);
17181
17383
  return {
17182
17384
  App(definition) {
@@ -17194,7 +17396,7 @@ function createExecutionContext(registries, getCurrentPages, getApp, wxDriver, k
17194
17396
  Page(definition) {
17195
17397
  return registerPageDefinition(registries, definition);
17196
17398
  },
17197
- console: kernel.diagnostics.createConsole(),
17399
+ console: kernel.diagnostics.createConsole(console, onConsole),
17198
17400
  clearInterval: (handle) => kernel.scheduler.clearInterval(handle),
17199
17401
  clearTimeout: (handle) => kernel.scheduler.clearTimeout(handle),
17200
17402
  getApp,
@@ -17204,13 +17406,17 @@ function createExecutionContext(registries, getCurrentPages, getApp, wxDriver, k
17204
17406
  require: void 0,
17205
17407
  setInterval: kernel.scheduler.setInterval.bind(kernel.scheduler),
17206
17408
  setTimeout: kernel.scheduler.setTimeout.bind(kernel.scheduler),
17409
+ TextDecoder: globalThis.TextDecoder,
17410
+ TextEncoder: globalThis.TextEncoder,
17411
+ URL: globalThis.URL,
17412
+ URLSearchParams: globalThis.URLSearchParams,
17207
17413
  wx,
17208
17414
  ...globals
17209
17415
  };
17210
17416
  }
17211
17417
  function createModuleLoader(registries, getCurrentPages, getApp, wxDriver, options) {
17212
17418
  const moduleCache = /* @__PURE__ */ new Map();
17213
- const executionContext = createExecutionContext(registries, getCurrentPages, getApp, wxDriver, options.kernel, options.globals ?? {});
17419
+ const executionContext = createExecutionContext(registries, getCurrentPages, getApp, wxDriver, options.kernel, options.globals ?? {}, options.onConsole);
17214
17420
  executionContext.globalThis = executionContext;
17215
17421
  function executeModule(filePath, loadContext) {
17216
17422
  const resolvedPath = path.resolve(filePath);
@@ -17240,14 +17446,24 @@ function createModuleLoader(registries, getCurrentPages, getApp, wxDriver, optio
17240
17446
  });
17241
17447
  localRequire.async = (request) => Promise.resolve().then(() => localRequire(request));
17242
17448
  try {
17243
- new vm.Script(`(function (exports, module, require, __filename, __dirname) { ${source}\n})`, { filename: resolvedPath }).runInNewContext(executionContext)(module.exports, module, localRequire, resolvedPath, path.dirname(resolvedPath));
17449
+ new vm.Script(`(function (exports, module, require, __filename, __dirname) { ${transformEsmToCommonJs(source)}\n})`, { filename: resolvedPath }).runInNewContext(executionContext)(module.exports, module, localRequire, resolvedPath, path.dirname(resolvedPath));
17244
17450
  const registeredDefinitions = [...registries.components.values()].filter((definition) => !registeredDefinitionsBefore.has(definition));
17245
- module.componentDefinitions = [.../* @__PURE__ */ new Set([...requiredComponentDefinitions, ...registeredDefinitions])];
17451
+ module.componentDefinitions = [.../* @__PURE__ */ new Set([
17452
+ ...requiredComponentDefinitions,
17453
+ ...loadContext?.componentDefinitions ?? [],
17454
+ ...registeredDefinitions
17455
+ ])];
17246
17456
  return module;
17247
17457
  } finally {
17248
17458
  registries.currentLoadContext = previousLoadContext;
17249
17459
  }
17250
17460
  }
17461
+ executionContext.requirePlugin = (alias) => {
17462
+ const plugin = options.plugins.find((item) => item.alias === alias);
17463
+ if (!plugin) throw new Error(`Unknown local plugin "${alias}" in headless runtime.`);
17464
+ const main = typeof plugin.config.main === "string" && plugin.config.main ? plugin.config.main : "index.js";
17465
+ return executeModule(path.resolve(options.miniprogramRootPath, plugin.virtualRoot, main), null).exports;
17466
+ };
17251
17467
  return {
17252
17468
  close() {
17253
17469
  moduleCache.clear();
@@ -17258,11 +17474,13 @@ function createModuleLoader(registries, getCurrentPages, getApp, wxDriver, optio
17258
17474
  return registries.appDefinition;
17259
17475
  },
17260
17476
  executePageModule(filePath, route) {
17261
- executeModule(filePath, {
17477
+ const fallbackDefinition = executeModule(filePath, {
17262
17478
  kind: "page",
17263
- route
17264
- });
17265
- const definition = registries.pages.get(route);
17479
+ route,
17480
+ componentDefinitions: []
17481
+ }).componentDefinitions.at(-1);
17482
+ const definition = registries.pages.get(route) ?? (fallbackDefinition ? normalizeComponentPageDefinition(fallbackDefinition) : void 0);
17483
+ if (!registries.pages.has(route) && definition) registerPageDefinition(registries, definition, route);
17266
17484
  if (!definition) throw new Error(`Page() was not registered for route "${route}" while executing ${normalize(filePath)}.`);
17267
17485
  return definition;
17268
17486
  },
@@ -19015,6 +19233,7 @@ var HeadlessSession = class {
19015
19233
  appDefinition = null;
19016
19234
  appInstance = null;
19017
19235
  moduleLoader;
19236
+ eventListeners = /* @__PURE__ */ new Map();
19018
19237
  registries;
19019
19238
  currentPageInstance = null;
19020
19239
  pages = [];
@@ -19156,7 +19375,10 @@ var HeadlessSession = class {
19156
19375
  }, {
19157
19376
  artifactSource: this.project.artifactSource,
19158
19377
  globals: options.globals,
19159
- kernel: this.kernel
19378
+ kernel: this.kernel,
19379
+ miniprogramRootPath: this.project.miniprogramRootPath,
19380
+ onConsole: (entry) => this.emit("console", entry),
19381
+ plugins: this.project.plugins
19160
19382
  });
19161
19383
  }
19162
19384
  assertActive() {
@@ -19170,11 +19392,26 @@ var HeadlessSession = class {
19170
19392
  this.renderRequestPending = false;
19171
19393
  this.wxState.close();
19172
19394
  this.moduleLoader.close();
19395
+ this.eventListeners.clear();
19173
19396
  this.kernel.close();
19174
19397
  }
19175
19398
  getDiagnostics() {
19176
19399
  return this.kernel.diagnostics.getEntries();
19177
19400
  }
19401
+ on(eventName, handler) {
19402
+ if (!eventName || typeof handler !== "function") return;
19403
+ const listeners = this.eventListeners.get(eventName) ?? /* @__PURE__ */ new Set();
19404
+ listeners.add(handler);
19405
+ this.eventListeners.set(eventName, listeners);
19406
+ }
19407
+ removeListener(eventName, handler) {
19408
+ const listeners = this.eventListeners.get(eventName);
19409
+ listeners?.delete(handler);
19410
+ if (listeners?.size === 0) this.eventListeners.delete(eventName);
19411
+ }
19412
+ emit(eventName, ...args) {
19413
+ for (const handler of [...this.eventListeners.get(eventName) ?? []]) handler(...args);
19414
+ }
19178
19415
  get isClosed() {
19179
19416
  return this.kernel.isClosed;
19180
19417
  }
@@ -19186,6 +19423,10 @@ var HeadlessSession = class {
19186
19423
  this.assertActive();
19187
19424
  return this.pages.slice();
19188
19425
  }
19426
+ getWx() {
19427
+ this.assertActive();
19428
+ return this.moduleLoader.wx;
19429
+ }
19189
19430
  callWxMethod(methodName, ...args) {
19190
19431
  this.assertActive();
19191
19432
  const method = this.moduleLoader.wx[methodName];
@@ -19581,9 +19822,19 @@ var HeadlessSession = class {
19581
19822
  this.appDefinition = this.moduleLoader.executeAppModule(appModulePath);
19582
19823
  this.appInstance = createAppInstance(this.appDefinition);
19583
19824
  this.appInstance.onLaunch?.(launchOptions);
19825
+ this.syncRuntimeAppState();
19584
19826
  this.appInstance.onShow?.(launchOptions);
19585
19827
  return this.appInstance;
19586
19828
  }
19829
+ syncRuntimeAppState() {
19830
+ const runtimeApp = this.appInstance?.__wevuRuntimeApp;
19831
+ if (!runtimeApp || typeof runtimeApp !== "object" || !this.appInstance) return;
19832
+ if (runtimeApp.globalData && typeof runtimeApp.globalData === "object") this.appInstance.globalData = {
19833
+ ...this.appInstance.globalData,
19834
+ ...runtimeApp.globalData
19835
+ };
19836
+ if (Object.hasOwn(runtimeApp, "routes")) this.appInstance.routes = runtimeApp.routes;
19837
+ }
19587
19838
  reLaunch(url) {
19588
19839
  const target = this.resolveNavigationTarget(url);
19589
19840
  this.bootstrap(createAppLaunchOptions(target.normalizedRoute, target.query));
@@ -19746,8 +19997,9 @@ var HeadlessSession = class {
19746
19997
  });
19747
19998
  }
19748
19999
  createFreshPage(target) {
19749
- const pageModulePath = path.resolve(this.project.miniprogramRootPath, `${target.routeRecord.route}.js`);
19750
- const pageConfigPath = path.resolve(this.project.miniprogramRootPath, `${target.routeRecord.route}.json`);
20000
+ const resourcePath = target.routeRecord.resourcePath ?? target.routeRecord.route;
20001
+ const pageModulePath = path.resolve(this.project.miniprogramRootPath, `${resourcePath}.js`);
20002
+ const pageConfigPath = path.resolve(this.project.miniprogramRootPath, `${resourcePath}.json`);
19751
20003
  const pageDefinition = this.moduleLoader.executePageModule(pageModulePath, target.routeRecord.route);
19752
20004
  const pageConfig = readJsonObject(this.project, pageConfigPath);
19753
20005
  const pageInstance = createPageInstance(`/${target.routeRecord.route}`, pageDefinition, target.query, {
@@ -19782,7 +20034,9 @@ var HeadlessSession = class {
19782
20034
  }
19783
20035
  resolveNavigationTarget(url) {
19784
20036
  const { pathname, query } = parseNavigationUrl(url);
19785
- const normalizedRoute = resolveNavigationPath(pathname, this.currentPageInstance ? stripLeadingSlash(this.currentPageInstance.route) : void 0);
20037
+ const baseRoute = this.currentPageInstance ? stripLeadingSlash(this.currentPageInstance.route) : void 0;
20038
+ const pluginRequest = resolvePluginRequest(this.project.plugins, pathname, "page");
20039
+ const normalizedRoute = pluginRequest ? `plugin-private://${pluginRequest.plugin.provider}/${pluginRequest.resourcePath.slice(pluginRequest.plugin.virtualRoot.length + 1)}` : resolveNavigationPath(pathname, baseRoute);
19786
20040
  const routeRecord = this.project.routes.find((item) => item.route === normalizedRoute);
19787
20041
  if (!routeRecord) {
19788
20042
  this.bootstrap(createAppLaunchOptions(normalizedRoute, query));
@@ -20231,7 +20485,10 @@ var HeadlessTestingPageHandle = class {
20231
20485
  return await runWithTimeout(async () => {
20232
20486
  this.session?.renderCurrentPage();
20233
20487
  const method = this.page[normalizedMethodName];
20234
- if (typeof method !== "function") throw new TypeError(`Method "${normalizedMethodName}" does not exist on headless page ${this.page.route}.`);
20488
+ if (typeof method !== "function") {
20489
+ if (options.routeOnly) return;
20490
+ throw new TypeError(`Method "${normalizedMethodName}" does not exist on headless page ${this.page.route}.`);
20491
+ }
20235
20492
  return cloneProtocolValue(await method.apply(this.page, args));
20236
20493
  }, options.timeout, `Timed out calling page method "${normalizedMethodName}" in headless testing runtime.`);
20237
20494
  }
@@ -20406,6 +20663,24 @@ var HeadlessTestingSessionHandle = class {
20406
20663
  async close() {
20407
20664
  this.session.close();
20408
20665
  }
20666
+ on(eventName, handler) {
20667
+ this.session.on(eventName, handler);
20668
+ return this;
20669
+ }
20670
+ removeListener(eventName, handler) {
20671
+ this.session.removeListener(eventName, handler);
20672
+ return this;
20673
+ }
20674
+ off(eventName, handler) {
20675
+ return this.removeListener(eventName, handler);
20676
+ }
20677
+ async evaluate(evaluator, ...args) {
20678
+ return await this.evaluateWithOptions(evaluator, {}, ...args);
20679
+ }
20680
+ async evaluateWithOptions(evaluator, options = {}, ...args) {
20681
+ this.session.assertActive();
20682
+ return await runWithTimeout(() => this.evaluateInRuntime(evaluator, args), options.timeout, "Timed out evaluating code in headless testing runtime.");
20683
+ }
20409
20684
  async currentPage() {
20410
20685
  this.session.assertActive();
20411
20686
  const current = this.session.getCurrentPages().at(-1);
@@ -20502,12 +20777,45 @@ var HeadlessTestingSessionHandle = class {
20502
20777
  if (!page) throw new Error(`Failed to ${methodName} route "${route}" through the headless wx runtime.`);
20503
20778
  return page;
20504
20779
  }
20780
+ async evaluateInRuntime(evaluator, args) {
20781
+ const runtimeGlobals = {
20782
+ getApp: () => this.session.getApp(),
20783
+ getCurrentPages: () => this.session.getCurrentPages(),
20784
+ wx: this.session.getWx()
20785
+ };
20786
+ const host = globalThis;
20787
+ const previous = /* @__PURE__ */ new Map();
20788
+ for (const [name, value] of Object.entries(runtimeGlobals)) {
20789
+ previous.set(name, Object.getOwnPropertyDescriptor(host, name));
20790
+ Object.defineProperty(host, name, {
20791
+ configurable: true,
20792
+ enumerable: false,
20793
+ value,
20794
+ writable: true
20795
+ });
20796
+ }
20797
+ try {
20798
+ const fn = typeof evaluator === "string" ? new vm.Script(`(${evaluator})`).runInThisContext() : evaluator;
20799
+ if (typeof fn !== "function") throw new TypeError("Headless evaluator must be a function or function source string.");
20800
+ return await fn(...args);
20801
+ } finally {
20802
+ for (const [name, descriptor] of previous) if (descriptor) Object.defineProperty(host, name, descriptor);
20803
+ else delete host[name];
20804
+ }
20805
+ }
20505
20806
  };
20506
20807
  //#endregion
20507
20808
  //#region ../../mpcore/packages/simulator/src/testing/launch.ts
20809
+ function resolveInitialRoute(session) {
20810
+ const entryPagePath = typeof session.project.appConfig.entryPagePath === "string" ? session.project.appConfig.entryPagePath.trim().replace(/^\/+/, "") : "";
20811
+ if (entryPagePath && session.project.routes.some((route) => route.route === entryPagePath)) return entryPagePath;
20812
+ return session.project.routes[0]?.route ?? null;
20813
+ }
20508
20814
  async function launch(options) {
20509
20815
  const session = createHeadlessSession({ projectPath: options.projectPath });
20510
20816
  session.bootstrap();
20817
+ const initialRoute = resolveInitialRoute(session);
20818
+ if (initialRoute) session.reLaunch(`/${initialRoute}`);
20511
20819
  return new HeadlessTestingSessionHandle(session.project, session);
20512
20820
  }
20513
20821
  //#endregion