@immense/vue-pom-generator 1.0.33 → 1.0.35

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/RELEASE_NOTES.md CHANGED
@@ -1,43 +1,28 @@
1
- ● # Release Notes: v1.0.33
1
+ ● # Release Notes: v1.0.35
2
2
 
3
3
  ## Highlights
4
-
5
- - Fixed singleton key test-id inference to correctly handle edge cases
6
- - Added comprehensive generation metrics and diagnostics system for better visibility into
7
- transform operations
8
- - Introduced new `generation-metrics.ts` module for tracking and reporting code generation
9
- statistics
10
- - Expanded transform logic with 192 additional lines of processing logic
11
- - Enhanced plugin architecture with improved metric collection in dev and build modes
4
+ - Fixed wrapper collision fallbacks in transform logic
5
+ - Added comprehensive test coverage for wrapper collision scenarios
6
+ - Improved PR workflow with release-notes preview comments
12
7
 
13
8
  ## Changes
14
9
 
15
- **Core Transformation**
16
- - Fixed singleton key test-id inference logic
17
- - Expanded `transform.ts` with enhanced processing capabilities
10
+ ### Bug Fixes
11
+ - Resolved issues with wrapper collision fallback behavior in transform module
18
12
 
19
- **Metrics & Diagnostics**
20
- - Added `plugin/support/generation-metrics.ts` for tracking generation statistics
21
- - Updated `plugin/vue-plugin.ts` with enhanced metrics collection (85 lines modified)
22
- - Improved diagnostic output in `plugin/support/dev-plugin.ts` and
23
- `plugin/support/build-plugin.ts`
13
+ ### Testing
14
+ - Added 41 lines of new tests for wrapper collision scenarios in `transform.test.ts`
24
15
 
25
- **Testing**
26
- - Added comprehensive test suite in `tests/generation-metrics.test.ts` (47 tests)
27
- - Expanded `tests/transform.test.ts` with 131 additional test cases
28
- - Enhanced `tests/options.test.ts` with 72 new test cases
16
+ ### Developer Experience
17
+ - Integrated automated release-notes preview comments on pull requests
29
18
 
30
- **Infrastructure**
31
- - Updated plugin creation in `plugin/create-vue-pom-generator-plugins.ts`
32
- - Minor utility adjustments in `utils.ts`
19
+ ## Breaking Changes
20
+ None
33
21
 
34
22
  ## Pull Requests Included
35
-
36
23
  - #1 Add PR release-notes preview comments (https://github.com/immense/vue-pom-generator/pull/1)
37
24
  by @dkattan
38
25
 
39
26
  ## Testing
40
-
41
- Added 250+ new test cases across generation metrics, transform logic, and options handling. All
42
- tests passing.
27
+ New test cases added to validate wrapper collision fallback fixes. All tests passing.
43
28
 
@@ -66,12 +66,27 @@ async function getRouteMetaByComponent(
66
66
  projectRoot?: string,
67
67
  routerEntry?: string,
68
68
  routerType?: "vue-router" | "nuxt",
69
+ options: {
70
+ viewsDir?: string;
71
+ scanDirs?: string[];
72
+ } = {},
69
73
  ): Promise<Record<string, RouteMeta>> {
70
74
  const root = projectRoot ?? process.cwd();
75
+ const viewsDir = options.viewsDir ?? "src/views";
76
+ const viewsDirAbs = path.isAbsolute(viewsDir) ? viewsDir : path.resolve(root, viewsDir);
77
+ const scanDirs = options.scanDirs?.length ? options.scanDirs : ["src"];
78
+ const extraRoots = process.cwd() !== root ? [process.cwd()] : [];
71
79
 
72
80
  const { routeMetaEntries } = routerType === "nuxt"
73
81
  ? await introspectNuxtPages(root)
74
- : await parseRouterFileFromCwd(resolveRouterEntry(root, routerEntry));
82
+ : await parseRouterFileFromCwd(resolveRouterEntry(root, routerEntry), {
83
+ componentNaming: {
84
+ projectRoot: root,
85
+ viewsDirAbs,
86
+ scanDirs,
87
+ extraRoots,
88
+ },
89
+ });
75
90
 
76
91
  const map = new Map<string, RouteMeta[]>();
77
92
  for (const entry of routeMetaEntries) {
@@ -382,6 +397,9 @@ export interface GenerateFilesOptions {
382
397
  /** The type of router introspection to perform. */
383
398
  routerType?: "vue-router" | "nuxt";
384
399
 
400
+ viewsDir?: string;
401
+ scanDirs?: string[];
402
+
385
403
  routeMetaByComponent?: Record<string, RouteMeta>;
386
404
  }
387
405
 
@@ -443,6 +461,9 @@ export async function generateFiles(
443
461
  vueRouterFluentChaining,
444
462
  routerEntry,
445
463
  routerType,
464
+ viewsDir,
465
+ scanDirs,
466
+ routeMetaByComponent: routeMetaByComponentOverride,
446
467
  } = options;
447
468
 
448
469
  const emitLanguages: Array<"ts" | "csharp"> = emitLanguagesOverride?.length
@@ -451,9 +472,13 @@ export async function generateFiles(
451
472
 
452
473
  const outDir = outDirOverride ?? "./pom";
453
474
 
454
- const routeMetaByComponent = vueRouterFluentChaining
455
- ? await getRouteMetaByComponent(projectRoot, routerEntry, routerType)
456
- : undefined;
475
+ const routeMetaByComponent = routeMetaByComponentOverride
476
+ ?? (vueRouterFluentChaining
477
+ ? await getRouteMetaByComponent(projectRoot, routerEntry, routerType, {
478
+ viewsDir,
479
+ scanDirs,
480
+ })
481
+ : undefined);
457
482
  const generatedFilePaths: string[] = [];
458
483
  const writeGeneratedFile = (file: GeneratedFileOutput) => {
459
484
  const resolvedFilePath = path.resolve(file.filePath);
@@ -81,6 +81,8 @@ export interface GenerateFilesOptions {
81
81
  routerEntry?: string;
82
82
  /** The type of router introspection to perform. */
83
83
  routerType?: "vue-router" | "nuxt";
84
+ viewsDir?: string;
85
+ scanDirs?: string[];
84
86
  routeMetaByComponent?: Record<string, RouteMeta>;
85
87
  }
86
88
  export declare function generateFiles(componentHierarchyMap: Map<string, IComponentDependencies>, vueFilesPathMap: Map<string, string>, basePageClassPath: string, options?: GenerateFilesOptions): Promise<void>;
@@ -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;AAyOD,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,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,iBA2EnC"}
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"}
package/dist/index.cjs CHANGED
@@ -2569,6 +2569,53 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
2569
2569
  registerGeneratedMethodSignature(generatedName, signature);
2570
2570
  }
2571
2571
  }
2572
+ function safeRealpath(value) {
2573
+ try {
2574
+ if (fs.existsSync(value)) {
2575
+ return fs.realpathSync(value);
2576
+ }
2577
+ } catch {
2578
+ return value;
2579
+ }
2580
+ const parent = path.dirname(value);
2581
+ if (!parent || parent === value) {
2582
+ return value;
2583
+ }
2584
+ const resolvedParent = safeRealpath(parent);
2585
+ return resolvedParent === parent ? value : path.join(resolvedParent, path.basename(value));
2586
+ }
2587
+ function resolveComponentNameFromPath(options) {
2588
+ const { projectRoot, viewsDirAbs, scanDirs, extraRoots = [] } = options;
2589
+ const cleanFilename = options.filename.includes("?") ? options.filename.substring(0, options.filename.indexOf("?")) : options.filename;
2590
+ const absFilename = path.isAbsolute(cleanFilename) ? cleanFilename : path.resolve(projectRoot, cleanFilename);
2591
+ const normalizedAbsFilename = path.normalize(safeRealpath(absFilename));
2592
+ const rootBases = [projectRoot, ...extraRoots.filter((r) => r !== projectRoot)];
2593
+ const roots = [viewsDirAbs, ...rootBases.flatMap((base) => scanDirs.map((d) => path.resolve(base, d)))];
2594
+ for (const base of rootBases) {
2595
+ for (const dir of scanDirs) {
2596
+ const absDir = path.resolve(base, dir);
2597
+ try {
2598
+ const pagesDir = path.join(absDir, "pages");
2599
+ if (fs.existsSync(pagesDir))
2600
+ roots.push(pagesDir);
2601
+ const componentsDir = path.join(absDir, "components");
2602
+ if (fs.existsSync(componentsDir))
2603
+ roots.push(componentsDir);
2604
+ } catch {
2605
+ }
2606
+ }
2607
+ }
2608
+ const potentialRoots = Array.from(new Set(roots.map((r) => path.normalize(safeRealpath(r))))).sort((a, b) => b.length - a.length);
2609
+ for (const root of potentialRoots) {
2610
+ if (normalizedAbsFilename.startsWith(root + path.sep) || normalizedAbsFilename === root) {
2611
+ const rel = path.relative(root, normalizedAbsFilename);
2612
+ const parsed = path.parse(rel);
2613
+ const segments = path.join(parsed.dir, parsed.name);
2614
+ return toPascalCase(segments);
2615
+ }
2616
+ }
2617
+ return toPascalCase(path.parse(normalizedAbsFilename).name);
2618
+ }
2572
2619
  let routerIntrospectionQueue = Promise.resolve();
2573
2620
  async function runRouterIntrospectionExclusive(fn) {
2574
2621
  const prev = routerIntrospectionQueue.catch(() => void 0);
@@ -2848,20 +2895,84 @@ function getRouteParamMeta(router, record, paramNames) {
2848
2895
  }
2849
2896
  });
2850
2897
  }
2851
- function getComponentNameFromRouteRecord(record) {
2852
- const comp = record.components?.default;
2853
- if (!comp)
2898
+ function normalizeRouteComponentFilePath(filePath, options = {}) {
2899
+ const queryIndex = filePath.indexOf("?");
2900
+ const cleanPath = queryIndex === -1 ? filePath : filePath.slice(0, queryIndex);
2901
+ if (cleanPath.startsWith("/@fs/")) {
2902
+ return path.normalize(cleanPath.slice("/@fs/".length));
2903
+ }
2904
+ if (path.isAbsolute(cleanPath)) {
2905
+ if (fs.existsSync(cleanPath) || !options.rootDir)
2906
+ return path.normalize(cleanPath);
2907
+ return path.normalize(path.resolve(options.rootDir, `.${cleanPath}`));
2908
+ }
2909
+ if (!options.rootDir)
2854
2910
  return null;
2911
+ return path.normalize(path.resolve(options.rootDir, cleanPath));
2912
+ }
2913
+ function getComponentInfoFromVueComponent(comp, options = {}) {
2914
+ if (!comp) {
2915
+ return {
2916
+ componentName: null,
2917
+ filePath: null
2918
+ };
2919
+ }
2920
+ let componentName = null;
2921
+ let filePath = null;
2855
2922
  if (typeof comp.__file === "string" && comp.__file.length) {
2923
+ filePath = normalizeRouteComponentFilePath(comp.__file, { rootDir: options.rootDir });
2856
2924
  const base = path.posix.basename(path.posix.normalize(comp.__file));
2857
2925
  if (base.toLowerCase().endsWith(".vue"))
2858
- return base.slice(0, -".vue".length);
2926
+ componentName = base.slice(0, -".vue".length);
2859
2927
  }
2860
- if (typeof comp.__name === "string" && comp.__name.length)
2861
- return comp.__name;
2862
- if (typeof comp.name === "string" && comp.name.length)
2863
- return comp.name;
2864
- return null;
2928
+ if (!componentName && typeof comp.__name === "string" && comp.__name.length)
2929
+ componentName = comp.__name;
2930
+ if (!componentName && options.allowFunctionNameFallback !== false && typeof comp.name === "string" && comp.name.length) {
2931
+ componentName = comp.name;
2932
+ }
2933
+ return {
2934
+ componentName,
2935
+ filePath
2936
+ };
2937
+ }
2938
+ async function getComponentInfoFromRouteRecord(record, options = {}) {
2939
+ const comp = record.components?.default;
2940
+ if (!comp) {
2941
+ return {
2942
+ componentName: null,
2943
+ filePath: null
2944
+ };
2945
+ }
2946
+ if (typeof comp !== "function") {
2947
+ return getComponentInfoFromVueComponent(comp, options);
2948
+ }
2949
+ const directInfo = getComponentInfoFromVueComponent(comp, {
2950
+ allowFunctionNameFallback: false,
2951
+ rootDir: options.rootDir
2952
+ });
2953
+ if (directInfo.componentName || directInfo.filePath)
2954
+ return directInfo;
2955
+ try {
2956
+ const loaded = await comp();
2957
+ const resolved = loaded && typeof loaded === "object" && "default" in loaded ? loaded.default : loaded;
2958
+ const loadedInfo = getComponentInfoFromVueComponent(resolved, options);
2959
+ if (loadedInfo.componentName || loadedInfo.filePath)
2960
+ return loadedInfo;
2961
+ } catch {
2962
+ }
2963
+ return getComponentInfoFromVueComponent(comp, options);
2964
+ }
2965
+ function resolveIntrospectedComponentName(componentInfo, componentNaming) {
2966
+ if (componentInfo.filePath && componentNaming) {
2967
+ return resolveComponentNameFromPath({
2968
+ filename: componentInfo.filePath,
2969
+ projectRoot: componentNaming.projectRoot,
2970
+ viewsDirAbs: componentNaming.viewsDirAbs,
2971
+ scanDirs: componentNaming.scanDirs,
2972
+ extraRoots: componentNaming.extraRoots
2973
+ });
2974
+ }
2975
+ return componentInfo.componentName;
2865
2976
  }
2866
2977
  async function ensureDomShim() {
2867
2978
  const domShimHtml = "<!doctype html><html><head></head><body><div id='app'></div></body></html>";
@@ -3107,7 +3218,8 @@ async function parseRouterFileFromCwd(routerEntryPath, options = {}) {
3107
3218
  const routePathMap = /* @__PURE__ */ new Map();
3108
3219
  const routeMetaEntries = [];
3109
3220
  for (const r of router.getRoutes()) {
3110
- const componentName = getComponentNameFromRouteRecord(r);
3221
+ const componentInfo = await getComponentInfoFromRouteRecord(r, { rootDir: cwd });
3222
+ const componentName = resolveIntrospectedComponentName(componentInfo, options.componentNaming);
3111
3223
  if (!componentName)
3112
3224
  continue;
3113
3225
  if (typeof r.path === "string" && r.path.length) {
@@ -3165,9 +3277,20 @@ function resolveRouterEntry(projectRoot, routerEntry) {
3165
3277
  const root = projectRoot ?? process.cwd();
3166
3278
  return path.isAbsolute(routerEntry) ? routerEntry : path.resolve(root, routerEntry);
3167
3279
  }
3168
- async function getRouteMetaByComponent(projectRoot, routerEntry, routerType) {
3280
+ async function getRouteMetaByComponent(projectRoot, routerEntry, routerType, options = {}) {
3169
3281
  const root = projectRoot ?? process.cwd();
3170
- const { routeMetaEntries } = routerType === "nuxt" ? await introspectNuxtPages(root) : await parseRouterFileFromCwd(resolveRouterEntry(root, routerEntry));
3282
+ const viewsDir = options.viewsDir ?? "src/views";
3283
+ const viewsDirAbs = path.isAbsolute(viewsDir) ? viewsDir : path.resolve(root, viewsDir);
3284
+ const scanDirs = options.scanDirs?.length ? options.scanDirs : ["src"];
3285
+ const extraRoots = process.cwd() !== root ? [process.cwd()] : [];
3286
+ const { routeMetaEntries } = routerType === "nuxt" ? await introspectNuxtPages(root) : await parseRouterFileFromCwd(resolveRouterEntry(root, routerEntry), {
3287
+ componentNaming: {
3288
+ projectRoot: root,
3289
+ viewsDirAbs,
3290
+ scanDirs,
3291
+ extraRoots
3292
+ }
3293
+ });
3171
3294
  const map = /* @__PURE__ */ new Map();
3172
3295
  for (const entry of routeMetaEntries) {
3173
3296
  const list = map.get(entry.componentName) ?? [];
@@ -3344,11 +3467,17 @@ async function generateFiles(componentHierarchyMap, vueFilesPathMap, basePageCla
3344
3467
  csharp,
3345
3468
  vueRouterFluentChaining,
3346
3469
  routerEntry,
3347
- routerType
3470
+ routerType,
3471
+ viewsDir,
3472
+ scanDirs,
3473
+ routeMetaByComponent: routeMetaByComponentOverride
3348
3474
  } = options;
3349
3475
  const emitLanguages = emitLanguagesOverride?.length ? emitLanguagesOverride : ["ts"];
3350
3476
  const outDir = outDirOverride ?? "./pom";
3351
- const routeMetaByComponent = vueRouterFluentChaining ? await getRouteMetaByComponent(projectRoot, routerEntry, routerType) : void 0;
3477
+ const routeMetaByComponent = routeMetaByComponentOverride ?? (vueRouterFluentChaining ? await getRouteMetaByComponent(projectRoot, routerEntry, routerType, {
3478
+ viewsDir,
3479
+ scanDirs
3480
+ }) : void 0);
3352
3481
  const generatedFilePaths = [];
3353
3482
  const writeGeneratedFile = (file) => {
3354
3483
  const resolvedFilePath = path.resolve(file.filePath);
@@ -4478,6 +4607,8 @@ function createBuildProcessorPlugin(options) {
4478
4607
  const {
4479
4608
  componentHierarchyMap,
4480
4609
  vueFilesPathMap,
4610
+ viewsDir,
4611
+ scanDirs,
4481
4612
  basePageClassPath,
4482
4613
  normalizedBasePagePath,
4483
4614
  outDir,
@@ -4513,7 +4644,14 @@ function createBuildProcessorPlugin(options) {
4513
4644
  } else {
4514
4645
  if (!resolvedRouterEntry)
4515
4646
  throw new Error("[vue-pom-generator] router.entry is required when router introspection is enabled.");
4516
- result = await parseRouterFileFromCwd(resolvedRouterEntry, { moduleShims: routerModuleShims });
4647
+ result = await parseRouterFileFromCwd(resolvedRouterEntry, {
4648
+ moduleShims: routerModuleShims,
4649
+ componentNaming: {
4650
+ projectRoot: projectRootRef.current,
4651
+ viewsDirAbs: path.isAbsolute(viewsDir) ? viewsDir : path.resolve(projectRootRef.current, viewsDir),
4652
+ scanDirs
4653
+ }
4654
+ });
4517
4655
  }
4518
4656
  const { routeNameMap, routePathMap } = result;
4519
4657
  setRouteNameToComponentNameMap(routeNameMap);
@@ -4564,7 +4702,9 @@ function createBuildProcessorPlugin(options) {
4564
4702
  testIdAttribute,
4565
4703
  vueRouterFluentChaining: routerAwarePoms,
4566
4704
  routerEntry: resolvedRouterEntry,
4567
- routerType
4705
+ routerType,
4706
+ viewsDir,
4707
+ scanDirs
4568
4708
  });
4569
4709
  buildGenerationMetricsByOutputKey.set(generationMetricsKey, metrics);
4570
4710
  loggerRef.current.info(`generated POMs (${metrics.entryCount} entries, ${metrics.selectorCount} selectors)`);
@@ -5220,14 +5360,14 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
5220
5360
  const warn = options.warn;
5221
5361
  const vueFilesPathMap = options.vueFilesPathMap;
5222
5362
  const wrapperSearchRoots = options.wrapperSearchRoots ?? [];
5223
- const safeRealpath = (p) => {
5363
+ const safeRealpath2 = (p) => {
5224
5364
  try {
5225
5365
  return fs.existsSync(p) ? fs.realpathSync(p) : p;
5226
5366
  } catch {
5227
5367
  return p;
5228
5368
  }
5229
5369
  };
5230
- const normalizedViewsDirAbs = path.normalize(safeRealpath(path.resolve(viewsDirAbs)));
5370
+ const normalizedViewsDirAbs = path.normalize(safeRealpath2(path.resolve(viewsDirAbs)));
5231
5371
  const generatedMethodContentByComponent = /* @__PURE__ */ new Map();
5232
5372
  const lastConditionalHintByParent = /* @__PURE__ */ new WeakMap();
5233
5373
  const lastConditionalMergeGroupByParent = /* @__PURE__ */ new WeakMap();
@@ -5315,7 +5455,7 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
5315
5455
  const parentIsRoot = context?.parent?.type === compilerCore.NodeTypes.ROOT;
5316
5456
  const parentElement = !parentIsRoot && context?.parent?.type === compilerCore.NodeTypes.ELEMENT ? context.parent : null;
5317
5457
  hierarchyMap.set(element, parentElement);
5318
- const normalizeFilePath = (filePath) => path.normalize(safeRealpath(path.resolve(filePath)));
5458
+ const normalizeFilePath = (filePath) => path.normalize(safeRealpath2(path.resolve(filePath)));
5319
5459
  const normalizedFilePath = normalizeFilePath(context.filename);
5320
5460
  const parentComponentName = componentName;
5321
5461
  const dependencies = (() => {
@@ -5502,14 +5642,23 @@ Fix: remove the explicit ${attrLabel}, or change existingIdBehavior to "overwrit
5502
5642
  upsertAttribute(element, "option-data-testid-prefix", optionDataTestIdPrefixValue);
5503
5643
  }
5504
5644
  const nativeRole = nativeWrappers[element.tag]?.role ?? element.tag;
5505
- const primarySemanticHint = semanticNameHint || conditionalHint || void 0;
5506
- const alternates = nameCollisionBehavior === "error" && semanticNameHint && conditionalHint ? [`${semanticNameHint} ${conditionalHint}`] : void 0;
5645
+ const wrapperHintCandidates = [
5646
+ semanticNameHint,
5647
+ getStaticAttributeContent(element, "title"),
5648
+ getStaticAttributeContent(element, "label"),
5649
+ getStaticAttributeContent(element, "okTitle"),
5650
+ getStaticAttributeContent(element, "cancelTitle"),
5651
+ getStaticAttributeContent(element, "id") || getStaticAttributeContent(element, "name"),
5652
+ getInnerText(element) || null,
5653
+ nameCollisionBehavior === "error" && semanticNameHint && conditionalHint ? `${semanticNameHint} ${conditionalHint}` : conditionalHint
5654
+ ].map((value) => (value ?? "").trim()).filter(Boolean).filter((value, index, values) => values.indexOf(value) === index);
5655
+ const [primarySemanticHint, ...alternates] = wrapperHintCandidates;
5507
5656
  const pomMergeKey = semanticNameHint && conditionalMergeGroupKey ? `wrapper:ifgroup:${conditionalMergeGroupKey}:model:${semanticNameHint}` : void 0;
5508
5657
  applyResolvedDataTestIdForElement({
5509
5658
  preferredGeneratedValue: nativeWrappersValue,
5510
5659
  nativeRoleOverride: nativeRole,
5511
5660
  semanticNameHint: primarySemanticHint,
5512
- semanticNameHintAlternates: alternates,
5661
+ semanticNameHintAlternates: alternates.length ? alternates : void 0,
5513
5662
  pomMergeKey
5514
5663
  });
5515
5664
  return;
@@ -5599,12 +5748,14 @@ Fix: remove the explicit ${attrLabel}, or change existingIdBehavior to "overwrit
5599
5748
  });
5600
5749
  const clickHint = trimLeadingSeparators(clickSuffix) || void 0;
5601
5750
  const idOrName = getIdOrName(element) || void 0;
5602
- const semanticNameHint2 = clickHint || idOrName || innerText || conditionalHint || void 0;
5751
+ const semanticHintCandidates = [clickHint, idOrName, innerText, conditionalHint].map((value) => (value ?? "").trim()).filter(Boolean).filter((value, index, values) => values.indexOf(value) === index);
5752
+ const [semanticNameHint2, ...semanticNameHintAlternates] = semanticHintCandidates;
5603
5753
  const pomMergeKey = clickHint ? `click:hint:${clickHint}` : void 0;
5604
5754
  const testId = getClickDataTestId(clickSuffix);
5605
5755
  applyResolvedDataTestIdForElement({
5606
5756
  preferredGeneratedValue: testId,
5607
5757
  semanticNameHint: semanticNameHint2,
5758
+ semanticNameHintAlternates,
5608
5759
  pomMergeKey
5609
5760
  });
5610
5761
  {
@@ -5645,37 +5796,6 @@ Fix: remove the explicit ${attrLabel}, or change existingIdBehavior to "overwrit
5645
5796
  }
5646
5797
  };
5647
5798
  }
5648
- function resolveComponentNameFromPath(options) {
5649
- const { projectRoot, viewsDirAbs, scanDirs, extraRoots = [] } = options;
5650
- const cleanFilename = options.filename.includes("?") ? options.filename.substring(0, options.filename.indexOf("?")) : options.filename;
5651
- const absFilename = path.isAbsolute(cleanFilename) ? cleanFilename : path.resolve(projectRoot, cleanFilename);
5652
- const rootBases = [projectRoot, ...extraRoots.filter((r) => r !== projectRoot)];
5653
- const roots = [viewsDirAbs, ...rootBases.flatMap((base) => scanDirs.map((d) => path.resolve(base, d)))];
5654
- for (const base of rootBases) {
5655
- for (const dir of scanDirs) {
5656
- const absDir = path.resolve(base, dir);
5657
- try {
5658
- const pagesDir = path.join(absDir, "pages");
5659
- if (fs.existsSync(pagesDir))
5660
- roots.push(pagesDir);
5661
- const componentsDir = path.join(absDir, "components");
5662
- if (fs.existsSync(componentsDir))
5663
- roots.push(componentsDir);
5664
- } catch {
5665
- }
5666
- }
5667
- }
5668
- const potentialRoots = Array.from(new Set(roots.map((r) => path.normalize(r)))).sort((a, b) => b.length - a.length);
5669
- for (const root of potentialRoots) {
5670
- if (absFilename.startsWith(root + path.sep) || absFilename === root) {
5671
- const rel = path.relative(root, absFilename);
5672
- const parsed = path.parse(rel);
5673
- const segments = path.join(parsed.dir, parsed.name);
5674
- return toPascalCase(segments);
5675
- }
5676
- }
5677
- return toPascalCase(path.parse(absFilename).name);
5678
- }
5679
5799
  const devStartupMetricsByOutputKey = /* @__PURE__ */ new Map();
5680
5800
  function createDevProcessorPlugin(options) {
5681
5801
  const {
@@ -5722,6 +5842,7 @@ function createDevProcessorPlugin(options) {
5722
5842
  scheduleVueFileRegen(ctx.file, "hmr");
5723
5843
  },
5724
5844
  configureServer(server) {
5845
+ const getViewsDirAbs = () => path.isAbsolute(viewsDir) ? viewsDir : path.resolve(projectRootRef.current, viewsDir);
5725
5846
  const routerInitPromise = (async () => {
5726
5847
  if (!routerAwarePoms) {
5727
5848
  setRouteNameToComponentNameMap(/* @__PURE__ */ new Map());
@@ -5734,7 +5855,15 @@ function createDevProcessorPlugin(options) {
5734
5855
  } else {
5735
5856
  if (!resolvedRouterEntry)
5736
5857
  throw new Error("[vue-pom-generator] router.entry is required when router introspection is enabled.");
5737
- result = await parseRouterFileFromCwd(resolvedRouterEntry, { moduleShims: routerModuleShims });
5858
+ result = await parseRouterFileFromCwd(resolvedRouterEntry, {
5859
+ moduleShims: routerModuleShims,
5860
+ componentNaming: {
5861
+ projectRoot: projectRootRef.current,
5862
+ viewsDirAbs: getViewsDirAbs(),
5863
+ scanDirs,
5864
+ extraRoots: [process.cwd()]
5865
+ }
5866
+ });
5738
5867
  }
5739
5868
  const { routeNameMap, routePathMap } = result;
5740
5869
  setRouteNameToComponentNameMap(routeNameMap);
@@ -5757,7 +5886,6 @@ function createDevProcessorPlugin(options) {
5757
5886
  const logDebug = (message) => loggerRef.current.debug(message);
5758
5887
  let scheduleVueFileRegenLocal = null;
5759
5888
  const formatMs = (ms) => `${ms.toFixed(1)}ms`;
5760
- const getViewsDirAbs = () => path.isAbsolute(viewsDir) ? viewsDir : path.resolve(projectRootRef.current, viewsDir);
5761
5889
  const extractTemplateFromSfc = (source, filename) => {
5762
5890
  const { descriptor } = compilerSfc.parse(source, {
5763
5891
  filename: filename ?? "anonymous.vue"
@@ -5899,7 +6027,9 @@ function createDevProcessorPlugin(options) {
5899
6027
  testIdAttribute,
5900
6028
  vueRouterFluentChaining: routerAwarePoms,
5901
6029
  routerEntry: resolvedRouterEntry,
5902
- routerType
6030
+ routerType,
6031
+ viewsDir,
6032
+ scanDirs
5903
6033
  });
5904
6034
  if (reason === "startup") {
5905
6035
  devStartupMetricsByOutputKey.set(generationMetricsKey, metrics);
@@ -6121,6 +6251,8 @@ function createSupportPlugins(options) {
6121
6251
  const tsProcessor = createBuildProcessorPlugin({
6122
6252
  componentHierarchyMap,
6123
6253
  vueFilesPathMap,
6254
+ viewsDir,
6255
+ scanDirs,
6124
6256
  basePageClassPath,
6125
6257
  normalizedBasePagePath,
6126
6258
  outDir,