@immense/vue-pom-generator 1.0.66 → 1.0.67

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.
Files changed (53) hide show
  1. package/AGENTS.md +21 -0
  2. package/README.md +1 -0
  3. package/RELEASE_NOTES.md +78 -11
  4. package/class-generation/index.ts +16 -5
  5. package/dist/class-generation/index.d.ts.map +1 -1
  6. package/dist/compiler-metadata-utils.d.ts.map +1 -1
  7. package/dist/index.cjs +257 -78
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.ts +1 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.mjs +257 -78
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/manifest-generator.d.ts +2 -0
  14. package/dist/manifest-generator.d.ts.map +1 -1
  15. package/dist/metadata-collector.d.ts +2 -0
  16. package/dist/metadata-collector.d.ts.map +1 -1
  17. package/dist/plugin/create-vue-pom-generator-plugins.d.ts.map +1 -1
  18. package/dist/plugin/internal/build-plugin.d.ts.map +1 -0
  19. package/dist/plugin/internal/dev-plugin.d.ts.map +1 -0
  20. package/dist/plugin/internal/virtual-modules.d.ts.map +1 -0
  21. package/dist/plugin/{support-plugins.d.ts → internal-plugins.d.ts} +14 -3
  22. package/dist/plugin/internal-plugins.d.ts.map +1 -0
  23. package/dist/plugin/runtime/annotator/client.d.ts +67 -0
  24. package/dist/plugin/runtime/annotator/client.d.ts.map +1 -0
  25. package/dist/plugin/runtime/annotator/format.d.ts +13 -0
  26. package/dist/plugin/runtime/annotator/format.d.ts.map +1 -0
  27. package/dist/plugin/runtime/annotator/plugin.d.ts +12 -0
  28. package/dist/plugin/runtime/annotator/plugin.d.ts.map +1 -0
  29. package/dist/plugin/runtime/annotator/styles.d.ts +3 -0
  30. package/dist/plugin/runtime/annotator/styles.d.ts.map +1 -0
  31. package/dist/plugin/runtime/annotator/vue-detector.d.ts +12 -0
  32. package/dist/plugin/runtime/annotator/vue-detector.d.ts.map +1 -0
  33. package/dist/plugin/types.d.ts +58 -3
  34. package/dist/plugin/types.d.ts.map +1 -1
  35. package/dist/plugin/vue-plugin.d.ts +4 -0
  36. package/dist/plugin/vue-plugin.d.ts.map +1 -1
  37. package/dist/transform.d.ts +4 -0
  38. package/dist/transform.d.ts.map +1 -1
  39. package/dist/utils.d.ts +19 -0
  40. package/dist/utils.d.ts.map +1 -1
  41. package/package.json +3 -1
  42. package/plugin/runtime/annotator/client.ts +1005 -0
  43. package/plugin/runtime/annotator/format.ts +76 -0
  44. package/plugin/runtime/annotator/plugin.ts +109 -0
  45. package/plugin/runtime/annotator/styles.ts +379 -0
  46. package/plugin/runtime/annotator/vue-detector.ts +216 -0
  47. package/dist/plugin/support/build-plugin.d.ts.map +0 -1
  48. package/dist/plugin/support/dev-plugin.d.ts.map +0 -1
  49. package/dist/plugin/support/virtual-modules.d.ts.map +0 -1
  50. package/dist/plugin/support-plugins.d.ts.map +0 -1
  51. /package/dist/plugin/{support → internal}/build-plugin.d.ts +0 -0
  52. /package/dist/plugin/{support → internal}/dev-plugin.d.ts +0 -0
  53. /package/dist/plugin/{support → internal}/virtual-modules.d.ts +0 -0
package/dist/index.cjs CHANGED
@@ -686,7 +686,7 @@ function createInlineParameter(name, options = {}) {
686
686
  initializer: options.initializer
687
687
  };
688
688
  }
689
- function removeByKeySegment$1(value) {
689
+ function removeByKeySegment(value) {
690
690
  const idx = value.lastIndexOf("ByKey");
691
691
  if (idx < 0) {
692
692
  return value;
@@ -890,13 +890,13 @@ function generateGetElementByDataTestId(componentName, methodName, nativeRole, s
890
890
  const roleSuffix = upperFirst$1(nativeRole || "Element");
891
891
  const baseName = upperFirst$1(methodName);
892
892
  const numericSuffix = baseName.startsWith(roleSuffix) ? baseName.slice(roleSuffix.length) : "";
893
- const hasRoleSuffix2 = baseName.endsWith(roleSuffix) || baseName.startsWith(roleSuffix) && isAllDigits(numericSuffix);
894
- const propertyName = hasRoleSuffix2 ? `${baseName}` : `${baseName}${roleSuffix}`;
893
+ const hasRoleSuffix = baseName.endsWith(roleSuffix) || baseName.startsWith(roleSuffix) && isAllDigits(numericSuffix);
894
+ const propertyName = hasRoleSuffix ? `${baseName}` : `${baseName}${roleSuffix}`;
895
895
  const selectorParams = orderPomPatternParameters(parameters, [selector]);
896
896
  const indexedVariable = getIndexedPomPatternVariable(selector);
897
897
  if (indexedVariable) {
898
898
  const keyType = getPomParameter(selectorParams, indexedVariable)?.typeExpression || "string";
899
- const keyedPropertyName = getterNameOverride ?? removeByKeySegment$1(propertyName);
899
+ const keyedPropertyName = getterNameOverride ?? removeByKeySegment(propertyName);
900
900
  return [
901
901
  createClassGetter({
902
902
  name: keyedPropertyName,
@@ -2935,7 +2935,7 @@ Fix: either (1) include ${bestKeyPreservePlaceholder} in your :${attrLabel} temp
2935
2935
  }
2936
2936
  return value.slice(0, idx) + value.slice(idx + "ByKey".length);
2937
2937
  };
2938
- const hasRoleSuffix2 = (baseName, roleSuffix) => {
2938
+ const hasRoleSuffix = (baseName, roleSuffix) => {
2939
2939
  if (baseName.endsWith(roleSuffix)) {
2940
2940
  return true;
2941
2941
  }
@@ -2945,13 +2945,13 @@ Fix: either (1) include ${bestKeyPreservePlaceholder} in your :${attrLabel} temp
2945
2945
  const getPrimaryGetterName = (primaryMethodName) => {
2946
2946
  const roleSuffix = upperFirst(normalizedRole || "Element");
2947
2947
  const baseName = upperFirst(primaryMethodName);
2948
- const propertyName = hasRoleSuffix2(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
2948
+ const propertyName = hasRoleSuffix(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
2949
2949
  return selectorIsParameterized ? removeByKeySegment2(propertyName) : propertyName;
2950
2950
  };
2951
2951
  const getPrimaryGetterNameCandidates = (primaryMethodName) => {
2952
2952
  const roleSuffix = upperFirst(normalizedRole || "Element");
2953
2953
  const baseName = upperFirst(primaryMethodName);
2954
- const propertyName = hasRoleSuffix2(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
2954
+ const propertyName = hasRoleSuffix(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
2955
2955
  if (!selectorIsParameterized) {
2956
2956
  return { primary: propertyName };
2957
2957
  }
@@ -3078,7 +3078,7 @@ Fix: either (1) include ${bestKeyPreservePlaceholder} in your :${attrLabel} temp
3078
3078
  if (conflicts && nameCollisionBehavior === "error") {
3079
3079
  const roleSuffix = upperFirst(normalizedRole || "Element");
3080
3080
  const baseNameUpper = upperFirst(baseWithSuffix);
3081
- if (!hasRoleSuffix2(baseNameUpper, roleSuffix)) {
3081
+ if (!hasRoleSuffix(baseNameUpper, roleSuffix)) {
3082
3082
  const baseWithRoleSuffix = `${baseWithSuffix}${roleSuffix}`;
3083
3083
  const candidateWithRoleSuffix = selectorIsParameterized ? `${baseWithRoleSuffix}ByKey` : baseWithRoleSuffix;
3084
3084
  const actionNameWithRoleSuffix = getPrimaryActionMethodName(candidateWithRoleSuffix);
@@ -3188,12 +3188,32 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
3188
3188
  alternateSelectors: void 0,
3189
3189
  mergeKey: args.pomMergeKey,
3190
3190
  parameters: normalizedParameters,
3191
- keyValuesOverride: args.keyValuesOverride ?? null
3191
+ keyValuesOverride: args.keyValuesOverride ?? null,
3192
+ generatedActionName: getPrimaryActionMethodName(methodName),
3193
+ generatedPropertyName: getterNameOverride ?? getPrimaryGetterName(methodName)
3192
3194
  // emitPrimary defaults to true; special cases (including merge) may set it to false below.
3193
3195
  };
3194
3196
  if (mergedIntoExisting && dataTestIdEntry.pom) {
3195
3197
  dataTestIdEntry.pom.emitPrimary = false;
3196
3198
  }
3199
+ if (addHtmlAttribute && args.annotatorMetadata && dataTestIdEntry.pom) {
3200
+ const filename = args.contextFilename?.trim();
3201
+ if (!filename) {
3202
+ throw new Error(`[vue-pom-generator] runtime.annotator.enabled requires contextFilename for ${args.componentName}.`);
3203
+ }
3204
+ const sourceLocation = args.element.loc?.start;
3205
+ if (!sourceLocation) {
3206
+ throw new Error(`[vue-pom-generator] runtime.annotator.enabled requires element source locations for ${args.componentName}.`);
3207
+ }
3208
+ const metadataAttributePrefix = args.annotatorMetadata.metadataAttributePrefix;
3209
+ upsertAttribute(args.element, args.annotatorMetadata.sourceAttribute, staticAttributeValue(`${filename}:${sourceLocation.line}:${sourceLocation.column}`));
3210
+ upsertAttribute(args.element, `${metadataAttributePrefix}-component`, staticAttributeValue(args.componentName));
3211
+ upsertAttribute(args.element, `${metadataAttributePrefix}-tag`, staticAttributeValue(args.element.tag));
3212
+ upsertAttribute(args.element, `${metadataAttributePrefix}-testid`, runtimeDataTestId);
3213
+ upsertAttribute(args.element, `${metadataAttributePrefix}-action`, staticAttributeValue(buildPomGeneratedActionName(dataTestIdEntry.pom)));
3214
+ upsertAttribute(args.element, `${metadataAttributePrefix}-property`, staticAttributeValue(buildPomGeneratedPropertyName(dataTestIdEntry.pom)));
3215
+ upsertAttribute(args.element, `${metadataAttributePrefix}-role`, staticAttributeValue(normalizedRole));
3216
+ }
3197
3217
  args.dependencies.childrenComponentSet.add(childComponentName);
3198
3218
  args.dependencies.usedComponentSet.add(childComponentName);
3199
3219
  args.dependencies.dataTestIdSet.add(dataTestIdEntry);
@@ -3392,7 +3412,7 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
3392
3412
  registerGeneratedMethodSignature(generatedName2, createPomMethodSignature([createPomParameterSpec("annotationText", `string = ""`)]));
3393
3413
  }
3394
3414
  }
3395
- return { selectorValue: dataTestId, runtimeValue: runtimeDataTestId, fromExisting };
3415
+ return { selectorValue: dataTestId, runtimeValue: runtimeDataTestId, fromExisting, entry: dataTestIdEntry };
3396
3416
  }
3397
3417
  const generatedName = ensureUniqueGeneratedName(`select${upperFirst(methodName || "Radio")}`);
3398
3418
  if (dataTestIdEntry.pom) {
@@ -3419,7 +3439,7 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
3419
3439
  createPomParameterSpec("annotationText", `string = ""`)
3420
3440
  ]));
3421
3441
  }
3422
- return { selectorValue: dataTestId, runtimeValue: runtimeDataTestId, fromExisting };
3442
+ return { selectorValue: dataTestId, runtimeValue: runtimeDataTestId, fromExisting, entry: dataTestIdEntry };
3423
3443
  }
3424
3444
  const staticKeyValues = args.keyValuesOverride ?? null;
3425
3445
  const needsKey = hasPomParameter(normalizedParameters, "key") && selectorIsParameterized;
@@ -3453,7 +3473,7 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
3453
3473
  ]));
3454
3474
  }
3455
3475
  }
3456
- return { selectorValue: dataTestId, runtimeValue: runtimeDataTestId, fromExisting };
3476
+ return { selectorValue: dataTestId, runtimeValue: runtimeDataTestId, fromExisting, entry: dataTestIdEntry };
3457
3477
  }
3458
3478
  if (dataTestIdEntry.pom) {
3459
3479
  if (dataTestIdEntry.pom.emitPrimary !== false) {
@@ -3467,7 +3487,66 @@ Fix: make the element identifiable (e.g. add id/name/inner text or use a more sp
3467
3487
  const generatedName = getGeneratedMethodName();
3468
3488
  registerGeneratedMethodSignature(generatedName, signature);
3469
3489
  }
3470
- return { selectorValue: dataTestId, runtimeValue: runtimeDataTestId, fromExisting };
3490
+ return { selectorValue: dataTestId, runtimeValue: runtimeDataTestId, fromExisting, entry: dataTestIdEntry };
3491
+ }
3492
+ function requireStructuredPomName(value, fieldName, pom) {
3493
+ const normalized = value?.trim();
3494
+ if (normalized) {
3495
+ return normalized;
3496
+ }
3497
+ throw new Error(`[vue-pom-generator] Missing ${fieldName} for POM spec ${pom.methodName}.`);
3498
+ }
3499
+ function buildPomGeneratedPropertyName(pom) {
3500
+ return requireStructuredPomName(pom.generatedPropertyName, "generatedPropertyName", pom);
3501
+ }
3502
+ function buildPomGeneratedActionName(pom) {
3503
+ return requireStructuredPomName(pom.generatedActionName, "generatedActionName", pom);
3504
+ }
3505
+ const STANDALONE_WRAPPER_FALLBACK_ROLES = /* @__PURE__ */ new Set([
3506
+ "button",
3507
+ "input",
3508
+ "select",
3509
+ "vselect",
3510
+ "checkbox",
3511
+ "toggle",
3512
+ "radio"
3513
+ ]);
3514
+ function stripTrailingAsciiDigits(value) {
3515
+ let end = value.length;
3516
+ while (end > 0 && isAsciiDigitCode(value.charCodeAt(end - 1))) {
3517
+ end -= 1;
3518
+ }
3519
+ return value.slice(0, end);
3520
+ }
3521
+ function matchesStandaloneWrapperFallbackMethodName(methodName, componentClassName) {
3522
+ let normalizedMethodName = methodName;
3523
+ if (normalizedMethodName.endsWith("ByKey")) {
3524
+ normalizedMethodName = normalizedMethodName.slice(0, -"ByKey".length);
3525
+ }
3526
+ normalizedMethodName = stripTrailingAsciiDigits(normalizedMethodName);
3527
+ return normalizedMethodName === componentClassName;
3528
+ }
3529
+ function shouldSuppressStandaloneWrapperFallbackSurface(componentName, dependencies) {
3530
+ if (dependencies.isView || (dependencies.pomExtraMethods?.length ?? 0) > 0) {
3531
+ return false;
3532
+ }
3533
+ const entries = Array.from(dependencies.dataTestIdSet ?? []);
3534
+ if (!entries.length) {
3535
+ return false;
3536
+ }
3537
+ const primaryEntries = entries.filter((entry) => {
3538
+ return !!entry.pom && entry.pom.emitPrimary !== false;
3539
+ });
3540
+ if (!primaryEntries.length || primaryEntries.length !== entries.length) {
3541
+ return false;
3542
+ }
3543
+ const componentClassName = toPascalCase(componentName.endsWith(".vue") ? componentName.slice(0, -4) : componentName);
3544
+ if (!componentClassName) {
3545
+ return false;
3546
+ }
3547
+ return primaryEntries.every(({ pom }) => {
3548
+ return STANDALONE_WRAPPER_FALLBACK_ROLES.has(pom.nativeRole) && matchesStandaloneWrapperFallbackMethodName(pom.methodName, componentClassName);
3549
+ });
3471
3550
  }
3472
3551
  function safeRealpath(value) {
3473
3552
  try {
@@ -4767,6 +4846,11 @@ async function generateFiles(componentHierarchyMap, vueFilesPathMap, basePageCla
4767
4846
  componentDirs,
4768
4847
  layoutDirs
4769
4848
  }) : void 0);
4849
+ const emittableComponentHierarchyMap = new Map(
4850
+ Array.from(componentHierarchyMap.entries()).filter(([componentName, dependencies]) => {
4851
+ return !shouldSuppressStandaloneWrapperFallbackSurface(componentName, dependencies);
4852
+ })
4853
+ );
4770
4854
  const generatedFilePaths = [];
4771
4855
  const writeGeneratedFile = (file) => {
4772
4856
  const resolvedFilePath = path.resolve(file.filePath);
@@ -4774,7 +4858,7 @@ async function generateFiles(componentHierarchyMap, vueFilesPathMap, basePageCla
4774
4858
  generatedFilePaths.push(resolvedFilePath);
4775
4859
  };
4776
4860
  if (emitLanguages.includes("ts")) {
4777
- const files = typescriptOutputStructure === "split" ? await generateSplitTypeScriptFiles(componentHierarchyMap, vueFilesPathMap, basePageClassPath, outDir, {
4861
+ const files = typescriptOutputStructure === "split" ? await generateSplitTypeScriptFiles(emittableComponentHierarchyMap, vueFilesPathMap, basePageClassPath, outDir, {
4778
4862
  customPomAttachments,
4779
4863
  projectRoot,
4780
4864
  customPomDir,
@@ -4784,7 +4868,7 @@ async function generateFiles(componentHierarchyMap, vueFilesPathMap, basePageCla
4784
4868
  testIdAttribute,
4785
4869
  routeMetaByComponent,
4786
4870
  vueRouterFluentChaining
4787
- }) : await generateAggregatedFiles(componentHierarchyMap, vueFilesPathMap, basePageClassPath, outDir, {
4871
+ }) : await generateAggregatedFiles(emittableComponentHierarchyMap, vueFilesPathMap, basePageClassPath, outDir, {
4788
4872
  customPomAttachments,
4789
4873
  projectRoot,
4790
4874
  customPomDir,
@@ -4798,7 +4882,7 @@ async function generateFiles(componentHierarchyMap, vueFilesPathMap, basePageCla
4798
4882
  for (const file of files) {
4799
4883
  writeGeneratedFile(file);
4800
4884
  }
4801
- const fixtureRegistryFile = maybeGenerateFixtureRegistry(componentHierarchyMap, {
4885
+ const fixtureRegistryFile = maybeGenerateFixtureRegistry(emittableComponentHierarchyMap, {
4802
4886
  generateFixtures,
4803
4887
  pomOutDir: outDir,
4804
4888
  projectRoot,
@@ -4809,7 +4893,7 @@ async function generateFiles(componentHierarchyMap, vueFilesPathMap, basePageCla
4809
4893
  }
4810
4894
  }
4811
4895
  if (emitLanguages.includes("csharp")) {
4812
- const csFiles = generateAggregatedCSharpFiles(componentHierarchyMap, outDir, {
4896
+ const csFiles = generateAggregatedCSharpFiles(emittableComponentHierarchyMap, outDir, {
4813
4897
  testIdAttribute,
4814
4898
  csharp
4815
4899
  });
@@ -5546,7 +5630,12 @@ function prepareViewObjectModelClass(componentName, dependencies, componentHiera
5546
5630
  const rawComponentRefsForInstances = isView ? usedComponentSet?.size ? usedComponentSet : childrenComponentSet : childrenComponentSet;
5547
5631
  const componentRefsForInstances = /* @__PURE__ */ new Set();
5548
5632
  for (const ref of rawComponentRefsForInstances) {
5549
- componentRefsForInstances.add(resolveTrackedComponentRef(ref) ?? normalizeTrackedComponentRef(ref));
5633
+ const resolvedRef = resolveTrackedComponentRef(ref) ?? normalizeTrackedComponentRef(ref);
5634
+ const resolvedDependencies = componentHierarchyMap.get(resolvedRef);
5635
+ if (!resolvedDependencies?.dataTestIdSet.size) {
5636
+ continue;
5637
+ }
5638
+ componentRefsForInstances.add(resolvedRef);
5550
5639
  }
5551
5640
  const hasChildComponent = (needle) => {
5552
5641
  const normalizedNeedle = normalizeTrackedComponentRef(needle);
@@ -7105,6 +7194,7 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
7105
7194
  const warn = options.warn;
7106
7195
  const vueFilesPathMap = options.vueFilesPathMap;
7107
7196
  const wrapperSearchRoots = options.wrapperSearchRoots ?? [];
7197
+ const annotatorMetadata = options.annotatorMetadata ?? null;
7108
7198
  const safeRealpath2 = (p) => {
7109
7199
  try {
7110
7200
  return fs.existsSync(p) ? fs.realpathSync(p) : p;
@@ -7368,7 +7458,8 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
7368
7458
  testIdAttribute,
7369
7459
  existingIdBehavior,
7370
7460
  nameCollisionBehavior,
7371
- warn
7461
+ warn,
7462
+ annotatorMetadata
7372
7463
  });
7373
7464
  };
7374
7465
  const { nativeWrappersValue, optionDataTestIdPrefixValue, semanticNameHint } = getNativeWrapperTransformInfo(element, componentName, nativeWrappers);
@@ -8359,48 +8450,6 @@ function collectAccessibilityReviewWarnings(manifest) {
8359
8450
  }
8360
8451
  return warnings;
8361
8452
  }
8362
- function removeByKeySegment(value) {
8363
- const idx = value.indexOf("ByKey");
8364
- if (idx < 0) {
8365
- return value;
8366
- }
8367
- return value.slice(0, idx) + value.slice(idx + "ByKey".length);
8368
- }
8369
- function hasRoleSuffix(baseName, roleSuffix) {
8370
- if (baseName.endsWith(roleSuffix)) {
8371
- return true;
8372
- }
8373
- const re = new RegExp(`^${roleSuffix}\\d+$`);
8374
- return re.test(baseName);
8375
- }
8376
- function getGeneratedPropertyName(pom) {
8377
- if (pom.getterNameOverride) {
8378
- return pom.getterNameOverride;
8379
- }
8380
- const roleSuffix = upperFirst(pom.nativeRole || "Element");
8381
- const baseName = upperFirst(pom.methodName);
8382
- const propertyName = hasRoleSuffix(baseName, roleSuffix) ? baseName : `${baseName}${roleSuffix}`;
8383
- return pom.selector.patternKind === "parameterized" ? removeByKeySegment(propertyName) : propertyName;
8384
- }
8385
- function getGeneratedActionName(entry, pom) {
8386
- const methodNameUpper = upperFirst(pom.methodName);
8387
- const radioMethodNameUpper = upperFirst(pom.methodName || "Radio");
8388
- const isNavigation = !!entry.targetPageObjectModelClass;
8389
- if (isNavigation) {
8390
- return `goTo${methodNameUpper}`;
8391
- }
8392
- switch (pom.nativeRole) {
8393
- case "input":
8394
- return `type${methodNameUpper}`;
8395
- case "select":
8396
- case "vselect":
8397
- return `select${methodNameUpper}`;
8398
- case "radio":
8399
- return `select${radioMethodNameUpper}`;
8400
- default:
8401
- return `click${methodNameUpper}`;
8402
- }
8403
- }
8404
8453
  function matchesPrimarySelector(extraMethod, pom) {
8405
8454
  if (extraMethod.selector.kind !== "testId") {
8406
8455
  return false;
@@ -8411,7 +8460,7 @@ function getManifestEntry(componentName, entry, componentMetadata, extraMethods)
8411
8460
  const testId = entry.selectorValue.formatted;
8412
8461
  const metadata = componentMetadata?.get(testId);
8413
8462
  const pom = entry.pom;
8414
- const generatedActionName = pom ? getGeneratedActionName(entry, pom) : null;
8463
+ const generatedActionName = pom ? buildPomGeneratedActionName(pom) : null;
8415
8464
  const extraActionNames = pom ? extraMethods.filter((extraMethod) => matchesPrimarySelector(extraMethod, pom)).map((extraMethod) => extraMethod.name).sort((a, b) => a.localeCompare(b)) : [];
8416
8465
  const generatedActionNames = Array.from(/* @__PURE__ */ new Set([
8417
8466
  ...generatedActionName ? [generatedActionName] : [],
@@ -8429,13 +8478,15 @@ function getManifestEntry(componentName, entry, componentMetadata, extraMethods)
8429
8478
  }) : componentName,
8430
8479
  inferredRole: pom?.nativeRole ?? null,
8431
8480
  ...accessibility ? { accessibility } : {},
8432
- generatedPropertyName: pom ? getGeneratedPropertyName(pom) : null,
8481
+ generatedPropertyName: pom ? buildPomGeneratedPropertyName(pom) : null,
8433
8482
  generatedActionName,
8434
8483
  generatedActionNames,
8435
8484
  emitPrimary: pom?.emitPrimary !== false,
8436
8485
  ...entry.targetPageObjectModelClass ? { targetPageObjectModelClass: entry.targetPageObjectModelClass } : {},
8437
8486
  ...metadata?.tag ? { sourceTag: metadata.tag } : {},
8438
8487
  ...metadata ? { sourceTagType: metadata.tagType } : {},
8488
+ ...metadata?.sourceLine !== void 0 ? { sourceLine: metadata.sourceLine } : {},
8489
+ ...metadata?.sourceColumn !== void 0 ? { sourceColumn: metadata.sourceColumn } : {},
8439
8490
  ...metadata?.patchFlag !== void 0 ? { patchFlag: metadata.patchFlag } : {},
8440
8491
  ...metadata?.dynamicProps?.length ? { dynamicProps: metadata.dynamicProps } : {},
8441
8492
  ...metadata?.hasClickHandler !== void 0 ? { hasClickHandler: metadata.hasClickHandler } : {},
@@ -8445,7 +8496,7 @@ function getManifestEntry(componentName, entry, componentMetadata, extraMethods)
8445
8496
  };
8446
8497
  }
8447
8498
  function buildPomManifest(componentHierarchyMap, elementMetadata) {
8448
- const manifestEntries = Array.from(componentHierarchyMap.entries()).sort((a, b) => a[0].localeCompare(b[0])).map(([componentName, dependencies]) => {
8499
+ const manifestEntries = Array.from(componentHierarchyMap.entries()).filter(([componentName, dependencies]) => !shouldSuppressStandaloneWrapperFallbackSurface(componentName, dependencies)).sort((a, b) => a[0].localeCompare(b[0])).map(([componentName, dependencies]) => {
8449
8500
  const entries = Array.from(dependencies.dataTestIdSet).sort((a, b) => a.selectorValue.formatted.localeCompare(b.selectorValue.formatted)).map((entry) => getManifestEntry(componentName, entry, elementMetadata.get(componentName), dependencies.pomExtraMethods ?? []));
8450
8501
  if (!entries.length) {
8451
8502
  return null;
@@ -8461,9 +8512,15 @@ function buildPomManifest(componentHierarchyMap, elementMetadata) {
8461
8512
  }).filter((entry) => entry !== null);
8462
8513
  return Object.fromEntries(manifestEntries);
8463
8514
  }
8464
- function buildTestIdManifest(pomManifest) {
8515
+ function buildTestIdManifest(componentHierarchyMap) {
8465
8516
  return Object.fromEntries(
8466
- Object.entries(pomManifest).map(([componentName, component]) => [componentName, Array.from(new Set(component.testIds)).sort((a, b) => a.localeCompare(b))])
8517
+ Array.from(componentHierarchyMap.entries()).sort((a, b) => a[0].localeCompare(b[0])).map(([componentName, dependencies]) => {
8518
+ const testIds = Array.from(dependencies.dataTestIdSet).map((entry) => entry.selectorValue.formatted).filter(Boolean);
8519
+ if (!testIds.length) {
8520
+ return null;
8521
+ }
8522
+ return [componentName, Array.from(new Set(testIds)).sort((a, b) => a.localeCompare(b))];
8523
+ }).filter((entry) => entry !== null)
8467
8524
  );
8468
8525
  }
8469
8526
  function writeConstJson(value) {
@@ -8473,7 +8530,7 @@ function writeConstJson(value) {
8473
8530
  }
8474
8531
  function generateTestIdsModule(componentHierarchyMap, elementMetadata) {
8475
8532
  const pomManifest = buildPomManifest(componentHierarchyMap, elementMetadata);
8476
- const testIdManifest = buildTestIdManifest(pomManifest);
8533
+ const testIdManifest = buildTestIdManifest(componentHierarchyMap);
8477
8534
  return renderSourceFile("virtual-testids.ts", (sourceFile) => {
8478
8535
  sourceFile.addStatements("// Virtual module: test id manifest");
8479
8536
  sourceFile.addVariableStatement({
@@ -8559,7 +8616,89 @@ function createTestIdsVirtualModulesPlugin(componentHierarchyMap, elementMetadat
8559
8616
  }
8560
8617
  };
8561
8618
  }
8562
- function createSupportPlugins(options) {
8619
+ const ANNOTATOR_CLIENT_VIRTUAL_ID = "virtual:vue-pom-generator/annotator-client";
8620
+ const ANNOTATOR_CLIENT_RESOLVED_ID = `\0${ANNOTATOR_CLIENT_VIRTUAL_ID}`;
8621
+ const DEFAULT_ENTRY_SUFFIXES = [
8622
+ "/src/main.ts",
8623
+ "/src/main.js",
8624
+ "/src/main.mts",
8625
+ "/src/main.tsx",
8626
+ "/src/main.jsx"
8627
+ ];
8628
+ function toFsImportPath(filePath) {
8629
+ return `/@fs/${filePath.replace(/\\/g, "/")}`;
8630
+ }
8631
+ function resolveAnnotatorClientPath() {
8632
+ const candidates = [
8633
+ node_url.fileURLToPath(new URL("./client.ts", typeof document === "undefined" ? require("url").pathToFileURL(__filename).href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("index.cjs", document.baseURI).href)),
8634
+ node_url.fileURLToPath(new URL("../plugin/runtime/annotator/client.ts", typeof document === "undefined" ? require("url").pathToFileURL(__filename).href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("index.cjs", document.baseURI).href))
8635
+ ];
8636
+ const resolved = candidates.find((candidate) => fs.existsSync(candidate));
8637
+ if (!resolved) {
8638
+ throw new Error("[vue-pom-generator] Unable to locate annotator client source.");
8639
+ }
8640
+ return path.resolve(resolved);
8641
+ }
8642
+ function createAnnotatorUiPlugin(options) {
8643
+ const clientPath = resolveAnnotatorClientPath();
8644
+ const clientImportPath = toFsImportPath(clientPath);
8645
+ const clientOptions = JSON.stringify({
8646
+ sourceAttribute: options.sourceAttribute,
8647
+ metadataAttributePrefix: options.metadataAttributePrefix,
8648
+ outputDetail: options.outputDetail,
8649
+ copyToClipboard: options.copyToClipboard,
8650
+ showComponentTree: options.showComponentTree
8651
+ });
8652
+ return {
8653
+ name: "vue-pom-generator:annotator-ui",
8654
+ apply: "serve",
8655
+ resolveId(id) {
8656
+ if (id === ANNOTATOR_CLIENT_VIRTUAL_ID) {
8657
+ return ANNOTATOR_CLIENT_RESOLVED_ID;
8658
+ }
8659
+ },
8660
+ load(id) {
8661
+ if (id !== ANNOTATOR_CLIENT_RESOLVED_ID) {
8662
+ return null;
8663
+ }
8664
+ return [
8665
+ `import { mountAnnotatorClient } from ${JSON.stringify(clientImportPath)};`,
8666
+ `mountAnnotatorClient(${clientOptions});`,
8667
+ ""
8668
+ ].join("\n");
8669
+ },
8670
+ transform(code, id) {
8671
+ if (!options.enabled) {
8672
+ return null;
8673
+ }
8674
+ const normalizedId = id.replace(/\\/g, "/");
8675
+ if (!DEFAULT_ENTRY_SUFFIXES.some((suffix) => normalizedId.endsWith(suffix))) {
8676
+ return null;
8677
+ }
8678
+ if (code.includes(ANNOTATOR_CLIENT_VIRTUAL_ID)) {
8679
+ return null;
8680
+ }
8681
+ return {
8682
+ code: `import ${JSON.stringify(ANNOTATOR_CLIENT_VIRTUAL_ID)};
8683
+ ${code}`,
8684
+ map: null
8685
+ };
8686
+ },
8687
+ transformIndexHtml() {
8688
+ if (!options.enabled) {
8689
+ return void 0;
8690
+ }
8691
+ const tag = {
8692
+ tag: "script",
8693
+ attrs: { type: "module" },
8694
+ children: `import ${JSON.stringify(ANNOTATOR_CLIENT_VIRTUAL_ID)};`,
8695
+ injectTo: "body"
8696
+ };
8697
+ return [tag];
8698
+ }
8699
+ };
8700
+ }
8701
+ function createInternalPlugins(options) {
8563
8702
  const {
8564
8703
  componentHierarchyMap,
8565
8704
  elementMetadata,
@@ -8575,7 +8714,8 @@ function createSupportPlugins(options) {
8575
8714
  generation,
8576
8715
  projectRootRef,
8577
8716
  basePageClassPath: basePageClassPathOverride,
8578
- loggerRef
8717
+ loggerRef,
8718
+ annotatorRuntime
8579
8719
  } = options;
8580
8720
  const {
8581
8721
  outDir,
@@ -8649,7 +8789,13 @@ function createSupportPlugins(options) {
8649
8789
  loggerRef
8650
8790
  });
8651
8791
  const virtualModules = createTestIdsVirtualModulesPlugin(componentHierarchyMap, elementMetadata);
8652
- return [tsProcessor, devProcessor, virtualModules];
8792
+ const annotatorUiPlugin = createAnnotatorUiPlugin({
8793
+ ...annotatorRuntime.ui,
8794
+ enabled: annotatorRuntime.enabled && annotatorRuntime.ui.enabled,
8795
+ sourceAttribute: annotatorRuntime.sourceAttribute,
8796
+ metadataAttributePrefix: annotatorRuntime.metadataAttributePrefix
8797
+ });
8798
+ return [tsProcessor, devProcessor, virtualModules, annotatorUiPlugin];
8653
8799
  }
8654
8800
  function findDataTestIdProp(element, attributeName = "data-testid") {
8655
8801
  return element.props.find(
@@ -8769,7 +8915,9 @@ function tryCreateElementMetadata(args) {
8769
8915
  staticAriaLabel: findStaticAttributeValue(element, "aria-label"),
8770
8916
  staticRole: findStaticAttributeValue(element, "role"),
8771
8917
  staticTitle: findStaticAttributeValue(element, "title"),
8772
- staticTextContent: collectStaticTextContent(element.children)
8918
+ staticTextContent: collectStaticTextContent(element.children),
8919
+ sourceLine: element.loc?.start.line,
8920
+ sourceColumn: element.loc?.start.column
8773
8921
  };
8774
8922
  return metadata;
8775
8923
  }
@@ -8841,7 +8989,8 @@ function createVuePluginWithTestIds(options) {
8841
8989
  loggerRef,
8842
8990
  getSourceDirs,
8843
8991
  getWrapperSearchRoots,
8844
- getProjectRoot
8992
+ getProjectRoot,
8993
+ annotatorMetadata
8845
8994
  } = options;
8846
8995
  const lastAccessibilityWarningSignatureByComponent = /* @__PURE__ */ new Map();
8847
8996
  const getComponentNameFromPath = (filename) => {
@@ -8912,7 +9061,8 @@ function createVuePluginWithTestIds(options) {
8912
9061
  missingSemanticNameBehavior,
8913
9062
  warn: (message) => loggerRef.current.warn(message),
8914
9063
  vueFilesPathMap,
8915
- wrapperSearchRoots: getWrapperSearchRoots()
9064
+ wrapperSearchRoots: getWrapperSearchRoots(),
9065
+ annotatorMetadata
8916
9066
  }
8917
9067
  )
8918
9068
  );
@@ -8967,7 +9117,8 @@ function createVuePluginWithTestIds(options) {
8967
9117
  missingSemanticNameBehavior,
8968
9118
  warn: (message) => loggerRef.current.warn(message),
8969
9119
  vueFilesPathMap,
8970
- wrapperSearchRoots: getWrapperSearchRoots()
9120
+ wrapperSearchRoots: getWrapperSearchRoots(),
9121
+ annotatorMetadata
8971
9122
  }
8972
9123
  );
8973
9124
  perFileTransform.set(componentName, transform);
@@ -9112,6 +9263,12 @@ function assertOneOf(value, allowed, name) {
9112
9263
  }
9113
9264
  throw new TypeError(`${name} must be one of: ${allowed.join(", ")}.`);
9114
9265
  }
9266
+ function assertDataAttributeName(value, name) {
9267
+ assertNonEmptyString(value, name);
9268
+ if (!value.startsWith("data-")) {
9269
+ throw new TypeError(`${name} must start with "data-".`);
9270
+ }
9271
+ }
9115
9272
  function readPackageJson(projectRoot) {
9116
9273
  const packageJsonPath = path.join(projectRoot, "package.json");
9117
9274
  if (!fs.existsSync(packageJsonPath)) {
@@ -9275,6 +9432,19 @@ function createVuePomGeneratorPlugins(options = {}) {
9275
9432
  const generationEnabled = generationOptions !== null;
9276
9433
  const vueGenerationOptions = generationOptions;
9277
9434
  const verbosity = options.logging?.verbosity ?? "warn";
9435
+ const annotatorRuntimeOptions = options.runtime?.annotator;
9436
+ const annotatorUiRuntimeOptions = annotatorRuntimeOptions?.ui;
9437
+ const resolvedAnnotatorRuntimeOptions = {
9438
+ enabled: annotatorRuntimeOptions?.enabled === true,
9439
+ sourceAttribute: annotatorRuntimeOptions?.sourceAttribute ?? "data-v-inspector",
9440
+ metadataAttributePrefix: annotatorRuntimeOptions?.metadataAttributePrefix ?? "data-v-pom",
9441
+ ui: {
9442
+ enabled: annotatorUiRuntimeOptions?.enabled === true,
9443
+ outputDetail: annotatorUiRuntimeOptions?.outputDetail ?? "forensic",
9444
+ copyToClipboard: annotatorUiRuntimeOptions?.copyToClipboard ?? false,
9445
+ showComponentTree: annotatorUiRuntimeOptions?.showComponentTree ?? true
9446
+ }
9447
+ };
9278
9448
  const vueOptions = options.vueOptions;
9279
9449
  const resolvedInjectionOptionsRef = {
9280
9450
  current: resolveInjectionSupportOptions({
@@ -9378,6 +9548,10 @@ function createVuePomGeneratorPlugins(options = {}) {
9378
9548
  assertNonEmptyStringArray(getComponentDirs(), "[vue-pom-generator] injection.componentDirs");
9379
9549
  assertNonEmptyStringArray(getLayoutDirs(), "[vue-pom-generator] injection.layoutDirs");
9380
9550
  assertNonEmptyStringArray(getWrapperSearchRoots(), "[vue-pom-generator] injection.wrapperSearchRoots");
9551
+ if (resolvedAnnotatorRuntimeOptions.enabled) {
9552
+ assertDataAttributeName(resolvedAnnotatorRuntimeOptions.sourceAttribute, "[vue-pom-generator] runtime.annotator.sourceAttribute");
9553
+ assertDataAttributeName(resolvedAnnotatorRuntimeOptions.metadataAttributePrefix, "[vue-pom-generator] runtime.annotator.metadataAttributePrefix");
9554
+ }
9381
9555
  if (generationEnabled) {
9382
9556
  assertNonEmptyString(resolvedGenerationOptions.outDir, "[vue-pom-generator] generation.outDir");
9383
9557
  assertOneOf(resolvedGenerationOptions.typescriptOutputStructure, ["aggregated", "split"], "[vue-pom-generator] generation.playwright.outputStructure");
@@ -9420,10 +9594,14 @@ function createVuePomGeneratorPlugins(options = {}) {
9420
9594
  loggerRef,
9421
9595
  getSourceDirs,
9422
9596
  getWrapperSearchRoots: getWrapperSearchRootsAbs,
9423
- getProjectRoot: () => projectRootRef.current
9597
+ getProjectRoot: () => projectRootRef.current,
9598
+ annotatorMetadata: resolvedAnnotatorRuntimeOptions.enabled ? {
9599
+ sourceAttribute: resolvedAnnotatorRuntimeOptions.sourceAttribute,
9600
+ metadataAttributePrefix: resolvedAnnotatorRuntimeOptions.metadataAttributePrefix
9601
+ } : null
9424
9602
  });
9425
9603
  templateCompilerOptionsForResolvedPlugin = templateCompilerOptions;
9426
- const supportPlugins = createSupportPlugins({
9604
+ const internalPlugins = createInternalPlugins({
9427
9605
  componentHierarchyMap,
9428
9606
  elementMetadata,
9429
9607
  vueFilesPathMap,
@@ -9438,7 +9616,8 @@ function createVuePomGeneratorPlugins(options = {}) {
9438
9616
  generation: resolvedGenerationOptions,
9439
9617
  projectRootRef,
9440
9618
  basePageClassPath: basePageClassPathOverride,
9441
- loggerRef
9619
+ loggerRef,
9620
+ annotatorRuntime: resolvedAnnotatorRuntimeOptions
9442
9621
  });
9443
9622
  if (isNuxt) {
9444
9623
  loggerRef.current.info("Nuxt environment detected. Skipping internal @vitejs/plugin-vue to avoid conflicts.");
@@ -9449,7 +9628,7 @@ function createVuePomGeneratorPlugins(options = {}) {
9449
9628
  configPlugin,
9450
9629
  metadataCollectorPlugin,
9451
9630
  ...usesExternalVuePlugin ? [] : [internalVuePlugin],
9452
- ...supportPlugins
9631
+ ...internalPlugins
9453
9632
  ];
9454
9633
  if (!generationEnabled) {
9455
9634
  const virtualModules = createTestIdsVirtualModulesPlugin(componentHierarchyMap, elementMetadata);