@immense/vue-pom-generator 1.0.30 → 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/README.md +7 -7
- package/RELEASE_NOTES.md +39 -26
- package/class-generation/index.ts +9 -0
- package/dist/index.cjs +197 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +198 -26
- package/dist/index.mjs.map +1 -1
- package/dist/plugin/types.d.ts +4 -4
- package/dist/utils.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -35,15 +35,15 @@ Exported entrypoints:
|
|
|
35
35
|
- `injection`: how `data-testid` (or your chosen attribute) is derived/injected
|
|
36
36
|
- `generation`: how Page Object Models (POMs) and Playwright helpers are generated
|
|
37
37
|
|
|
38
|
-
The generator emits an aggregated output under `generation.outDir` (default `tests/playwright/
|
|
38
|
+
The generator emits an aggregated output under `generation.outDir` (default `tests/playwright/__generated__`):
|
|
39
39
|
|
|
40
|
-
- `tests/playwright/
|
|
41
|
-
- `tests/playwright/
|
|
42
|
-
- managed `.gitattributes` files
|
|
40
|
+
- `tests/playwright/__generated__/page-object-models.g.ts` (generated; do not edit)
|
|
41
|
+
- `tests/playwright/__generated__/index.ts` (generated stable barrel)
|
|
42
|
+
- managed `.gitattributes` files only when you emit outside `__generated__`
|
|
43
43
|
|
|
44
44
|
If `generation.playwright.fixtures` is enabled, it also emits:
|
|
45
45
|
|
|
46
|
-
- `tests/playwright/
|
|
46
|
+
- `tests/playwright/__generated__/fixtures.g.ts` (generated; do not edit)
|
|
47
47
|
|
|
48
48
|
### Vite config example
|
|
49
49
|
|
|
@@ -98,8 +98,8 @@ export default defineConfig(() => {
|
|
|
98
98
|
namespace: "MyProject.Tests.Generated",
|
|
99
99
|
},
|
|
100
100
|
|
|
101
|
-
// Default: tests/playwright/
|
|
102
|
-
outDir: "tests/playwright/
|
|
101
|
+
// Default: tests/playwright/__generated__
|
|
102
|
+
outDir: "tests/playwright/__generated__",
|
|
103
103
|
|
|
104
104
|
// Controls how to handle duplicate generated member names within a single POM class.
|
|
105
105
|
// - "error": fail compilation
|
package/RELEASE_NOTES.md
CHANGED
|
@@ -1,44 +1,57 @@
|
|
|
1
|
-
●
|
|
1
|
+
● I need to see the actual diff content to understand what the fix addressed. Let me get the file
|
|
2
|
+
diffs:
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
● Let me look at specific key changes to understand what was fixed:
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
- Enhanced plugin system with better type definitions and support
|
|
7
|
-
- Improved test coverage with expanded transform test cases
|
|
8
|
-
- Added CI automation for PR release note previews
|
|
6
|
+
● Based on the commit and PR details, here are the release notes:
|
|
9
7
|
|
|
10
|
-
|
|
8
|
+
---
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
- Refactored wrapper search roots configuration in transform logic
|
|
14
|
-
- Enhanced transform logic with improved structure (198 additions, significant reorganization)
|
|
10
|
+
## Highlights
|
|
15
11
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
- Improved
|
|
19
|
-
|
|
20
|
-
-
|
|
12
|
+
- Changed default output directory from `tests/playwright/generated` to
|
|
13
|
+
`tests/playwright/__generated__` for better convention adherence
|
|
14
|
+
- Improved `.gitattributes` generation: now skips managed entries for paths using
|
|
15
|
+
`__generated__` heuristic (GitHub Linguist auto-detects these)
|
|
16
|
+
- Enhanced slot scope key extraction logic for more robust click handler naming in generated
|
|
17
|
+
POMs
|
|
18
|
+
- Added comprehensive utility functions for analyzing assignment patterns, object destructuring,
|
|
19
|
+
and binding identifiers
|
|
20
|
+
|
|
21
|
+
## Changes
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
- Updated
|
|
23
|
+
**Output Directory Convention**
|
|
24
|
+
- Updated default `generation.outDir` to `tests/playwright/__generated__`
|
|
25
|
+
- Updated default Playwright fixtures output to use `__generated__` subdirectory
|
|
26
|
+
- `.gitattributes` entries are now only generated when using custom output directories outside
|
|
27
|
+
`__generated__`
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
-
|
|
29
|
+
**Click Handler Naming**
|
|
30
|
+
- Improved slot scope key candidate extraction with priority-based selection
|
|
31
|
+
- Added support for object destructuring patterns in slot scope analysis
|
|
32
|
+
- Enhanced fallback key expression generation for slot scoped elements
|
|
33
|
+
- Better handling of assignment patterns, rest elements, and object properties
|
|
28
34
|
|
|
29
|
-
|
|
30
|
-
-
|
|
35
|
+
**Code Quality**
|
|
36
|
+
- Added test coverage for new utility functions in `class-generation-coverage.test.ts` and
|
|
37
|
+
`utils-coverage.test.ts`
|
|
38
|
+
- Expanded Babel type guards usage for more precise AST node detection
|
|
39
|
+
- Refactored slot scope analysis with dedicated helper functions
|
|
31
40
|
|
|
32
41
|
## Breaking Changes
|
|
33
42
|
|
|
34
|
-
|
|
43
|
+
- **Default output directory** changed from `tests/playwright/generated` to
|
|
44
|
+
`tests/playwright/__generated__`. If you're relying on the default, generated files will move to
|
|
45
|
+
the new location. To preserve the old behavior, explicitly set `generation.outDir:
|
|
46
|
+
"tests/playwright/generated"` in your config.
|
|
35
47
|
|
|
36
48
|
## Pull Requests Included
|
|
37
49
|
|
|
38
|
-
- #1 Add PR release-notes preview
|
|
39
|
-
|
|
50
|
+
- [#1 Add PR release-notes preview
|
|
51
|
+
comments](https://github.com/immense/vue-pom-generator/pull/1) by @dkattan
|
|
40
52
|
|
|
41
53
|
## Testing
|
|
42
54
|
|
|
43
|
-
|
|
55
|
+
Added test coverage for new utility functions covering class generation and slot scope analysis
|
|
56
|
+
edge cases.
|
|
44
57
|
|
|
@@ -524,6 +524,11 @@ function escapeGitAttributesPattern(value: string): string {
|
|
|
524
524
|
return output;
|
|
525
525
|
}
|
|
526
526
|
|
|
527
|
+
function pathUsesGeneratedHeuristic(filePath: string): boolean {
|
|
528
|
+
const normalized = path.normalize(filePath);
|
|
529
|
+
return normalized.split(path.sep).includes("__generated__");
|
|
530
|
+
}
|
|
531
|
+
|
|
527
532
|
function buildManagedGitAttributesBlock(entries: string[]): string {
|
|
528
533
|
return [
|
|
529
534
|
GENERATED_GITATTRIBUTES_BLOCK_START,
|
|
@@ -581,6 +586,10 @@ function buildGeneratedGitAttributesFiles(generatedFilePaths: string[]): Generat
|
|
|
581
586
|
continue;
|
|
582
587
|
}
|
|
583
588
|
|
|
589
|
+
if (pathUsesGeneratedHeuristic(resolvedFilePath)) {
|
|
590
|
+
continue;
|
|
591
|
+
}
|
|
592
|
+
|
|
584
593
|
const dir = path.dirname(resolvedFilePath);
|
|
585
594
|
const entry = `${escapeGitAttributesPattern(path.basename(resolvedFilePath))} linguist-generated`;
|
|
586
595
|
const entries = entriesByDir.get(dir) ?? new Set<string>();
|
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
|
-
|
|
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 (
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
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();
|
|
@@ -6197,7 +6369,7 @@ function createVuePomGeneratorPlugins(options = {}) {
|
|
|
6197
6369
|
const excludedComponents = injection.excludeComponents ?? [];
|
|
6198
6370
|
const testIdAttribute = (injection.attribute ?? "data-testid").trim() || "data-testid";
|
|
6199
6371
|
const existingIdBehavior = injection.existingIdBehavior ?? "preserve";
|
|
6200
|
-
const outDir = (generationOptions?.outDir ?? "tests/playwright/
|
|
6372
|
+
const outDir = (generationOptions?.outDir ?? "tests/playwright/__generated__").trim();
|
|
6201
6373
|
const emitLanguages = generationOptions?.emit && generationOptions.emit.length ? generationOptions.emit : ["ts"];
|
|
6202
6374
|
const nameCollisionBehavior = generationOptions?.nameCollisionBehavior ?? "suffix";
|
|
6203
6375
|
const routerEntry = generationOptions?.router?.entry;
|