@immense/vue-pom-generator 1.0.35 → 1.0.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -964,6 +964,18 @@ function nodeHandlerAttributeInfo(node) {
964
964
  }
965
965
  return null;
966
966
  };
967
+ const getRootIdentifierFromMemberChain = (node2) => {
968
+ if (!node2) {
969
+ return null;
970
+ }
971
+ if (isIdentifierNode(node2)) {
972
+ return node2.name;
973
+ }
974
+ if (isMemberExpressionNode(node2)) {
975
+ return getRootIdentifierFromMemberChain(node2.object);
976
+ }
977
+ return null;
978
+ };
967
979
  const getAssignmentTargetName = (lhs) => {
968
980
  if (!lhs) {
969
981
  return null;
@@ -1018,7 +1030,10 @@ function nodeHandlerAttributeInfo(node) {
1018
1030
  }
1019
1031
  if (isMemberExpressionNode(arg)) {
1020
1032
  const stableName = getLastIdentifierFromMemberChain(arg);
1021
- if (stableName) {
1033
+ const rootName = getRootIdentifierFromMemberChain(arg);
1034
+ const firstChar = (rootName ?? "").charAt(0);
1035
+ const isConstantLikeRoot = firstChar !== "" && firstChar === firstChar.toUpperCase() && firstChar !== firstChar.toLowerCase();
1036
+ if (stableName && isConstantLikeRoot) {
1022
1037
  return toPascalCase(stableName.slice(0, 24));
1023
1038
  }
1024
1039
  }
@@ -1036,12 +1051,15 @@ function nodeHandlerAttributeInfo(node) {
1036
1051
  const first = args.length > 0 ? args[0] : null;
1037
1052
  if (!isObjectExpressionNode(first)) {
1038
1053
  const parts2 = [];
1039
- for (const arg of args.slice(0, 2)) {
1054
+ for (const arg of args.slice(0, 4)) {
1040
1055
  const w = stableWordFromValue(arg ?? null);
1041
1056
  if (!w) {
1042
- return null;
1057
+ continue;
1043
1058
  }
1044
1059
  parts2.push(w);
1060
+ if (parts2.length >= 2) {
1061
+ break;
1062
+ }
1045
1063
  }
1046
1064
  if (parts2.length === 0) {
1047
1065
  return null;
@@ -4466,8 +4484,9 @@ function getWidgetInstancesForView(componentName, dataTestIdSet, availableClassI
4466
4484
  } else {
4467
4485
  continue;
4468
4486
  }
4469
- if (!availableClassIdentifiers.has(className))
4487
+ if (!availableClassIdentifiers.has(className)) {
4470
4488
  continue;
4489
+ }
4471
4490
  const viewPrefix = `${componentName}-`;
4472
4491
  const descriptorRaw = stem.startsWith(viewPrefix) ? stem.slice(viewPrefix.length) : stem;
4473
4492
  const descriptorPascal = toPascalCaseLocal(descriptorRaw);
@@ -4536,32 +4555,31 @@ function getConstructor(childrenComponent, componentHierarchyMap, attachmentsFor
4536
4555
  return `${content}
4537
4556
  `;
4538
4557
  }
4539
- function getGenerationMetrics(componentHierarchyMap) {
4540
- let selectorCount = 0;
4541
- let generatedMethodCount = 0;
4542
- for (const deps of componentHierarchyMap.values()) {
4543
- selectorCount += deps.dataTestIdSet?.size ?? 0;
4544
- generatedMethodCount += deps.generatedMethods?.size ?? 0;
4558
+ function summarizeHierarchyMap(componentHierarchyMap) {
4559
+ let interactiveComponentCount = 0;
4560
+ let dataTestIdCount = 0;
4561
+ for (const dependencies of componentHierarchyMap.values()) {
4562
+ const selectorCount = dependencies.dataTestIdSet?.size ?? 0;
4563
+ if (selectorCount > 0) {
4564
+ interactiveComponentCount += 1;
4565
+ dataTestIdCount += selectorCount;
4566
+ }
4545
4567
  }
4546
4568
  return {
4547
4569
  entryCount: componentHierarchyMap.size,
4548
- selectorCount,
4549
- generatedMethodCount
4570
+ interactiveComponentCount,
4571
+ dataTestIdCount
4550
4572
  };
4551
4573
  }
4552
- function isLessRich(current, previous) {
4553
- if (current.entryCount !== previous.entryCount) {
4554
- return current.entryCount < previous.entryCount;
4574
+ function isLessRich(candidate, previous) {
4575
+ if (candidate.dataTestIdCount !== previous.dataTestIdCount) {
4576
+ return candidate.dataTestIdCount < previous.dataTestIdCount;
4555
4577
  }
4556
- if (current.selectorCount !== previous.selectorCount) {
4557
- return current.selectorCount < previous.selectorCount;
4578
+ if (candidate.interactiveComponentCount !== previous.interactiveComponentCount) {
4579
+ return candidate.interactiveComponentCount < previous.interactiveComponentCount;
4558
4580
  }
4559
- return current.generatedMethodCount < previous.generatedMethodCount;
4560
- }
4561
- function getGenerationMetricsKey(projectRoot, outDir) {
4562
- return path.resolve(projectRoot, outDir ?? "./pom");
4581
+ return candidate.entryCount < previous.entryCount;
4563
4582
  }
4564
- const buildGenerationMetricsByOutputKey = /* @__PURE__ */ new Map();
4565
4583
  function createBuildProcessorPlugin(options) {
4566
4584
  const {
4567
4585
  componentHierarchyMap,
@@ -4586,6 +4604,11 @@ function createBuildProcessorPlugin(options) {
4586
4604
  routerModuleShims,
4587
4605
  loggerRef
4588
4606
  } = options;
4607
+ let lastGeneratedMetrics = {
4608
+ entryCount: 0,
4609
+ interactiveComponentCount: 0,
4610
+ dataTestIdCount: 0
4611
+ };
4589
4612
  return {
4590
4613
  name: "vue-pom-generator-build",
4591
4614
  // This plugin exists to generate code on build output; it is not needed during dev-server HMR.
@@ -4638,17 +4661,18 @@ function createBuildProcessorPlugin(options) {
4638
4661
  }
4639
4662
  this.addWatchFile(pointerPath);
4640
4663
  },
4641
- buildEnd() {
4642
- const metrics = getGenerationMetrics(componentHierarchyMap);
4643
- if (metrics.entryCount <= 0 || metrics.selectorCount <= 0) {
4664
+ async buildEnd(error) {
4665
+ if (error) {
4666
+ return;
4667
+ }
4668
+ const metrics = summarizeHierarchyMap(componentHierarchyMap);
4669
+ if (metrics.dataTestIdCount <= 0) {
4644
4670
  return;
4645
4671
  }
4646
- const generationMetricsKey = getGenerationMetricsKey(projectRootRef.current, outDir);
4647
- const previousMetrics = buildGenerationMetricsByOutputKey.get(generationMetricsKey);
4648
- if (previousMetrics && isLessRich(metrics, previousMetrics)) {
4672
+ if (isLessRich(metrics, lastGeneratedMetrics)) {
4649
4673
  return;
4650
4674
  }
4651
- generateFiles(componentHierarchyMap, vueFilesPathMap, normalizedBasePagePath, {
4675
+ await generateFiles(componentHierarchyMap, vueFilesPathMap, normalizedBasePagePath, {
4652
4676
  outDir,
4653
4677
  emitLanguages,
4654
4678
  csharp,
@@ -4665,8 +4689,8 @@ function createBuildProcessorPlugin(options) {
4665
4689
  viewsDir,
4666
4690
  scanDirs
4667
4691
  });
4668
- buildGenerationMetricsByOutputKey.set(generationMetricsKey, metrics);
4669
- loggerRef.current.info(`generated POMs (${metrics.entryCount} entries, ${metrics.selectorCount} selectors)`);
4692
+ lastGeneratedMetrics = metrics;
4693
+ loggerRef.current.info(`generated POMs (${metrics.entryCount} entries, ${metrics.interactiveComponentCount} interactive components, ${metrics.dataTestIdCount} selectors)`);
4670
4694
  },
4671
4695
  closeBundle() {
4672
4696
  loggerRef.current.info("build complete");
@@ -5755,7 +5779,6 @@ Fix: remove the explicit ${attrLabel}, or change existingIdBehavior to "overwrit
5755
5779
  }
5756
5780
  };
5757
5781
  }
5758
- const devStartupMetricsByOutputKey = /* @__PURE__ */ new Map();
5759
5782
  function createDevProcessorPlugin(options) {
5760
5783
  const {
5761
5784
  nativeWrappers,
@@ -5921,11 +5944,7 @@ function createDevProcessorPlugin(options) {
5921
5944
  nativeWrappers,
5922
5945
  excludedComponents,
5923
5946
  getViewsDirAbs(),
5924
- {
5925
- existingIdBehavior: "preserve",
5926
- testIdAttribute,
5927
- wrapperSearchRoots: getWrapperSearchRoots()
5928
- }
5947
+ { existingIdBehavior: "preserve", testIdAttribute, wrapperSearchRoots: getWrapperSearchRoots() }
5929
5948
  )
5930
5949
  ]
5931
5950
  });
@@ -5957,21 +5976,6 @@ function createDevProcessorPlugin(options) {
5957
5976
  logInfo(`initial compile: ${compiledCount}/${totalVueFiles} files in ${formatMs(t1 - t0)} (components=${snapshotHierarchy.size})`);
5958
5977
  };
5959
5978
  const generateAggregatedFromSnapshot = (reason) => {
5960
- const metrics = getGenerationMetrics(snapshotHierarchy);
5961
- if (metrics.entryCount <= 0 || metrics.selectorCount <= 0) {
5962
- logInfo(`generate(${reason}): skipped empty snapshot (components=${metrics.entryCount}, selectors=${metrics.selectorCount})`);
5963
- return;
5964
- }
5965
- const generationMetricsKey = getGenerationMetricsKey(projectRootRef.current, outDir);
5966
- if (reason === "startup") {
5967
- const previousMetrics = devStartupMetricsByOutputKey.get(generationMetricsKey);
5968
- if (previousMetrics && isLessRich(metrics, previousMetrics)) {
5969
- logInfo(
5970
- `generate(${reason}): skipped smaller snapshot (components=${metrics.entryCount}, selectors=${metrics.selectorCount})`
5971
- );
5972
- return;
5973
- }
5974
- }
5975
5979
  const t0 = performance.now();
5976
5980
  generateFiles(snapshotHierarchy, snapshotVuePathMap, normalizedBasePagePath, {
5977
5981
  outDir,
@@ -5986,15 +5990,10 @@ function createDevProcessorPlugin(options) {
5986
5990
  testIdAttribute,
5987
5991
  vueRouterFluentChaining: routerAwarePoms,
5988
5992
  routerEntry: resolvedRouterEntry,
5989
- routerType,
5990
- viewsDir,
5991
- scanDirs
5993
+ routerType
5992
5994
  });
5993
- if (reason === "startup") {
5994
- devStartupMetricsByOutputKey.set(generationMetricsKey, metrics);
5995
- }
5996
5995
  const t1 = performance.now();
5997
- logInfo(`generate(${reason}): components=${metrics.entryCount} selectors=${metrics.selectorCount} in ${formatMs(t1 - t0)}`);
5996
+ logInfo(`generate(${reason}): components=${snapshotHierarchy.size} in ${formatMs(t1 - t0)}`);
5998
5997
  };
5999
5998
  const initialBuildPromise = (async () => {
6000
5999
  const t0 = performance.now();
@@ -6241,6 +6240,7 @@ function createSupportPlugins(options) {
6241
6240
  basePageClassPath,
6242
6241
  outDir,
6243
6242
  emitLanguages,
6243
+ csharp,
6244
6244
  generateFixtures,
6245
6245
  customPomAttachments,
6246
6246
  customPomDir,
@@ -6605,7 +6605,7 @@ function createVuePluginWithTestIds(options) {
6605
6605
  };
6606
6606
  }
6607
6607
  };
6608
- return { metadataCollectorPlugin, internalVuePlugin, nuxtVueBridgePlugin };
6608
+ return { metadataCollectorPlugin, internalVuePlugin, nuxtVueBridgePlugin, templateCompilerOptions };
6609
6609
  }
6610
6610
  function assertNonEmptyString(value, name) {
6611
6611
  if (!value || !value.trim()) {
@@ -6650,6 +6650,58 @@ function assertRouterModuleShims(value, name) {
6650
6650
  function resolveFromProjectRoot(projectRoot, maybePath) {
6651
6651
  return path.isAbsolute(maybePath) ? maybePath : path.resolve(projectRoot, maybePath);
6652
6652
  }
6653
+ const sharedGeneratorStateRegistry = /* @__PURE__ */ new Map();
6654
+ function toArray(value) {
6655
+ return Array.isArray(value) ? value : [];
6656
+ }
6657
+ function getSharedGeneratorState(key) {
6658
+ let state = sharedGeneratorStateRegistry.get(key);
6659
+ if (!state) {
6660
+ state = {
6661
+ componentTestIds: /* @__PURE__ */ new Map(),
6662
+ elementMetadata: /* @__PURE__ */ new Map(),
6663
+ semanticNameMap: /* @__PURE__ */ new Map(),
6664
+ componentHierarchyMap: /* @__PURE__ */ new Map(),
6665
+ vueFilesPathMap: /* @__PURE__ */ new Map()
6666
+ };
6667
+ sharedGeneratorStateRegistry.set(key, state);
6668
+ }
6669
+ return state;
6670
+ }
6671
+ function applyTemplateCompilerOptionsToResolvedVuePlugin(config, templateCompilerOptions, mode) {
6672
+ const viteVuePlugin = (config.plugins ?? []).find((plugin) => plugin.name === "vite:vue");
6673
+ if (!viteVuePlugin?.api) {
6674
+ if (mode === "external") {
6675
+ throw new Error(
6676
+ '[vue-pom-generator] vuePluginOwnership="external" requires the resolved Vite Vue plugin, but none was found. Add vue() to your Vite plugins before spreading createVuePomGeneratorPlugins(...).'
6677
+ );
6678
+ }
6679
+ throw new Error("[vue-pom-generator] Nuxt mode requires the resolved Vite Vue plugin, but none was found.");
6680
+ }
6681
+ const currentOptions = viteVuePlugin.api.options ?? {};
6682
+ const currentTemplate = currentOptions.template ?? {};
6683
+ const currentCompilerOptions = currentTemplate.compilerOptions ?? {};
6684
+ const mergedNodeTransforms = [
6685
+ ...toArray(currentCompilerOptions.nodeTransforms),
6686
+ ...toArray(templateCompilerOptions.nodeTransforms)
6687
+ ];
6688
+ const mergedExpressionPlugins = Array.from(/* @__PURE__ */ new Set([
6689
+ ...toArray(currentCompilerOptions.expressionPlugins),
6690
+ ...toArray(templateCompilerOptions.expressionPlugins)
6691
+ ]));
6692
+ viteVuePlugin.api.options = {
6693
+ ...currentOptions,
6694
+ template: {
6695
+ ...currentTemplate,
6696
+ compilerOptions: {
6697
+ ...currentCompilerOptions,
6698
+ ...templateCompilerOptions,
6699
+ ...mergedExpressionPlugins.length > 0 ? { expressionPlugins: mergedExpressionPlugins } : {},
6700
+ nodeTransforms: mergedNodeTransforms
6701
+ }
6702
+ }
6703
+ };
6704
+ }
6653
6705
  function assertNotVitePluginInstance(options) {
6654
6706
  const candidate = options;
6655
6707
  const pluginLikeKeys = [
@@ -6668,7 +6720,7 @@ function assertNotVitePluginInstance(options) {
6668
6720
  return;
6669
6721
  }
6670
6722
  throw new TypeError(
6671
- `[vue-pom-generator] Invalid options: received an object that looks like a Vite plugin (found key: "${pluginLikeKey}"). Do not pass vue() into createVuePomGeneratorPlugins(...). Pass Vue plugin options via { vueOptions: { ... } } instead.`
6723
+ `[vue-pom-generator] Invalid options: received an object that looks like a Vite plugin (found key: "${pluginLikeKey}"). Do not pass vue() into createVuePomGeneratorPlugins(...). Pass Vue plugin options via { vueOptions: { ... } } instead, or add vue() separately in Vite and use { vuePluginOwnership: "external" }.`
6672
6724
  );
6673
6725
  }
6674
6726
  function createVuePomGeneratorPlugins(options = {}) {
@@ -6692,6 +6744,12 @@ function createVuePomGeneratorPlugins(options = {}) {
6692
6744
  const routerEntry = generationOptions?.router?.entry;
6693
6745
  const routerType = generationOptions?.router?.type ?? "vue-router";
6694
6746
  const routerModuleShims = generationOptions?.router?.moduleShims;
6747
+ const isNuxt = routerType === "nuxt";
6748
+ if (isNuxt && options.vuePluginOwnership === "internal") {
6749
+ throw new Error('[vue-pom-generator] Nuxt projects must use the resolved app-owned vite:vue plugin. Omit vuePluginOwnership or set it to "external".');
6750
+ }
6751
+ const vuePluginOwnership = isNuxt ? "external" : options.vuePluginOwnership ?? "internal";
6752
+ const usesExternalVuePlugin = vuePluginOwnership === "external";
6695
6753
  const csharp = generationOptions?.csharp;
6696
6754
  const generateFixtures = generationOptions?.playwright?.fixtures;
6697
6755
  const customPoms = generationOptions?.playwright?.customPoms;
@@ -6700,6 +6758,17 @@ function createVuePomGeneratorPlugins(options = {}) {
6700
6758
  const resolvedCustomPomImportAliases = customPoms?.importAliases;
6701
6759
  const resolvedCustomPomImportCollisionBehavior = customPoms?.importNameCollisionBehavior ?? "error";
6702
6760
  const basePageClassPathOverride = generationOptions?.basePageClassPath;
6761
+ const sharedStateKey = JSON.stringify({
6762
+ cwd: process.cwd(),
6763
+ viewsDir,
6764
+ scanDirs,
6765
+ wrapperSearchRoots,
6766
+ outDir,
6767
+ testIdAttribute,
6768
+ routerType,
6769
+ vuePluginOwnership
6770
+ });
6771
+ const sharedState = getSharedGeneratorState(sharedStateKey);
6703
6772
  const projectRootRef = { current: process.cwd() };
6704
6773
  const loggerRef = {
6705
6774
  current: createLogger({ verbosity })
@@ -6720,18 +6789,17 @@ function createVuePomGeneratorPlugins(options = {}) {
6720
6789
  assertNonEmptyString(routerEntry, "[vue-pom-generator] generation.router.entry");
6721
6790
  }
6722
6791
  }
6792
+ if (usesExternalVuePlugin) {
6793
+ applyTemplateCompilerOptionsToResolvedVuePlugin(config, templateCompilerOptions, isNuxt ? "nuxt" : vuePluginOwnership);
6794
+ }
6723
6795
  loggerRef.current.info(`projectRoot=${projectRootRef.current}`);
6724
6796
  loggerRef.current.info(`Active plugins: ${config.plugins.map((p) => p.name).filter((n) => n.includes("vue-pom")).join(", ")}`);
6725
6797
  }
6726
6798
  };
6727
6799
  const getViewsDirAbs = () => resolveFromProjectRoot(projectRootRef.current, viewsDir);
6728
6800
  const getWrapperSearchRootsAbs = () => wrapperSearchRoots.map((root) => resolveFromProjectRoot(projectRootRef.current, root));
6729
- const componentTestIds = /* @__PURE__ */ new Map();
6730
- const elementMetadata = /* @__PURE__ */ new Map();
6731
- const semanticNameMap = /* @__PURE__ */ new Map();
6732
- const componentHierarchyMap = /* @__PURE__ */ new Map();
6733
- const vueFilesPathMap = /* @__PURE__ */ new Map();
6734
- const { metadataCollectorPlugin, internalVuePlugin, nuxtVueBridgePlugin } = createVuePluginWithTestIds({
6801
+ const { componentTestIds, elementMetadata, semanticNameMap, componentHierarchyMap, vueFilesPathMap } = sharedState;
6802
+ const { metadataCollectorPlugin, internalVuePlugin, templateCompilerOptions } = createVuePluginWithTestIds({
6735
6803
  vueOptions,
6736
6804
  existingIdBehavior,
6737
6805
  nameCollisionBehavior,
@@ -6775,14 +6843,15 @@ function createVuePomGeneratorPlugins(options = {}) {
6775
6843
  routerType,
6776
6844
  routerModuleShims
6777
6845
  });
6778
- const isNuxt = routerType === "nuxt";
6779
6846
  if (isNuxt) {
6780
6847
  loggerRef.current.info("Nuxt environment detected. Skipping internal @vitejs/plugin-vue to avoid conflicts.");
6848
+ } else if (usesExternalVuePlugin) {
6849
+ loggerRef.current.info('vuePluginOwnership="external" enabled. Patching the resolved vite:vue plugin instead of creating an internal one.');
6781
6850
  }
6782
6851
  const resultPlugins = [
6783
6852
  configPlugin,
6784
6853
  metadataCollectorPlugin,
6785
- ...isNuxt ? [nuxtVueBridgePlugin] : [internalVuePlugin],
6854
+ ...usesExternalVuePlugin ? [] : [internalVuePlugin],
6786
6855
  ...supportPlugins
6787
6856
  ];
6788
6857
  if (!generationEnabled) {
@@ -6790,7 +6859,7 @@ function createVuePomGeneratorPlugins(options = {}) {
6790
6859
  return [
6791
6860
  configPlugin,
6792
6861
  metadataCollectorPlugin,
6793
- ...isNuxt ? [nuxtVueBridgePlugin] : [internalVuePlugin],
6862
+ ...usesExternalVuePlugin ? [] : [internalVuePlugin],
6794
6863
  virtualModules
6795
6864
  ];
6796
6865
  }