diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 0000000..70ab4c5
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,128 @@
+name: ๐ Bug Report
+description: Report a bug or unexpected behavior
+title: "[Bug] "
+labels: ["bug"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for reporting a bug! Please fill out the form below.
+
+ - type: textarea
+ id: description
+ attributes:
+ label: Description
+ description: A clear and concise description of what the bug is
+ placeholder: Describe the bug...
+ validations:
+ required: true
+
+ - type: textarea
+ id: reproduction
+ attributes:
+ label: Steps to Reproduce
+ description: Steps to reproduce the behavior
+ placeholder: |
+ 1. Create SwcNative instance
+ 2. Call parseSync with...
+ 3. See error
+ validations:
+ required: true
+
+ - type: textarea
+ id: expected
+ attributes:
+ label: Expected Behavior
+ description: What you expected to happen
+ placeholder: The code should...
+ validations:
+ required: true
+
+ - type: textarea
+ id: actual
+ attributes:
+ label: Actual Behavior
+ description: What actually happened
+ placeholder: Instead, I got...
+ validations:
+ required: true
+
+ - type: textarea
+ id: code
+ attributes:
+ label: Code Example
+ description: A minimal code example that reproduces the issue
+ render: kotlin
+ placeholder: |
+ ```kotlin
+ val swc = SwcNative()
+ // Your code here
+ ```
+ validations:
+ required: true
+
+ - type: textarea
+ id: error
+ attributes:
+ label: Error Message / Stack Trace
+ description: If applicable, paste the error message or stack trace
+ render: text
+ placeholder: |
+ Exception in thread "main" java.lang.RuntimeException: ...
+
+ - type: dropdown
+ id: module
+ attributes:
+ label: Affected Module
+ description: Which module is affected?
+ options:
+ - swc-binding
+ - swc-generator
+ - swc-jni
+ - Documentation
+ - Other
+ validations:
+ required: true
+
+ - type: input
+ id: version
+ attributes:
+ label: Version
+ description: swc-binding version (e.g., 0.7.0)
+ placeholder: 0.7.0
+ validations:
+ required: true
+
+ - type: input
+ id: kotlin_version
+ attributes:
+ label: Kotlin Version
+ description: Kotlin version (e.g., 2.2.10)
+ placeholder: 2.2.10
+
+ - type: input
+ id: jvm_version
+ attributes:
+ label: JVM Version
+ description: Java version (e.g., 17)
+ placeholder: 17
+
+ - type: dropdown
+ id: os
+ attributes:
+ label: Operating System
+ options:
+ - Linux
+ - macOS
+ - Windows
+ - Other
+ validations:
+ required: true
+
+ - type: textarea
+ id: additional
+ attributes:
+ label: Additional Context
+ description: Any other context, screenshots, or information about the problem
+ placeholder: Add any other context about the problem here...
+
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000..617668d
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,9 @@
+blank_issues_enabled: false
+contact_links:
+ - name: ๐ฌ Discussions
+ url: https://github.com/yidafu/swc-binding/discussions
+ about: Ask questions and discuss ideas with the community
+ - name: ๐ Documentation
+ url: https://yidafu.github.io/swc-binding/docs/
+ about: Check out our documentation
+
diff --git a/.github/ISSUE_TEMPLATE/documentation.yml b/.github/ISSUE_TEMPLATE/documentation.yml
new file mode 100644
index 0000000..f47a6f7
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/documentation.yml
@@ -0,0 +1,66 @@
+name: ๐ Documentation
+description: Report documentation issues or suggest improvements
+title: "[Docs] "
+labels: ["documentation"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for helping improve our documentation!
+
+ - type: dropdown
+ id: type
+ attributes:
+ label: Type
+ description: What type of documentation issue is this?
+ options:
+ - Typo or grammar error
+ - Missing information
+ - Unclear or confusing
+ - Outdated information
+ - Broken link
+ - Other
+ validations:
+ required: true
+
+ - type: textarea
+ id: location
+ attributes:
+ label: Location
+ description: Where is the documentation issue? (file path, URL, section name)
+ placeholder: README.md, swc-binding/README.md, API docs, etc.
+ validations:
+ required: true
+
+ - type: textarea
+ id: description
+ attributes:
+ label: Description
+ description: Describe the documentation issue or improvement
+ placeholder: The documentation says... but it should say...
+ validations:
+ required: true
+
+ - type: textarea
+ id: suggestion
+ attributes:
+ label: Suggested Change
+ description: If you have a suggestion for how to fix it, please provide it here
+ placeholder: |
+ Change:
+ ```
+ Old text
+ ```
+
+ To:
+ ```
+ New text
+ ```
+
+ - type: textarea
+ id: additional
+ attributes:
+ label: Additional Context
+ description: Any other context about the documentation issue
+ placeholder: Add any other context here...
+
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
new file mode 100644
index 0000000..94c1ba5
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -0,0 +1,81 @@
+name: โจ Feature Request
+description: Suggest a new feature or enhancement
+title: "[Feature] "
+labels: ["enhancement"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for suggesting a feature! Please fill out the form below.
+
+ - type: textarea
+ id: problem
+ attributes:
+ label: Problem Statement
+ description: What problem does this feature solve?
+ placeholder: I'm always frustrated when...
+ validations:
+ required: true
+
+ - type: textarea
+ id: solution
+ attributes:
+ label: Proposed Solution
+ description: Describe the solution you'd like
+ placeholder: I would like to see...
+ validations:
+ required: true
+
+ - type: textarea
+ id: alternatives
+ attributes:
+ label: Alternatives Considered
+ description: Describe any alternative solutions or features you've considered
+ placeholder: Other approaches I've thought about...
+
+ - type: dropdown
+ id: module
+ attributes:
+ label: Affected Module
+ description: Which module would this feature affect?
+ options:
+ - swc-binding
+ - swc-generator
+ - swc-jni
+ - Documentation
+ - Other
+ validations:
+ required: true
+
+ - type: textarea
+ id: api
+ attributes:
+ label: Proposed API
+ description: If applicable, describe the proposed API or interface
+ render: kotlin
+ placeholder: |
+ ```kotlin
+ // Proposed API example
+ swc.newMethod(...)
+ ```
+
+ - type: dropdown
+ id: priority
+ attributes:
+ label: Priority
+ description: How important is this feature to you?
+ options:
+ - Low - Nice to have
+ - Medium - Would be helpful
+ - High - Important for my use case
+ - Critical - Blocking my project
+ validations:
+ required: true
+
+ - type: textarea
+ id: additional
+ attributes:
+ label: Additional Context
+ description: Any other context, examples, or mockups about the feature request
+ placeholder: Add any other context or screenshots about the feature request here...
+
diff --git a/.github/ISSUE_TEMPLATE/question.yml b/.github/ISSUE_TEMPLATE/question.yml
new file mode 100644
index 0000000..9999d06
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/question.yml
@@ -0,0 +1,59 @@
+name: โ Question
+description: Ask a question about the project
+title: "[Question] "
+labels: ["question"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Have a question? We're here to help!
+
+ - type: textarea
+ id: question
+ attributes:
+ label: Question
+ description: What would you like to know?
+ placeholder: Your question here...
+ validations:
+ required: true
+
+ - type: textarea
+ id: context
+ attributes:
+ label: Context
+ description: Provide any relevant context about your question
+ placeholder: |
+ I'm trying to...
+ I've looked at...
+ I'm using version...
+
+ - type: textarea
+ id: code
+ attributes:
+ label: Code Example (if applicable)
+ description: If your question involves code, please share a minimal example
+ render: kotlin
+ placeholder: |
+ ```kotlin
+ // Your code here
+ ```
+
+ - type: dropdown
+ id: module
+ attributes:
+ label: Related Module
+ description: Which module is your question about?
+ options:
+ - swc-binding
+ - swc-generator
+ - swc-jni
+ - General
+ - Other
+
+ - type: textarea
+ id: additional
+ attributes:
+ label: Additional Information
+ description: Any other information that might help answer your question
+ placeholder: Add any other context here...
+
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 0000000..b70b0e6
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,79 @@
+## Description
+
+
+
+## Type of Change
+
+
+
+- [ ] ๐ Bug fix (non-breaking change which fixes an issue)
+- [ ] โจ New feature (non-breaking change which adds functionality)
+- [ ] ๐ฅ Breaking change (fix or feature that would cause existing functionality to not work as expected)
+- [ ] ๐ Documentation update
+- [ ] ๐ง Refactoring (no functional changes)
+- [ ] โก Performance improvement
+- [ ] โ
Test update
+- [ ] ๐จ Build/CI changes
+- [ ] ๐จ Style/formatting changes
+- [ ] ๐งน Chore (other changes that don't modify src or test files)
+
+## Related Issues
+
+
+
+Closes #
+Related to #
+
+## Changes Made
+
+
+
+-
+-
+-
+
+## Module(s) Affected
+
+
+
+- [ ] swc-binding
+- [ ] swc-generator
+- [ ] swc-jni
+- [ ] Documentation
+- [ ] Build/CI
+- [ ] Other:
+
+## Testing
+
+
+
+- [ ] Unit tests added/updated
+- [ ] Integration tests added/updated
+- [ ] Manual testing performed
+
+**Test Steps:**
+1.
+2.
+3.
+
+## Checklist
+
+
+
+- [ ] My code follows the project's style guidelines
+- [ ] I have performed a self-review of my own code
+- [ ] I have commented my code, particularly in hard-to-understand areas
+- [ ] I have updated the documentation accordingly
+- [ ] My changes generate no new warnings
+- [ ] I have added tests that prove my fix is effective or that my feature works
+- [ ] New and existing unit tests pass locally with my changes
+- [ ] Any dependent changes have been merged and published
+
+## Screenshots (if applicable)
+
+
+
+## Additional Notes
+
+
+
diff --git a/.vscode/settings.json b/.vscode/settings.json
index be97557..da8ab11 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,3 +1,4 @@
{
- "java.compile.nullAnalysis.mode": "disabled"
+ "java.compile.nullAnalysis.mode": "disabled",
+ "java.configuration.updateBuildConfiguration": "disabled"
}
\ No newline at end of file
diff --git a/README.md b/README.md
index 07171ed..15fb1c2 100644
--- a/README.md
+++ b/README.md
@@ -4,9 +4,9 @@
[](LICENSE)
[](https://search.maven.org/artifact/dev.yidafu.swc/swc-binding)
-[](https://kotlinlang.org/)
-[](https://github.com/swc-project/swc)
-[](https://www.rust-lang.org/)
+[](https://kotlinlang.org/)
+[](https://github.com/swc-project/swc)
+[](https://www.rust-lang.org/)
[](https://www.oracle.com/java/)
[SWC](https://github.com/swc-project/swc) JVM binding in Kotlin.
@@ -40,7 +40,6 @@
- [Article](#article)
- [License](#license)
-
## Installation
```kotlin
@@ -167,6 +166,7 @@ swc.parseAsync(
#### Available Async Methods
All synchronous methods have async counterparts:
+
- `parseAsync()` - Asynchronously parse code
- `parseFileAsync()` - Asynchronously parse file
- `transformAsync()` - Asynchronously transform code
@@ -207,7 +207,6 @@ Native method
fun parseSync(code: String, options: String, filename: String?): String
```
-
### parseFileSync
[see more](https://yidafu.github.io/swc-binding/docs/swc-binding/dev.yidafu.swc/-swc-native/parse-file-sync.html)
diff --git a/swc-binding/README.md b/swc-binding/README.md
index 52dad7e..8fa6769 100644
--- a/swc-binding/README.md
+++ b/swc-binding/README.md
@@ -4,8 +4,9 @@
[](../LICENSE)
[](https://search.maven.org/artifact/dev.yidafu.swc/swc-binding)
-[](https://kotlinlang.org/)
-[](https://github.com/swc-project/swc)
+[](https://kotlinlang.org/)
+[](https://github.com/swc-project/swc)
+[](https://www.rust-lang.org/)
[](https://www.oracle.com/java/)
Kotlin/JVM library that provides idiomatic bindings to SWC via JNI. This is the published artifact used by applications.
@@ -32,18 +33,372 @@ val output = swc.transformSync(
println(output.code)
```
+## SwcNative Usage Guide
+
+`SwcNative` is the main entry point for all SWC operations. It provides both synchronous and asynchronous methods for parsing, transforming, printing, and minifying JavaScript/TypeScript code.
+
+### Creating an Instance
+
+```kotlin
+val swc = SwcNative()
+```
+
+The `SwcNative` instance automatically loads the native library (`swc_jni`) when created. The library is loaded from the JAR resources if not found in the system library path.
+
+### Synchronous Methods
+
+All synchronous methods block the calling thread until the operation completes. Use them for simple scripts or when blocking is acceptable.
+
+#### Parsing Code
+
+Parse JavaScript/TypeScript code into an AST:
+
+```kotlin
+val swc = SwcNative()
+val ast = swc.parseSync(
+ code = "const x = 42;",
+ options = esParseOptions { },
+ filename = "test.js" // Optional
+)
+```
+
+**TypeScript parsing:**
+
+```kotlin
+val ast = swc.parseSync(
+ code = "const x: number = 42;",
+ options = tsParseOptions {
+ tsx = true
+ decorators = true
+ },
+ filename = "test.ts"
+)
+```
+
+**JSX parsing:**
+
+```kotlin
+val ast = swc.parseSync(
+ code = """
+ function App() {
+ return
Hello
;
+ }
+ """.trimIndent(),
+ options = esParseOptions {
+ jsx = true
+ },
+ filename = "App.jsx"
+)
+```
+
+#### Parsing Files
+
+Parse a file directly:
+
+```kotlin
+val ast = swc.parseFileSync(
+ filepath = "src/index.ts",
+ options = tsParseOptions {
+ tsx = true
+ }
+)
+```
+
+#### Transforming Code
+
+Transform code (transpile, minify, apply plugins, etc.):
+
+```kotlin
+val output = swc.transformSync(
+ code = "const arrow = () => 42;",
+ isModule = false,
+ options = options {
+ jsc {
+ target = JscTarget.ES5
+ parser {
+ syntax = Syntax.ECMASCRIPT
+ }
+ }
+ }
+)
+println(output.code) // Transformed code
+```
+
+**Transforming TypeScript to JavaScript:**
+
+```kotlin
+val output = swc.transformSync(
+ code = "const x: number = 42;",
+ isModule = true,
+ options = options {
+ jsc {
+ target = JscTarget.ES2020
+ parser {
+ syntax = Syntax.TYPESCRIPT
+ tsx = true
+ }
+ }
+ }
+)
+```
+
+**Transforming files:**
+
+```kotlin
+val output = swc.transformFileSync(
+ filepath = "src/index.ts",
+ isModule = true,
+ options = options {
+ jsc {
+ target = JscTarget.ES2020
+ parser {
+ syntax = Syntax.TYPESCRIPT
+ }
+ }
+ }
+)
+```
+
+#### Printing AST
+
+Convert an AST back to code:
+
+```kotlin
+val ast = swc.parseSync("const x = 42;", esParseOptions { }, "test.js")
+val output = swc.printSync(ast, options { })
+println(output.code) // "const x = 42;"
+```
+
+#### Minifying Code
+
+Minify JavaScript/TypeScript code:
+
+```kotlin
+val code = """
+ function add(a, b) {
+ return a + b;
+ }
+""".trimIndent()
+
+val output = swc.minifySync(
+ src = code,
+ options = JsMinifyOptions()
+)
+println(output.code) // Minified code
+```
+
+### Asynchronous Methods
+
+Asynchronous methods execute in background threads and don't block the calling thread. They are available in two forms: callback-based (Java-friendly) and coroutine-based (Kotlin-idiomatic).
+
+#### Using Coroutines (Recommended for Kotlin)
+
+All async methods have suspend function variants that work seamlessly with Kotlin coroutines:
+
+```kotlin
+import kotlinx.coroutines.*
+
+suspend fun parseCode() {
+ val swc = SwcNative()
+ val ast = swc.parseAsync(
+ code = "const x = 42;",
+ options = esParseOptions { },
+ filename = "test.js"
+ )
+ println("Parsed: ${ast.type}")
+}
+
+// Usage
+runBlocking {
+ parseCode()
+}
+```
+
+**Parallel parsing:**
+
+```kotlin
+suspend fun parseMultipleFiles() = coroutineScope {
+ val swc = SwcNative()
+ val options = esParseOptions { }
+
+ val results = listOf("file1.js", "file2.js", "file3.js")
+ .map { file ->
+ async { swc.parseFileAsync(file, options) }
+ }
+ .awaitAll()
+
+ results.forEach { ast ->
+ println("Parsed: ${ast.type}")
+ }
+}
+```
+
+**Async transform:**
+
+```kotlin
+suspend fun transformCode() {
+ val swc = SwcNative()
+ val output = swc.transformAsync(
+ code = "const arrow = () => 42;",
+ isModule = false,
+ options = options {
+ jsc {
+ target = JscTarget.ES5
+ parser {
+ syntax = Syntax.ECMASCRIPT
+ }
+ }
+ }
+ )
+ println(output.code)
+}
+```
+
+**Async minify:**
+
+```kotlin
+suspend fun minifyCode() {
+ val swc = SwcNative()
+ val output = swc.minifyAsync(
+ src = "function add(a, b) { return a + b; }",
+ options = JsMinifyOptions()
+ )
+ println(output.code)
+}
+```
+
+#### Using Callbacks (Java-friendly)
+
+Callback-based methods work in both Kotlin and Java:
+
+```kotlin
+val swc = SwcNative()
+swc.parseAsync(
+ code = "const x = 42;",
+ options = esParseOptions { },
+ filename = "test.js",
+ onSuccess = { ast ->
+ println("Success: ${ast.type}")
+ },
+ onError = { error ->
+ println("Error: $error")
+ }
+)
+```
+
+**Java usage:**
+
+```java
+SwcNative swc = new SwcNative();
+swc.parseAsync("const x = 42;", options, null,
+ (Program ast) -> System.out.println("Success: " + ast.getType()),
+ (String error) -> System.err.println("Error: " + error)
+);
+```
+
+### Available Methods Summary
+
+#### Synchronous Method List
+
+- `parseSync(code, options, filename?)` - Parse code to AST
+- `parseFileSync(filepath, options)` - Parse file to AST
+- `transformSync(code, isModule, options)` - Transform code
+- `transformFileSync(filepath, isModule, options)` - Transform file
+- `printSync(program, options)` - Print AST to code
+- `minifySync(src, options)` - Minify code
+
+#### Asynchronous Method List (Coroutines)
+
+- `parseAsync(code, options, filename?)` - Parse code to AST (suspend)
+- `parseFileAsync(filepath, options)` - Parse file to AST (suspend)
+- `transformAsync(code, isModule, options)` - Transform code (suspend)
+- `transformFileAsync(filepath, isModule, options)` - Transform file (suspend)
+- `printAsync(program, options)` - Print AST to code (suspend)
+- `minifyAsync(src, options)` - Minify code (suspend)
+
+#### Asynchronous Method List (Callbacks)
+
+- `parseAsync(code, options, filename?, onSuccess, onError)` - Parse code with callbacks
+- `parseFileAsync(filepath, options, onSuccess, onError)` - Parse file with callbacks
+- `transformAsync(code, isModule, options, onSuccess, onError)` - Transform with callbacks
+- `transformFileAsync(filepath, isModule, options, onSuccess, onError)` - Transform file with callbacks
+- `printAsync(program, options, onSuccess, onError)` - Print AST with callbacks
+- `minifyAsync(src, options, onSuccess, onError)` - Minify with callbacks
+
+### Error Handling
+
+All methods throw `RuntimeException` on failure. For async methods using coroutines, exceptions are propagated:
+
+```kotlin
+try {
+ val ast = swc.parseSync("invalid syntax", esParseOptions { }, "test.js")
+} catch (e: RuntimeException) {
+ println("Parse failed: ${e.message}")
+}
+```
+
+**With coroutines:**
+
+```kotlin
+try {
+ val ast = swc.parseAsync("invalid syntax", esParseOptions { }, "test.js")
+} catch (e: RuntimeException) {
+ println("Parse failed: ${e.message}")
+}
+```
+
+**With callbacks:**
+
+```kotlin
+swc.parseAsync(
+ code = "invalid syntax",
+ options = esParseOptions { },
+ filename = "test.js",
+ onSuccess = { /* ... */ },
+ onError = { error ->
+ println("Parse failed: $error")
+ }
+)
+```
+
+### Threading Model
+
+- **Synchronous methods**: Execute on the calling thread and block until completion.
+- **Asynchronous methods**: Execute in background threads managed by the Rust runtime. Callbacks are invoked from Rust background threads. Coroutines suspend the calling coroutine and resume when the operation completes.
+
+### SwcNative Best Practices
+
+- **Use async methods for production**: Async methods don't block threads and provide better performance for concurrent operations.
+
+- **Prefer coroutines in Kotlin**: Use suspend functions for cleaner, more idiomatic Kotlin code.
+
+- **Reuse SwcNative instances**: Creating a new `SwcNative()` instance is lightweight, but reusing instances is fine for multiple operations.
+
+- **Handle errors appropriately**: Always wrap operations in try-catch blocks or handle errors in callbacks.
+
+- **Use appropriate parser options**: Choose `esParseOptions` for JavaScript/JSX and `tsParseOptions` for TypeScript/TSX.
+
## Documentation
-- API Docs: https://yidafu.github.io/swc-binding/docs/
+- API Docs: [https://yidafu.github.io/swc-binding/docs/](https://yidafu.github.io/swc-binding/docs/)
- Samples: `swc-binding/src/test`
-## Important Notes
+## DSL Usage Guide
-### Manual AST Construction
+The swc-binding library provides a type-safe DSL (Domain-Specific Language) for building AST nodes and configuration objects. The DSL uses Kotlin's builder pattern to create SWC AST nodes in an idiomatic way.
-When manually constructing AST nodes using the DSL, you **must** explicitly set all boolean fields to avoid serialization errors. The Rust backend expects non-null boolean values, but Kotlin's nullable boolean fields (`Boolean?`) will serialize as `null` if not set.
+### Importing DSL Functions
+
+```kotlin
+import dev.yidafu.swc.generated.dsl.* // ktlint-disable no-wildcard-imports
+```
+
+### Creating AST Nodes
+
+#### Module
+
+A `Module` represents an ES module with a body containing statements and declarations:
-**Example - Correct:**
```kotlin
val mod = module {
span = emptySpan()
@@ -51,14 +406,20 @@ val mod = module {
variableDeclaration {
span = emptySpan()
kind = VariableDeclarationKind.CONST
- declare = false // โ
Explicitly set
+ declare = false
declarations = arrayOf(
variableDeclarator {
+ span = emptySpan()
id = identifier {
+ span = emptySpan()
value = "x"
- optional = false // โ
Explicitly set
+ optional = false
+ }
+ init = numericLiteral {
+ span = emptySpan()
+ value = 42.0
+ raw = "42"
}
- init = numericLiteral { ... }
}
)
}
@@ -66,6 +427,308 @@ val mod = module {
}
```
+### Important Notes
+
+#### Manual AST Construction
+
+When manually constructing AST nodes using the DSL, it's **recommended** to explicitly set boolean fields for clarity and compatibility. While many fields have default values and `@EncodeDefault` annotations that handle serialization, explicitly setting boolean fields makes your code more maintainable and avoids potential edge cases.
+
+**Example - Correct:**
+```kotlin
+variableDeclaration {
+ span = emptySpan()
+ kind = VariableDeclarationKind.CONST
+ declare = false // โ
Explicitly set
+ declarations = arrayOf(
+ variableDeclarator {
+ span = emptySpan()
+ id = identifier {
+ span = emptySpan()
+ value = "x"
+ optional = false // โ
Explicitly set
+ }
+ init = numericLiteral {
+ span = emptySpan()
+ value = 42.0
+ raw = "42"
+ }
+ }
+ )
+}
+```
+
+#### Variable Declaration
+
+```kotlin
+variableDeclaration {
+ span = emptySpan()
+ kind = VariableDeclarationKind.CONST // or LET, VAR
+ declare = false
+ declarations = arrayOf(
+ variableDeclarator {
+ span = emptySpan()
+ id = identifier {
+ span = emptySpan()
+ value = "myVar"
+ optional = false
+ }
+ init = stringLiteral {
+ span = emptySpan()
+ value = "Hello"
+ raw = "\"Hello\""
+ }
+ }
+ )
+}
+```
+
+#### Function Declaration
+
+```kotlin
+functionDeclaration {
+ span = emptySpan()
+ identifier = identifier {
+ span = emptySpan()
+ value = "add"
+ optional = false
+ }
+ declare = false
+ async = false
+ generator = false
+ params = arrayOf(
+ param {
+ span = emptySpan()
+ pat = identifier {
+ span = emptySpan()
+ value = "a"
+ optional = false
+ }
+ },
+ param {
+ span = emptySpan()
+ pat = identifier {
+ span = emptySpan()
+ value = "b"
+ optional = false
+ }
+ }
+ )
+ body = blockStatement {
+ span = emptySpan()
+ stmts = arrayOf(
+ returnStatement {
+ span = emptySpan()
+ arg = binaryExpression {
+ span = emptySpan()
+ operator = BinaryOperator.PLUS
+ left = identifier {
+ span = emptySpan()
+ value = "a"
+ optional = false
+ }
+ right = identifier {
+ span = emptySpan()
+ value = "b"
+ optional = false
+ }
+ }
+ }
+ )
+ }
+}
+```
+
+#### Class Declaration
+
+```kotlin
+classDeclaration {
+ span = emptySpan()
+ identifier = identifier {
+ span = emptySpan()
+ value = "MyClass"
+ optional = false
+ }
+ declare = false
+ abstract = false
+ body = arrayOf(
+ classProperty {
+ span = emptySpan()
+ key = identifier {
+ span = emptySpan()
+ value = "name"
+ optional = false
+ }
+ value = stringLiteral {
+ span = emptySpan()
+ value = "Example"
+ raw = "\"Example\""
+ }
+ typeAnnotation = tsTypeAnnotation {
+ span = emptySpan()
+ typeAnnotation = tsKeywordType {
+ span = emptySpan()
+ kind = TsKeywordTypeKind.STRING
+ }
+ }
+ }
+ )
+ superClass = null
+ superTypeParams = null
+ implements = arrayOf()
+ decorators = arrayOf()
+}
+```
+
+#### Import Declaration
+
+```kotlin
+importDeclaration {
+ span = emptySpan()
+ specifiers = arrayOf(
+ importDefaultSpecifier {
+ local = identifier {
+ span = emptySpan()
+ value = "React"
+ optional = false
+ }
+ }
+ )
+ source = stringLiteral {
+ span = emptySpan()
+ value = "react"
+ raw = "\"react\""
+ }
+ typeOnly = false
+}
+```
+
+### Creating Configuration Objects
+
+#### Parser Options
+
+**TypeScript Parser Options:**
+
+```kotlin
+val tsOptions = tsParseOptions {
+ tsx = true
+ decorators = true
+ dynamicImport = true
+ target = JscTarget.ES2020
+}
+```
+
+**ECMAScript Parser Options:**
+
+```kotlin
+val esOptions = esParseOptions {
+ jsx = true
+ allowReturnOutsideFunction = true
+ allowAwaitOutsideFunction = false
+ allowSuperOutsideMethod = false
+ allowImportExportEverywhere = false
+ allowUndeclaredExports = false
+}
+```
+
+#### Transform Options
+
+```kotlin
+val transformOptions = options {
+ jsc {
+ target = JscTarget.ES2020
+ parser {
+ syntax = Syntax.TYPESCRIPT
+ tsx = true
+ decorators = true
+ }
+ transform {
+ react {
+ runtime = ReactConfigRuntime.CLASSIC
+ pragma = "React.createElement"
+ pragmaFrag = "React.Fragment"
+ }
+ }
+ minify {
+ compress = JsMinifyCompressOptions()
+ mangle = JsMinifyMangleOptions()
+ }
+ }
+ module {
+ type = ModuleConfigType.ES6
+ }
+ minify = false
+ isModule = true
+}
+```
+
+### Common Patterns
+
+#### Creating Literals
+
+**String Literal:**
+
+```kotlin
+stringLiteral {
+ span = emptySpan()
+ value = "Hello, World!"
+ raw = "\"Hello, World!\""
+}
+```
+
+**Numeric Literal:**
+
+```kotlin
+numericLiteral {
+ span = emptySpan()
+ value = 42.0
+ raw = "42"
+}
+```
+
+**Boolean Literal:**
+
+```kotlin
+booleanLiteral {
+ span = emptySpan()
+ value = true
+}
+```
+
+#### Creating Expressions
+
+**Binary Expression:**
+
+```kotlin
+binaryExpression {
+ span = emptySpan()
+ operator = BinaryOperator.PLUS // or MINUS, MULTIPLY, etc.
+ left = identifier { value = "a"; span = emptySpan(); optional = false }
+ right = identifier { value = "b"; span = emptySpan(); optional = false }
+}
+```
+
+**Call Expression:**
+
+```kotlin
+callExpression {
+ span = emptySpan()
+ callee = identifier {
+ span = emptySpan()
+ value = "console"
+ optional = false
+ }
+ arguments = arrayOf(
+ argument {
+ expr = stringLiteral {
+ span = emptySpan()
+ value = "Hello"
+ raw = "\"Hello\""
+ }
+ }
+ )
+}
+```
+
**Example - Incorrect:**
```kotlin
val mod = module {
@@ -79,12 +742,52 @@ val mod = module {
}
```
-**Common boolean fields that must be set:**
-- `Identifier.optional: Boolean?` โ set to `false`
-- `VariableDeclaration.declare: Boolean?` โ set to `false`
-- `ArrowFunctionExpression.async: Boolean?` โ set to `false`
-- `ArrowFunctionExpression.generator: Boolean?` โ set to `false`
-- `TemplateElement.tail: Boolean?` โ set to `false` or `true` (depending on position)
+**Member Expression:**
+
+```kotlin
+memberExpression {
+ span = emptySpan()
+ obj = identifier {
+ span = emptySpan()
+ value = "console"
+ optional = false
+ }
+ prop = identifier {
+ span = emptySpan()
+ value = "log"
+ optional = false
+ }
+ computed = false
+}
+```
+
+### Important Notes
+
+#### Span Fields
+
+All AST nodes require a `span` field. Use `emptySpan()` to create a default span:
+
+```kotlin
+span = emptySpan() // Creates a span with start=0, end=0, ctxt=0
+```
+
+#### Boolean Fields
+
+When manually constructing AST nodes, it's **recommended** to explicitly set boolean fields for clarity and to ensure compatibility. While many boolean fields have `@EncodeDefault` annotations and may work without explicit values, setting them explicitly makes your code more maintainable and avoids potential serialization issues.
+
+**Common boolean fields you should set:**
+
+- `Identifier.optional: Boolean?` โ typically set to `false`
+- `VariableDeclaration.declare: Boolean?` โ typically set to `false`
+- `FunctionDeclaration.async: Boolean?` โ set to `false` for non-async functions
+- `FunctionDeclaration.generator: Boolean?` โ set to `false` for non-generator functions
+- `ArrowFunctionExpression.async: Boolean?` โ set to `false` for non-async arrow functions
+- `ArrowFunctionExpression.generator: Boolean?` โ set to `false` for non-generator arrow functions
+- `TemplateElement.tail: Boolean?` โ set to `false` or `true` (depending on position in template)
+
+#### Best Practices
+
+- **Prefer parsing over manual construction**: Use `parseSync` to parse code into AST instead of manually constructing it, as parsed ASTs have all fields properly initialized.
**Recommended approach:** Use `parseSync` to parse code into AST instead of manually constructing it, as parsed ASTs have all fields properly initialized.
@@ -94,8 +797,14 @@ val mod = swcNative.parseSync("const x = 42;", esParseOptions { }, "test.js") as
val output = swcNative.printSync(mod, options { })
```
-## License
+- **Use DSL for modifications**: The DSL is most useful when you need to modify or create specific AST nodes programmatically.
-MIT
+- **Always set span fields**: Every AST node requires a `span` field. Use `emptySpan()` for default values.
+- **Set boolean fields explicitly**: While not always required, explicitly setting boolean fields improves code clarity and ensures compatibility.
+For more examples, see the test files in `swc-binding/src/test`.
+
+## License
+
+MIT
diff --git a/swc-generator/README.md b/swc-generator/README.md
index 8d7e1b9..74cb401 100644
--- a/swc-generator/README.md
+++ b/swc-generator/README.md
@@ -3,8 +3,8 @@
[Read this in Chinese: README.zh-CN.md](README.zh-CN.md)
[](../LICENSE)
-[](https://kotlinlang.org/)
-[](https://github.com/swc-project/swc)
+[](https://kotlinlang.org/)
+[](https://github.com/swc-project/swc)
[](https://www.oracle.com/java/)
Kotlin-based code generator for SWC Kotlin/JVM bindings. It reads SWC type definitions and generates strongly-typed Kotlin APIs and DSL helpers.
@@ -46,6 +46,7 @@ Kotlin-based code generator for SWC Kotlin/JVM bindings. It reads SWC type defin
Some Rust structs contain independent `ctxt: SyntaxContext` fields (not within `span`), and these fields don't have `serde(default)`, making them required during deserialization. The code generator automatically adds `ctxt: Int = 0` fields with `@EncodeDefault` annotations to the following classes:
**Statement-related:**
+
- `BlockStatement`: corresponds to Rust `BlockStmt` struct
**Expression-related:**
@@ -55,21 +56,100 @@ Some Rust structs contain independent `ctxt: SyntaxContext` fields (not within `
- `TaggedTemplateExpression`: corresponds to Rust `TaggedTpl` struct
**Declaration-related:**
+
- `FunctionDeclaration`: corresponds to Rust `Function` struct (expanded through `FnDecl`'s `#[serde(flatten)]`)
- `VariableDeclaration`: corresponds to Rust `VarDecl` struct
**Class-related:**
+
- `Class`: corresponds to Rust `Class` struct
- `PrivateProperty`: corresponds to Rust `PrivateProp` struct
- `Constructor`: corresponds to Rust `Constructor` struct
**Identifier-related:**
+
- `Identifier`: corresponds to Rust `Ident` struct
This special handling ensures the generated Kotlin code is compatible with Rust's deserialization requirements.
**Note**: Although the Rust `Span` struct itself does not contain a `ctxt` field (only `start` and `end`), the Kotlin `Span` class retains the `ctxt` field for compatibility with TypeScript definitions and existing code. These node-level independent `ctxt` fields are additional fields needed to match Rust's deserialization requirements.
+### Explicit Type Field Handling
+
+Some classes require explicit `type` fields even though they have `@JsonClassDiscriminator` and `@SerialName` annotations. This is because when these classes are serialized as concrete types (e.g., `Array`), the discriminator doesn't automatically add the `type` field. The following classes have explicit `type` fields added:
+
+- `VariableDeclarator`
+- `Param` (Kotlin class name, serialized as "Parameter")
+- `BlockStatement`
+- `JSXOpeningElement`
+- `JSXClosingElement`
+
+### Property Type Overrides
+
+Certain properties require special type mappings that differ from their TypeScript definitions:
+
+**Global Type Overrides:**
+
+Properties that are declared as `any` in TypeScript but are mapped to `String?` in Kotlin:
+
+- `global_defs` / `globalDefs`
+- `targets`
+- `sequences`
+- `pure_getters` / `pureGetters`
+- `top_retain` / `topRetain`
+- `toplevel`
+
+**Interface-Specific Overrides:**
+
+- `ForOfStatement.await`: Declared as `Span?` in TypeScript but actually should be `Boolean?` in Kotlin. This is a SWC-specific case that requires special handling.
+
+### Kotlin Reserved Word Mapping
+
+To avoid conflicts with Kotlin keywords, certain property names are automatically mapped:
+
+- `object` โ `jsObject`
+- `inline` โ `jsInline`
+- `in` โ `jsIn`
+- `super` โ `jsSuper`
+- `class` โ `jsClass`
+- `interface` โ `jsInterface`
+- `fun` โ `jsFun`
+- `val` โ `jsVal`
+- `var` โ `jsVar`
+- `when` โ `jsWhen`
+- `is` โ `jsIs`
+- `as` โ `jsAs`
+- `import` โ `jsImport`
+- `package` โ `jsPackage`
+
+### Type Name Overrides
+
+To avoid conflicts with Kotlin built-in types or reserved names, certain type names are mapped:
+
+- `Class` โ `JsClass`
+- `Super` โ `JsSuper`
+- `Import` โ `JsImport`
+- `ExprOrSpread` โ `Argument` (unified mapping to avoid dual-type decoding/DSL conflicts)
+
+### Span Property Handling
+
+Special handling for `span` and span coordinate properties:
+
+- **`span` property**: Uses `emptySpan()` function call as default value, ensuring the `ctxt` field is included
+- **Span coordinate properties** (`start`, `end`, `ctxt` in `Span` class): Use `Int` type with default value `0`
+- **`start` and `end`**: Annotated with `@EncodeDefault` to ensure serialization
+- **`ctxt`**: Not annotated with `@EncodeDefault` because `@swc/core` doesn't output default `ctxt` values of 0
+
+### Serialization Annotations
+
+The generator automatically adds appropriate serialization annotations:
+
+- **`@Serializable`**: Added to sealed interfaces and final classes
+- **`@JsonClassDiscriminator`**: Added to base interfaces for polymorphic serialization
+- **`@SerialName`**: Added to properties to ensure correct serialization names
+- **`@SwcDslMarker`**: Added to interfaces and implementation classes for DSL type safety
+- **`@OptIn(ExperimentalSerializationApi)`**: Added to implementation classes that use experimental serialization features
+
## Development
- See `swc-generator/ARCHITECTURE.md` and `ARCHITECTURE_OVERVIEW.md`
@@ -79,5 +159,3 @@ This special handling ensures the generated Kotlin code is compatible with Rust'
## License
MIT
-
-