Skip to content

feat: Add SWC plugin implementation for engine syntax #149

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ jobs:
- name: Build
run: |
yarn ./packages/engine.utils run build
yarn build --exclude @c11/engine.utils
yarn build --exclude @c11/engine.utils @c11/engine.swc-plugin-syntax
- name: Test
run: yarn test
run: yarn test --exclude @c11/engine.swc-plugin-syntax
- name: Upload coverage report
uses: codecov/codecov-action@v1
94 changes: 94 additions & 0 deletions .github/workflows/swc-plugin.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: SWC Plugin CI

on:
push:
branches: [ master ]
paths:
- 'packages/engine.swc-plugin-syntax/**'
pull_request:
paths:
- 'packages/engine.swc-plugin-syntax/**'

jobs:
build-and-test:
name: Build and Test SWC Plugin
runs-on: ubuntu-latest
defaults:
run:
working-directory: packages/engine.swc-plugin-syntax

steps:
- uses: actions/checkout@v3

- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
target: wasm32-wasi
override: true
components: rustfmt, clippy

- name: Cache Rust dependencies
uses: actions/cache@v3
with:
path: |
~/.cargo/registry
~/.cargo/git
packages/engine.swc-plugin-syntax/target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}

- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh

- name: Check formatting
run: cargo fmt -- --check

- name: Run clippy
run: cargo clippy -- -D warnings

- name: Run Rust tests
run: cargo test

- name: Build WASM
run: |
cargo build --release --target wasm32-wasi

- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: swc-plugin-wasm
path: packages/engine.swc-plugin-syntax/target/wasm32-wasi/release/engine_swc_plugin_syntax.wasm

integration:
name: Integration with Node.js
needs: build-and-test
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- uses: actions/setup-node@v3
with:
node-version: "20"

- name: Download WASM artifact
uses: actions/download-artifact@v3
with:
name: swc-plugin-wasm
path: packages/engine.swc-plugin-syntax/dist

- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn config get cacheFolder)"

- uses: actions/cache@v3
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: yarn-${{ hashFiles('yarn.lock') }}

- name: Install dependencies
run: yarn install --immutable

- name: Run integration tests
run: yarn test
working-directory: packages/engine.swc-plugin-syntax
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ The Code11 Engine is a declarative state management system and application build

Get started using the [cli](https://code11.github.io/engine/docs/cli):

The engine supports both Babel and SWC for processing type annotations:
- `@c11/engine.babel-plugin-syntax` - Babel plugin (default)
- `@c11/engine.swc-plugin-syntax` - SWC plugin (faster alternative)

```
npx @c11/engine.cli create my-app
cd my-app
Expand Down
15 changes: 15 additions & 0 deletions packages/engine.swc-plugin-syntax/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.0] - 2025-02-21

### Added
- Initial release of the SWC plugin
- Support for `view` and `producer` type annotations
- Instrumentation output in `.app-structure.json`
- Full test coverage
- Documentation and examples
32 changes: 32 additions & 0 deletions packages/engine.swc-plugin-syntax/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[package]
name = "engine_swc_plugin_syntax"
version = "1.0.0"
edition = "2021"
authors = ["Code11 Team"]
description = "SWC plugin for processing engine type annotations"
license = "MIT"
repository = "https://github.com/code11/engine"
documentation = "https://github.com/code11/engine/tree/main/packages/engine.swc-plugin-syntax#readme"

[lib]
crate-type = ["cdylib"]

[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
swc_core = { version = "0.90", features = ["plugin_transform"] }
swc_ecma_ast = "0.112"
swc_ecma_parser = "0.141"
swc_ecma_visit = "0.98"
swc_common = { version = "0.33", features = ["tty-emitter"] }
anyhow = "1.0"
tokio = { version = "1.0", features = ["full"] }

[dev-dependencies]
testing = "0.35.6"
tempfile = "3.8"

[profile.release]
lto = true
opt-level = 3
codegen-units = 1
95 changes: 95 additions & 0 deletions packages/engine.swc-plugin-syntax/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# @c11/engine.swc-plugin-syntax

[![SWC Plugin CI](https://github.com/code11/engine/actions/workflows/swc-plugin.yml/badge.svg)](https://github.com/code11/engine/actions/workflows/swc-plugin.yml)

A SWC plugin that processes TypeScript type annotations to identify special engine keywords (`view` and `producer`) and transforms them into instrumented code. This is a Rust implementation of the functionality provided by [@c11/engine.babel-plugin-syntax](../engine.babel-plugin-syntax).

## Installation

```bash
npm install @c11/engine.swc-plugin-syntax
```

## Usage

Add the plugin to your SWC configuration:

```json
{
"jsc": {
"experimental": {
"plugins": [
["@c11/engine.swc-plugin-syntax", {
"output": true,
"viewLibrary": "engineViewLibrary"
}]
]
}
}
}
```

### Configuration Options

- `output` (boolean): Whether to generate instrumentation output in `.app-structure.json`
- `viewLibrary` (string): The library name to import view functions from
- `root` (string): Root path for output file generation

## Examples

### View Component

```typescript
const MyComponent: view = ({ count = observe.count }) => {
return <div>{count}</div>
};
```

### Producer Function

```typescript
const incrementCount: producer = ({ count = update.count }) => {
count(prev => prev + 1);
};
```

## Development

### Prerequisites

- Rust toolchain
- wasm32-wasi target (`rustup target add wasm32-wasi`)
- Node.js and npm/yarn

### Building

```bash
cargo build --target wasm32-wasi --release
```

### Testing

```bash
cargo test
```

### Running Examples

See the [examples](./examples) directory for usage examples.

## Comparison with Babel Plugin

This SWC plugin is a direct port of the Babel plugin functionality to Rust. It:
- Uses the same visitor pattern for AST manipulation
- Generates identical instrumentation output
- Maintains the same configuration options
- Produces compatible `.app-structure.json` output

The main differences are:
- Implemented in Rust using SWC's plugin system
- Better performance due to Rust and SWC optimizations
- Slightly different error messages due to Rust's error handling

## License

MIT License - see [LICENSE](./LICENSE) for details
26 changes: 26 additions & 0 deletions packages/engine.swc-plugin-syntax/examples/basic-usage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Example of using view and producer annotations

// View component with observation
const Counter: view = ({ count = observe.count }) => {
return <div>Count: {count}</div>;
};

// Producer function with update
const incrementCount: producer = ({ count = update.count }) => {
count(prev => prev + 1);
};

// Multiple declarations
const resetCount: producer = ({ count = update.count }) => {
count(0);
};

// Empty arguments
const EmptyView: view = () => {
return <div>Empty View</div>;
};

// Props passthrough
const PropsView: view = (props) => {
return <div {...props}>Props View</div>;
};
36 changes: 36 additions & 0 deletions packages/engine.swc-plugin-syntax/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "@c11/engine.swc-plugin-syntax",
"version": "1.0.0",
"description": "SWC plugin for processing engine type annotations",
"main": "target/wasm32-wasi/release/engine_swc_plugin_syntax.wasm",
"files": [
"target/wasm32-wasi/release/engine_swc_plugin_syntax.wasm",
"README.md",
"LICENSE"
],
"scripts": {
"build": "cargo build --target wasm32-wasi --release",
"test": "cargo test",
"prepublishOnly": "npm run build"
},
"keywords": [
"swc-plugin",
"engine",
"typescript",
"ast",
"transformation"
],
"author": "Code11 Team",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/code11/engine.git"
},
"bugs": {
"url": "https://github.com/code11/engine/issues"
},
"homepage": "https://github.com/code11/engine#readme",
"engines": {
"node": ">=16.0.0"
}
}
26 changes: 26 additions & 0 deletions packages/engine.swc-plugin-syntax/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use swc_core::{
ecma::{
ast::Program,
visit::{as_folder, FoldWith, VisitMut},
},
plugin::{plugin_transform, proxies::TransformPluginProgramMetadata},
};

#[derive(Debug)]
pub struct EngineSyntaxVisitor;

impl EngineSyntaxVisitor {
pub fn new() -> Self {
EngineSyntaxVisitor
}
}

impl VisitMut for EngineSyntaxVisitor {
// Implement necessary visit_mut_* methods here
// This is where we'll add our syntax transformation logic
}

#[plugin_transform]
pub fn process_transform(program: Program, _metadata: TransformPluginProgramMetadata) -> Program {
program.fold_with(&mut as_folder(EngineSyntaxVisitor::new()))
}
19 changes: 19 additions & 0 deletions packages/engine.swc-plugin-syntax/tests/integration_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use swc_core::ecma::{
transforms::testing::test,
visit::as_folder,
};
use engine_swc_plugin_syntax::EngineSyntaxVisitor;

#[test]
fn basic_plugin_test() {
test!(
Default::default(),
|_| as_folder(EngineSyntaxVisitor::new()),
// Test input
r#"const x = 1;"#,
// Expected output
r#"const x = 1;"#,
// Test name
ok("should pass through basic code")
);
}
Loading
Loading