@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/dist/index.mjs CHANGED
@@ -2528,6 +2528,53 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
2528
2528
  registerGeneratedMethodSignature(generatedName, signature);
2529
2529
  }
2530
2530
  }
2531
+ function safeRealpath(value) {
2532
+ try {
2533
+ if (fs.existsSync(value)) {
2534
+ return fs.realpathSync(value);
2535
+ }
2536
+ } catch {
2537
+ return value;
2538
+ }
2539
+ const parent = path.dirname(value);
2540
+ if (!parent || parent === value) {
2541
+ return value;
2542
+ }
2543
+ const resolvedParent = safeRealpath(parent);
2544
+ return resolvedParent === parent ? value : path.join(resolvedParent, path.basename(value));
2545
+ }
2546
+ function resolveComponentNameFromPath(options) {
2547
+ const { projectRoot, viewsDirAbs, scanDirs, extraRoots = [] } = options;
2548
+ const cleanFilename = options.filename.includes("?") ? options.filename.substring(0, options.filename.indexOf("?")) : options.filename;
2549
+ const absFilename = path.isAbsolute(cleanFilename) ? cleanFilename : path.resolve(projectRoot, cleanFilename);
2550
+ const normalizedAbsFilename = path.normalize(safeRealpath(absFilename));
2551
+ const rootBases = [projectRoot, ...extraRoots.filter((r) => r !== projectRoot)];
2552
+ const roots = [viewsDirAbs, ...rootBases.flatMap((base) => scanDirs.map((d) => path.resolve(base, d)))];
2553
+ for (const base of rootBases) {
2554
+ for (const dir of scanDirs) {
2555
+ const absDir = path.resolve(base, dir);
2556
+ try {
2557
+ const pagesDir = path.join(absDir, "pages");
2558
+ if (fs.existsSync(pagesDir))
2559
+ roots.push(pagesDir);
2560
+ const componentsDir = path.join(absDir, "components");
2561
+ if (fs.existsSync(componentsDir))
2562
+ roots.push(componentsDir);
2563
+ } catch {
2564
+ }
2565
+ }
2566
+ }
2567
+ const potentialRoots = Array.from(new Set(roots.map((r) => path.normalize(safeRealpath(r))))).sort((a, b) => b.length - a.length);
2568
+ for (const root of potentialRoots) {
2569
+ if (normalizedAbsFilename.startsWith(root + path.sep) || normalizedAbsFilename === root) {
2570
+ const rel = path.relative(root, normalizedAbsFilename);
2571
+ const parsed = path.parse(rel);
2572
+ const segments = path.join(parsed.dir, parsed.name);
2573
+ return toPascalCase(segments);
2574
+ }
2575
+ }
2576
+ return toPascalCase(path.parse(normalizedAbsFilename).name);
2577
+ }
2531
2578
  let routerIntrospectionQueue = Promise.resolve();
2532
2579
  async function runRouterIntrospectionExclusive(fn) {
2533
2580
  const prev = routerIntrospectionQueue.catch(() => void 0);
@@ -2807,20 +2854,84 @@ function getRouteParamMeta(router, record, paramNames) {
2807
2854
  }
2808
2855
  });
2809
2856
  }
2810
- function getComponentNameFromRouteRecord(record) {
2811
- const comp = record.components?.default;
2812
- if (!comp)
2857
+ function normalizeRouteComponentFilePath(filePath, options = {}) {
2858
+ const queryIndex = filePath.indexOf("?");
2859
+ const cleanPath = queryIndex === -1 ? filePath : filePath.slice(0, queryIndex);
2860
+ if (cleanPath.startsWith("/@fs/")) {
2861
+ return path.normalize(cleanPath.slice("/@fs/".length));
2862
+ }
2863
+ if (path.isAbsolute(cleanPath)) {
2864
+ if (fs.existsSync(cleanPath) || !options.rootDir)
2865
+ return path.normalize(cleanPath);
2866
+ return path.normalize(path.resolve(options.rootDir, `.${cleanPath}`));
2867
+ }
2868
+ if (!options.rootDir)
2813
2869
  return null;
2870
+ return path.normalize(path.resolve(options.rootDir, cleanPath));
2871
+ }
2872
+ function getComponentInfoFromVueComponent(comp, options = {}) {
2873
+ if (!comp) {
2874
+ return {
2875
+ componentName: null,
2876
+ filePath: null
2877
+ };
2878
+ }
2879
+ let componentName = null;
2880
+ let filePath = null;
2814
2881
  if (typeof comp.__file === "string" && comp.__file.length) {
2882
+ filePath = normalizeRouteComponentFilePath(comp.__file, { rootDir: options.rootDir });
2815
2883
  const base = path.posix.basename(path.posix.normalize(comp.__file));
2816
2884
  if (base.toLowerCase().endsWith(".vue"))
2817
- return base.slice(0, -".vue".length);
2885
+ componentName = base.slice(0, -".vue".length);
2818
2886
  }
2819
- if (typeof comp.__name === "string" && comp.__name.length)
2820
- return comp.__name;
2821
- if (typeof comp.name === "string" && comp.name.length)
2822
- return comp.name;
2823
- return null;
2887
+ if (!componentName && typeof comp.__name === "string" && comp.__name.length)
2888
+ componentName = comp.__name;
2889
+ if (!componentName && options.allowFunctionNameFallback !== false && typeof comp.name === "string" && comp.name.length) {
2890
+ componentName = comp.name;
2891
+ }
2892
+ return {
2893
+ componentName,
2894
+ filePath
2895
+ };
2896
+ }
2897
+ async function getComponentInfoFromRouteRecord(record, options = {}) {
2898
+ const comp = record.components?.default;
2899
+ if (!comp) {
2900
+ return {
2901
+ componentName: null,
2902
+ filePath: null
2903
+ };
2904
+ }
2905
+ if (typeof comp !== "function") {
2906
+ return getComponentInfoFromVueComponent(comp, options);
2907
+ }
2908
+ const directInfo = getComponentInfoFromVueComponent(comp, {
2909
+ allowFunctionNameFallback: false,
2910
+ rootDir: options.rootDir
2911
+ });
2912
+ if (directInfo.componentName || directInfo.filePath)
2913
+ return directInfo;
2914
+ try {
2915
+ const loaded = await comp();
2916
+ const resolved = loaded && typeof loaded === "object" && "default" in loaded ? loaded.default : loaded;
2917
+ const loadedInfo = getComponentInfoFromVueComponent(resolved, options);
2918
+ if (loadedInfo.componentName || loadedInfo.filePath)
2919
+ return loadedInfo;
2920
+ } catch {
2921
+ }
2922
+ return getComponentInfoFromVueComponent(comp, options);
2923
+ }
2924
+ function resolveIntrospectedComponentName(componentInfo, componentNaming) {
2925
+ if (componentInfo.filePath && componentNaming) {
2926
+ return resolveComponentNameFromPath({
2927
+ filename: componentInfo.filePath,
2928
+ projectRoot: componentNaming.projectRoot,
2929
+ viewsDirAbs: componentNaming.viewsDirAbs,
2930
+ scanDirs: componentNaming.scanDirs,
2931
+ extraRoots: componentNaming.extraRoots
2932
+ });
2933
+ }
2934
+ return componentInfo.componentName;
2824
2935
  }
2825
2936
  async function ensureDomShim() {
2826
2937
  const domShimHtml = "<!doctype html><html><head></head><body><div id='app'></div></body></html>";
@@ -3066,7 +3177,8 @@ async function parseRouterFileFromCwd(routerEntryPath, options = {}) {
3066
3177
  const routePathMap = /* @__PURE__ */ new Map();
3067
3178
  const routeMetaEntries = [];
3068
3179
  for (const r of router.getRoutes()) {
3069
- const componentName = getComponentNameFromRouteRecord(r);
3180
+ const componentInfo = await getComponentInfoFromRouteRecord(r, { rootDir: cwd });
3181
+ const componentName = resolveIntrospectedComponentName(componentInfo, options.componentNaming);
3070
3182
  if (!componentName)
3071
3183
  continue;
3072
3184
  if (typeof r.path === "string" && r.path.length) {
@@ -3124,9 +3236,20 @@ function resolveRouterEntry(projectRoot, routerEntry) {
3124
3236
  const root = projectRoot ?? process.cwd();
3125
3237
  return path.isAbsolute(routerEntry) ? routerEntry : path.resolve(root, routerEntry);
3126
3238
  }
3127
- async function getRouteMetaByComponent(projectRoot, routerEntry, routerType) {
3239
+ async function getRouteMetaByComponent(projectRoot, routerEntry, routerType, options = {}) {
3128
3240
  const root = projectRoot ?? process.cwd();
3129
- const { routeMetaEntries } = routerType === "nuxt" ? await introspectNuxtPages(root) : await parseRouterFileFromCwd(resolveRouterEntry(root, routerEntry));
3241
+ const viewsDir = options.viewsDir ?? "src/views";
3242
+ const viewsDirAbs = path.isAbsolute(viewsDir) ? viewsDir : path.resolve(root, viewsDir);
3243
+ const scanDirs = options.scanDirs?.length ? options.scanDirs : ["src"];
3244
+ const extraRoots = process.cwd() !== root ? [process.cwd()] : [];
3245
+ const { routeMetaEntries } = routerType === "nuxt" ? await introspectNuxtPages(root) : await parseRouterFileFromCwd(resolveRouterEntry(root, routerEntry), {
3246
+ componentNaming: {
3247
+ projectRoot: root,
3248
+ viewsDirAbs,
3249
+ scanDirs,
3250
+ extraRoots
3251
+ }
3252
+ });
3130
3253
  const map = /* @__PURE__ */ new Map();
3131
3254
  for (const entry of routeMetaEntries) {
3132
3255
  const list = map.get(entry.componentName) ?? [];
@@ -3303,11 +3426,17 @@ async function generateFiles(componentHierarchyMap, vueFilesPathMap, basePageCla
3303
3426
  csharp,
3304
3427
  vueRouterFluentChaining,
3305
3428
  routerEntry,
3306
- routerType
3429
+ routerType,
3430
+ viewsDir,
3431
+ scanDirs,
3432
+ routeMetaByComponent: routeMetaByComponentOverride
3307
3433
  } = options;
3308
3434
  const emitLanguages = emitLanguagesOverride?.length ? emitLanguagesOverride : ["ts"];
3309
3435
  const outDir = outDirOverride ?? "./pom";
3310
- const routeMetaByComponent = vueRouterFluentChaining ? await getRouteMetaByComponent(projectRoot, routerEntry, routerType) : void 0;
3436
+ const routeMetaByComponent = routeMetaByComponentOverride ?? (vueRouterFluentChaining ? await getRouteMetaByComponent(projectRoot, routerEntry, routerType, {
3437
+ viewsDir,
3438
+ scanDirs
3439
+ }) : void 0);
3311
3440
  const generatedFilePaths = [];
3312
3441
  const writeGeneratedFile = (file) => {
3313
3442
  const resolvedFilePath = path.resolve(file.filePath);
@@ -4437,6 +4566,8 @@ function createBuildProcessorPlugin(options) {
4437
4566
  const {
4438
4567
  componentHierarchyMap,
4439
4568
  vueFilesPathMap,
4569
+ viewsDir,
4570
+ scanDirs,
4440
4571
  basePageClassPath,
4441
4572
  normalizedBasePagePath,
4442
4573
  outDir,
@@ -4472,7 +4603,14 @@ function createBuildProcessorPlugin(options) {
4472
4603
  } else {
4473
4604
  if (!resolvedRouterEntry)
4474
4605
  throw new Error("[vue-pom-generator] router.entry is required when router introspection is enabled.");
4475
- result = await parseRouterFileFromCwd(resolvedRouterEntry, { moduleShims: routerModuleShims });
4606
+ result = await parseRouterFileFromCwd(resolvedRouterEntry, {
4607
+ moduleShims: routerModuleShims,
4608
+ componentNaming: {
4609
+ projectRoot: projectRootRef.current,
4610
+ viewsDirAbs: path.isAbsolute(viewsDir) ? viewsDir : path.resolve(projectRootRef.current, viewsDir),
4611
+ scanDirs
4612
+ }
4613
+ });
4476
4614
  }
4477
4615
  const { routeNameMap, routePathMap } = result;
4478
4616
  setRouteNameToComponentNameMap(routeNameMap);
@@ -4523,7 +4661,9 @@ function createBuildProcessorPlugin(options) {
4523
4661
  testIdAttribute,
4524
4662
  vueRouterFluentChaining: routerAwarePoms,
4525
4663
  routerEntry: resolvedRouterEntry,
4526
- routerType
4664
+ routerType,
4665
+ viewsDir,
4666
+ scanDirs
4527
4667
  });
4528
4668
  buildGenerationMetricsByOutputKey.set(generationMetricsKey, metrics);
4529
4669
  loggerRef.current.info(`generated POMs (${metrics.entryCount} entries, ${metrics.selectorCount} selectors)`);
@@ -5179,14 +5319,14 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
5179
5319
  const warn = options.warn;
5180
5320
  const vueFilesPathMap = options.vueFilesPathMap;
5181
5321
  const wrapperSearchRoots = options.wrapperSearchRoots ?? [];
5182
- const safeRealpath = (p) => {
5322
+ const safeRealpath2 = (p) => {
5183
5323
  try {
5184
5324
  return fs.existsSync(p) ? fs.realpathSync(p) : p;
5185
5325
  } catch {
5186
5326
  return p;
5187
5327
  }
5188
5328
  };
5189
- const normalizedViewsDirAbs = path.normalize(safeRealpath(path.resolve(viewsDirAbs)));
5329
+ const normalizedViewsDirAbs = path.normalize(safeRealpath2(path.resolve(viewsDirAbs)));
5190
5330
  const generatedMethodContentByComponent = /* @__PURE__ */ new Map();
5191
5331
  const lastConditionalHintByParent = /* @__PURE__ */ new WeakMap();
5192
5332
  const lastConditionalMergeGroupByParent = /* @__PURE__ */ new WeakMap();
@@ -5274,7 +5414,7 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
5274
5414
  const parentIsRoot = context?.parent?.type === NodeTypes.ROOT;
5275
5415
  const parentElement = !parentIsRoot && context?.parent?.type === NodeTypes.ELEMENT ? context.parent : null;
5276
5416
  hierarchyMap.set(element, parentElement);
5277
- const normalizeFilePath = (filePath) => path.normalize(safeRealpath(path.resolve(filePath)));
5417
+ const normalizeFilePath = (filePath) => path.normalize(safeRealpath2(path.resolve(filePath)));
5278
5418
  const normalizedFilePath = normalizeFilePath(context.filename);
5279
5419
  const parentComponentName = componentName;
5280
5420
  const dependencies = (() => {
@@ -5558,12 +5698,14 @@ Fix: remove the explicit ${attrLabel}, or change existingIdBehavior to "overwrit
5558
5698
  });
5559
5699
  const clickHint = trimLeadingSeparators(clickSuffix) || void 0;
5560
5700
  const idOrName = getIdOrName(element) || void 0;
5561
- const semanticNameHint2 = clickHint || idOrName || innerText || conditionalHint || void 0;
5701
+ const semanticHintCandidates = [clickHint, idOrName, innerText, conditionalHint].map((value) => (value ?? "").trim()).filter(Boolean).filter((value, index, values) => values.indexOf(value) === index);
5702
+ const [semanticNameHint2, ...semanticNameHintAlternates] = semanticHintCandidates;
5562
5703
  const pomMergeKey = clickHint ? `click:hint:${clickHint}` : void 0;
5563
5704
  const testId = getClickDataTestId(clickSuffix);
5564
5705
  applyResolvedDataTestIdForElement({
5565
5706
  preferredGeneratedValue: testId,
5566
5707
  semanticNameHint: semanticNameHint2,
5708
+ semanticNameHintAlternates,
5567
5709
  pomMergeKey
5568
5710
  });
5569
5711
  {
@@ -5604,37 +5746,6 @@ Fix: remove the explicit ${attrLabel}, or change existingIdBehavior to "overwrit
5604
5746
  }
5605
5747
  };
5606
5748
  }
5607
- function resolveComponentNameFromPath(options) {
5608
- const { projectRoot, viewsDirAbs, scanDirs, extraRoots = [] } = options;
5609
- const cleanFilename = options.filename.includes("?") ? options.filename.substring(0, options.filename.indexOf("?")) : options.filename;
5610
- const absFilename = path.isAbsolute(cleanFilename) ? cleanFilename : path.resolve(projectRoot, cleanFilename);
5611
- const rootBases = [projectRoot, ...extraRoots.filter((r) => r !== projectRoot)];
5612
- const roots = [viewsDirAbs, ...rootBases.flatMap((base) => scanDirs.map((d) => path.resolve(base, d)))];
5613
- for (const base of rootBases) {
5614
- for (const dir of scanDirs) {
5615
- const absDir = path.resolve(base, dir);
5616
- try {
5617
- const pagesDir = path.join(absDir, "pages");
5618
- if (fs.existsSync(pagesDir))
5619
- roots.push(pagesDir);
5620
- const componentsDir = path.join(absDir, "components");
5621
- if (fs.existsSync(componentsDir))
5622
- roots.push(componentsDir);
5623
- } catch {
5624
- }
5625
- }
5626
- }
5627
- const potentialRoots = Array.from(new Set(roots.map((r) => path.normalize(r)))).sort((a, b) => b.length - a.length);
5628
- for (const root of potentialRoots) {
5629
- if (absFilename.startsWith(root + path.sep) || absFilename === root) {
5630
- const rel = path.relative(root, absFilename);
5631
- const parsed = path.parse(rel);
5632
- const segments = path.join(parsed.dir, parsed.name);
5633
- return toPascalCase(segments);
5634
- }
5635
- }
5636
- return toPascalCase(path.parse(absFilename).name);
5637
- }
5638
5749
  const devStartupMetricsByOutputKey = /* @__PURE__ */ new Map();
5639
5750
  function createDevProcessorPlugin(options) {
5640
5751
  const {
@@ -5681,6 +5792,7 @@ function createDevProcessorPlugin(options) {
5681
5792
  scheduleVueFileRegen(ctx.file, "hmr");
5682
5793
  },
5683
5794
  configureServer(server) {
5795
+ const getViewsDirAbs = () => path.isAbsolute(viewsDir) ? viewsDir : path.resolve(projectRootRef.current, viewsDir);
5684
5796
  const routerInitPromise = (async () => {
5685
5797
  if (!routerAwarePoms) {
5686
5798
  setRouteNameToComponentNameMap(/* @__PURE__ */ new Map());
@@ -5693,7 +5805,15 @@ function createDevProcessorPlugin(options) {
5693
5805
  } else {
5694
5806
  if (!resolvedRouterEntry)
5695
5807
  throw new Error("[vue-pom-generator] router.entry is required when router introspection is enabled.");
5696
- result = await parseRouterFileFromCwd(resolvedRouterEntry, { moduleShims: routerModuleShims });
5808
+ result = await parseRouterFileFromCwd(resolvedRouterEntry, {
5809
+ moduleShims: routerModuleShims,
5810
+ componentNaming: {
5811
+ projectRoot: projectRootRef.current,
5812
+ viewsDirAbs: getViewsDirAbs(),
5813
+ scanDirs,
5814
+ extraRoots: [process.cwd()]
5815
+ }
5816
+ });
5697
5817
  }
5698
5818
  const { routeNameMap, routePathMap } = result;
5699
5819
  setRouteNameToComponentNameMap(routeNameMap);
@@ -5716,7 +5836,6 @@ function createDevProcessorPlugin(options) {
5716
5836
  const logDebug = (message) => loggerRef.current.debug(message);
5717
5837
  let scheduleVueFileRegenLocal = null;
5718
5838
  const formatMs = (ms) => `${ms.toFixed(1)}ms`;
5719
- const getViewsDirAbs = () => path.isAbsolute(viewsDir) ? viewsDir : path.resolve(projectRootRef.current, viewsDir);
5720
5839
  const extractTemplateFromSfc = (source, filename) => {
5721
5840
  const { descriptor } = parse$1(source, {
5722
5841
  filename: filename ?? "anonymous.vue"
@@ -5858,7 +5977,9 @@ function createDevProcessorPlugin(options) {
5858
5977
  testIdAttribute,
5859
5978
  vueRouterFluentChaining: routerAwarePoms,
5860
5979
  routerEntry: resolvedRouterEntry,
5861
- routerType
5980
+ routerType,
5981
+ viewsDir,
5982
+ scanDirs
5862
5983
  });
5863
5984
  if (reason === "startup") {
5864
5985
  devStartupMetricsByOutputKey.set(generationMetricsKey, metrics);
@@ -6080,6 +6201,8 @@ function createSupportPlugins(options) {
6080
6201
  const tsProcessor = createBuildProcessorPlugin({
6081
6202
  componentHierarchyMap,
6082
6203
  vueFilesPathMap,
6204
+ viewsDir,
6205
+ scanDirs,
6083
6206
  basePageClassPath,
6084
6207
  normalizedBasePagePath,
6085
6208
  outDir,