@immense/vue-pom-generator 1.0.29 → 1.0.31

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.cjs CHANGED
@@ -606,6 +606,124 @@ function getTemplateSlotScope(node) {
606
606
  }
607
607
  return null;
608
608
  }
609
+ function isSimpleScopeIdentifier(value) {
610
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(value);
611
+ }
612
+ function buildSlotScopeFallbackKeyExpression(identifier) {
613
+ return `${identifier}.key ?? ${identifier}.data?.id ?? ${identifier}.id ?? ${identifier}.value ?? ${identifier}`;
614
+ }
615
+ function tryGetBindingIdentifierName(node) {
616
+ if (!node) {
617
+ return null;
618
+ }
619
+ if (types.isIdentifier(node)) {
620
+ return node.name;
621
+ }
622
+ if (types.isAssignmentPattern(node)) {
623
+ return tryGetBindingIdentifierName(node.left);
624
+ }
625
+ if (types.isRestElement(node)) {
626
+ return tryGetBindingIdentifierName(node.argument);
627
+ }
628
+ return null;
629
+ }
630
+ function getSlotScopeObjectPropertyKeyName(node) {
631
+ if (types.isIdentifier(node)) {
632
+ return node.name;
633
+ }
634
+ if (types.isStringLiteral(node)) {
635
+ return node.value;
636
+ }
637
+ return null;
638
+ }
639
+ function tryGetSlotScopeKeyCandidate(node) {
640
+ if (!node) {
641
+ return null;
642
+ }
643
+ if (types.isIdentifier(node)) {
644
+ return {
645
+ priority: 2,
646
+ expression: buildSlotScopeFallbackKeyExpression(node.name)
647
+ };
648
+ }
649
+ if (types.isAssignmentPattern(node)) {
650
+ return tryGetSlotScopeKeyCandidate(node.left);
651
+ }
652
+ if (types.isRestElement(node)) {
653
+ return tryGetSlotScopeKeyCandidate(node.argument);
654
+ }
655
+ if (!types.isObjectPattern(node)) {
656
+ return null;
657
+ }
658
+ let best = null;
659
+ for (const property of node.properties) {
660
+ let candidate = null;
661
+ if (types.isRestElement(property)) {
662
+ candidate = tryGetSlotScopeKeyCandidate(property.argument);
663
+ if (candidate) {
664
+ candidate = { ...candidate, priority: Math.max(candidate.priority, 2) };
665
+ }
666
+ } else if (types.isObjectProperty(property)) {
667
+ const keyName = getSlotScopeObjectPropertyKeyName(property.key);
668
+ const bindingName = tryGetBindingIdentifierName(property.value) ?? tryGetBindingIdentifierName(property.key);
669
+ if (bindingName) {
670
+ if (keyName === "key" || bindingName === "key") {
671
+ candidate = {
672
+ priority: 0,
673
+ expression: bindingName
674
+ };
675
+ } else {
676
+ candidate = {
677
+ priority: keyName === "data" ? 1 : 2,
678
+ expression: buildSlotScopeFallbackKeyExpression(bindingName)
679
+ };
680
+ }
681
+ }
682
+ }
683
+ if (candidate && (!best || candidate.priority < best.priority)) {
684
+ best = candidate;
685
+ }
686
+ }
687
+ return best;
688
+ }
689
+ function tryGetTemplateSlotScopeKeyExpression(scope) {
690
+ const trimmed = scope.trim();
691
+ if (!trimmed) {
692
+ return null;
693
+ }
694
+ if (isSimpleScopeIdentifier(trimmed)) {
695
+ return buildSlotScopeFallbackKeyExpression(trimmed);
696
+ }
697
+ try {
698
+ const parsed = parser.parse(`(${trimmed}) => {}`, {
699
+ sourceType: "module",
700
+ plugins: ["typescript"]
701
+ });
702
+ const statement = parsed.program.body[0];
703
+ if (statement && types.isExpressionStatement(statement) && types.isArrowFunctionExpression(statement.expression)) {
704
+ return tryGetSlotScopeKeyCandidate(statement.expression.params[0])?.expression ?? null;
705
+ }
706
+ } catch {
707
+ }
708
+ if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
709
+ const inner = trimmed.slice(1, -1).trim();
710
+ let cutIdx = -1;
711
+ const commaIdx = inner.indexOf(",");
712
+ const colonIdx = inner.indexOf(":");
713
+ if (commaIdx !== -1 && colonIdx !== -1) {
714
+ cutIdx = Math.min(commaIdx, colonIdx);
715
+ } else if (commaIdx !== -1) {
716
+ cutIdx = commaIdx;
717
+ } else if (colonIdx !== -1) {
718
+ cutIdx = colonIdx;
719
+ }
720
+ const first = (cutIdx === -1 ? inner : inner.slice(0, cutIdx)).trim();
721
+ if (first && isSimpleScopeIdentifier(first)) {
722
+ return buildSlotScopeFallbackKeyExpression(first);
723
+ }
724
+ }
725
+ return trimmed;
726
+ }
609
727
  function nodeHasToDirective(node) {
610
728
  const toDirective = findDirectiveByName(node, "bind", "to");
611
729
  if (toDirective?.exp) {
@@ -681,25 +799,7 @@ function getContainedInSlotDataKeyValue(node, hierarchyMap2) {
681
799
  if (parent.type === compilerCore.NodeTypes.ELEMENT && parent.tag === "template") {
682
800
  const scope = getTemplateSlotScope(parent);
683
801
  if (scope) {
684
- let key = scope.trim();
685
- if (key.startsWith("{") && key.endsWith("}")) {
686
- const inner = key.slice(1, -1).trim();
687
- let cutIdx = -1;
688
- const commaIdx = inner.indexOf(",");
689
- const colonIdx = inner.indexOf(":");
690
- if (commaIdx !== -1 && colonIdx !== -1) {
691
- cutIdx = Math.min(commaIdx, colonIdx);
692
- } else if (commaIdx !== -1) {
693
- cutIdx = commaIdx;
694
- } else if (colonIdx !== -1) {
695
- cutIdx = colonIdx;
696
- }
697
- const first = (cutIdx === -1 ? inner : inner.slice(0, cutIdx)).trim();
698
- if (first) {
699
- key = first;
700
- }
701
- }
702
- return key;
802
+ return tryGetTemplateSlotScopeKeyExpression(scope);
703
803
  }
704
804
  }
705
805
  parent = getParent(hierarchyMap2, parent);
@@ -1350,11 +1450,11 @@ function getStableClickHandlerNameFromExpression(exp) {
1350
1450
  return extractNameFromCallee(callee);
1351
1451
  }
1352
1452
  if (types.isAssignmentExpression(exp)) {
1353
- const left = exp.left;
1354
- if (types.isIdentifier(left))
1355
- return left.name;
1356
- if (types.isMemberExpression(left) || types.isOptionalMemberExpression(left))
1357
- return extractMemberPropertyName(left);
1453
+ const left = getAssignmentTargetNameFromBabel(exp.left);
1454
+ if (left) {
1455
+ const right = getStableAssignmentValueSuffixFromBabel(exp.right);
1456
+ return `Set${toPascalCase(left)}${right}`;
1457
+ }
1358
1458
  return "";
1359
1459
  }
1360
1460
  if (types.isOptionalMemberExpression(exp)) {
@@ -1429,6 +1529,71 @@ function extractMemberPropertyName(member) {
1429
1529
  }
1430
1530
  return "";
1431
1531
  }
1532
+ function getLastIdentifierFromMemberChainBabel(node) {
1533
+ if (!node) {
1534
+ return "";
1535
+ }
1536
+ if (types.isIdentifier(node)) {
1537
+ return node.name;
1538
+ }
1539
+ if (types.isMemberExpression(node) || types.isOptionalMemberExpression(node)) {
1540
+ if (!node.computed) {
1541
+ if (types.isIdentifier(node.property)) {
1542
+ return node.property.name;
1543
+ }
1544
+ } else if (types.isStringLiteral(node.property)) {
1545
+ return node.property.value;
1546
+ }
1547
+ }
1548
+ return "";
1549
+ }
1550
+ function getAssignmentTargetNameFromBabel(node) {
1551
+ if (!node) {
1552
+ return "";
1553
+ }
1554
+ if (types.isIdentifier(node)) {
1555
+ return node.name;
1556
+ }
1557
+ if (types.isMemberExpression(node) || types.isOptionalMemberExpression(node)) {
1558
+ if (!node.computed && types.isIdentifier(node.property) && node.property.name === "value") {
1559
+ return getLastIdentifierFromMemberChainBabel(node.object);
1560
+ }
1561
+ return getLastIdentifierFromMemberChainBabel(node);
1562
+ }
1563
+ return "";
1564
+ }
1565
+ function getStableAssignmentValueSuffixFromBabel(node) {
1566
+ if (!node) {
1567
+ return "";
1568
+ }
1569
+ if (types.isBooleanLiteral(node)) {
1570
+ return node.value ? "True" : "False";
1571
+ }
1572
+ if (types.isNumericLiteral(node)) {
1573
+ return `Value${String(node.value)}`;
1574
+ }
1575
+ if (types.isNullLiteral(node)) {
1576
+ return "Null";
1577
+ }
1578
+ if (types.isStringLiteral(node)) {
1579
+ const cleaned = (node.value ?? "").trim();
1580
+ return cleaned ? toPascalCase(cleaned.slice(0, 24)) : "";
1581
+ }
1582
+ if (types.isTemplateLiteral(node) && node.expressions.length === 0) {
1583
+ const value = node.quasis.map((q) => q.value?.cooked ?? "").join("").trim();
1584
+ return value ? toPascalCase(value.slice(0, 24)) : "";
1585
+ }
1586
+ if (types.isMemberExpression(node) || types.isOptionalMemberExpression(node)) {
1587
+ const name = getLastIdentifierFromMemberChainBabel(node);
1588
+ return name ? toPascalCase(name.slice(0, 24)) : "";
1589
+ }
1590
+ if (types.isIdentifier(node)) {
1591
+ const firstChar = node.name.charAt(0);
1592
+ const isUpperAlpha = firstChar !== "" && firstChar === firstChar.toUpperCase() && firstChar !== firstChar.toLowerCase();
1593
+ return isUpperAlpha ? toPascalCase(node.name.slice(0, 24)) : "";
1594
+ }
1595
+ return "";
1596
+ }
1432
1597
  function isCompilerGeneratedReferenceRoot(name) {
1433
1598
  return name.startsWith("_") || name.startsWith("$");
1434
1599
  }
@@ -3240,6 +3405,10 @@ function escapeGitAttributesPattern(value) {
3240
3405
  }
3241
3406
  return output;
3242
3407
  }
3408
+ function pathUsesGeneratedHeuristic(filePath) {
3409
+ const normalized = path.normalize(filePath);
3410
+ return normalized.split(path.sep).includes("__generated__");
3411
+ }
3243
3412
  function buildManagedGitAttributesBlock(entries) {
3244
3413
  return [
3245
3414
  GENERATED_GITATTRIBUTES_BLOCK_START,
@@ -3287,6 +3456,9 @@ function buildGeneratedGitAttributesFiles(generatedFilePaths) {
3287
3456
  if (path.basename(resolvedFilePath) === ".gitattributes") {
3288
3457
  continue;
3289
3458
  }
3459
+ if (pathUsesGeneratedHeuristic(resolvedFilePath)) {
3460
+ continue;
3461
+ }
3290
3462
  const dir = path.dirname(resolvedFilePath);
3291
3463
  const entry = `${escapeGitAttributesPattern(path.basename(resolvedFilePath))} linguist-generated`;
3292
3464
  const entries = entriesByDir.get(dir) ?? /* @__PURE__ */ new Set();
@@ -4363,15 +4535,57 @@ function createBuildProcessorPlugin(options) {
4363
4535
  const TESTID_CLICK_EVENT_NAME = "__testid_event__";
4364
4536
  const TESTID_CLICK_EVENT_STRICT_FLAG = "__testid_click_event_strict__";
4365
4537
  const CLICK_EVENT_NAME = TESTID_CLICK_EVENT_NAME;
4366
- const inferredNativeWrapperConfigByTag = /* @__PURE__ */ new Map();
4367
- const inferredSfcPathByTag = /* @__PURE__ */ new Map();
4368
- let indexedVueSfcPathsByBasename = null;
4538
+ const inferredNativeWrapperConfigByLookup = /* @__PURE__ */ new Map();
4539
+ const inferredSfcPathByLookup = /* @__PURE__ */ new Map();
4540
+ const indexedVueSfcPathsByRoots = /* @__PURE__ */ new Map();
4369
4541
  function toKebabCaseTag(tag) {
4370
- return tag.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[_.\s]+/g, "-").toLowerCase();
4542
+ let result = "";
4543
+ let previousWasSeparator = false;
4544
+ for (let i = 0; i < tag.length; i += 1) {
4545
+ const ch = tag[i];
4546
+ const code = ch.charCodeAt(0);
4547
+ if (ch === "_" || ch === "-" || ch === "." || ch === " " || ch === " " || ch === "\n" || ch === "\r") {
4548
+ if (result && !previousWasSeparator) {
4549
+ result += "-";
4550
+ }
4551
+ previousWasSeparator = true;
4552
+ continue;
4553
+ }
4554
+ const previous = i > 0 ? tag[i - 1] : "";
4555
+ const previousCode = previous ? previous.charCodeAt(0) : 0;
4556
+ const hasPrevious = i > 0;
4557
+ const shouldInsertSeparator = hasPrevious && isAsciiUppercaseLetterCode(code) && (isAsciiLetterCode(previousCode) || isAsciiDigitCode(previousCode)) && !previousWasSeparator;
4558
+ if (shouldInsertSeparator) {
4559
+ result += "-";
4560
+ }
4561
+ result += ch.toLowerCase();
4562
+ previousWasSeparator = false;
4563
+ }
4564
+ return result;
4371
4565
  }
4372
- function buildVueSfcPathIndex() {
4373
- if (indexedVueSfcPathsByBasename) {
4374
- return indexedVueSfcPathsByBasename;
4566
+ function normalizeSearchRoots(wrapperSearchRoots) {
4567
+ const normalized = /* @__PURE__ */ new Set();
4568
+ for (const root of wrapperSearchRoots) {
4569
+ const resolved = path.resolve(root);
4570
+ try {
4571
+ if (!fs.existsSync(resolved) || !fs.statSync(resolved).isDirectory()) {
4572
+ continue;
4573
+ }
4574
+ normalized.add(path.normalize(fs.realpathSync(resolved)));
4575
+ } catch {
4576
+ continue;
4577
+ }
4578
+ }
4579
+ return [...normalized];
4580
+ }
4581
+ function buildSearchRootsKey(searchRoots) {
4582
+ return searchRoots.join("\n");
4583
+ }
4584
+ function buildVueSfcPathIndex(searchRoots) {
4585
+ const indexKey = buildSearchRootsKey(searchRoots);
4586
+ const existingIndex = indexedVueSfcPathsByRoots.get(indexKey);
4587
+ if (existingIndex) {
4588
+ return existingIndex;
4375
4589
  }
4376
4590
  const index = /* @__PURE__ */ new Map();
4377
4591
  const ignoredDirNames = /* @__PURE__ */ new Set([
@@ -4389,22 +4603,6 @@ function buildVueSfcPathIndex() {
4389
4603
  "out",
4390
4604
  "tmp"
4391
4605
  ]);
4392
- const cwd = process.cwd();
4393
- const parent = path.resolve(cwd, "..");
4394
- const searchRoots = /* @__PURE__ */ new Set();
4395
- const enqueueIfDirectory = (dirPath) => {
4396
- try {
4397
- if (fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory()) {
4398
- searchRoots.add(dirPath);
4399
- }
4400
- } catch {
4401
- }
4402
- };
4403
- for (const root of [cwd, parent]) {
4404
- for (const rel of ["src", "components", "app", "shared", "packages", "libs"]) {
4405
- enqueueIfDirectory(path.resolve(root, rel));
4406
- }
4407
- }
4408
4606
  const stack = [...searchRoots];
4409
4607
  const seenDirs = /* @__PURE__ */ new Set();
4410
4608
  while (stack.length > 0) {
@@ -4437,52 +4635,58 @@ function buildVueSfcPathIndex() {
4437
4635
  index.set(entry.name, matches);
4438
4636
  }
4439
4637
  }
4440
- indexedVueSfcPathsByBasename = index;
4638
+ indexedVueSfcPathsByRoots.set(indexKey, index);
4441
4639
  return index;
4442
4640
  }
4443
- function tryResolveSfcPathForTag(tag, vueFilesPathMap) {
4444
- if (inferredSfcPathByTag.has(tag)) {
4445
- return inferredSfcPathByTag.get(tag) ?? null;
4446
- }
4641
+ function tryResolveSfcPathForTag(tag, vueFilesPathMap, wrapperSearchRoots = []) {
4447
4642
  const registeredPath = vueFilesPathMap?.get(tag);
4643
+ const normalizedSearchRoots = normalizeSearchRoots(wrapperSearchRoots);
4644
+ const lookupKey = `${tag}
4645
+ ${registeredPath ?? ""}
4646
+ ${buildSearchRootsKey(normalizedSearchRoots)}`;
4647
+ if (inferredSfcPathByLookup.has(lookupKey)) {
4648
+ return inferredSfcPathByLookup.get(lookupKey) ?? null;
4649
+ }
4448
4650
  const candidateNames = [`${tag}.vue`, `${toKebabCaseTag(tag)}.vue`];
4449
4651
  const directCandidates = [
4450
4652
  registeredPath ? path.resolve(process.cwd(), registeredPath) : null,
4451
- ...candidateNames.flatMap((fileName) => [
4452
- path.resolve(process.cwd(), "src/components", fileName),
4453
- path.resolve(process.cwd(), "components", fileName),
4454
- path.resolve(process.cwd(), "app/components", fileName),
4455
- path.resolve(process.cwd(), "shared/ui/src/components", fileName),
4456
- path.resolve(process.cwd(), "..", "shared/ui/src/components", fileName)
4457
- ])
4653
+ ...normalizedSearchRoots.flatMap((root) => candidateNames.map((fileName) => path.join(root, fileName)))
4458
4654
  ].filter((value) => !!value);
4459
4655
  const directMatch = directCandidates.find((candidatePath) => fs.existsSync(candidatePath));
4460
4656
  if (directMatch) {
4461
- inferredSfcPathByTag.set(tag, directMatch);
4657
+ inferredSfcPathByLookup.set(lookupKey, directMatch);
4462
4658
  return directMatch;
4463
4659
  }
4464
- const index = buildVueSfcPathIndex();
4660
+ if (normalizedSearchRoots.length === 0) {
4661
+ inferredSfcPathByLookup.set(lookupKey, null);
4662
+ return null;
4663
+ }
4664
+ const index = buildVueSfcPathIndex(normalizedSearchRoots);
4465
4665
  const scorePath = (candidatePath) => {
4466
- let score = candidatePath.length;
4467
- if (candidatePath.includes(`${path.sep}src${path.sep}components${path.sep}`)) {
4468
- score -= 50;
4469
- }
4470
- if (candidatePath.includes(`${path.sep}shared${path.sep}`)) {
4471
- score -= 10;
4472
- }
4473
- return score;
4666
+ const rootIndex = normalizedSearchRoots.findIndex((root) => {
4667
+ return candidatePath === root || candidatePath.startsWith(root + path.sep);
4668
+ });
4669
+ const effectiveRootIndex = rootIndex === -1 ? Number.MAX_SAFE_INTEGER : rootIndex;
4670
+ const relativeLength = rootIndex === -1 ? candidatePath.length : path.relative(normalizedSearchRoots[rootIndex], candidatePath).length;
4671
+ return [effectiveRootIndex, relativeLength, candidatePath];
4474
4672
  };
4673
+ let bestMatch = null;
4674
+ let bestScore = null;
4475
4675
  for (const fileName of candidateNames) {
4476
4676
  const matches = index.get(fileName);
4477
4677
  if (!matches?.length) {
4478
4678
  continue;
4479
4679
  }
4480
- const bestMatch = matches.slice().sort((a, b) => scorePath(a) - scorePath(b))[0] ?? null;
4481
- inferredSfcPathByTag.set(tag, bestMatch);
4482
- return bestMatch;
4680
+ for (const match of matches) {
4681
+ const score = scorePath(match);
4682
+ if (!bestScore || score[0] < bestScore[0] || score[0] === bestScore[0] && score[1] < bestScore[1] || score[0] === bestScore[0] && score[1] === bestScore[1] && score[2] < bestScore[2]) {
4683
+ bestScore = score;
4684
+ bestMatch = match;
4685
+ }
4686
+ }
4483
4687
  }
4484
- inferredSfcPathByTag.set(tag, null);
4485
- return null;
4688
+ inferredSfcPathByLookup.set(lookupKey, bestMatch);
4689
+ return bestMatch;
4486
4690
  }
4487
4691
  function trimLeadingSeparators(value) {
4488
4692
  if (!value) {
@@ -4597,7 +4801,7 @@ function tryExtractStableHintFromConditionalExpressionSource(source) {
4597
4801
  return null;
4598
4802
  }
4599
4803
  }
4600
- function tryInferNativeWrapperRoleFromSfc(tag, vueFilesPathMap, seenTags = /* @__PURE__ */ new Set()) {
4804
+ function tryInferNativeWrapperRoleFromSfc(tag, vueFilesPathMap, wrapperSearchRoots = [], seenTags = /* @__PURE__ */ new Set()) {
4601
4805
  const first = tag.charCodeAt(0);
4602
4806
  const isUpper = isAsciiUppercaseLetterCode(first);
4603
4807
  if (!isUpper)
@@ -4605,19 +4809,23 @@ function tryInferNativeWrapperRoleFromSfc(tag, vueFilesPathMap, seenTags = /* @_
4605
4809
  if (seenTags.has(tag)) {
4606
4810
  return null;
4607
4811
  }
4608
- const cached = inferredNativeWrapperConfigByTag.get(tag);
4812
+ const normalizedSearchRoots = normalizeSearchRoots(wrapperSearchRoots);
4813
+ const cacheKey = `${tag}
4814
+ ${vueFilesPathMap?.get(tag) ?? ""}
4815
+ ${buildSearchRootsKey(normalizedSearchRoots)}`;
4816
+ const cached = inferredNativeWrapperConfigByLookup.get(cacheKey);
4609
4817
  if (cached)
4610
4818
  return cached.role ? cached : null;
4611
- const filePath = tryResolveSfcPathForTag(tag, vueFilesPathMap);
4819
+ const filePath = tryResolveSfcPathForTag(tag, vueFilesPathMap, normalizedSearchRoots);
4612
4820
  if (!filePath) {
4613
- inferredNativeWrapperConfigByTag.set(tag, { role: "" });
4821
+ inferredNativeWrapperConfigByLookup.set(cacheKey, { role: "" });
4614
4822
  return null;
4615
4823
  }
4616
4824
  let source = "";
4617
4825
  try {
4618
4826
  source = fs.readFileSync(filePath, "utf8");
4619
4827
  } catch {
4620
- inferredNativeWrapperConfigByTag.set(tag, { role: "" });
4828
+ inferredNativeWrapperConfigByLookup.set(cacheKey, { role: "" });
4621
4829
  return null;
4622
4830
  }
4623
4831
  let template = "";
@@ -4625,11 +4833,11 @@ function tryInferNativeWrapperRoleFromSfc(tag, vueFilesPathMap, seenTags = /* @_
4625
4833
  const { descriptor } = compilerSfc.parse(source, { filename: filePath });
4626
4834
  template = descriptor.template?.content ?? "";
4627
4835
  } catch {
4628
- inferredNativeWrapperConfigByTag.set(tag, { role: "" });
4836
+ inferredNativeWrapperConfigByLookup.set(cacheKey, { role: "" });
4629
4837
  return null;
4630
4838
  }
4631
4839
  if (!template.trim()) {
4632
- inferredNativeWrapperConfigByTag.set(tag, { role: "" });
4840
+ inferredNativeWrapperConfigByLookup.set(cacheKey, { role: "" });
4633
4841
  return null;
4634
4842
  }
4635
4843
  try {
@@ -4648,6 +4856,7 @@ function tryInferNativeWrapperRoleFromSfc(tag, vueFilesPathMap, seenTags = /* @_
4648
4856
  });
4649
4857
  return (typeAttr?.value?.content ?? "").toLowerCase();
4650
4858
  };
4859
+ let inferRoleFromNode;
4651
4860
  const inferRoleFromElement = (element) => {
4652
4861
  const elementTag = (element.tag || "").toLowerCase();
4653
4862
  const inputType = getStaticTypeAttribute(element);
@@ -4667,7 +4876,7 @@ function tryInferNativeWrapperRoleFromSfc(tag, vueFilesPathMap, seenTags = /* @_
4667
4876
  if (elementTag === "button" || elementTag === "ubutton")
4668
4877
  return { role: "button" };
4669
4878
  if (isComponentLikeTag(element.tag) && element.tag !== tag) {
4670
- const nested = tryInferNativeWrapperRoleFromSfc(element.tag, vueFilesPathMap, nextSeen);
4879
+ const nested = tryInferNativeWrapperRoleFromSfc(element.tag, vueFilesPathMap, normalizedSearchRoots, nextSeen);
4671
4880
  if (nested)
4672
4881
  return nested;
4673
4882
  }
@@ -4678,7 +4887,7 @@ function tryInferNativeWrapperRoleFromSfc(tag, vueFilesPathMap, seenTags = /* @_
4678
4887
  }
4679
4888
  return null;
4680
4889
  };
4681
- const inferRoleFromNode = (node) => {
4890
+ inferRoleFromNode = (node) => {
4682
4891
  if (!node || typeof node !== "object")
4683
4892
  return null;
4684
4893
  if (node.type === compilerCore.NodeTypes.ELEMENT) {
@@ -4703,14 +4912,14 @@ function tryInferNativeWrapperRoleFromSfc(tag, vueFilesPathMap, seenTags = /* @_
4703
4912
  };
4704
4913
  const inferred = inferRoleFromNode(ast);
4705
4914
  if (inferred) {
4706
- inferredNativeWrapperConfigByTag.set(tag, inferred);
4915
+ inferredNativeWrapperConfigByLookup.set(cacheKey, inferred);
4707
4916
  return inferred;
4708
4917
  }
4709
4918
  } catch {
4710
- inferredNativeWrapperConfigByTag.set(tag, { role: "" });
4919
+ inferredNativeWrapperConfigByLookup.set(cacheKey, { role: "" });
4711
4920
  return null;
4712
4921
  }
4713
- inferredNativeWrapperConfigByTag.set(tag, { role: "" });
4922
+ inferredNativeWrapperConfigByLookup.set(cacheKey, { role: "" });
4714
4923
  return null;
4715
4924
  }
4716
4925
  function tryWrapClickDirectiveForTestEvents(element, testIdAttribute) {
@@ -4885,6 +5094,7 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
4885
5094
  const nameCollisionBehavior = options.nameCollisionBehavior ?? "suffix";
4886
5095
  const warn = options.warn;
4887
5096
  const vueFilesPathMap = options.vueFilesPathMap;
5097
+ const wrapperSearchRoots = options.wrapperSearchRoots ?? [];
4888
5098
  const safeRealpath = (p) => {
4889
5099
  try {
4890
5100
  return fs.existsSync(p) ? fs.realpathSync(p) : p;
@@ -5010,7 +5220,7 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
5010
5220
  dependencies.usedComponentSet.add(element.tag);
5011
5221
  }
5012
5222
  if (!nativeWrappers[element.tag]) {
5013
- const inferred = tryInferNativeWrapperRoleFromSfc(element.tag, vueFilesPathMap);
5223
+ const inferred = tryInferNativeWrapperRoleFromSfc(element.tag, vueFilesPathMap, wrapperSearchRoots);
5014
5224
  if (inferred?.role) {
5015
5225
  nativeWrappers[element.tag] = { role: inferred.role, inferred: true };
5016
5226
  } else if (element.tag.endsWith("Button") || element.tag === "AylaButton") {
@@ -5301,6 +5511,7 @@ function createDevProcessorPlugin(options) {
5301
5511
  excludedComponents,
5302
5512
  viewsDir,
5303
5513
  scanDirs,
5514
+ getWrapperSearchRoots,
5304
5515
  projectRootRef,
5305
5516
  normalizedBasePagePath,
5306
5517
  basePageClassPath,
@@ -5451,7 +5662,11 @@ function createDevProcessorPlugin(options) {
5451
5662
  nativeWrappers,
5452
5663
  excludedComponents,
5453
5664
  getViewsDirAbs(),
5454
- { existingIdBehavior: "preserve", testIdAttribute }
5665
+ {
5666
+ existingIdBehavior: "preserve",
5667
+ testIdAttribute,
5668
+ wrapperSearchRoots: getWrapperSearchRoots()
5669
+ }
5455
5670
  )
5456
5671
  ]
5457
5672
  });
@@ -5676,6 +5891,7 @@ function createSupportPlugins(options) {
5676
5891
  excludedComponents,
5677
5892
  viewsDir,
5678
5893
  scanDirs,
5894
+ getWrapperSearchRoots,
5679
5895
  outDir,
5680
5896
  emitLanguages,
5681
5897
  csharp,
@@ -5738,6 +5954,7 @@ function createSupportPlugins(options) {
5738
5954
  excludedComponents,
5739
5955
  viewsDir,
5740
5956
  scanDirs,
5957
+ getWrapperSearchRoots,
5741
5958
  projectRootRef,
5742
5959
  normalizedBasePagePath,
5743
5960
  basePageClassPath,
@@ -5910,6 +6127,7 @@ function createVuePluginWithTestIds(options) {
5910
6127
  testIdAttribute,
5911
6128
  loggerRef,
5912
6129
  scanDirs = ["src"],
6130
+ getWrapperSearchRoots,
5913
6131
  getProjectRoot
5914
6132
  } = options;
5915
6133
  const getComponentNameFromPath = (filename) => {
@@ -5978,7 +6196,8 @@ function createVuePluginWithTestIds(options) {
5978
6196
  testIdAttribute,
5979
6197
  nameCollisionBehavior,
5980
6198
  warn: (message) => loggerRef.current.warn(message),
5981
- vueFilesPathMap
6199
+ vueFilesPathMap,
6200
+ wrapperSearchRoots: getWrapperSearchRoots()
5982
6201
  }
5983
6202
  )
5984
6203
  );
@@ -6007,7 +6226,8 @@ function createVuePluginWithTestIds(options) {
6007
6226
  testIdAttribute,
6008
6227
  nameCollisionBehavior,
6009
6228
  warn: (message) => loggerRef.current.warn(message),
6010
- vueFilesPathMap
6229
+ vueFilesPathMap,
6230
+ wrapperSearchRoots: getWrapperSearchRoots()
6011
6231
  }
6012
6232
  );
6013
6233
  perFileTransform.set(componentName, transform);
@@ -6075,6 +6295,13 @@ function assertNonEmptyString(value, name) {
6075
6295
  throw new Error(`${name} must be a non-empty string.`);
6076
6296
  }
6077
6297
  }
6298
+ function assertNonEmptyStringArray(value, name) {
6299
+ if (!value)
6300
+ return;
6301
+ for (const [index, entry] of value.entries()) {
6302
+ assertNonEmptyString(entry, `${name}[${index}]`);
6303
+ }
6304
+ }
6078
6305
  function assertRouterModuleShims(value, name) {
6079
6306
  if (!value)
6080
6307
  return;
@@ -6137,11 +6364,12 @@ function createVuePomGeneratorPlugins(options = {}) {
6137
6364
  const vueOptions = options.vueOptions;
6138
6365
  const viewsDir = injection.viewsDir ?? "src/views";
6139
6366
  const scanDirs = injection.scanDirs ?? ["src"];
6367
+ const wrapperSearchRoots = injection.wrapperSearchRoots ?? [];
6140
6368
  const nativeWrappers = injection.nativeWrappers ?? {};
6141
6369
  const excludedComponents = injection.excludeComponents ?? [];
6142
6370
  const testIdAttribute = (injection.attribute ?? "data-testid").trim() || "data-testid";
6143
6371
  const existingIdBehavior = injection.existingIdBehavior ?? "preserve";
6144
- const outDir = (generationOptions?.outDir ?? "tests/playwright/generated").trim();
6372
+ const outDir = (generationOptions?.outDir ?? "tests/playwright/__generated__").trim();
6145
6373
  const emitLanguages = generationOptions?.emit && generationOptions.emit.length ? generationOptions.emit : ["ts"];
6146
6374
  const nameCollisionBehavior = generationOptions?.nameCollisionBehavior ?? "suffix";
6147
6375
  const routerEntry = generationOptions?.router?.entry;
@@ -6167,6 +6395,7 @@ function createVuePomGeneratorPlugins(options = {}) {
6167
6395
  loggerRef.current = createLogger({ verbosity, viteLogger: config.logger });
6168
6396
  assertNonEmptyString(testIdAttribute, "[vue-pom-generator] injection.attribute");
6169
6397
  assertNonEmptyString(viewsDir, "[vue-pom-generator] injection.viewsDir");
6398
+ assertNonEmptyStringArray(wrapperSearchRoots, "[vue-pom-generator] injection.wrapperSearchRoots");
6170
6399
  if (generationEnabled) {
6171
6400
  assertNonEmptyString(outDir, "[vue-pom-generator] generation.outDir");
6172
6401
  assertRouterModuleShims(routerModuleShims, "[vue-pom-generator] generation.router.moduleShims");
@@ -6179,6 +6408,7 @@ function createVuePomGeneratorPlugins(options = {}) {
6179
6408
  }
6180
6409
  };
6181
6410
  const getViewsDirAbs = () => resolveFromProjectRoot(projectRootRef.current, viewsDir);
6411
+ const getWrapperSearchRootsAbs = () => wrapperSearchRoots.map((root) => resolveFromProjectRoot(projectRootRef.current, root));
6182
6412
  const componentTestIds = /* @__PURE__ */ new Map();
6183
6413
  const elementMetadata = /* @__PURE__ */ new Map();
6184
6414
  const semanticNameMap = /* @__PURE__ */ new Map();
@@ -6198,6 +6428,7 @@ function createVuePomGeneratorPlugins(options = {}) {
6198
6428
  testIdAttribute,
6199
6429
  loggerRef,
6200
6430
  scanDirs,
6431
+ getWrapperSearchRoots: getWrapperSearchRootsAbs,
6201
6432
  getProjectRoot: () => projectRootRef.current
6202
6433
  });
6203
6434
  const routerAwarePoms = typeof routerEntry === "string" && routerEntry.trim().length > 0 || routerType === "nuxt";
@@ -6209,6 +6440,7 @@ function createVuePomGeneratorPlugins(options = {}) {
6209
6440
  excludedComponents,
6210
6441
  viewsDir,
6211
6442
  scanDirs,
6443
+ getWrapperSearchRoots: getWrapperSearchRootsAbs,
6212
6444
  outDir,
6213
6445
  emitLanguages,
6214
6446
  csharp,