@immense/vue-pom-generator 1.0.36 → 1.0.38

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.cjs CHANGED
@@ -3519,7 +3519,8 @@ async function generateFiles(componentHierarchyMap, vueFilesPathMap, basePageCla
3519
3519
  const fixtureRegistryFile = maybeGenerateFixtureRegistry(componentHierarchyMap, {
3520
3520
  generateFixtures,
3521
3521
  pomOutDir: outDir,
3522
- projectRoot
3522
+ projectRoot,
3523
+ customPomDir
3523
3524
  });
3524
3525
  if (fixtureRegistryFile) {
3525
3526
  writeGeneratedFile(fixtureRegistryFile);
@@ -3918,6 +3919,9 @@ function maybeGenerateFixtureRegistry(componentHierarchyMap, options) {
3918
3919
  const fixtureFileName = looksLikeFilePath ? path.basename(fixtureOutRel) : "fixtures.g.ts";
3919
3920
  const root = options.projectRoot ?? process.cwd();
3920
3921
  const fixtureOutDirAbs = path.isAbsolute(fixtureOutDirRel) ? fixtureOutDirRel : path.resolve(root, fixtureOutDirRel);
3922
+ const customPomDirRel = options.customPomDir ?? "tests/playwright/pom/custom";
3923
+ const customPomDirAbs = path.isAbsolute(customPomDirRel) ? customPomDirRel : path.resolve(root, customPomDirRel);
3924
+ const overridePomDirAbs = path.resolve(path.dirname(customPomDirAbs), "overrides");
3921
3925
  const pomDirAbs = path.isAbsolute(pomOutDir) ? pomOutDir : path.resolve(root, pomOutDir);
3922
3926
  const pomImport = toPosixRelativePath(fixtureOutDirAbs, pomDirAbs);
3923
3927
  const viewClassNames = Array.from(componentHierarchyMap.entries()).filter(([, deps]) => !!deps.isView).map(([name]) => name).sort((a, b) => a.localeCompare(b));
@@ -3940,6 +3944,22 @@ function maybeGenerateFixtureRegistry(componentHierarchyMap, options) {
3940
3944
  return false;
3941
3945
  return true;
3942
3946
  }).sort((a, b) => a.localeCompare(b));
3947
+ const fixtureClassNames = [...viewClassNames, ...componentClassNames];
3948
+ const overrideCtorEntries = fixtureClassNames.map((name) => {
3949
+ const overrideFilePath = path.join(overridePomDirAbs, `${name}.ts`);
3950
+ if (!fs.existsSync(overrideFilePath))
3951
+ return null;
3952
+ return {
3953
+ className: name,
3954
+ localIdentifier: `${name}Override`,
3955
+ importSpecifier: stripExtension(toPosixRelativePath(fixtureOutDirAbs, overrideFilePath))
3956
+ };
3957
+ }).filter((entry) => !!entry);
3958
+ const overrideCtorByClassName = new Map(overrideCtorEntries.map((entry) => [entry.className, entry.localIdentifier]));
3959
+ const overrideImports = overrideCtorEntries.length ? `${overrideCtorEntries.map((entry) => `import { ${entry.className} as ${entry.localIdentifier} } from "${entry.importSpecifier}";`).join("\n")}
3960
+
3961
+ ` : "";
3962
+ const fixtureCtorExpression = (name) => overrideCtorByClassName.get(name) ?? `Pom.${name}`;
3943
3963
  const header = `${eslintSuppressionHeader}/**
3944
3964
  * DO NOT MODIFY BY HAND
3945
3965
  *
@@ -3948,8 +3968,8 @@ function maybeGenerateFixtureRegistry(componentHierarchyMap, options) {
3948
3968
  */
3949
3969
 
3950
3970
  `;
3951
- const fixturesTypeEntries = viewClassNames.map((name) => ` ${lowerFirst(name)}: Pom.${name},`).join("\n");
3952
- const componentFixturesTypeEntries = componentClassNames.map((name) => ` ${lowerFirst(name)}: Pom.${name},`).join("\n");
3971
+ const fixturesTypeEntries = viewClassNames.map((name) => ` ${lowerFirst(name)}: ${fixtureCtorExpression(name)},`).join("\n");
3972
+ const componentFixturesTypeEntries = componentClassNames.map((name) => ` ${lowerFirst(name)}: ${fixtureCtorExpression(name)},`).join("\n");
3953
3973
  const pomFactoryType = `export type PomConstructor<T> = new (page: PwPage) => T;
3954
3974
 
3955
3975
  export interface PomFactory {
@@ -3962,8 +3982,7 @@ export interface PomFactory {
3962
3982
  import { expect, test as base } from "@playwright/test";
3963
3983
  import type { Page as PwPage } from "@playwright/test";
3964
3984
  import * as Pom from "${pomImport}";
3965
-
3966
- export interface PlaywrightOptions {
3985
+ ${overrideImports}export interface PlaywrightOptions {
3967
3986
  animation: Pom.PlaywrightAnimationOptions;
3968
3987
  }
3969
3988
 
@@ -4525,8 +4544,9 @@ function getWidgetInstancesForView(componentName, dataTestIdSet, availableClassI
4525
4544
  } else {
4526
4545
  continue;
4527
4546
  }
4528
- if (!availableClassIdentifiers.has(className))
4547
+ if (!availableClassIdentifiers.has(className)) {
4529
4548
  continue;
4549
+ }
4530
4550
  const viewPrefix = `${componentName}-`;
4531
4551
  const descriptorRaw = stem.startsWith(viewPrefix) ? stem.slice(viewPrefix.length) : stem;
4532
4552
  const descriptorPascal = toPascalCaseLocal(descriptorRaw);
@@ -4595,32 +4615,31 @@ function getConstructor(childrenComponent, componentHierarchyMap, attachmentsFor
4595
4615
  return `${content}
4596
4616
  `;
4597
4617
  }
4598
- function getGenerationMetrics(componentHierarchyMap) {
4599
- let selectorCount = 0;
4600
- let generatedMethodCount = 0;
4601
- for (const deps of componentHierarchyMap.values()) {
4602
- selectorCount += deps.dataTestIdSet?.size ?? 0;
4603
- generatedMethodCount += deps.generatedMethods?.size ?? 0;
4618
+ function summarizeHierarchyMap(componentHierarchyMap) {
4619
+ let interactiveComponentCount = 0;
4620
+ let dataTestIdCount = 0;
4621
+ for (const dependencies of componentHierarchyMap.values()) {
4622
+ const selectorCount = dependencies.dataTestIdSet?.size ?? 0;
4623
+ if (selectorCount > 0) {
4624
+ interactiveComponentCount += 1;
4625
+ dataTestIdCount += selectorCount;
4626
+ }
4604
4627
  }
4605
4628
  return {
4606
4629
  entryCount: componentHierarchyMap.size,
4607
- selectorCount,
4608
- generatedMethodCount
4630
+ interactiveComponentCount,
4631
+ dataTestIdCount
4609
4632
  };
4610
4633
  }
4611
- function isLessRich(current, previous) {
4612
- if (current.entryCount !== previous.entryCount) {
4613
- return current.entryCount < previous.entryCount;
4634
+ function isLessRich(candidate, previous) {
4635
+ if (candidate.dataTestIdCount !== previous.dataTestIdCount) {
4636
+ return candidate.dataTestIdCount < previous.dataTestIdCount;
4614
4637
  }
4615
- if (current.selectorCount !== previous.selectorCount) {
4616
- return current.selectorCount < previous.selectorCount;
4638
+ if (candidate.interactiveComponentCount !== previous.interactiveComponentCount) {
4639
+ return candidate.interactiveComponentCount < previous.interactiveComponentCount;
4617
4640
  }
4618
- return current.generatedMethodCount < previous.generatedMethodCount;
4619
- }
4620
- function getGenerationMetricsKey(projectRoot, outDir) {
4621
- return path.resolve(projectRoot, outDir ?? "./pom");
4641
+ return candidate.entryCount < previous.entryCount;
4622
4642
  }
4623
- const buildGenerationMetricsByOutputKey = /* @__PURE__ */ new Map();
4624
4643
  function createBuildProcessorPlugin(options) {
4625
4644
  const {
4626
4645
  componentHierarchyMap,
@@ -4645,6 +4664,11 @@ function createBuildProcessorPlugin(options) {
4645
4664
  routerModuleShims,
4646
4665
  loggerRef
4647
4666
  } = options;
4667
+ let lastGeneratedMetrics = {
4668
+ entryCount: 0,
4669
+ interactiveComponentCount: 0,
4670
+ dataTestIdCount: 0
4671
+ };
4648
4672
  return {
4649
4673
  name: "vue-pom-generator-build",
4650
4674
  // This plugin exists to generate code on build output; it is not needed during dev-server HMR.
@@ -4697,17 +4721,18 @@ function createBuildProcessorPlugin(options) {
4697
4721
  }
4698
4722
  this.addWatchFile(pointerPath);
4699
4723
  },
4700
- buildEnd() {
4701
- const metrics = getGenerationMetrics(componentHierarchyMap);
4702
- if (metrics.entryCount <= 0 || metrics.selectorCount <= 0) {
4724
+ async buildEnd(error) {
4725
+ if (error) {
4703
4726
  return;
4704
4727
  }
4705
- const generationMetricsKey = getGenerationMetricsKey(projectRootRef.current, outDir);
4706
- const previousMetrics = buildGenerationMetricsByOutputKey.get(generationMetricsKey);
4707
- if (previousMetrics && isLessRich(metrics, previousMetrics)) {
4728
+ const metrics = summarizeHierarchyMap(componentHierarchyMap);
4729
+ if (metrics.dataTestIdCount <= 0) {
4708
4730
  return;
4709
4731
  }
4710
- generateFiles(componentHierarchyMap, vueFilesPathMap, normalizedBasePagePath, {
4732
+ if (isLessRich(metrics, lastGeneratedMetrics)) {
4733
+ return;
4734
+ }
4735
+ await generateFiles(componentHierarchyMap, vueFilesPathMap, normalizedBasePagePath, {
4711
4736
  outDir,
4712
4737
  emitLanguages,
4713
4738
  csharp,
@@ -4724,8 +4749,8 @@ function createBuildProcessorPlugin(options) {
4724
4749
  viewsDir,
4725
4750
  scanDirs
4726
4751
  });
4727
- buildGenerationMetricsByOutputKey.set(generationMetricsKey, metrics);
4728
- loggerRef.current.info(`generated POMs (${metrics.entryCount} entries, ${metrics.selectorCount} selectors)`);
4752
+ lastGeneratedMetrics = metrics;
4753
+ loggerRef.current.info(`generated POMs (${metrics.entryCount} entries, ${metrics.interactiveComponentCount} interactive components, ${metrics.dataTestIdCount} selectors)`);
4729
4754
  },
4730
4755
  closeBundle() {
4731
4756
  loggerRef.current.info("build complete");
@@ -5814,7 +5839,6 @@ Fix: remove the explicit ${attrLabel}, or change existingIdBehavior to "overwrit
5814
5839
  }
5815
5840
  };
5816
5841
  }
5817
- const devStartupMetricsByOutputKey = /* @__PURE__ */ new Map();
5818
5842
  function createDevProcessorPlugin(options) {
5819
5843
  const {
5820
5844
  nativeWrappers,
@@ -5980,11 +6004,7 @@ function createDevProcessorPlugin(options) {
5980
6004
  nativeWrappers,
5981
6005
  excludedComponents,
5982
6006
  getViewsDirAbs(),
5983
- {
5984
- existingIdBehavior: "preserve",
5985
- testIdAttribute,
5986
- wrapperSearchRoots: getWrapperSearchRoots()
5987
- }
6007
+ { existingIdBehavior: "preserve", testIdAttribute, wrapperSearchRoots: getWrapperSearchRoots() }
5988
6008
  )
5989
6009
  ]
5990
6010
  });
@@ -6016,21 +6036,6 @@ function createDevProcessorPlugin(options) {
6016
6036
  logInfo(`initial compile: ${compiledCount}/${totalVueFiles} files in ${formatMs(t1 - t0)} (components=${snapshotHierarchy.size})`);
6017
6037
  };
6018
6038
  const generateAggregatedFromSnapshot = (reason) => {
6019
- const metrics = getGenerationMetrics(snapshotHierarchy);
6020
- if (metrics.entryCount <= 0 || metrics.selectorCount <= 0) {
6021
- logInfo(`generate(${reason}): skipped empty snapshot (components=${metrics.entryCount}, selectors=${metrics.selectorCount})`);
6022
- return;
6023
- }
6024
- const generationMetricsKey = getGenerationMetricsKey(projectRootRef.current, outDir);
6025
- if (reason === "startup") {
6026
- const previousMetrics = devStartupMetricsByOutputKey.get(generationMetricsKey);
6027
- if (previousMetrics && isLessRich(metrics, previousMetrics)) {
6028
- logInfo(
6029
- `generate(${reason}): skipped smaller snapshot (components=${metrics.entryCount}, selectors=${metrics.selectorCount})`
6030
- );
6031
- return;
6032
- }
6033
- }
6034
6039
  const t0 = node_perf_hooks.performance.now();
6035
6040
  generateFiles(snapshotHierarchy, snapshotVuePathMap, normalizedBasePagePath, {
6036
6041
  outDir,
@@ -6045,15 +6050,10 @@ function createDevProcessorPlugin(options) {
6045
6050
  testIdAttribute,
6046
6051
  vueRouterFluentChaining: routerAwarePoms,
6047
6052
  routerEntry: resolvedRouterEntry,
6048
- routerType,
6049
- viewsDir,
6050
- scanDirs
6053
+ routerType
6051
6054
  });
6052
- if (reason === "startup") {
6053
- devStartupMetricsByOutputKey.set(generationMetricsKey, metrics);
6054
- }
6055
6055
  const t1 = node_perf_hooks.performance.now();
6056
- logInfo(`generate(${reason}): components=${metrics.entryCount} selectors=${metrics.selectorCount} in ${formatMs(t1 - t0)}`);
6056
+ logInfo(`generate(${reason}): components=${snapshotHierarchy.size} in ${formatMs(t1 - t0)}`);
6057
6057
  };
6058
6058
  const initialBuildPromise = (async () => {
6059
6059
  const t0 = node_perf_hooks.performance.now();
@@ -6300,6 +6300,7 @@ function createSupportPlugins(options) {
6300
6300
  basePageClassPath,
6301
6301
  outDir,
6302
6302
  emitLanguages,
6303
+ csharp,
6303
6304
  generateFixtures,
6304
6305
  customPomAttachments,
6305
6306
  customPomDir,
@@ -6664,7 +6665,7 @@ function createVuePluginWithTestIds(options) {
6664
6665
  };
6665
6666
  }
6666
6667
  };
6667
- return { metadataCollectorPlugin, internalVuePlugin, nuxtVueBridgePlugin };
6668
+ return { metadataCollectorPlugin, internalVuePlugin, nuxtVueBridgePlugin, templateCompilerOptions };
6668
6669
  }
6669
6670
  function assertNonEmptyString(value, name) {
6670
6671
  if (!value || !value.trim()) {
@@ -6709,6 +6710,58 @@ function assertRouterModuleShims(value, name) {
6709
6710
  function resolveFromProjectRoot(projectRoot, maybePath) {
6710
6711
  return path.isAbsolute(maybePath) ? maybePath : path.resolve(projectRoot, maybePath);
6711
6712
  }
6713
+ const sharedGeneratorStateRegistry = /* @__PURE__ */ new Map();
6714
+ function toArray(value) {
6715
+ return Array.isArray(value) ? value : [];
6716
+ }
6717
+ function getSharedGeneratorState(key) {
6718
+ let state = sharedGeneratorStateRegistry.get(key);
6719
+ if (!state) {
6720
+ state = {
6721
+ componentTestIds: /* @__PURE__ */ new Map(),
6722
+ elementMetadata: /* @__PURE__ */ new Map(),
6723
+ semanticNameMap: /* @__PURE__ */ new Map(),
6724
+ componentHierarchyMap: /* @__PURE__ */ new Map(),
6725
+ vueFilesPathMap: /* @__PURE__ */ new Map()
6726
+ };
6727
+ sharedGeneratorStateRegistry.set(key, state);
6728
+ }
6729
+ return state;
6730
+ }
6731
+ function applyTemplateCompilerOptionsToResolvedVuePlugin(config, templateCompilerOptions, mode) {
6732
+ const viteVuePlugin = (config.plugins ?? []).find((plugin) => plugin.name === "vite:vue");
6733
+ if (!viteVuePlugin?.api) {
6734
+ if (mode === "external") {
6735
+ throw new Error(
6736
+ '[vue-pom-generator] vuePluginOwnership="external" requires the resolved Vite Vue plugin, but none was found. Add vue() to your Vite plugins before spreading createVuePomGeneratorPlugins(...).'
6737
+ );
6738
+ }
6739
+ throw new Error("[vue-pom-generator] Nuxt mode requires the resolved Vite Vue plugin, but none was found.");
6740
+ }
6741
+ const currentOptions = viteVuePlugin.api.options ?? {};
6742
+ const currentTemplate = currentOptions.template ?? {};
6743
+ const currentCompilerOptions = currentTemplate.compilerOptions ?? {};
6744
+ const mergedNodeTransforms = [
6745
+ ...toArray(currentCompilerOptions.nodeTransforms),
6746
+ ...toArray(templateCompilerOptions.nodeTransforms)
6747
+ ];
6748
+ const mergedExpressionPlugins = Array.from(/* @__PURE__ */ new Set([
6749
+ ...toArray(currentCompilerOptions.expressionPlugins),
6750
+ ...toArray(templateCompilerOptions.expressionPlugins)
6751
+ ]));
6752
+ viteVuePlugin.api.options = {
6753
+ ...currentOptions,
6754
+ template: {
6755
+ ...currentTemplate,
6756
+ compilerOptions: {
6757
+ ...currentCompilerOptions,
6758
+ ...templateCompilerOptions,
6759
+ ...mergedExpressionPlugins.length > 0 ? { expressionPlugins: mergedExpressionPlugins } : {},
6760
+ nodeTransforms: mergedNodeTransforms
6761
+ }
6762
+ }
6763
+ };
6764
+ }
6712
6765
  function assertNotVitePluginInstance(options) {
6713
6766
  const candidate = options;
6714
6767
  const pluginLikeKeys = [
@@ -6727,7 +6780,7 @@ function assertNotVitePluginInstance(options) {
6727
6780
  return;
6728
6781
  }
6729
6782
  throw new TypeError(
6730
- `[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.`
6783
+ `[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" }.`
6731
6784
  );
6732
6785
  }
6733
6786
  function createVuePomGeneratorPlugins(options = {}) {
@@ -6751,6 +6804,12 @@ function createVuePomGeneratorPlugins(options = {}) {
6751
6804
  const routerEntry = generationOptions?.router?.entry;
6752
6805
  const routerType = generationOptions?.router?.type ?? "vue-router";
6753
6806
  const routerModuleShims = generationOptions?.router?.moduleShims;
6807
+ const isNuxt = routerType === "nuxt";
6808
+ if (isNuxt && options.vuePluginOwnership === "internal") {
6809
+ throw new Error('[vue-pom-generator] Nuxt projects must use the resolved app-owned vite:vue plugin. Omit vuePluginOwnership or set it to "external".');
6810
+ }
6811
+ const vuePluginOwnership = isNuxt ? "external" : options.vuePluginOwnership ?? "internal";
6812
+ const usesExternalVuePlugin = vuePluginOwnership === "external";
6754
6813
  const csharp = generationOptions?.csharp;
6755
6814
  const generateFixtures = generationOptions?.playwright?.fixtures;
6756
6815
  const customPoms = generationOptions?.playwright?.customPoms;
@@ -6759,6 +6818,17 @@ function createVuePomGeneratorPlugins(options = {}) {
6759
6818
  const resolvedCustomPomImportAliases = customPoms?.importAliases;
6760
6819
  const resolvedCustomPomImportCollisionBehavior = customPoms?.importNameCollisionBehavior ?? "error";
6761
6820
  const basePageClassPathOverride = generationOptions?.basePageClassPath;
6821
+ const sharedStateKey = JSON.stringify({
6822
+ cwd: process.cwd(),
6823
+ viewsDir,
6824
+ scanDirs,
6825
+ wrapperSearchRoots,
6826
+ outDir,
6827
+ testIdAttribute,
6828
+ routerType,
6829
+ vuePluginOwnership
6830
+ });
6831
+ const sharedState = getSharedGeneratorState(sharedStateKey);
6762
6832
  const projectRootRef = { current: process.cwd() };
6763
6833
  const loggerRef = {
6764
6834
  current: createLogger({ verbosity })
@@ -6779,18 +6849,17 @@ function createVuePomGeneratorPlugins(options = {}) {
6779
6849
  assertNonEmptyString(routerEntry, "[vue-pom-generator] generation.router.entry");
6780
6850
  }
6781
6851
  }
6852
+ if (usesExternalVuePlugin) {
6853
+ applyTemplateCompilerOptionsToResolvedVuePlugin(config, templateCompilerOptions, isNuxt ? "nuxt" : vuePluginOwnership);
6854
+ }
6782
6855
  loggerRef.current.info(`projectRoot=${projectRootRef.current}`);
6783
6856
  loggerRef.current.info(`Active plugins: ${config.plugins.map((p) => p.name).filter((n) => n.includes("vue-pom")).join(", ")}`);
6784
6857
  }
6785
6858
  };
6786
6859
  const getViewsDirAbs = () => resolveFromProjectRoot(projectRootRef.current, viewsDir);
6787
6860
  const getWrapperSearchRootsAbs = () => wrapperSearchRoots.map((root) => resolveFromProjectRoot(projectRootRef.current, root));
6788
- const componentTestIds = /* @__PURE__ */ new Map();
6789
- const elementMetadata = /* @__PURE__ */ new Map();
6790
- const semanticNameMap = /* @__PURE__ */ new Map();
6791
- const componentHierarchyMap = /* @__PURE__ */ new Map();
6792
- const vueFilesPathMap = /* @__PURE__ */ new Map();
6793
- const { metadataCollectorPlugin, internalVuePlugin, nuxtVueBridgePlugin } = createVuePluginWithTestIds({
6861
+ const { componentTestIds, elementMetadata, semanticNameMap, componentHierarchyMap, vueFilesPathMap } = sharedState;
6862
+ const { metadataCollectorPlugin, internalVuePlugin, templateCompilerOptions } = createVuePluginWithTestIds({
6794
6863
  vueOptions,
6795
6864
  existingIdBehavior,
6796
6865
  nameCollisionBehavior,
@@ -6834,14 +6903,15 @@ function createVuePomGeneratorPlugins(options = {}) {
6834
6903
  routerType,
6835
6904
  routerModuleShims
6836
6905
  });
6837
- const isNuxt = routerType === "nuxt";
6838
6906
  if (isNuxt) {
6839
6907
  loggerRef.current.info("Nuxt environment detected. Skipping internal @vitejs/plugin-vue to avoid conflicts.");
6908
+ } else if (usesExternalVuePlugin) {
6909
+ loggerRef.current.info('vuePluginOwnership="external" enabled. Patching the resolved vite:vue plugin instead of creating an internal one.');
6840
6910
  }
6841
6911
  const resultPlugins = [
6842
6912
  configPlugin,
6843
6913
  metadataCollectorPlugin,
6844
- ...isNuxt ? [nuxtVueBridgePlugin] : [internalVuePlugin],
6914
+ ...usesExternalVuePlugin ? [] : [internalVuePlugin],
6845
6915
  ...supportPlugins
6846
6916
  ];
6847
6917
  if (!generationEnabled) {
@@ -6849,7 +6919,7 @@ function createVuePomGeneratorPlugins(options = {}) {
6849
6919
  return [
6850
6920
  configPlugin,
6851
6921
  metadataCollectorPlugin,
6852
- ...isNuxt ? [nuxtVueBridgePlugin] : [internalVuePlugin],
6922
+ ...usesExternalVuePlugin ? [] : [internalVuePlugin],
6853
6923
  virtualModules
6854
6924
  ];
6855
6925
  }