Commit 92888fa
JavaScript: Templating engine enhancements (#6240)
* JavaScript: Add CaptureValue for typed property access in templates
Implement typed captures with property access using a CaptureValue architecture that enables clean syntax like `${method.name}` in templates.
Key changes:
- Add CaptureValue class that encapsulates property access on captured nodes with a resolve() method
- Update Proxy to return CaptureValue for property access (e.g., method.name)
- Add getName() method to Capture interface for accessing string names
- Simplify Template.apply() to normalize Maps upfront instead of using NormalizedCaptureMap wrapper
- Remove NormalizedCaptureMap class (no longer needed)
- Update all internal code to use CAPTURE_NAME_SYMBOL for name access
- Add tests for property access and array access in templates
Benefits:
- Cleaner architecture with resolution logic encapsulated in CaptureValue
- No string parsing needed - property paths stored directly
- User-friendly API - Capture objects work directly as Map keys
- Supports deep property chains (method.name.simpleName)
- Array access works (elements[0].element)
- Simpler implementation without normalization wrapper
* JavaScript: Add CaptureValue for typed property access in templates
Implement typed captures with property access using a CaptureValue architecture that enables clean syntax like `${method.name}` in templates.
Key changes:
- Add CaptureValue class that encapsulates property access on captured nodes with a resolve() method
- Update Proxy to return CaptureValue for property access (e.g., method.name)
- Add getName() method to Capture interface for accessing string names
- Simplify Template.apply() to normalize Maps upfront instead of using NormalizedCaptureMap wrapper
- Remove NormalizedCaptureMap class (no longer needed)
- Update all internal code to use CAPTURE_NAME_SYMBOL for name access
- Add tests for property access and array access in templates
Benefits:
- Cleaner architecture with resolution logic encapsulated in CaptureValue
- No string parsing needed - property paths stored directly
- User-friendly API - Capture objects work directly as Map keys
- Supports deep property chains (method.name.simpleName)
- Array access works (elements[0].element)
- Simpler implementation without normalization wrapper
* Revert accidental change to `comparator.ts`
* JavaScript: Implement generic lenient type matching for patterns
Implements Feature #6 from ADR 0007 by adding configurable lenient type
matching to the pattern matching comparator. This eliminates the need for
node-specific workarounds and provides a clean, generic solution.
Key changes:
- Add lenientTypeMatching parameter to JavaScriptSemanticComparatorVisitor
- Implement generic type field comparison in base comparator
- Add lenientTypeMatching configuration option to PatternOptions
- Remove redundant node-specific overrides from PatternMatchingComparator
- Default to lenient matching (true) for backward compatibility
- Add comprehensive tests for both lenient and strict matching modes
This allows patterns without type annotations to match code with type
annotations, enabling faster prototyping while still supporting strict
type validation when needed.
* JavaScript: Rename 'imports' to 'context' in template options
Implements Feature #3 from ADR 0007 by renaming the `imports` option to
`context` to better reflect its purpose. The option now supports any type
of declaration (imports, types, functions, etc.) that provides context for
type attribution, not just import statements.
Key changes:
- Add `context` field to PatternOptions and TemplateOptions
- Deprecate `imports` field with @deprecated JSDoc annotation
- Update internal implementation to use `contextStatements` parameter name
- Maintain full backward compatibility - `imports` continues to work
- Update tests to use new `context` naming
- Add comprehensive JSDoc examples showing various context types
The `imports` option remains functional as an alias, ensuring no breaking
changes for existing code. The new `context` name better communicates that
various declaration types can be provided for type attribution.
* JavaScript: Implement Builder API for dynamic template construction
Implements Feature #5 from ADR 0007 - Builder API for programmatic
template and pattern construction when structure is not known at
compile time.
Implementation:
- Added TemplateBuilder class with fluent API (.code(), .param(), .build())
- Added PatternBuilder class with fluent API (.code(), .capture(), .build())
- Added Template.builder() and Pattern.builder() static factory methods
- Builders delegate to existing template()/pattern() functions via synthetic TemplateStringsArray
- Zero logic duplication - builders just provide alternative construction API
Key Features:
- Conditional construction: add parts based on runtime logic
- Loop-based generation: dynamically create repetitive patterns
- Composition: build templates from reusable fragments
- Type-safe: full TypeScript support with method chaining
Testing:
- Comprehensive test suite with 19 tests covering all functionality
- Tests for conditional construction, loops, composition, and integration
- All 108 test suites pass (956 tests total)
Documentation:
- Added detailed JSDoc with examples for both builder classes
- Clarified capture() behavior: type parameters are for IDE autocomplete only
- Improved ADR examples for Feature #2 (Constrained Captures)
- Added warnings about structural vs. semantic matching
This addresses ADR concerns about template composition (Additional
Concerns #8) and provides the foundation for dynamic recipe generation.
* JavaScript: Add param() function for template-only parameters
Implements Feature #7 from ADR 0007 - provides semantic clarity for
standalone templates that don't involve pattern matching.
Implementation:
- Added TemplateParam interface (simpler than Capture, no property access)
- Added TemplateParamImpl class (no Proxy overhead)
- Added param<T>(name?) function returning TemplateParam<T>
- Updated TemplateParameter type union to include TemplateParam
- Updated template processing to handle TemplateParam same as Capture
Key Benefits:
- Semantic clarity: param() for standalone templates, capture() for patterns
- Type safety: Distinct TemplateParam type allows future extensions
- Performance: No Proxy overhead since property access isn't needed
- Simpler mental model for users
When to Use:
- param() for standalone templates (no pattern matching)
- capture() for patterns and templates used with patterns
Example:
```typescript
// ✅ GOOD: Use param() for standalone templates
const value = param<J.Literal>('value');
template`return ${value} * 2;`.apply(cursor, node, values);
// ✅ GOOD: Use capture() with patterns
const expr = capture('expr');
pattern`foo(${expr})`;
template`bar(${expr})`;
```
Testing:
- All 108 test suites pass (956 tests)
- No breaking changes - fully backward compatible
Documentation:
- Comprehensive JSDoc with usage examples
- Clear guidance on when to use param() vs capture()
- Updated ADR to mark Feature #7 as implemented
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <[email protected]>
* JavaScript: Add tests for param() function
Adds comprehensive test coverage for the param() function to verify:
- Basic standalone template usage with param()
- Named and unnamed parameters
- Multiple parameters in templates
- Type verification (TemplateParam vs Capture)
All 5 tests pass, demonstrating that param() works correctly
for standalone template parameter substitution.
* Update ADR
* Support for variadic captures
* Add support for capture constraints
* Polish API for variadic capture constraints
* Polish
* Add support for non-capturing captures via `any()`
* Simplify `capture()` API
In most cases client code doesn't really need to use named captures, so the name is now moved into the options.
Address some review comments.
* Remove variadic `separator` option
Turns out we don't need it after all!
* Support matching statements in statement blocks
* Fix fixtures setup
* Fix bug in pattern matching logic
* Simplify `JavaScriptComparatorVisitor`
* Split templating into multiple submodules
* Restore `references` in `tsconfig.json`
* Polish
---------
Co-authored-by: Claude <[email protected]>1 parent 46ec2f6 commit 92888fa
File tree
38 files changed
+8374
-4577
lines changed- doc/adr
- rewrite-javascript/rewrite
- fixtures
- src/javascript
- templating
- test
- javascript
- templating
- rpc
38 files changed
+8374
-4577
lines changedLarge diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
37 | 37 | | |
38 | 38 | | |
39 | 39 | | |
40 | | - | |
| 40 | + | |
41 | 41 | | |
42 | 42 | | |
43 | 43 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| 10 | + | |
10 | 11 | | |
11 | 12 | | |
12 | 13 | | |
13 | 14 | | |
14 | 15 | | |
15 | | - | |
| 16 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
| 25 | + | |
25 | 26 | | |
26 | 27 | | |
27 | 28 | | |
| |||
0 commit comments