@immense/vue-pom-generator 1.0.36 → 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/README.md CHANGED
@@ -147,13 +147,35 @@ export default defineConfig(() => {
147
147
 
148
148
  Notes:
149
149
 
150
- - `vuePomGenerator(...)` already wires `@vitejs/plugin-vue` internally for non-Nuxt apps.
151
- - Do not pass `vue()` into `createVuePomGeneratorPlugins(...)`; pass Vue options via `vueOptions`.
150
+ - `vuePomGenerator(...)` wires `@vitejs/plugin-vue` internally by default for standard Vue apps.
151
+ - Do not pass `vue()` into `createVuePomGeneratorPlugins(...)`; pass Vue plugin options via `vueOptions`.
152
+ - When the app should own `vue()` explicitly, set `vuePluginOwnership: "external"` and add `vue()` separately in your Vite config.
152
153
 
153
154
  - **Injection is enabled by plugin inclusion** (there is no longer an `injection.enabled` flag).
154
155
  - **Generation is enabled by default** and can be disabled via `generation: false`.
155
156
  - **Router-aware POM helpers are enabled** when `generation.router.entry` is provided (the generator will introspect your router).
156
157
 
158
+ ### External Vue plugin ownership
159
+
160
+ If your app should own the core Vue Vite plugin explicitly, add `vue()` yourself and let this package patch the resolved plugin:
161
+
162
+ ```ts
163
+ import vue from "@vitejs/plugin-vue";
164
+ import { defineConfig } from "vite";
165
+ import { defineVuePomGeneratorConfig, vuePomGenerator } from "@immense/vue-pom-generator";
166
+
167
+ const pomConfig = defineVuePomGeneratorConfig({
168
+ vuePluginOwnership: "external",
169
+ });
170
+
171
+ export default defineConfig({
172
+ plugins: [
173
+ vue(),
174
+ ...vuePomGenerator(pomConfig),
175
+ ],
176
+ });
177
+ ```
178
+
157
179
  ### `generation.router`
158
180
 
159
181
  Controls router introspection for `:to` analysis and navigation helper generation.
@@ -204,7 +226,9 @@ When you want CI/builds to fail on explicit test ids, pair `existingIdBehavior:
204
226
 
205
227
  ### ESLint cleanup rule: remove existing test-id attributes
206
228
 
207
- Use the `remove-existing-test-id-attributes` rule to strip explicit test-id attributes from `.vue` files before or while enforcing `existingIdBehavior: "error"`.
229
+ Use the `remove-existing-test-id-attributes` rule to strip explicit test-id usage from `.vue` files before or while enforcing `existingIdBehavior: "error"`.
230
+
231
+ The fixer handles both template attributes like `data-testid="save-button"` and object-literal keys such as `inputAttr: { 'data-testid': 'save-button' }` inside Vue SFC expressions/scripts.
208
232
 
209
233
  Add this to your ESLint flat-config file, typically `eslint.config.ts` (or `eslint.config.js` / `eslint.config.mjs` at the project root):
210
234
 
package/RELEASE_NOTES.md CHANGED
@@ -1,33 +1,44 @@
1
- ## v1.0.36
1
+ # Release Notes: v1.0.37
2
2
 
3
- ### Highlights
3
+ ## Highlights
4
4
 
5
- - Fixed handler collision suffixes to prevent naming conflicts in strict error mode
6
- - Improved handler suffix derivation by skipping unstable leading arguments
7
- - Enhanced detection of constant-like member expressions for more stable naming
8
- - Added comprehensive test coverage for collision avoidance scenarios
5
+ - Added support for external Vue plugin ownership, enabling better plugin architecture
6
+ extensibility
7
+ - Stabilized generator plugin refactor with improved plugin handling and configuration
8
+ - Enhanced build plugin with expanded functionality (72+ lines added)
9
+ - Refactored test suite with improved coverage and organization
10
+ - Added automated PR release-notes preview comments to development workflow
9
11
 
10
- ### Changes
12
+ ## Changes
11
13
 
12
- **Bug Fixes**
13
- - Fixed handler collision suffixes when generating test IDs and POM methods (#27dc566)
14
- - Skip unstable leading arguments when deriving handler suffixes from function arguments
15
- - Check up to 4 arguments (previously 2) to find stable literal/constant values
16
- - Validate constant-like root identifiers (e.g., `RebootPreference.Suppress`) before using
17
- member expression names
14
+ ### Plugin System
15
+ - Added external Vue plugin ownership support for more flexible plugin architecture
16
+ - Refactored `create-vue-pom-generator-plugins.ts` with 134 lines of improvements
17
+ - Enhanced `build-plugin.ts` with additional capabilities and refinements
18
+ - Simplified `dev-plugin.ts` by removing ~34 lines of redundant code
19
+ - Updated plugin types with new interfaces and type definitions
18
20
 
19
- **Testing**
20
- - Added test for strict-mode collision avoidance with later stable args
21
- - Added utility test coverage for `getRootIdentifierFromMemberChain` function
22
- - New test validates `clickRunDeploymentActionAssign` method generation without conflicts
21
+ ### Documentation
22
+ - Expanded README.md with 30+ lines of additional documentation
23
23
 
24
- ### Pull Requests Included
24
+ ### Testing
25
+ - Restructured `options.test.ts` with 163 lines of changes for better test coverage and clarity
25
26
 
26
- - [#1](https://github.com/immense/vue-pom-generator/pull/1) - Add PR release-notes preview
27
- comments (merged 2026-02-02, not in v1.0.36 window)
27
+ ### Code Quality
28
+ - Improved class generation logic
29
+ - Refined Vue plugin integration points
28
30
 
29
- ### Testing
31
+ ## Breaking Changes
32
+
33
+ None identified.
34
+
35
+ ## Pull Requests Included
36
+
37
+ - [#1](https://github.com/immense/vue-pom-generator/pull/1) Add PR release-notes preview
38
+ comments (@dkattan)
39
+
40
+ ## Testing
30
41
 
31
- Added 44 lines of test coverage across `transform.test.ts` and `utils-coverage.test.ts` to
32
- validate collision handling improvements.
42
+ Test suite refactored and expanded in `tests/options.test.ts` with improved organization and
43
+ coverage.
33
44
 
@@ -233,7 +233,6 @@ function generateExtraClickMethodContent(spec: PomExtraClickMethodSpec): string
233
233
  if (labelNeedsTemplate) {
234
234
  lines.push(` const label = ${labelExpr};`);
235
235
  }
236
-
237
236
  const rootArg = rootNeedsTemplate ? "rootTestId" : rootExpr;
238
237
  const labelArg = labelNeedsTemplate ? "label" : labelExpr;
239
238
  lines.push(` await this.clickWithinTestIdByLabel(${rootArg}, ${labelArg}, ${annotationArg}, ${waitArg});`);
@@ -1896,8 +1895,9 @@ function getWidgetInstancesForView(
1896
1895
  continue;
1897
1896
  }
1898
1897
 
1899
- if (!availableClassIdentifiers.has(className))
1898
+ if (!availableClassIdentifiers.has(className)) {
1900
1899
  continue;
1900
+ }
1901
1901
 
1902
1902
  // Prefer stripping the view prefix (e.g. PreferencesPage-) for cleaner member names.
1903
1903
  const viewPrefix = `${componentName}-`;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../class-generation/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,oCAAoC,EAAE,MAAM,sBAAsB,CAAC;AAE5E,OAAO,EAAE,sBAAsB,EAAoE,MAAM,UAAU,CAAC;AAQpH,OAAO,EAAE,oCAAoC,EAAE,CAAC;AA8ChD,UAAU,SAAS;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAwPD,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;;;;;;;;;OAWG;IACH,gBAAgB,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAE1D;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;;;;OAOG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEhD;;;;;OAKG;IACH,oCAAoC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IAEzD;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,KAAK,CAAC;QAC3B,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,wBAAwB,EAAE,MAAM,EAAE,CAAC;QAEnC;;;WAGG;QACH,QAAQ,CAAC,EAAE,OAAO,GAAG,YAAY,GAAG,MAAM,CAAC;KAC5C,CAAC,CAAC;IAEH,yEAAyE;IACzE,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,uDAAuD;IACvD,aAAa,CAAC,EAAE,KAAK,CAAC,IAAI,GAAG,QAAQ,CAAC,CAAC;IAEvC,6BAA6B;IAC7B,MAAM,CAAC,EAAE;QACP,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IAEF,6EAA6E;IAC7E,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAElC,2FAA2F;IAC3F,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,mDAAmD;IACnD,UAAU,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC;IAEnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IAEpB,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;CAClD;AAwCD,wBAAsB,aAAa,CACjC,qBAAqB,EAAE,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,EAC1D,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,iBAAiB,EAAE,MAAM,EACzB,OAAO,GAAE,oBAAyB,iBAkFnC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../class-generation/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,oCAAoC,EAAE,MAAM,sBAAsB,CAAC;AAE5E,OAAO,EAAE,sBAAsB,EAAoE,MAAM,UAAU,CAAC;AAQpH,OAAO,EAAE,oCAAoC,EAAE,CAAC;AA8ChD,UAAU,SAAS;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAuPD,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;;;;;;;;;OAWG;IACH,gBAAgB,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAE1D;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;;;;OAOG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEhD;;;;;OAKG;IACH,oCAAoC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IAEzD;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,KAAK,CAAC;QAC3B,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,wBAAwB,EAAE,MAAM,EAAE,CAAC;QAEnC;;;WAGG;QACH,QAAQ,CAAC,EAAE,OAAO,GAAG,YAAY,GAAG,MAAM,CAAC;KAC5C,CAAC,CAAC;IAEH,yEAAyE;IACzE,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,uDAAuD;IACvD,aAAa,CAAC,EAAE,KAAK,CAAC,IAAI,GAAG,QAAQ,CAAC,CAAC;IAEvC,6BAA6B;IAC7B,MAAM,CAAC,EAAE;QACP,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IAEF,6EAA6E;IAC7E,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAElC,2FAA2F;IAC3F,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,mDAAmD;IACnD,UAAU,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC;IAEnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IAEpB,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;CAClD;AAwCD,wBAAsB,aAAa,CACjC,qBAAqB,EAAE,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,EAC1D,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,iBAAiB,EAAE,MAAM,EACzB,OAAO,GAAE,oBAAyB,iBAkFnC"}
package/dist/index.cjs CHANGED
@@ -4525,8 +4525,9 @@ function getWidgetInstancesForView(componentName, dataTestIdSet, availableClassI
4525
4525
  } else {
4526
4526
  continue;
4527
4527
  }
4528
- if (!availableClassIdentifiers.has(className))
4528
+ if (!availableClassIdentifiers.has(className)) {
4529
4529
  continue;
4530
+ }
4530
4531
  const viewPrefix = `${componentName}-`;
4531
4532
  const descriptorRaw = stem.startsWith(viewPrefix) ? stem.slice(viewPrefix.length) : stem;
4532
4533
  const descriptorPascal = toPascalCaseLocal(descriptorRaw);
@@ -4595,32 +4596,31 @@ function getConstructor(childrenComponent, componentHierarchyMap, attachmentsFor
4595
4596
  return `${content}
4596
4597
  `;
4597
4598
  }
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;
4599
+ function summarizeHierarchyMap(componentHierarchyMap) {
4600
+ let interactiveComponentCount = 0;
4601
+ let dataTestIdCount = 0;
4602
+ for (const dependencies of componentHierarchyMap.values()) {
4603
+ const selectorCount = dependencies.dataTestIdSet?.size ?? 0;
4604
+ if (selectorCount > 0) {
4605
+ interactiveComponentCount += 1;
4606
+ dataTestIdCount += selectorCount;
4607
+ }
4604
4608
  }
4605
4609
  return {
4606
4610
  entryCount: componentHierarchyMap.size,
4607
- selectorCount,
4608
- generatedMethodCount
4611
+ interactiveComponentCount,
4612
+ dataTestIdCount
4609
4613
  };
4610
4614
  }
4611
- function isLessRich(current, previous) {
4612
- if (current.entryCount !== previous.entryCount) {
4613
- return current.entryCount < previous.entryCount;
4615
+ function isLessRich(candidate, previous) {
4616
+ if (candidate.dataTestIdCount !== previous.dataTestIdCount) {
4617
+ return candidate.dataTestIdCount < previous.dataTestIdCount;
4614
4618
  }
4615
- if (current.selectorCount !== previous.selectorCount) {
4616
- return current.selectorCount < previous.selectorCount;
4619
+ if (candidate.interactiveComponentCount !== previous.interactiveComponentCount) {
4620
+ return candidate.interactiveComponentCount < previous.interactiveComponentCount;
4617
4621
  }
4618
- return current.generatedMethodCount < previous.generatedMethodCount;
4619
- }
4620
- function getGenerationMetricsKey(projectRoot, outDir) {
4621
- return path.resolve(projectRoot, outDir ?? "./pom");
4622
+ return candidate.entryCount < previous.entryCount;
4622
4623
  }
4623
- const buildGenerationMetricsByOutputKey = /* @__PURE__ */ new Map();
4624
4624
  function createBuildProcessorPlugin(options) {
4625
4625
  const {
4626
4626
  componentHierarchyMap,
@@ -4645,6 +4645,11 @@ function createBuildProcessorPlugin(options) {
4645
4645
  routerModuleShims,
4646
4646
  loggerRef
4647
4647
  } = options;
4648
+ let lastGeneratedMetrics = {
4649
+ entryCount: 0,
4650
+ interactiveComponentCount: 0,
4651
+ dataTestIdCount: 0
4652
+ };
4648
4653
  return {
4649
4654
  name: "vue-pom-generator-build",
4650
4655
  // This plugin exists to generate code on build output; it is not needed during dev-server HMR.
@@ -4697,17 +4702,18 @@ function createBuildProcessorPlugin(options) {
4697
4702
  }
4698
4703
  this.addWatchFile(pointerPath);
4699
4704
  },
4700
- buildEnd() {
4701
- const metrics = getGenerationMetrics(componentHierarchyMap);
4702
- if (metrics.entryCount <= 0 || metrics.selectorCount <= 0) {
4705
+ async buildEnd(error) {
4706
+ if (error) {
4703
4707
  return;
4704
4708
  }
4705
- const generationMetricsKey = getGenerationMetricsKey(projectRootRef.current, outDir);
4706
- const previousMetrics = buildGenerationMetricsByOutputKey.get(generationMetricsKey);
4707
- if (previousMetrics && isLessRich(metrics, previousMetrics)) {
4709
+ const metrics = summarizeHierarchyMap(componentHierarchyMap);
4710
+ if (metrics.dataTestIdCount <= 0) {
4708
4711
  return;
4709
4712
  }
4710
- generateFiles(componentHierarchyMap, vueFilesPathMap, normalizedBasePagePath, {
4713
+ if (isLessRich(metrics, lastGeneratedMetrics)) {
4714
+ return;
4715
+ }
4716
+ await generateFiles(componentHierarchyMap, vueFilesPathMap, normalizedBasePagePath, {
4711
4717
  outDir,
4712
4718
  emitLanguages,
4713
4719
  csharp,
@@ -4724,8 +4730,8 @@ function createBuildProcessorPlugin(options) {
4724
4730
  viewsDir,
4725
4731
  scanDirs
4726
4732
  });
4727
- buildGenerationMetricsByOutputKey.set(generationMetricsKey, metrics);
4728
- loggerRef.current.info(`generated POMs (${metrics.entryCount} entries, ${metrics.selectorCount} selectors)`);
4733
+ lastGeneratedMetrics = metrics;
4734
+ loggerRef.current.info(`generated POMs (${metrics.entryCount} entries, ${metrics.interactiveComponentCount} interactive components, ${metrics.dataTestIdCount} selectors)`);
4729
4735
  },
4730
4736
  closeBundle() {
4731
4737
  loggerRef.current.info("build complete");
@@ -5814,7 +5820,6 @@ Fix: remove the explicit ${attrLabel}, or change existingIdBehavior to "overwrit
5814
5820
  }
5815
5821
  };
5816
5822
  }
5817
- const devStartupMetricsByOutputKey = /* @__PURE__ */ new Map();
5818
5823
  function createDevProcessorPlugin(options) {
5819
5824
  const {
5820
5825
  nativeWrappers,
@@ -5980,11 +5985,7 @@ function createDevProcessorPlugin(options) {
5980
5985
  nativeWrappers,
5981
5986
  excludedComponents,
5982
5987
  getViewsDirAbs(),
5983
- {
5984
- existingIdBehavior: "preserve",
5985
- testIdAttribute,
5986
- wrapperSearchRoots: getWrapperSearchRoots()
5987
- }
5988
+ { existingIdBehavior: "preserve", testIdAttribute, wrapperSearchRoots: getWrapperSearchRoots() }
5988
5989
  )
5989
5990
  ]
5990
5991
  });
@@ -6016,21 +6017,6 @@ function createDevProcessorPlugin(options) {
6016
6017
  logInfo(`initial compile: ${compiledCount}/${totalVueFiles} files in ${formatMs(t1 - t0)} (components=${snapshotHierarchy.size})`);
6017
6018
  };
6018
6019
  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
6020
  const t0 = node_perf_hooks.performance.now();
6035
6021
  generateFiles(snapshotHierarchy, snapshotVuePathMap, normalizedBasePagePath, {
6036
6022
  outDir,
@@ -6045,15 +6031,10 @@ function createDevProcessorPlugin(options) {
6045
6031
  testIdAttribute,
6046
6032
  vueRouterFluentChaining: routerAwarePoms,
6047
6033
  routerEntry: resolvedRouterEntry,
6048
- routerType,
6049
- viewsDir,
6050
- scanDirs
6034
+ routerType
6051
6035
  });
6052
- if (reason === "startup") {
6053
- devStartupMetricsByOutputKey.set(generationMetricsKey, metrics);
6054
- }
6055
6036
  const t1 = node_perf_hooks.performance.now();
6056
- logInfo(`generate(${reason}): components=${metrics.entryCount} selectors=${metrics.selectorCount} in ${formatMs(t1 - t0)}`);
6037
+ logInfo(`generate(${reason}): components=${snapshotHierarchy.size} in ${formatMs(t1 - t0)}`);
6057
6038
  };
6058
6039
  const initialBuildPromise = (async () => {
6059
6040
  const t0 = node_perf_hooks.performance.now();
@@ -6300,6 +6281,7 @@ function createSupportPlugins(options) {
6300
6281
  basePageClassPath,
6301
6282
  outDir,
6302
6283
  emitLanguages,
6284
+ csharp,
6303
6285
  generateFixtures,
6304
6286
  customPomAttachments,
6305
6287
  customPomDir,
@@ -6664,7 +6646,7 @@ function createVuePluginWithTestIds(options) {
6664
6646
  };
6665
6647
  }
6666
6648
  };
6667
- return { metadataCollectorPlugin, internalVuePlugin, nuxtVueBridgePlugin };
6649
+ return { metadataCollectorPlugin, internalVuePlugin, nuxtVueBridgePlugin, templateCompilerOptions };
6668
6650
  }
6669
6651
  function assertNonEmptyString(value, name) {
6670
6652
  if (!value || !value.trim()) {
@@ -6709,6 +6691,58 @@ function assertRouterModuleShims(value, name) {
6709
6691
  function resolveFromProjectRoot(projectRoot, maybePath) {
6710
6692
  return path.isAbsolute(maybePath) ? maybePath : path.resolve(projectRoot, maybePath);
6711
6693
  }
6694
+ const sharedGeneratorStateRegistry = /* @__PURE__ */ new Map();
6695
+ function toArray(value) {
6696
+ return Array.isArray(value) ? value : [];
6697
+ }
6698
+ function getSharedGeneratorState(key) {
6699
+ let state = sharedGeneratorStateRegistry.get(key);
6700
+ if (!state) {
6701
+ state = {
6702
+ componentTestIds: /* @__PURE__ */ new Map(),
6703
+ elementMetadata: /* @__PURE__ */ new Map(),
6704
+ semanticNameMap: /* @__PURE__ */ new Map(),
6705
+ componentHierarchyMap: /* @__PURE__ */ new Map(),
6706
+ vueFilesPathMap: /* @__PURE__ */ new Map()
6707
+ };
6708
+ sharedGeneratorStateRegistry.set(key, state);
6709
+ }
6710
+ return state;
6711
+ }
6712
+ function applyTemplateCompilerOptionsToResolvedVuePlugin(config, templateCompilerOptions, mode) {
6713
+ const viteVuePlugin = (config.plugins ?? []).find((plugin) => plugin.name === "vite:vue");
6714
+ if (!viteVuePlugin?.api) {
6715
+ if (mode === "external") {
6716
+ throw new Error(
6717
+ '[vue-pom-generator] vuePluginOwnership="external" requires the resolved Vite Vue plugin, but none was found. Add vue() to your Vite plugins before spreading createVuePomGeneratorPlugins(...).'
6718
+ );
6719
+ }
6720
+ throw new Error("[vue-pom-generator] Nuxt mode requires the resolved Vite Vue plugin, but none was found.");
6721
+ }
6722
+ const currentOptions = viteVuePlugin.api.options ?? {};
6723
+ const currentTemplate = currentOptions.template ?? {};
6724
+ const currentCompilerOptions = currentTemplate.compilerOptions ?? {};
6725
+ const mergedNodeTransforms = [
6726
+ ...toArray(currentCompilerOptions.nodeTransforms),
6727
+ ...toArray(templateCompilerOptions.nodeTransforms)
6728
+ ];
6729
+ const mergedExpressionPlugins = Array.from(/* @__PURE__ */ new Set([
6730
+ ...toArray(currentCompilerOptions.expressionPlugins),
6731
+ ...toArray(templateCompilerOptions.expressionPlugins)
6732
+ ]));
6733
+ viteVuePlugin.api.options = {
6734
+ ...currentOptions,
6735
+ template: {
6736
+ ...currentTemplate,
6737
+ compilerOptions: {
6738
+ ...currentCompilerOptions,
6739
+ ...templateCompilerOptions,
6740
+ ...mergedExpressionPlugins.length > 0 ? { expressionPlugins: mergedExpressionPlugins } : {},
6741
+ nodeTransforms: mergedNodeTransforms
6742
+ }
6743
+ }
6744
+ };
6745
+ }
6712
6746
  function assertNotVitePluginInstance(options) {
6713
6747
  const candidate = options;
6714
6748
  const pluginLikeKeys = [
@@ -6727,7 +6761,7 @@ function assertNotVitePluginInstance(options) {
6727
6761
  return;
6728
6762
  }
6729
6763
  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.`
6764
+ `[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
6765
  );
6732
6766
  }
6733
6767
  function createVuePomGeneratorPlugins(options = {}) {
@@ -6751,6 +6785,12 @@ function createVuePomGeneratorPlugins(options = {}) {
6751
6785
  const routerEntry = generationOptions?.router?.entry;
6752
6786
  const routerType = generationOptions?.router?.type ?? "vue-router";
6753
6787
  const routerModuleShims = generationOptions?.router?.moduleShims;
6788
+ const isNuxt = routerType === "nuxt";
6789
+ if (isNuxt && options.vuePluginOwnership === "internal") {
6790
+ throw new Error('[vue-pom-generator] Nuxt projects must use the resolved app-owned vite:vue plugin. Omit vuePluginOwnership or set it to "external".');
6791
+ }
6792
+ const vuePluginOwnership = isNuxt ? "external" : options.vuePluginOwnership ?? "internal";
6793
+ const usesExternalVuePlugin = vuePluginOwnership === "external";
6754
6794
  const csharp = generationOptions?.csharp;
6755
6795
  const generateFixtures = generationOptions?.playwright?.fixtures;
6756
6796
  const customPoms = generationOptions?.playwright?.customPoms;
@@ -6759,6 +6799,17 @@ function createVuePomGeneratorPlugins(options = {}) {
6759
6799
  const resolvedCustomPomImportAliases = customPoms?.importAliases;
6760
6800
  const resolvedCustomPomImportCollisionBehavior = customPoms?.importNameCollisionBehavior ?? "error";
6761
6801
  const basePageClassPathOverride = generationOptions?.basePageClassPath;
6802
+ const sharedStateKey = JSON.stringify({
6803
+ cwd: process.cwd(),
6804
+ viewsDir,
6805
+ scanDirs,
6806
+ wrapperSearchRoots,
6807
+ outDir,
6808
+ testIdAttribute,
6809
+ routerType,
6810
+ vuePluginOwnership
6811
+ });
6812
+ const sharedState = getSharedGeneratorState(sharedStateKey);
6762
6813
  const projectRootRef = { current: process.cwd() };
6763
6814
  const loggerRef = {
6764
6815
  current: createLogger({ verbosity })
@@ -6779,18 +6830,17 @@ function createVuePomGeneratorPlugins(options = {}) {
6779
6830
  assertNonEmptyString(routerEntry, "[vue-pom-generator] generation.router.entry");
6780
6831
  }
6781
6832
  }
6833
+ if (usesExternalVuePlugin) {
6834
+ applyTemplateCompilerOptionsToResolvedVuePlugin(config, templateCompilerOptions, isNuxt ? "nuxt" : vuePluginOwnership);
6835
+ }
6782
6836
  loggerRef.current.info(`projectRoot=${projectRootRef.current}`);
6783
6837
  loggerRef.current.info(`Active plugins: ${config.plugins.map((p) => p.name).filter((n) => n.includes("vue-pom")).join(", ")}`);
6784
6838
  }
6785
6839
  };
6786
6840
  const getViewsDirAbs = () => resolveFromProjectRoot(projectRootRef.current, viewsDir);
6787
6841
  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({
6842
+ const { componentTestIds, elementMetadata, semanticNameMap, componentHierarchyMap, vueFilesPathMap } = sharedState;
6843
+ const { metadataCollectorPlugin, internalVuePlugin, templateCompilerOptions } = createVuePluginWithTestIds({
6794
6844
  vueOptions,
6795
6845
  existingIdBehavior,
6796
6846
  nameCollisionBehavior,
@@ -6834,14 +6884,15 @@ function createVuePomGeneratorPlugins(options = {}) {
6834
6884
  routerType,
6835
6885
  routerModuleShims
6836
6886
  });
6837
- const isNuxt = routerType === "nuxt";
6838
6887
  if (isNuxt) {
6839
6888
  loggerRef.current.info("Nuxt environment detected. Skipping internal @vitejs/plugin-vue to avoid conflicts.");
6889
+ } else if (usesExternalVuePlugin) {
6890
+ loggerRef.current.info('vuePluginOwnership="external" enabled. Patching the resolved vite:vue plugin instead of creating an internal one.');
6840
6891
  }
6841
6892
  const resultPlugins = [
6842
6893
  configPlugin,
6843
6894
  metadataCollectorPlugin,
6844
- ...isNuxt ? [nuxtVueBridgePlugin] : [internalVuePlugin],
6895
+ ...usesExternalVuePlugin ? [] : [internalVuePlugin],
6845
6896
  ...supportPlugins
6846
6897
  ];
6847
6898
  if (!generationEnabled) {
@@ -6849,7 +6900,7 @@ function createVuePomGeneratorPlugins(options = {}) {
6849
6900
  return [
6850
6901
  configPlugin,
6851
6902
  metadataCollectorPlugin,
6852
- ...isNuxt ? [nuxtVueBridgePlugin] : [internalVuePlugin],
6903
+ ...usesExternalVuePlugin ? [] : [internalVuePlugin],
6853
6904
  virtualModules
6854
6905
  ];
6855
6906
  }