Skip to content
Draft
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
171 changes: 171 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ napi-build = "2.4.1"
napi-derive = "3.6.3"
pathdiff = "0.2.3"
rstack-ignore = { path = "crates/rstack-ignore" }
# The wire format must match @swc-next/decoder exactly.
swc_next_bridge = { version = "=0.2.0", features = ["parser"] }

# Local development: 16 codegen units are sufficient for this small workspace while preserving
# parallel compilation and full debugging support.
Expand Down
1 change: 1 addition & 0 deletions crates/rstack-binding/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ test = false
napi.workspace = true
napi-derive.workspace = true
rstack-ignore.workspace = true
swc_next_bridge.workspace = true

[build-dependencies]
napi-build.workspace = true
4 changes: 4 additions & 0 deletions crates/rstack-binding/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#![deny(clippy::all)]

mod parser;

pub use parser::parse_swc_next;

use std::path::Path;

use napi::{bindgen_prelude::Uint8Array, Error, Status};
Expand Down
43 changes: 43 additions & 0 deletions crates/rstack-binding/src/parser.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use napi::{bindgen_prelude::Buffer, Error, Status};
use napi_derive::napi;
use swc_next_bridge::{parse_to_buffer, CommentMode, Lang, ParserOptions, SourceType};

/// Returns the SWC Next 0.2.0 wire format consumed by @swc-next/decoder.
/// Keep parsing and serialization in the upstream crate, not in the NAPI adapter.
#[napi]
pub fn parse_swc_next(source: String, lang: String, source_type: String) -> napi::Result<Buffer> {
let lang = match lang.as_str() {
"js" => Lang::Js,
"jsx" => Lang::Jsx,
"ts" => Lang::Ts,
"tsx" => Lang::Tsx,
"dts" => Lang::Dts,
_ => {
return Err(Error::new(
Status::InvalidArg,
"Unsupported SWC Next language.",
))
}
};
let source_type = match source_type.as_str() {
"module" => SourceType::Module,
"commonjs" => SourceType::CommonJs,
_ => {
return Err(Error::new(
Status::InvalidArg,
"Unsupported SWC Next source type.",
))
}
};

Ok(parse_to_buffer(
&source,
Some(ParserOptions {
lang: Some(lang),
source_type: Some(source_type),
preserve_parens: Some(true),
comments: Some(CommentMode::Flat),
}),
)
.into())
}
32 changes: 32 additions & 0 deletions packages/rstack/SWC_NEXT_POC.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# SWC next formatter PoC

This branch replaces the default Yuku parser in `rs fmt` with SWC Next 0.2.0 while retaining Prettier's ESTree printer and the existing formatting adapter.

`swc_next_bridge = "=0.2.0"` compiles parsing and serialization into the existing rstack native binding. A thin NAPI function returns a Buffer to `@swc-next/decoder@0.2.0`; the generated loader is resolved lazily when parsing starts. No parser npm package or separate SWC Next native binary is installed.

Inferred JavaScript, JSX, TypeScript, TSX, and embedded script formatting use SWC Next. Explicit `babel` and `typescript` options still select Prettier's own parsers, and project plugins retain precedence. The experimental explicit parser names are `swc-next` and `swc-next-ts`, replacing `yuku` and `yuku-ts`.

The cache namespace includes the parser version and integration route so switching between the original formatter and either PoC cannot reuse stale formatting results. The decoder and Rust serializer must be upgraded together; all SWC Next packages in this PoC are locked to 0.2.0.

## Try it

```sh
pnpm install
pnpm build
pnpm --filter rstack build:native
printf 'const view=<Component value={{foo:1}} />' | pnpm exec rs fmt --stdin-filepath example.tsx
pnpm test
pnpm check
```

Native changes also require `cargo fmt --all -- --check`, `cargo clippy --workspace --all-targets --locked -- -D warnings`, and `cargo test --workspace --locked`.

## Compatibility and limits

SWC Next 0.2.0 accepts function implementations in declaration files, for example `export function value() { return 1; }` in `example.d.ts`. Yuku previously rejected this with an ambient-context diagnostic. The tests explicitly record this upstream difference; valid declarations, comments, Unicode locations, JSX/TSX, CommonJS, pragmas, and syntax errors remain covered. No fallback to another parser hides SWC Next errors.

Validation is on macOS arm64 with the repository's Node.js, pnpm, and Rust versions. The complete formatter/CLI suite covers worker execution, stdin, LSP, Vue, Svelte, and caching. Cross-platform binaries and formatter-wide conformance against Prettier's upstream corpus have not been validated. No throughput advantage is claimed by this PoC.

## Upstream

[SWC Next 0.2.0 source](https://github.com/swc-project/swc-next/tree/v0.2.0)
4 changes: 2 additions & 2 deletions packages/rstack/THIRD_PARTY_NOTICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

## Prettier yuku parser adapter
## Prettier SWC next parser adapter

The local Yuku parser adapter includes portions derived from
The local SWC Next parser adapter includes portions derived from
[@prettier/plugin-yuku](https://github.com/prettier/prettier/tree/main/packages/plugin-yuku)
and Prettier's JavaScript parser postprocessing. The adapter reuses the public
ESTree printer, formatter options, and parser utilities from the installed
Expand Down
4 changes: 4 additions & 0 deletions packages/rstack/binding.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -702,3 +702,7 @@ if (!nativeBinding) {
module.exports = nativeBinding
module.exports.GitIgnoreMatcher = nativeBinding.GitIgnoreMatcher
module.exports.IgnoreMatcher = nativeBinding.IgnoreMatcher
module.exports.parseSwcNext = nativeBinding.parseSwcNext
module.exports.CommentMode = nativeBinding.CommentMode
module.exports.Lang = nativeBinding.Lang
module.exports.SourceType = nativeBinding.SourceType
Loading