@immense/vue-pom-generator 1.0.63 → 1.0.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/RELEASE_NOTES.md +21 -20
- package/dist/index.cjs +75 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +75 -21
- package/dist/index.mjs.map +1 -1
- package/dist/plugin/create-vue-pom-generator-plugins.d.ts.map +1 -1
- package/dist/plugin/resolved-generation-options.d.ts +2 -1
- package/dist/plugin/resolved-generation-options.d.ts.map +1 -1
- package/dist/plugin/support/build-plugin.d.ts.map +1 -1
- package/dist/plugin/support/dev-plugin.d.ts.map +1 -1
- package/dist/plugin/types.d.ts +12 -1
- package/dist/plugin/types.d.ts.map +1 -1
- package/dist/plugin/vue-plugin.d.ts +2 -1
- package/dist/plugin/vue-plugin.d.ts.map +1 -1
- package/dist/transform.d.ts +1 -0
- package/dist/transform.d.ts.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/package.json +1 -1
package/RELEASE_NOTES.md
CHANGED
|
@@ -1,30 +1,31 @@
|
|
|
1
|
-
●
|
|
2
|
-
## Highlights
|
|
1
|
+
● ## v1.0.65
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
- Added comprehensive test coverage for nested component POM attachment
|
|
3
|
+
### Highlights
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
- Fixed parsing of guarded handler expressions (e.g., `@click="condition && handler"`)
|
|
6
|
+
- Improved semantic name resolution for wrapped and conditional event handlers
|
|
7
|
+
- Enhanced Vue compiler AST traversal to handle logical expressions (`&&` operators)
|
|
8
8
|
|
|
9
|
-
###
|
|
10
|
-
- Resolved issue where nested component POMs were not correctly associated with view classes
|
|
11
|
-
during generation
|
|
12
|
-
- Improved POM attachment logic in class generation module
|
|
9
|
+
### Changes
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
- Added
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
**Parser Improvements**
|
|
12
|
+
- Added support for `LogicalExpression` nodes in handler attribute parsing
|
|
13
|
+
- Introduced `unwrapSemanticHelperCall` to handle Vue compiler helpers (`_unref`,
|
|
14
|
+
`_withModifiers`, etc.)
|
|
15
|
+
- Refactored `resolveSemanticName` to recursively unwrap nested expressions and guarded calls
|
|
16
|
+
- Handler names now correctly extracted from conditional expressions like `isValid &&
|
|
17
|
+
submitForm`
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
**Testing**
|
|
20
|
+
- Added test coverage for guarded handler scenarios
|
|
21
|
+
- Updated transform and utils test suites to validate new parsing logic
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
### Pull Requests Included
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
No pull request was created for this fix; it was committed directly to `main`.
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
### Testing
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
test`.
|
|
29
|
-
```
|
|
29
|
+
Validated with expanded unit test coverage in `tests/transform.test.ts` and
|
|
30
|
+
`tests/utils-coverage.test.ts`.
|
|
30
31
|
|
package/dist/index.cjs
CHANGED
|
@@ -258,6 +258,7 @@ function resolveGenerationSupportOptions(options) {
|
|
|
258
258
|
customPomImportAliases: options.customPomImportAliases,
|
|
259
259
|
customPomImportNameCollisionBehavior: options.customPomImportNameCollisionBehavior ?? "error",
|
|
260
260
|
nameCollisionBehavior: options.nameCollisionBehavior ?? "error",
|
|
261
|
+
missingSemanticNameBehavior: options.missingSemanticNameBehavior ?? "error",
|
|
261
262
|
existingIdBehavior: options.existingIdBehavior ?? "error",
|
|
262
263
|
testIdAttribute: (options.testIdAttribute ?? "data-testid").trim() || "data-testid",
|
|
263
264
|
accessibilityAudit: options.accessibilityAudit ?? false,
|
|
@@ -1894,6 +1895,12 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
1894
1895
|
const n = node2;
|
|
1895
1896
|
return typeof n.computed === "boolean" && typeof n.key === "object" && n.key !== null && typeof n.value === "object" && n.value !== null;
|
|
1896
1897
|
};
|
|
1898
|
+
const isLogicalExpressionNode = (node2) => {
|
|
1899
|
+
if (!isNodeType2(node2, "LogicalExpression"))
|
|
1900
|
+
return false;
|
|
1901
|
+
const n = node2;
|
|
1902
|
+
return typeof n.operator === "string" && typeof n.left === "object" && n.left !== null && typeof n.right === "object" && n.right !== null;
|
|
1903
|
+
};
|
|
1897
1904
|
const getLastIdentifierFromMemberChain = (node2) => {
|
|
1898
1905
|
if (!node2)
|
|
1899
1906
|
return null;
|
|
@@ -2064,48 +2071,66 @@ function nodeHandlerAttributeInfo(node) {
|
|
|
2064
2071
|
const semanticNameHint2 = suffix ? `${toPascalCase(name)}${suffix}` : toPascalCase(name);
|
|
2065
2072
|
return semanticNameHint2;
|
|
2066
2073
|
};
|
|
2074
|
+
const unwrapSemanticHelperCall = (candidateExpr) => {
|
|
2075
|
+
if (!isCallExpressionNode(candidateExpr)) {
|
|
2076
|
+
return null;
|
|
2077
|
+
}
|
|
2078
|
+
const calleeName = getLastIdentifierFromMemberChain(candidateExpr.callee);
|
|
2079
|
+
if (calleeName !== "_unref" && calleeName !== "unref" && calleeName !== "_withModifiers" && calleeName !== "withModifiers") {
|
|
2080
|
+
return null;
|
|
2081
|
+
}
|
|
2082
|
+
const firstArg = candidateExpr.arguments[0];
|
|
2083
|
+
return typeof firstArg === "object" && firstArg !== null ? firstArg : null;
|
|
2084
|
+
};
|
|
2067
2085
|
const resolveSemanticName = (candidateExpr) => {
|
|
2068
2086
|
if (!candidateExpr) {
|
|
2069
2087
|
return null;
|
|
2070
2088
|
}
|
|
2089
|
+
const unwrappedHelperCandidate = unwrapSemanticHelperCall(candidateExpr);
|
|
2090
|
+
if (unwrappedHelperCandidate) {
|
|
2091
|
+
return resolveSemanticName(unwrappedHelperCandidate);
|
|
2092
|
+
}
|
|
2071
2093
|
const direct = getLastIdentifierFromMemberChain(candidateExpr);
|
|
2072
2094
|
if (direct) {
|
|
2073
2095
|
return toPascalCase(direct);
|
|
2074
2096
|
}
|
|
2097
|
+
const directCall = tryFromCallExpression(candidateExpr);
|
|
2098
|
+
if (directCall) {
|
|
2099
|
+
return directCall;
|
|
2100
|
+
}
|
|
2101
|
+
if (isAssignmentExpressionNode(candidateExpr)) {
|
|
2102
|
+
const lhs = getAssignmentTargetName(candidateExpr.left);
|
|
2103
|
+
if (lhs) {
|
|
2104
|
+
const rhs = stableWordFromValue(candidateExpr.right);
|
|
2105
|
+
return `Set${toPascalCase(lhs)}${rhs ?? ""}`;
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
if (isLogicalExpressionNode(candidateExpr) && candidateExpr.operator === "&&") {
|
|
2109
|
+
return resolveSemanticName(candidateExpr.right);
|
|
2110
|
+
}
|
|
2075
2111
|
if (isArrowFunctionExpressionNode(candidateExpr)) {
|
|
2076
2112
|
const body = candidateExpr.body;
|
|
2077
|
-
const directCall = tryFromCallExpression(body);
|
|
2078
|
-
if (directCall) {
|
|
2079
|
-
return directCall;
|
|
2080
|
-
}
|
|
2081
|
-
if (isAssignmentExpressionNode(body)) {
|
|
2082
|
-
const lhs = getAssignmentTargetName(body.left);
|
|
2083
|
-
if (lhs) {
|
|
2084
|
-
const rhs = stableWordFromValue(body.right);
|
|
2085
|
-
return `Set${toPascalCase(lhs)}${rhs ?? ""}`;
|
|
2086
|
-
}
|
|
2087
|
-
}
|
|
2088
2113
|
if (isBlockStatementNode(body)) {
|
|
2089
2114
|
const stmts = body.body ?? [];
|
|
2090
2115
|
if (stmts.length > 0) {
|
|
2091
2116
|
const firstStmt = stmts[0];
|
|
2092
2117
|
if (isReturnStatementNode(firstStmt)) {
|
|
2093
|
-
const fromReturn =
|
|
2118
|
+
const fromReturn = resolveSemanticName(firstStmt.argument ?? null);
|
|
2094
2119
|
if (fromReturn) {
|
|
2095
2120
|
return fromReturn;
|
|
2096
2121
|
}
|
|
2097
2122
|
}
|
|
2098
2123
|
if (isExpressionStatementNode(firstStmt)) {
|
|
2099
|
-
const fromExpr =
|
|
2124
|
+
const fromExpr = resolveSemanticName(firstStmt.expression ?? null);
|
|
2100
2125
|
if (fromExpr) {
|
|
2101
2126
|
return fromExpr;
|
|
2102
2127
|
}
|
|
2103
2128
|
}
|
|
2104
2129
|
}
|
|
2105
2130
|
}
|
|
2106
|
-
const bodyName =
|
|
2131
|
+
const bodyName = resolveSemanticName(body);
|
|
2107
2132
|
if (bodyName) {
|
|
2108
|
-
return
|
|
2133
|
+
return bodyName;
|
|
2109
2134
|
}
|
|
2110
2135
|
}
|
|
2111
2136
|
return null;
|
|
@@ -7076,6 +7101,7 @@ function createTestIdTransform(componentName, componentHierarchyMap, nativeWrapp
|
|
|
7076
7101
|
const existingIdBehavior = options.existingIdBehavior ?? "error";
|
|
7077
7102
|
const testIdAttribute = (options.testIdAttribute || "data-testid").trim() || "data-testid";
|
|
7078
7103
|
const nameCollisionBehavior = options.nameCollisionBehavior ?? "error";
|
|
7104
|
+
const missingSemanticNameBehavior = options.missingSemanticNameBehavior ?? "error";
|
|
7079
7105
|
const warn = options.warn;
|
|
7080
7106
|
const vueFilesPathMap = options.vueFilesPathMap;
|
|
7081
7107
|
const wrapperSearchRoots = options.wrapperSearchRoots ?? [];
|
|
@@ -7519,13 +7545,16 @@ Fix: remove the explicit ${attrLabel}, or change existingIdBehavior to "overwrit
|
|
|
7519
7545
|
}
|
|
7520
7546
|
const isSubmit = element.props.find((p) => p.type === compilerCore.NodeTypes.ATTRIBUTE && p.name === "type")?.value?.content === "submit";
|
|
7521
7547
|
if (isSubmit) {
|
|
7522
|
-
|
|
7548
|
+
let identifier = getStaticIdOrNameHint(element) || innerText;
|
|
7523
7549
|
if (!identifier) {
|
|
7524
|
-
|
|
7525
|
-
|
|
7526
|
-
|
|
7527
|
-
|
|
7528
|
-
|
|
7550
|
+
if (missingSemanticNameBehavior === "error") {
|
|
7551
|
+
const loc = element.loc?.start;
|
|
7552
|
+
const locationHint = loc ? `${loc.line}:${loc.column}` : "unknown";
|
|
7553
|
+
throw new Error(
|
|
7554
|
+
`[vue-pom-generator] submit button appears identifiable but no usable identity could be derived in ${componentName} (${context.filename ?? "unknown"}:${locationHint}) — id/name were missing/empty and innerText was also missing/invalid. Fix: give the button a static id/name, a static inner text, or set missingSemanticNameBehavior = "ignore" to fall back to the generic "submit" identifier.`
|
|
7555
|
+
);
|
|
7556
|
+
}
|
|
7557
|
+
identifier = "submit";
|
|
7529
7558
|
}
|
|
7530
7559
|
const testId = getSubmitDataTestId(identifier);
|
|
7531
7560
|
applyResolvedDataTestIdForElement({
|
|
@@ -7592,6 +7621,7 @@ function createBuildProcessorPlugin(options) {
|
|
|
7592
7621
|
customPomImportNameCollisionBehavior,
|
|
7593
7622
|
testIdAttribute,
|
|
7594
7623
|
nameCollisionBehavior,
|
|
7624
|
+
missingSemanticNameBehavior,
|
|
7595
7625
|
existingIdBehavior,
|
|
7596
7626
|
routerAwarePoms,
|
|
7597
7627
|
routerType,
|
|
@@ -7687,6 +7717,9 @@ function createBuildProcessorPlugin(options) {
|
|
|
7687
7717
|
prefixIdentifiers: true,
|
|
7688
7718
|
inline: isScriptSetup,
|
|
7689
7719
|
bindingMetadata,
|
|
7720
|
+
// See dev-plugin.ts — same rationale: enable TS in template
|
|
7721
|
+
// expressions so `(row: RowType) => ...` handlers parse.
|
|
7722
|
+
expressionPlugins: ["typescript"],
|
|
7690
7723
|
nodeTransforms: [
|
|
7691
7724
|
createTestIdTransform(
|
|
7692
7725
|
componentName,
|
|
@@ -7698,6 +7731,7 @@ function createBuildProcessorPlugin(options) {
|
|
|
7698
7731
|
existingIdBehavior: existingIdBehavior ?? "error",
|
|
7699
7732
|
testIdAttribute,
|
|
7700
7733
|
nameCollisionBehavior,
|
|
7734
|
+
missingSemanticNameBehavior,
|
|
7701
7735
|
warn: (message) => loggerRef.current.warn(message),
|
|
7702
7736
|
vueFilesPathMap,
|
|
7703
7737
|
wrapperSearchRoots: getWrapperSearchRoots()
|
|
@@ -7845,6 +7879,7 @@ function createDevProcessorPlugin(options) {
|
|
|
7845
7879
|
customPomImportAliases,
|
|
7846
7880
|
customPomImportNameCollisionBehavior,
|
|
7847
7881
|
nameCollisionBehavior,
|
|
7882
|
+
missingSemanticNameBehavior,
|
|
7848
7883
|
existingIdBehavior,
|
|
7849
7884
|
testIdAttribute,
|
|
7850
7885
|
routerAwarePoms,
|
|
@@ -8029,6 +8064,12 @@ function createDevProcessorPlugin(options) {
|
|
|
8029
8064
|
prefixIdentifiers: true,
|
|
8030
8065
|
inline: isScriptSetup,
|
|
8031
8066
|
bindingMetadata,
|
|
8067
|
+
// Vue templates may contain TypeScript type annotations in expressions
|
|
8068
|
+
// (e.g. `@row-click="(row: RowType) => navigateTo(...)"` in Nuxt + TS
|
|
8069
|
+
// apps). Without this flag, Vue's internal processExpression step
|
|
8070
|
+
// delegates to @babel/parser without the TS plugin and crashes on
|
|
8071
|
+
// "Unexpected token, expected ','".
|
|
8072
|
+
expressionPlugins: ["typescript"],
|
|
8032
8073
|
nodeTransforms: [
|
|
8033
8074
|
createTestIdTransform(
|
|
8034
8075
|
componentName,
|
|
@@ -8039,6 +8080,7 @@ function createDevProcessorPlugin(options) {
|
|
|
8039
8080
|
{
|
|
8040
8081
|
existingIdBehavior: existingIdBehavior ?? "error",
|
|
8041
8082
|
nameCollisionBehavior,
|
|
8083
|
+
missingSemanticNameBehavior,
|
|
8042
8084
|
testIdAttribute,
|
|
8043
8085
|
warn: (message) => loggerRef.current.warn(message),
|
|
8044
8086
|
vueFilesPathMap: provisionalVuePathMap,
|
|
@@ -8786,6 +8828,7 @@ function createVuePluginWithTestIds(options) {
|
|
|
8786
8828
|
vueOptions,
|
|
8787
8829
|
existingIdBehavior,
|
|
8788
8830
|
nameCollisionBehavior,
|
|
8831
|
+
missingSemanticNameBehavior = "error",
|
|
8789
8832
|
nativeWrappers,
|
|
8790
8833
|
elementMetadata,
|
|
8791
8834
|
semanticNameMap,
|
|
@@ -8866,6 +8909,7 @@ function createVuePluginWithTestIds(options) {
|
|
|
8866
8909
|
existingIdBehavior,
|
|
8867
8910
|
testIdAttribute,
|
|
8868
8911
|
nameCollisionBehavior,
|
|
8912
|
+
missingSemanticNameBehavior,
|
|
8869
8913
|
warn: (message) => loggerRef.current.warn(message),
|
|
8870
8914
|
vueFilesPathMap,
|
|
8871
8915
|
wrapperSearchRoots: getWrapperSearchRoots()
|
|
@@ -8920,6 +8964,7 @@ function createVuePluginWithTestIds(options) {
|
|
|
8920
8964
|
existingIdBehavior,
|
|
8921
8965
|
testIdAttribute,
|
|
8922
8966
|
nameCollisionBehavior,
|
|
8967
|
+
missingSemanticNameBehavior,
|
|
8923
8968
|
warn: (message) => loggerRef.current.warn(message),
|
|
8924
8969
|
vueFilesPathMap,
|
|
8925
8970
|
wrapperSearchRoots: getWrapperSearchRoots()
|
|
@@ -8967,9 +9012,16 @@ function createVuePluginWithTestIds(options) {
|
|
|
8967
9012
|
const compile = compilerDom2.compile;
|
|
8968
9013
|
const { descriptor } = parse(code, { filename: cleanPath });
|
|
8969
9014
|
if (descriptor.template) {
|
|
9015
|
+
const mergedExpressionPlugins = Array.from(
|
|
9016
|
+
/* @__PURE__ */ new Set([
|
|
9017
|
+
"typescript",
|
|
9018
|
+
...userCompilerOptions.expressionPlugins ?? []
|
|
9019
|
+
])
|
|
9020
|
+
);
|
|
8970
9021
|
compile(descriptor.template.content, {
|
|
8971
9022
|
...userCompilerOptions,
|
|
8972
9023
|
filename: cleanPath,
|
|
9024
|
+
expressionPlugins: mergedExpressionPlugins,
|
|
8973
9025
|
nodeTransforms: getNodeTransforms(cleanPath, componentName)
|
|
8974
9026
|
});
|
|
8975
9027
|
loggerRef.current.debug(`Metadata collected for ${cleanPath}`);
|
|
@@ -9266,6 +9318,7 @@ function createVuePomGeneratorPlugins(options = {}) {
|
|
|
9266
9318
|
customPomImportAliases: resolvedCustomPomImportAliases,
|
|
9267
9319
|
customPomImportNameCollisionBehavior: customPoms?.importNameCollisionBehavior,
|
|
9268
9320
|
nameCollisionBehavior: generationOptions?.nameCollisionBehavior,
|
|
9321
|
+
missingSemanticNameBehavior: generationOptions?.missingSemanticNameBehavior,
|
|
9269
9322
|
existingIdBehavior: resolvedInjectionOptions.existingIdBehavior,
|
|
9270
9323
|
testIdAttribute,
|
|
9271
9324
|
accessibilityAudit: generationOptions?.accessibilityAudit,
|
|
@@ -9354,6 +9407,7 @@ function createVuePomGeneratorPlugins(options = {}) {
|
|
|
9354
9407
|
vueOptions,
|
|
9355
9408
|
existingIdBehavior: resolvedGenerationOptions.existingIdBehavior,
|
|
9356
9409
|
nameCollisionBehavior: resolvedGenerationOptions.nameCollisionBehavior,
|
|
9410
|
+
missingSemanticNameBehavior: resolvedGenerationOptions.missingSemanticNameBehavior,
|
|
9357
9411
|
nativeWrappers,
|
|
9358
9412
|
elementMetadata,
|
|
9359
9413
|
semanticNameMap,
|