@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.mjs CHANGED
@@ -3478,7 +3478,8 @@ async function generateFiles(componentHierarchyMap, vueFilesPathMap, basePageCla
3478
3478
  const fixtureRegistryFile = maybeGenerateFixtureRegistry(componentHierarchyMap, {
3479
3479
  generateFixtures,
3480
3480
  pomOutDir: outDir,
3481
- projectRoot
3481
+ projectRoot,
3482
+ customPomDir
3482
3483
  });
3483
3484
  if (fixtureRegistryFile) {
3484
3485
  writeGeneratedFile(fixtureRegistryFile);
@@ -3877,6 +3878,9 @@ function maybeGenerateFixtureRegistry(componentHierarchyMap, options) {
3877
3878
  const fixtureFileName = looksLikeFilePath ? path.basename(fixtureOutRel) : "fixtures.g.ts";
3878
3879
  const root = options.projectRoot ?? process.cwd();
3879
3880
  const fixtureOutDirAbs = path.isAbsolute(fixtureOutDirRel) ? fixtureOutDirRel : path.resolve(root, fixtureOutDirRel);
3881
+ const customPomDirRel = options.customPomDir ?? "tests/playwright/pom/custom";
3882
+ const customPomDirAbs = path.isAbsolute(customPomDirRel) ? customPomDirRel : path.resolve(root, customPomDirRel);
3883
+ const overridePomDirAbs = path.resolve(path.dirname(customPomDirAbs), "overrides");
3880
3884
  const pomDirAbs = path.isAbsolute(pomOutDir) ? pomOutDir : path.resolve(root, pomOutDir);
3881
3885
  const pomImport = toPosixRelativePath(fixtureOutDirAbs, pomDirAbs);
3882
3886
  const viewClassNames = Array.from(componentHierarchyMap.entries()).filter(([, deps]) => !!deps.isView).map(([name]) => name).sort((a, b) => a.localeCompare(b));
@@ -3899,6 +3903,22 @@ function maybeGenerateFixtureRegistry(componentHierarchyMap, options) {
3899
3903
  return false;
3900
3904
  return true;
3901
3905
  }).sort((a, b) => a.localeCompare(b));
3906
+ const fixtureClassNames = [...viewClassNames, ...componentClassNames];
3907
+ const overrideCtorEntries = fixtureClassNames.map((name) => {
3908
+ const overrideFilePath = path.join(overridePomDirAbs, `${name}.ts`);
3909
+ if (!fs.existsSync(overrideFilePath))
3910
+ return null;
3911
+ return {
3912
+ className: name,
3913
+ localIdentifier: `${name}Override`,
3914
+ importSpecifier: stripExtension(toPosixRelativePath(fixtureOutDirAbs, overrideFilePath))
3915
+ };
3916
+ }).filter((entry) => !!entry);
3917
+ const overrideCtorByClassName = new Map(overrideCtorEntries.map((entry) => [entry.className, entry.localIdentifier]));
3918
+ const overrideImports = overrideCtorEntries.length ? `${overrideCtorEntries.map((entry) => `import { ${entry.className} as ${entry.localIdentifier} } from "${entry.importSpecifier}";`).join("\n")}
3919
+
3920
+ ` : "";
3921
+ const fixtureCtorExpression = (name) => overrideCtorByClassName.get(name) ?? `Pom.${name}`;
3902
3922
  const header = `${eslintSuppressionHeader}/**
3903
3923
  * DO NOT MODIFY BY HAND
3904
3924
  *
@@ -3907,8 +3927,8 @@ function maybeGenerateFixtureRegistry(componentHierarchyMap, options) {
3907
3927
  */
3908
3928
 
3909
3929
  `;
3910
- const fixturesTypeEntries = viewClassNames.map((name) => ` ${lowerFirst(name)}: Pom.${name},`).join("\n");
3911
- const componentFixturesTypeEntries = componentClassNames.map((name) => ` ${lowerFirst(name)}: Pom.${name},`).join("\n");
3930
+ const fixturesTypeEntries = viewClassNames.map((name) => ` ${lowerFirst(name)}: ${fixtureCtorExpression(name)},`).join("\n");
3931
+ const componentFixturesTypeEntries = componentClassNames.map((name) => ` ${lowerFirst(name)}: ${fixtureCtorExpression(name)},`).join("\n");
3912
3932
  const pomFactoryType = `export type PomConstructor<T> = new (page: PwPage) => T;
3913
3933
 
3914
3934
  export interface PomFactory {
@@ -3921,8 +3941,7 @@ export interface PomFactory {
3921
3941
  import { expect, test as base } from "@playwright/test";
3922
3942
  import type { Page as PwPage } from "@playwright/test";
3923
3943
  import * as Pom from "${pomImport}";
3924
-
3925
- export interface PlaywrightOptions {
3944
+ ${overrideImports}export interface PlaywrightOptions {
3926
3945
  animation: Pom.PlaywrightAnimationOptions;
3927
3946
  }
3928
3947
 
@@ -4484,8 +4503,9 @@ function getWidgetInstancesForView(componentName, dataTestIdSet, availableClassI
4484
4503
  } else {
4485
4504
  continue;
4486
4505
  }
4487
- if (!availableClassIdentifiers.has(className))
4506
+ if (!availableClassIdentifiers.has(className)) {
4488
4507
  continue;
4508
+ }
4489
4509
  const viewPrefix = `${componentName}-`;
4490
4510
  const descriptorRaw = stem.startsWith(viewPrefix) ? stem.slice(viewPrefix.length) : stem;
4491
4511
  const descriptorPascal = toPascalCaseLocal(descriptorRaw);
@@ -4554,32 +4574,31 @@ function getConstructor(childrenComponent, componentHierarchyMap, attachmentsFor
4554
4574
  return `${content}
4555
4575
  `;
4556
4576
  }
4557
- function getGenerationMetrics(componentHierarchyMap) {
4558
- let selectorCount = 0;
4559
- let generatedMethodCount = 0;
4560
- for (const deps of componentHierarchyMap.values()) {
4561
- selectorCount += deps.dataTestIdSet?.size ?? 0;
4562
- generatedMethodCount += deps.generatedMethods?.size ?? 0;
4577
+ function summarizeHierarchyMap(componentHierarchyMap) {
4578
+ let interactiveComponentCount = 0;
4579
+ let dataTestIdCount = 0;
4580
+ for (const dependencies of componentHierarchyMap.values()) {
4581
+ const selectorCount = dependencies.dataTestIdSet?.size ?? 0;
4582
+ if (selectorCount > 0) {
4583
+ interactiveComponentCount += 1;
4584
+ dataTestIdCount += selectorCount;
4585
+ }
4563
4586
  }
4564
4587
  return {
4565
4588
  entryCount: componentHierarchyMap.size,
4566
- selectorCount,
4567
- generatedMethodCount
4589
+ interactiveComponentCount,
4590
+ dataTestIdCount
4568
4591
  };
4569
4592
  }
4570
- function isLessRich(current, previous) {
4571
- if (current.entryCount !== previous.entryCount) {
4572
- return current.entryCount < previous.entryCount;
4593
+ function isLessRich(candidate, previous) {
4594
+ if (candidate.dataTestIdCount !== previous.dataTestIdCount) {
4595
+ return candidate.dataTestIdCount < previous.dataTestIdCount;
4573
4596
  }
4574
- if (current.selectorCount !== previous.selectorCount) {
4575
- return current.selectorCount < previous.selectorCount;
4597
+ if (candidate.interactiveComponentCount !== previous.interactiveComponentCount) {
4598
+ return candidate.interactiveComponentCount < previous.interactiveComponentCount;
4576
4599
  }
4577
- return current.generatedMethodCount < previous.generatedMethodCount;
4578
- }
4579
- function getGenerationMetricsKey(projectRoot, outDir) {
4580
- return path.resolve(projectRoot, outDir ?? "./pom");
4600
+ return candidate.entryCount < previous.entryCount;
4581
4601
  }
4582
- const buildGenerationMetricsByOutputKey = /* @__PURE__ */ new Map();
4583
4602
  function createBuildProcessorPlugin(options) {
4584
4603
  const {
4585
4604
  componentHierarchyMap,
@@ -4604,6 +4623,11 @@ function createBuildProcessorPlugin(options) {
4604
4623
  routerModuleShims,
4605
4624
  loggerRef
4606
4625
  } = options;
4626
+ let lastGeneratedMetrics = {
4627
+ entryCount: 0,
4628
+ interactiveComponentCount: 0,
4629
+ dataTestIdCount: 0
4630
+ };
4607
4631
  return {
4608
4632
  name: "vue-pom-generator-build",
4609
4633
  // This plugin exists to generate code on build output; it is not needed during dev-server HMR.
@@ -4656,17 +4680,18 @@ function createBuildProcessorPlugin(options) {
4656
4680
  }
4657
4681
  this.addWatchFile(pointerPath);
4658
4682
  },
4659
- buildEnd() {
4660
- const metrics = getGenerationMetrics(componentHierarchyMap);
4661
- if (metrics.entryCount <= 0 || metrics.selectorCount <= 0) {
4683
+ async buildEnd(error) {
4684
+ if (error) {
4662
4685
  return;
4663
4686
  }
4664
- const generationMetricsKey = getGenerationMetricsKey(projectRootRef.current, outDir);
4665
- const previousMetrics = buildGenerationMetricsByOutputKey.get(generationMetricsKey);
4666
- if (previousMetrics && isLessRich(metrics, previousMetrics)) {
4687
+ const metrics = summarizeHierarchyMap(componentHierarchyMap);
4688
+ if (metrics.dataTestIdCount <= 0) {
4667
4689
  return;
4668
4690
  }
4669
- generateFiles(componentHierarchyMap, vueFilesPathMap, normalizedBasePagePath, {
4691
+ if (isLessRich(metrics, lastGeneratedMetrics)) {
4692
+ return;
4693
+ }
4694
+ await generateFiles(componentHierarchyMap, vueFilesPathMap, normalizedBasePagePath, {
4670
4695
  outDir,
4671
4696
  emitLanguages,
4672
4697
  csharp,
@@ -4683,8 +4708,8 @@ function createBuildProcessorPlugin(options) {
4683
4708
  viewsDir,
4684
4709
  scanDirs
4685
4710
  });
4686
- buildGenerationMetricsByOutputKey.set(generationMetricsKey, metrics);
4687
- loggerRef.current.info(`generated POMs (${metrics.entryCount} entries, ${metrics.selectorCount} selectors)`);
4711
+ lastGeneratedMetrics = metrics;
4712
+ loggerRef.current.info(`generated POMs (${metrics.entryCount} entries, ${metrics.interactiveComponentCount} interactive components, ${metrics.dataTestIdCount} selectors)`);
4688
4713
  },
4689
4714
  closeBundle() {
4690
4715
  loggerRef.current.info("build complete");
@@ -5773,7 +5798,6 @@ Fix: remove the explicit ${attrLabel}, or change existingIdBehavior to "overwrit
5773
5798
  }
5774
5799
  };
5775
5800
  }
5776
- const devStartupMetricsByOutputKey = /* @__PURE__ */ new Map();
5777
5801
  function createDevProcessorPlugin(options) {
5778
5802
  const {
5779
5803
  nativeWrappers,
@@ -5939,11 +5963,7 @@ function createDevProcessorPlugin(options) {
5939
5963
  nativeWrappers,
5940
5964
  excludedComponents,
5941
5965
  getViewsDirAbs(),
5942
- {
5943
- existingIdBehavior: "preserve",
5944
- testIdAttribute,
5945
- wrapperSearchRoots: getWrapperSearchRoots()
5946
- }
5966
+ { existingIdBehavior: "preserve", testIdAttribute, wrapperSearchRoots: getWrapperSearchRoots() }
5947
5967
  )
5948
5968
  ]
5949
5969
  });
@@ -5975,21 +5995,6 @@ function createDevProcessorPlugin(options) {
5975
5995
  logInfo(`initial compile: ${compiledCount}/${totalVueFiles} files in ${formatMs(t1 - t0)} (components=${snapshotHierarchy.size})`);
5976
5996
  };
5977
5997
  const generateAggregatedFromSnapshot = (reason) => {
5978
- const metrics = getGenerationMetrics(snapshotHierarchy);
5979
- if (metrics.entryCount <= 0 || metrics.selectorCount <= 0) {
5980
- logInfo(`generate(${reason}): skipped empty snapshot (components=${metrics.entryCount}, selectors=${metrics.selectorCount})`);
5981
- return;
5982
- }
5983
- const generationMetricsKey = getGenerationMetricsKey(projectRootRef.current, outDir);
5984
- if (reason === "startup") {
5985
- const previousMetrics = devStartupMetricsByOutputKey.get(generationMetricsKey);
5986
- if (previousMetrics && isLessRich(metrics, previousMetrics)) {
5987
- logInfo(
5988
- `generate(${reason}): skipped smaller snapshot (components=${metrics.entryCount}, selectors=${metrics.selectorCount})`
5989
- );
5990
- return;
5991
- }
5992
- }
5993
5998
  const t0 = performance.now();
5994
5999
  generateFiles(snapshotHierarchy, snapshotVuePathMap, normalizedBasePagePath, {
5995
6000
  outDir,
@@ -6004,15 +6009,10 @@ function createDevProcessorPlugin(options) {
6004
6009
  testIdAttribute,
6005
6010
  vueRouterFluentChaining: routerAwarePoms,
6006
6011
  routerEntry: resolvedRouterEntry,
6007
- routerType,
6008
- viewsDir,
6009
- scanDirs
6012
+ routerType
6010
6013
  });
6011
- if (reason === "startup") {
6012
- devStartupMetricsByOutputKey.set(generationMetricsKey, metrics);
6013
- }
6014
6014
  const t1 = performance.now();
6015
- logInfo(`generate(${reason}): components=${metrics.entryCount} selectors=${metrics.selectorCount} in ${formatMs(t1 - t0)}`);
6015
+ logInfo(`generate(${reason}): components=${snapshotHierarchy.size} in ${formatMs(t1 - t0)}`);
6016
6016
  };
6017
6017
  const initialBuildPromise = (async () => {
6018
6018
  const t0 = performance.now();
@@ -6259,6 +6259,7 @@ function createSupportPlugins(options) {
6259
6259
  basePageClassPath,
6260
6260
  outDir,
6261
6261
  emitLanguages,
6262
+ csharp,
6262
6263
  generateFixtures,
6263
6264
  customPomAttachments,
6264
6265
  customPomDir,
@@ -6623,7 +6624,7 @@ function createVuePluginWithTestIds(options) {
6623
6624
  };
6624
6625
  }
6625
6626
  };
6626
- return { metadataCollectorPlugin, internalVuePlugin, nuxtVueBridgePlugin };
6627
+ return { metadataCollectorPlugin, internalVuePlugin, nuxtVueBridgePlugin, templateCompilerOptions };
6627
6628
  }
6628
6629
  function assertNonEmptyString(value, name) {
6629
6630
  if (!value || !value.trim()) {
@@ -6668,6 +6669,58 @@ function assertRouterModuleShims(value, name) {
6668
6669
  function resolveFromProjectRoot(projectRoot, maybePath) {
6669
6670
  return path.isAbsolute(maybePath) ? maybePath : path.resolve(projectRoot, maybePath);
6670
6671
  }
6672
+ const sharedGeneratorStateRegistry = /* @__PURE__ */ new Map();
6673
+ function toArray(value) {
6674
+ return Array.isArray(value) ? value : [];
6675
+ }
6676
+ function getSharedGeneratorState(key) {
6677
+ let state = sharedGeneratorStateRegistry.get(key);
6678
+ if (!state) {
6679
+ state = {
6680
+ componentTestIds: /* @__PURE__ */ new Map(),
6681
+ elementMetadata: /* @__PURE__ */ new Map(),
6682
+ semanticNameMap: /* @__PURE__ */ new Map(),
6683
+ componentHierarchyMap: /* @__PURE__ */ new Map(),
6684
+ vueFilesPathMap: /* @__PURE__ */ new Map()
6685
+ };
6686
+ sharedGeneratorStateRegistry.set(key, state);
6687
+ }
6688
+ return state;
6689
+ }
6690
+ function applyTemplateCompilerOptionsToResolvedVuePlugin(config, templateCompilerOptions, mode) {
6691
+ const viteVuePlugin = (config.plugins ?? []).find((plugin) => plugin.name === "vite:vue");
6692
+ if (!viteVuePlugin?.api) {
6693
+ if (mode === "external") {
6694
+ throw new Error(
6695
+ '[vue-pom-generator] vuePluginOwnership="external" requires the resolved Vite Vue plugin, but none was found. Add vue() to your Vite plugins before spreading createVuePomGeneratorPlugins(...).'
6696
+ );
6697
+ }
6698
+ throw new Error("[vue-pom-generator] Nuxt mode requires the resolved Vite Vue plugin, but none was found.");
6699
+ }
6700
+ const currentOptions = viteVuePlugin.api.options ?? {};
6701
+ const currentTemplate = currentOptions.template ?? {};
6702
+ const currentCompilerOptions = currentTemplate.compilerOptions ?? {};
6703
+ const mergedNodeTransforms = [
6704
+ ...toArray(currentCompilerOptions.nodeTransforms),
6705
+ ...toArray(templateCompilerOptions.nodeTransforms)
6706
+ ];
6707
+ const mergedExpressionPlugins = Array.from(/* @__PURE__ */ new Set([
6708
+ ...toArray(currentCompilerOptions.expressionPlugins),
6709
+ ...toArray(templateCompilerOptions.expressionPlugins)
6710
+ ]));
6711
+ viteVuePlugin.api.options = {
6712
+ ...currentOptions,
6713
+ template: {
6714
+ ...currentTemplate,
6715
+ compilerOptions: {
6716
+ ...currentCompilerOptions,
6717
+ ...templateCompilerOptions,
6718
+ ...mergedExpressionPlugins.length > 0 ? { expressionPlugins: mergedExpressionPlugins } : {},
6719
+ nodeTransforms: mergedNodeTransforms
6720
+ }
6721
+ }
6722
+ };
6723
+ }
6671
6724
  function assertNotVitePluginInstance(options) {
6672
6725
  const candidate = options;
6673
6726
  const pluginLikeKeys = [
@@ -6686,7 +6739,7 @@ function assertNotVitePluginInstance(options) {
6686
6739
  return;
6687
6740
  }
6688
6741
  throw new TypeError(
6689
- `[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.`
6742
+ `[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" }.`
6690
6743
  );
6691
6744
  }
6692
6745
  function createVuePomGeneratorPlugins(options = {}) {
@@ -6710,6 +6763,12 @@ function createVuePomGeneratorPlugins(options = {}) {
6710
6763
  const routerEntry = generationOptions?.router?.entry;
6711
6764
  const routerType = generationOptions?.router?.type ?? "vue-router";
6712
6765
  const routerModuleShims = generationOptions?.router?.moduleShims;
6766
+ const isNuxt = routerType === "nuxt";
6767
+ if (isNuxt && options.vuePluginOwnership === "internal") {
6768
+ throw new Error('[vue-pom-generator] Nuxt projects must use the resolved app-owned vite:vue plugin. Omit vuePluginOwnership or set it to "external".');
6769
+ }
6770
+ const vuePluginOwnership = isNuxt ? "external" : options.vuePluginOwnership ?? "internal";
6771
+ const usesExternalVuePlugin = vuePluginOwnership === "external";
6713
6772
  const csharp = generationOptions?.csharp;
6714
6773
  const generateFixtures = generationOptions?.playwright?.fixtures;
6715
6774
  const customPoms = generationOptions?.playwright?.customPoms;
@@ -6718,6 +6777,17 @@ function createVuePomGeneratorPlugins(options = {}) {
6718
6777
  const resolvedCustomPomImportAliases = customPoms?.importAliases;
6719
6778
  const resolvedCustomPomImportCollisionBehavior = customPoms?.importNameCollisionBehavior ?? "error";
6720
6779
  const basePageClassPathOverride = generationOptions?.basePageClassPath;
6780
+ const sharedStateKey = JSON.stringify({
6781
+ cwd: process.cwd(),
6782
+ viewsDir,
6783
+ scanDirs,
6784
+ wrapperSearchRoots,
6785
+ outDir,
6786
+ testIdAttribute,
6787
+ routerType,
6788
+ vuePluginOwnership
6789
+ });
6790
+ const sharedState = getSharedGeneratorState(sharedStateKey);
6721
6791
  const projectRootRef = { current: process.cwd() };
6722
6792
  const loggerRef = {
6723
6793
  current: createLogger({ verbosity })
@@ -6738,18 +6808,17 @@ function createVuePomGeneratorPlugins(options = {}) {
6738
6808
  assertNonEmptyString(routerEntry, "[vue-pom-generator] generation.router.entry");
6739
6809
  }
6740
6810
  }
6811
+ if (usesExternalVuePlugin) {
6812
+ applyTemplateCompilerOptionsToResolvedVuePlugin(config, templateCompilerOptions, isNuxt ? "nuxt" : vuePluginOwnership);
6813
+ }
6741
6814
  loggerRef.current.info(`projectRoot=${projectRootRef.current}`);
6742
6815
  loggerRef.current.info(`Active plugins: ${config.plugins.map((p) => p.name).filter((n) => n.includes("vue-pom")).join(", ")}`);
6743
6816
  }
6744
6817
  };
6745
6818
  const getViewsDirAbs = () => resolveFromProjectRoot(projectRootRef.current, viewsDir);
6746
6819
  const getWrapperSearchRootsAbs = () => wrapperSearchRoots.map((root) => resolveFromProjectRoot(projectRootRef.current, root));
6747
- const componentTestIds = /* @__PURE__ */ new Map();
6748
- const elementMetadata = /* @__PURE__ */ new Map();
6749
- const semanticNameMap = /* @__PURE__ */ new Map();
6750
- const componentHierarchyMap = /* @__PURE__ */ new Map();
6751
- const vueFilesPathMap = /* @__PURE__ */ new Map();
6752
- const { metadataCollectorPlugin, internalVuePlugin, nuxtVueBridgePlugin } = createVuePluginWithTestIds({
6820
+ const { componentTestIds, elementMetadata, semanticNameMap, componentHierarchyMap, vueFilesPathMap } = sharedState;
6821
+ const { metadataCollectorPlugin, internalVuePlugin, templateCompilerOptions } = createVuePluginWithTestIds({
6753
6822
  vueOptions,
6754
6823
  existingIdBehavior,
6755
6824
  nameCollisionBehavior,
@@ -6793,14 +6862,15 @@ function createVuePomGeneratorPlugins(options = {}) {
6793
6862
  routerType,
6794
6863
  routerModuleShims
6795
6864
  });
6796
- const isNuxt = routerType === "nuxt";
6797
6865
  if (isNuxt) {
6798
6866
  loggerRef.current.info("Nuxt environment detected. Skipping internal @vitejs/plugin-vue to avoid conflicts.");
6867
+ } else if (usesExternalVuePlugin) {
6868
+ loggerRef.current.info('vuePluginOwnership="external" enabled. Patching the resolved vite:vue plugin instead of creating an internal one.');
6799
6869
  }
6800
6870
  const resultPlugins = [
6801
6871
  configPlugin,
6802
6872
  metadataCollectorPlugin,
6803
- ...isNuxt ? [nuxtVueBridgePlugin] : [internalVuePlugin],
6873
+ ...usesExternalVuePlugin ? [] : [internalVuePlugin],
6804
6874
  ...supportPlugins
6805
6875
  ];
6806
6876
  if (!generationEnabled) {
@@ -6808,7 +6878,7 @@ function createVuePomGeneratorPlugins(options = {}) {
6808
6878
  return [
6809
6879
  configPlugin,
6810
6880
  metadataCollectorPlugin,
6811
- ...isNuxt ? [nuxtVueBridgePlugin] : [internalVuePlugin],
6881
+ ...usesExternalVuePlugin ? [] : [internalVuePlugin],
6812
6882
  virtualModules
6813
6883
  ];
6814
6884
  }