@immense/vue-pom-generator 1.0.64 → 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 CHANGED
@@ -1,48 +1,31 @@
1
- Based on the commit message and changes, here are the release notes:
1
+ ## v1.0.65
2
2
 
3
- ---
3
+ ### Highlights
4
4
 
5
- # v1.0.64
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)
6
8
 
7
- ## Highlights
9
+ ### Changes
8
10
 
9
- - **TypeScript template expression support** – Compiler now parses TypeScript annotations in Vue
10
- template expressions (e.g., `@row-click="(row: RowType) => ..."`), fixing crashes in Nuxt 4 +
11
- TS codebases
12
- - **Graceful submit-button fallback** – Submit buttons with dynamic labels now respect
13
- `missingSemanticNameBehavior: "ignore"` and generate `SubmitButton` / `clickSubmit()` methods
14
- instead of throwing
15
- - **Improved TypeScript compatibility** – All three plugin compilation paths (dev-plugin,
16
- build-plugin, vue-plugin) now support TypeScript expression syntax
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`
17
18
 
18
- ## Changes
19
+ **Testing**
20
+ - Added test coverage for guarded handler scenarios
21
+ - Updated transform and utils test suites to validate new parsing logic
19
22
 
20
- ### Parser Enhancements
21
- - Added `expressionPlugins: ["typescript"]` to all `compilerDom.compile()` call sites
22
- (dev-plugin, build-plugin, vue-plugin)
23
- - Vue plugin merges expression plugins with user-supplied `userCompilerOptions` to preserve
24
- custom configuration
23
+ ### Pull Requests Included
25
24
 
26
- ### Submit Button Handling
27
- - Submit buttons with dynamic inner text (e.g., ternary expressions like `{{ isNew ? 'Create' :
28
- 'Save' }}`) now honor `missingSemanticNameBehavior: "ignore"` option
29
- - Falls back to literal `"submit"` identifier when semantic name cannot be derived, generating
30
- `SubmitButton` / `clickSubmit()` POM surface
31
- - Default behavior (throwing on missing semantic name) remains unchanged
25
+ No pull request was created for this fix; it was committed directly to `main`.
32
26
 
33
27
  ### Testing
34
- - Added 3 regression tests for TypeScript expression parsing and submit-button fallback behavior
35
- - Test suite: 163 → 166 tests passing
36
- - All validation passes: typecheck, eslint, build
37
28
 
38
- ## Pull Requests Included
39
-
40
- - fix: parse TypeScript template expressions + allow submit-button fallback
41
- [#20](https://github.com/immense/vue-pom-generator/pull/20)
42
-
43
- ## Testing
44
-
45
- Validated on Nuxt 4 + Vue 3 + TypeScript codebase. Three new regression tests added covering
46
- TypeScript expression parsing and submit-button fallback scenarios. Full test suite passes (166
47
- tests).
29
+ Validated with expanded unit test coverage in `tests/transform.test.ts` and
30
+ `tests/utils-coverage.test.ts`.
48
31
 
package/dist/index.cjs CHANGED
@@ -1895,6 +1895,12 @@ function nodeHandlerAttributeInfo(node) {
1895
1895
  const n = node2;
1896
1896
  return typeof n.computed === "boolean" && typeof n.key === "object" && n.key !== null && typeof n.value === "object" && n.value !== null;
1897
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
+ };
1898
1904
  const getLastIdentifierFromMemberChain = (node2) => {
1899
1905
  if (!node2)
1900
1906
  return null;
@@ -2065,48 +2071,66 @@ function nodeHandlerAttributeInfo(node) {
2065
2071
  const semanticNameHint2 = suffix ? `${toPascalCase(name)}${suffix}` : toPascalCase(name);
2066
2072
  return semanticNameHint2;
2067
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
+ };
2068
2085
  const resolveSemanticName = (candidateExpr) => {
2069
2086
  if (!candidateExpr) {
2070
2087
  return null;
2071
2088
  }
2089
+ const unwrappedHelperCandidate = unwrapSemanticHelperCall(candidateExpr);
2090
+ if (unwrappedHelperCandidate) {
2091
+ return resolveSemanticName(unwrappedHelperCandidate);
2092
+ }
2072
2093
  const direct = getLastIdentifierFromMemberChain(candidateExpr);
2073
2094
  if (direct) {
2074
2095
  return toPascalCase(direct);
2075
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
+ }
2076
2111
  if (isArrowFunctionExpressionNode(candidateExpr)) {
2077
2112
  const body = candidateExpr.body;
2078
- const directCall = tryFromCallExpression(body);
2079
- if (directCall) {
2080
- return directCall;
2081
- }
2082
- if (isAssignmentExpressionNode(body)) {
2083
- const lhs = getAssignmentTargetName(body.left);
2084
- if (lhs) {
2085
- const rhs = stableWordFromValue(body.right);
2086
- return `Set${toPascalCase(lhs)}${rhs ?? ""}`;
2087
- }
2088
- }
2089
2113
  if (isBlockStatementNode(body)) {
2090
2114
  const stmts = body.body ?? [];
2091
2115
  if (stmts.length > 0) {
2092
2116
  const firstStmt = stmts[0];
2093
2117
  if (isReturnStatementNode(firstStmt)) {
2094
- const fromReturn = tryFromCallExpression(firstStmt.argument ?? null);
2118
+ const fromReturn = resolveSemanticName(firstStmt.argument ?? null);
2095
2119
  if (fromReturn) {
2096
2120
  return fromReturn;
2097
2121
  }
2098
2122
  }
2099
2123
  if (isExpressionStatementNode(firstStmt)) {
2100
- const fromExpr = tryFromCallExpression(firstStmt.expression ?? null);
2124
+ const fromExpr = resolveSemanticName(firstStmt.expression ?? null);
2101
2125
  if (fromExpr) {
2102
2126
  return fromExpr;
2103
2127
  }
2104
2128
  }
2105
2129
  }
2106
2130
  }
2107
- const bodyName = getLastIdentifierFromMemberChain(body);
2131
+ const bodyName = resolveSemanticName(body);
2108
2132
  if (bodyName) {
2109
- return toPascalCase(bodyName);
2133
+ return bodyName;
2110
2134
  }
2111
2135
  }
2112
2136
  return null;