@shadow-js/compiler
v0.2.0
Published
JSX compiler and transforms for ShadowJS
Readme
ShadowJS Compiler
The ShadowJS JSX compiler that transforms JSX code into Shadow-compatible reactive expressions. This compiler enables Shadow's fine-grained reactivity by automatically wrapping JSX expressions in reactive functions.
✨ Features
- 🎯 JSX Transformation: Converts JSX to reactive expressions automatically
- ⚡ Compile-time Optimization: Optimizes JSX at build time
- 🎨 React-like Syntax: Familiar JSX syntax with reactive enhancements
- 🔧 TypeScript Support: Full TypeScript and TSX compilation
- 📦 Zero Runtime: All transformations happen at compile time
- 🎭 Control Flow Enhancement: Special handling for Show, Match, For components
- 🎯 Ref Handling: Automatic ref assignment functions
- 🎨 Style Reactivity: Makes style objects reactive
📦 Installation
npm install @shadow-js/compilerUsually used together with the Vite plugin:
npm install @shadow-js/compiler @shadow-js/vite🚀 Quick Start
The compiler is typically used through the Vite plugin, but can also be used directly:
import { transformSource } from "@shadow-js/compiler";
const jsxCode = `
const App = () => (
<div>
<Show when={count() > 0} fallback={<div>No items</div>}>
<div>Count: {count()}</div>
</Show>
</div>
);
`;
const transformedCode = transformSource(jsxCode);
console.log(transformedCode);🎯 How It Works
The compiler transforms JSX in two main phases:
Phase 1: Arrow Function Transformations
Transforms JSX in arrow function bodies:
// Input
const App = () => <div>{count()}</div>;
// Output
const App = () => <div>{() => count()}</div>;Phase 2: Return Statement Transformations
Transforms JSX in return statements:
// Input
function App() {
return <div>{items.length}</div>;
}
// Output
function App() {
return <div>{() => items.length}</div>;
}🔧 API Reference
Main Functions
| Function | Description |
| ------------------------------------ | ---------------------------------------------------------- |
| transformSource(code) | Transform JSX source code to Shadow-compatible expressions |
| transformArrowImplicitBodies(code) | Transform JSX in arrow function implicit bodies |
| transformAllReturnParens(code) | Transform JSX in return statement parentheses |
Utility Functions
| Function | Description |
| ---------------------------------------- | -------------------------------------- |
| applyJsxTransforms(jsxContent) | Apply JSX-specific transformations |
| findMatching(code, start, open, close) | Find matching brackets in code |
| hasJsxTag(code) | Check if code contains JSX tags |
| isFunction(code) | Check if code is a function expression |
🎯 Transformation Examples
Basic Expression Wrapping
// Input
<div>Hello {name}</div>
// Output
<div>Hello {() => name}</div>Control Flow Enhancement
// Input
<Show when={count > 0}>
<div>Count: {count}</div>
</Show>
// Output
<Show when={() => count > 0}>
<div>Count: {() => count}</div>
</Show>Ref Handling
// Input
<div ref={myRef} />
// Output
<div ref={(el) => myRef = el} />Style Object Reactivity
// Input
<div style={{ color: "red" }} />
// Output
<div style={() => ({ color: "red" })} />For Loop Enhancement
// Input
<For each={items}>
{(item) => <li>{item.name}</li>}
</For>
// Output
<For each={() => items}>
{(item) => <li>{() => item.name}</li>}
</For>🏗️ Architecture
The compiler consists of several transformation modules:
1. Source Transformation (transformSource.ts)
Main entry point that orchestrates all transformations:
- Coordinates arrow function and return statement processing
- Handles error reporting and recovery
- Provides the public API
2. Arrow Function Processing (transformArrowImplicitBodies.ts)
Handles JSX in arrow function bodies:
- Uses simple lexer to skip strings and comments
- Finds arrow functions with implicit JSX bodies
- Applies JSX transformations to function bodies
3. Return Statement Processing (transformAllReturnParens.ts)
Handles JSX in return statements:
- Finds
return (statements - Applies transformations to JSX content
- Handles nested function contexts
4. JSX Transformations (applyJsxTransforms.ts)
Core JSX transformation logic:
- Expression wrapping: Makes child expressions reactive
- Attribute processing: Handles special attributes (ref, style, when, etc.)
- Recursive processing: Handles nested JSX and function expressions
- String/comment handling: Preserves code structure
5. Utility Functions
Supporting utilities for code analysis:
- Bracket matching: Find matching parentheses, braces, brackets
- JSX detection: Identify JSX tags in code
- Function detection: Identify function expressions
- Reference checking: Identify bare references vs. expressions
🔧 Advanced Usage
Custom Transformations
You can extend the compiler by creating custom transformations:
import { applyJsxTransforms } from "@shadow-js/compiler";
function customTransform(jsxContent: string): string {
// Apply built-in transformations first
let result = applyJsxTransforms(jsxContent);
// Add custom transformations
result = result.replace(/@custom/g, "customTransform");
return result;
}Integration with Build Tools
Vite Plugin Integration
The recommended way to use the compiler:
// vite.config.ts
import { defineConfig } from "vite";
import shadow from "@shadow-js/vite";
export default defineConfig({
plugins: [shadow()],
});Manual Integration
For custom build setups:
import { transformSource } from "@shadow-js/compiler";
export function shadowPlugin() {
return {
name: "shadow-compiler",
transform(code, id) {
if (id.endsWith(".tsx") || id.endsWith(".jsx")) {
return transformSource(code);
}
},
};
}Error Handling
The compiler provides detailed error messages:
try {
const transformed = transformSource(sourceCode);
// Use transformed code
} catch (error) {
console.error("ShadowJS compilation failed:", error.message);
// Handle compilation error
}🎯 Best Practices
Code Organization
- Keep JSX expressions simple and readable
- Use meaningful variable names in reactive expressions
- Avoid complex logic in JSX expressions
Performance Considerations
- The compiler runs at build time, no runtime overhead
- Transformations are optimized for minimal code generation
- Use build-time analysis for optimal results
Debugging
- Check transformed output when debugging reactivity issues
- Use source maps for accurate error locations
- Test transformations with simple examples first
🔍 Compiler Options
While the compiler currently has minimal configuration, future versions may include:
- Transformation modes: Different levels of reactivity
- Custom attribute handling: Support for custom JSX attributes
- Optimization levels: Different compilation strategies
- Source map generation: Enhanced debugging support
🧪 Testing
Testing Transformations
import { transformSource } from "@shadow-js/compiler";
describe("ShadowJS Compiler", () => {
test("transforms basic JSX expressions", () => {
const input = "<div>{name}</div>";
const output = transformSource(input);
expect(output).toContain("{() => name}");
});
test("handles control flow components", () => {
const input = "<Show when={count > 0}><div>Count</div></Show>";
const output = transformSource(input);
expect(output).toContain("when={() => count > 0}");
});
});📊 Performance
Compilation Speed
- Fast parsing: Simple lexer for string/comment skipping
- Efficient transformations: Targeted regex replacements
- Minimal passes: Usually 1-2 transformation passes
Bundle Impact
- Zero runtime: All transformations happen at build time
- Small overhead: Minimal additional code generated
- Tree-shaking friendly: Generated code works with tree-shaking
🐛 Troubleshooting
Common Issues
Expression not reactive: Check if expression is properly wrapped
// Wrong <div>{count + 1}</div> // Right <div>{() => count + 1}</div>Control flow not working: Ensure conditions are wrapped in functions
// Wrong <Show when={count > 0}> // Right <Show when={() => count > 0}>Ref not working: Check ref assignment function syntax
// Wrong <div ref={myRef} /> // Right - compiler transforms this automatically <div ref={myRef} /> // Becomes: <div ref={(el) => myRef = el} />
Debug Mode
Enable debug mode for detailed transformation logs:
// Enable in development
process.env.SHADOW_DEBUG = "true";
const transformed = transformSource(sourceCode);
console.log("Transformation result:", transformed);📚 Examples
See the examples documentation for:
- Basic transformation patterns
- Advanced usage scenarios
- Integration examples
- Common transformation patterns
🤝 Contributing
We welcome contributions! See the Contributing Guide for details.
Areas for contribution:
- New transformation patterns
- Performance optimizations
- Additional utility functions
- Better error messages
- Enhanced TypeScript support
📄 License
MIT License - see LICENSE for details.
📞 Support
- Documentation: Compiler API
- Examples: Transformation Examples
- Issues: GitHub Issues
- Discussions: GitHub Discussions
Built by Jehaad AL-Johani for compile-time JSX transformations.
