@immense/vue-pom-generator 1.0.33 → 1.0.34

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,35 +1,31 @@
1
- ● # Release Notes: v1.0.33
1
+ ● # v1.0.34
2
2
 
3
3
  ## Highlights
4
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
5
+ - Fixed strict click collision detection to prevent false positives
6
+ - Improved lazy route component naming for better code generation
7
+ - Enhanced router introspection with significant improvements to route handling
8
+ - Added comprehensive test coverage for class generation and router introspection
12
9
 
13
10
  ## Changes
14
11
 
15
- **Core Transformation**
16
- - Fixed singleton key test-id inference logic
17
- - Expanded `transform.ts` with enhanced processing capabilities
12
+ **Bug Fixes**
13
+ - Fixed strict click collision detection logic in transform pipeline
14
+ - Corrected lazy route component naming in class generation
18
15
 
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`
16
+ **Testing & Quality**
17
+ - Added 70+ lines of new tests for class-generation coverage
18
+ - Added 64+ lines of new tests for router introspection
19
+ - Added 37+ lines of new tests for transform logic
24
20
 
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
21
+ **Internal Improvements**
22
+ - Enhanced router introspection with 140+ lines of improvements
23
+ - Updated plugin path utilities with better handling
24
+ - Improved support plugins for both build and dev modes
29
25
 
30
- **Infrastructure**
31
- - Updated plugin creation in `plugin/create-vue-pom-generator-plugins.ts`
32
- - Minor utility adjustments in `utils.ts`
26
+ ## Breaking Changes
27
+
28
+ None.
33
29
 
34
30
  ## Pull Requests Included
35
31
 
@@ -38,6 +34,6 @@
38
34
 
39
35
  ## Testing
40
36
 
41
- Added 250+ new test cases across generation metrics, transform logic, and options handling. All
42
- tests passing.
37
+ Comprehensive test coverage added across class generation, router introspection, and transform
38
+ modules. All tests passing.
43
39
 
@@ -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 = (() => {
@@ -5599,12 +5739,14 @@ Fix: remove the explicit ${attrLabel}, or change existingIdBehavior to "overwrit
5599
5739
  });
5600
5740
  const clickHint = trimLeadingSeparators(clickSuffix) || void 0;
5601
5741
  const idOrName = getIdOrName(element) || void 0;
5602
- const semanticNameHint2 = clickHint || idOrName || innerText || conditionalHint || void 0;
5742
+ const semanticHintCandidates = [clickHint, idOrName, innerText, conditionalHint].map((value) => (value ?? "").trim()).filter(Boolean).filter((value, index, values) => values.indexOf(value) === index);
5743
+ const [semanticNameHint2, ...semanticNameHintAlternates] = semanticHintCandidates;
5603
5744
  const pomMergeKey = clickHint ? `click:hint:${clickHint}` : void 0;
5604
5745
  const testId = getClickDataTestId(clickSuffix);
5605
5746
  applyResolvedDataTestIdForElement({
5606
5747
  preferredGeneratedValue: testId,
5607
5748
  semanticNameHint: semanticNameHint2,
5749
+ semanticNameHintAlternates,
5608
5750
  pomMergeKey
5609
5751
  });
5610
5752
  {
@@ -5645,37 +5787,6 @@ Fix: remove the explicit ${attrLabel}, or change existingIdBehavior to "overwrit
5645
5787
  }
5646
5788
  };
5647
5789
  }
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
5790
  const devStartupMetricsByOutputKey = /* @__PURE__ */ new Map();
5680
5791
  function createDevProcessorPlugin(options) {
5681
5792
  const {
@@ -5722,6 +5833,7 @@ function createDevProcessorPlugin(options) {
5722
5833
  scheduleVueFileRegen(ctx.file, "hmr");
5723
5834
  },
5724
5835
  configureServer(server) {
5836
+ const getViewsDirAbs = () => path.isAbsolute(viewsDir) ? viewsDir : path.resolve(projectRootRef.current, viewsDir);
5725
5837
  const routerInitPromise = (async () => {
5726
5838
  if (!routerAwarePoms) {
5727
5839
  setRouteNameToComponentNameMap(/* @__PURE__ */ new Map());
@@ -5734,7 +5846,15 @@ function createDevProcessorPlugin(options) {
5734
5846
  } else {
5735
5847
  if (!resolvedRouterEntry)
5736
5848
  throw new Error("[vue-pom-generator] router.entry is required when router introspection is enabled.");
5737
- result = await parseRouterFileFromCwd(resolvedRouterEntry, { moduleShims: routerModuleShims });
5849
+ result = await parseRouterFileFromCwd(resolvedRouterEntry, {
5850
+ moduleShims: routerModuleShims,
5851
+ componentNaming: {
5852
+ projectRoot: projectRootRef.current,
5853
+ viewsDirAbs: getViewsDirAbs(),
5854
+ scanDirs,
5855
+ extraRoots: [process.cwd()]
5856
+ }
5857
+ });
5738
5858
  }
5739
5859
  const { routeNameMap, routePathMap } = result;
5740
5860
  setRouteNameToComponentNameMap(routeNameMap);
@@ -5757,7 +5877,6 @@ function createDevProcessorPlugin(options) {
5757
5877
  const logDebug = (message) => loggerRef.current.debug(message);
5758
5878
  let scheduleVueFileRegenLocal = null;
5759
5879
  const formatMs = (ms) => `${ms.toFixed(1)}ms`;
5760
- const getViewsDirAbs = () => path.isAbsolute(viewsDir) ? viewsDir : path.resolve(projectRootRef.current, viewsDir);
5761
5880
  const extractTemplateFromSfc = (source, filename) => {
5762
5881
  const { descriptor } = compilerSfc.parse(source, {
5763
5882
  filename: filename ?? "anonymous.vue"
@@ -5899,7 +6018,9 @@ function createDevProcessorPlugin(options) {
5899
6018
  testIdAttribute,
5900
6019
  vueRouterFluentChaining: routerAwarePoms,
5901
6020
  routerEntry: resolvedRouterEntry,
5902
- routerType
6021
+ routerType,
6022
+ viewsDir,
6023
+ scanDirs
5903
6024
  });
5904
6025
  if (reason === "startup") {
5905
6026
  devStartupMetricsByOutputKey.set(generationMetricsKey, metrics);
@@ -6121,6 +6242,8 @@ function createSupportPlugins(options) {
6121
6242
  const tsProcessor = createBuildProcessorPlugin({
6122
6243
  componentHierarchyMap,
6123
6244
  vueFilesPathMap,
6245
+ viewsDir,
6246
+ scanDirs,
6124
6247
  basePageClassPath,
6125
6248
  normalizedBasePagePath,
6126
6249
  outDir,