diff --git a/.github/npm-release-lines.json b/.github/npm-release-lines.json new file mode 100644 index 000000000000..815f663b3c5c --- /dev/null +++ b/.github/npm-release-lines.json @@ -0,0 +1,4 @@ +{ + "1": "latest", + "2": "next" +} diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 770a759b3ab2..1f6eced44af6 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -456,6 +456,63 @@ jobs: run: | pnpm test + node-v2-test: + name: Test node v2 preview + continue-on-error: true + if: >- + ${{ !contains(github.event.head_commit.message, 'chore: ') }} + runs-on: macos-latest + permissions: + contents: read + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false + submodules: true + - name: Install Rust + uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable + with: + toolchain: nightly-2026-04-10 + targets: wasm32-wasip1 + - uses: ./.github/actions/setup-node + with: + node-version: 20 + install-dependencies: "false" + - name: Prepare + run: | + corepack enable + pnpm install --frozen-lockfile + - name: Test v2 native API + working-directory: packages/core-v2 + run: pnpm test:native + - name: Test v1 plugin compatibility + working-directory: packages/core-v2 + run: pnpm test:plugin-v1 + + wasm-v2-test: + name: Test wasm v2 preview + continue-on-error: true + if: >- + ${{ !contains(github.event.head_commit.message, 'chore: ') }} + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false + submodules: true + - name: Install Rust + uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable + - uses: ./.github/actions/setup-node + with: + node-version: 20 + - name: Install wasm-pack + run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + - name: Test v2 Wasm API + working-directory: bindings/binding_core_wasm_v2 + run: ./scripts/test.sh + react-compiler-test: name: Test react-compiler bindings - ${{ matrix.os }} if: >- diff --git a/.github/workflows/publish-npm-package.yml b/.github/workflows/publish-npm-package.yml index 2d978891335e..5af3c223891e 100644 --- a/.github/workflows/publish-npm-package.yml +++ b/.github/workflows/publish-npm-package.yml @@ -44,6 +44,11 @@ on: skipPublishing: type: boolean required: true + npmTag: + description: "Override the configured npm dist-tag" + type: string + required: false + default: "" jobs: check-if-build-required: @@ -68,7 +73,7 @@ jobs: run: | set -euo pipefail case "$PACKAGE" in - core | html | minifier | react-compiler) ;; + core | core-v2 | html | minifier | react-compiler) ;; *) echo "Unsupported package: $PACKAGE" >&2 exit 1 @@ -366,7 +371,7 @@ jobs: run: | set -euo pipefail case "$PACKAGE" in - core | html | minifier | react-compiler) ;; + core | core-v2 | html | minifier | react-compiler) ;; *) echo "Unsupported package: $PACKAGE" >&2 exit 1 @@ -403,7 +408,7 @@ jobs: run: | set -euo pipefail case "$PACKAGE" in - core | html | minifier | react-compiler) ;; + core | core-v2 | html | minifier | react-compiler) ;; *) echo "Unsupported package: $PACKAGE" >&2 exit 1 @@ -515,7 +520,7 @@ jobs: run: | set -euo pipefail case "$PACKAGE" in - core | html | minifier | react-compiler) ;; + core | core-v2 | html | minifier | react-compiler) ;; *) echo "Unsupported package: $PACKAGE" >&2 exit 1 @@ -598,7 +603,7 @@ jobs: run: | set -euo pipefail case "$PACKAGE" in - core | html | minifier | react-compiler) ;; + core | core-v2 | html | minifier | react-compiler) ;; *) echo "Unsupported package: $PACKAGE" >&2 exit 1 @@ -662,12 +667,13 @@ jobs: - name: Tag npm release env: + EXPLICIT_NPM_TAG: ${{ inputs.npmTag }} RELEASE_VERSION: ${{ inputs.version }} run: | - if [[ "$RELEASE_VERSION" != *"nightly"* ]]; then - echo "NPM_TAG=latest" >> "$GITHUB_ENV" + if [[ -n "$EXPLICIT_NPM_TAG" ]]; then + echo "NPM_TAG=$EXPLICIT_NPM_TAG" >> "$GITHUB_ENV" else - echo "NPM_TAG=nightly" >> "$GITHUB_ENV" + echo "NPM_TAG=$(node scripts/resolve-npm-tag.mjs "$RELEASE_VERSION")" >> "$GITHUB_ENV" fi - name: Install dependencies @@ -679,6 +685,13 @@ jobs: pattern: "bindings-${{ inputs.buildType }}-${{ inputs.package }}-${{ inputs.version}}-*" path: ./packages/${{ inputs.package }}/artifacts + - name: Prepare v2 package identity + if: ${{ inputs.package == 'core-v2' }} + env: + RELEASE_VERSION: ${{ inputs.version }} + run: | + node scripts/prepare-npm-v2-package.mjs packages/core-v2 "$RELEASE_VERSION" @swc/core + - name: List binaries shell: bash env: @@ -686,6 +699,7 @@ jobs: run: ls -R "packages/$PACKAGE/artifacts" - name: Move binaries + if: ${{ inputs.package != 'core-v2' }} shell: bash env: PACKAGE: ${{ inputs.package }} @@ -696,6 +710,11 @@ jobs: ls -R ./artifacts_cli npm run artifacts + - name: Prepare v2 native packages + if: ${{ inputs.package == 'core-v2' }} + working-directory: packages/core-v2 + run: npm run artifacts + - name: List npm shell: bash env: @@ -703,12 +722,13 @@ jobs: run: ls -R "packages/$PACKAGE/scripts/npm" - name: Set fallback dependencies + if: ${{ inputs.package != 'core-v2' }} shell: bash run: | node ./scripts/update_fallback_dependencies.js - name: Publish - if: ${{ !inputs.skipPublishing }} + if: ${{ !inputs.skipPublishing && inputs.package != 'core-v2' }} env: GITHUB_TOKEN: ${{ github.token }} PACKAGE: ${{ inputs.package }} @@ -716,6 +736,16 @@ jobs: pnpm --filter "./packages/$PACKAGE" prepack pnpm publish -r --filter "./packages/$PACKAGE" --filter "./packages/$PACKAGE/scripts/npm/*" --access public --tag "$NPM_TAG" --force --no-git-checks + - name: Publish v2 preview + if: ${{ !inputs.skipPublishing && inputs.package == 'core-v2' }} + working-directory: packages/core-v2 + run: | + npm run prepack + for package_dir in scripts/npm/*; do + npm publish "$package_dir" --access public --tag "$NPM_TAG" + done + npm publish . --access public --tag "$NPM_TAG" + - name: Upload CLI binaries to gh release if: ${{ !inputs.skipPublishing && inputs.package == 'core' }} shell: bash @@ -780,12 +810,13 @@ jobs: - name: Tag npm release env: + EXPLICIT_NPM_TAG: ${{ inputs.npmTag }} RELEASE_VERSION: ${{ inputs.version }} run: | - if [[ "$RELEASE_VERSION" != *"nightly"* ]]; then - echo "NPM_TAG=latest" >> "$GITHUB_ENV" + if [[ -n "$EXPLICIT_NPM_TAG" ]]; then + echo "NPM_TAG=$EXPLICIT_NPM_TAG" >> "$GITHUB_ENV" else - echo "NPM_TAG=nightly" >> "$GITHUB_ENV" + echo "NPM_TAG=$(node scripts/resolve-npm-tag.mjs "$RELEASE_VERSION")" >> "$GITHUB_ENV" fi - name: Setup node diff --git a/.github/workflows/publish-npm-v2-preview.yml b/.github/workflows/publish-npm-v2-preview.yml new file mode 100644 index 000000000000..ee9fc56d8e8b --- /dev/null +++ b/.github/workflows/publish-npm-v2-preview.yml @@ -0,0 +1,86 @@ +name: "Publish SWC v2 preview" + +permissions: {} + +on: + workflow_dispatch: + inputs: + version: + description: "The 2.x prerelease version to publish" + required: true + type: string + skipPublishing: + description: "Build and validate without publishing" + required: false + default: false + type: boolean + +jobs: + publish-types: + name: Publish @swc/types v2 + runs-on: ubuntu-latest + environment: publish + permissions: + contents: read + id-token: write + steps: + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + with: + persist-credentials: false + ref: "v${{ inputs.version }}" + - uses: ./.github/actions/setup-node + with: + cache: "false" + - run: corepack enable && pnpm install --frozen-lockfile + - run: pnpm --filter @swc-internal/types-v2 build + - name: Stage packages + run: node scripts/stage-npm-v2.mjs "${{ inputs.version }}" + - name: Publish types + if: ${{ !inputs.skipPublishing }} + run: npm publish "target/npm-v2/${{ inputs.version }}/types" --access public --tag next + + publish-core: + name: Publish @swc/core v2 + needs: publish-types + permissions: + contents: write + id-token: write + uses: ./.github/workflows/publish-npm-package.yml + with: + buildType: "v2-preview" + package: "core-v2" + version: ${{ inputs.version }} + buildCli: false + skipBuild: false + publishWasm: false + skipPublishing: ${{ inputs.skipPublishing }} + npmTag: "next" + + publish-wasm: + name: Publish @swc/wasm v2 + needs: publish-types + runs-on: ubuntu-latest + environment: publish + permissions: + contents: read + id-token: write + steps: + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + with: + persist-credentials: false + ref: "v${{ inputs.version }}" + - uses: ./.github/actions/setup-node + with: + cache: "false" + - run: corepack enable && pnpm install --frozen-lockfile + - name: Install wasm-pack + run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + - name: Build Wasm + working-directory: bindings/binding_core_wasm_v2 + run: wasm-pack build --out-name wasm --release --scope=swc --target nodejs + - name: Prepare package identity + run: | + node scripts/prepare-npm-v2-package.mjs bindings/binding_core_wasm_v2/pkg "${{ inputs.version }}" @swc/wasm + - name: Publish Wasm + if: ${{ !inputs.skipPublishing }} + run: npm publish bindings/binding_core_wasm_v2/pkg --access public --tag next diff --git a/Cargo.lock b/Cargo.lock index e8f0ddb3c951..35d823106c45 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -388,6 +388,27 @@ dependencies = [ "rustc-hash 2.1.1", "serde", "serde_json", + "swc_api_common", + "swc_core", + "swc_malloc", + "tracing", +] + +[[package]] +name = "binding_core_node_v2" +version = "0.1.0" +dependencies = [ + "anyhow", + "backtrace", + "napi", + "napi-build", + "napi-derive", + "par-core", + "path-clean", + "rustc-hash 2.1.1", + "serde", + "serde_json", + "swc_api_common", "swc_core", "swc_malloc", "tracing", @@ -406,6 +427,19 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "binding_core_wasm_v2" +version = "0.1.0" +dependencies = [ + "anyhow", + "getrandom 0.3.4", + "serde", + "serde-wasm-bindgen", + "swc_core", + "tracing", + "wasm-bindgen", +] + [[package]] name = "binding_es_ast_viewer" version = "1.15.43" @@ -5861,6 +5895,19 @@ dependencies = [ "swc_malloc", ] +[[package]] +name = "swc_api_common" +version = "0.1.0" +dependencies = [ + "anyhow", + "serde", + "serde_json", + "swc_common", + "swc_ecma_ast", + "swc_ecma_visit", + "swc_nodejs_common", +] + [[package]] name = "swc_arena" version = "0.1.0" diff --git a/bindings/binding_core_node/Cargo.toml b/bindings/binding_core_node/Cargo.toml index 39426f170a74..aad31b3bcebf 100644 --- a/bindings/binding_core_node/Cargo.toml +++ b/bindings/binding_core_node/Cargo.toml @@ -13,7 +13,9 @@ bench = false crate-type = ["cdylib"] [features] -default = ["swc_v1", "plugin"] +api_v1 = [] +api_v2 = [] +default = ["api_v1", "swc_v1", "plugin"] plugin = [ "swc_core/plugin_backend_wasmer", "swc_core/plugin_transform_host_native", @@ -34,17 +36,18 @@ __plugin_transform_vtest = [ napi-build = { workspace = true } [dependencies] -anyhow = { workspace = true } -backtrace = { workspace = true } -napi = { workspace = true, features = ["napi3", "serde-json"] } -napi-derive = { workspace = true, features = ["type-def"] } -par-core = { workspace = true, features = ["chili"] } -path-clean = { workspace = true } -rustc-hash = { workspace = true } -serde = { workspace = true, features = ["derive"] } -serde_json = { workspace = true, features = ["unbounded_depth"] } -tracing = { workspace = true, features = ["release_max_level_info"] } +anyhow = { workspace = true } +backtrace = { workspace = true } +napi = { workspace = true, features = ["napi3", "serde-json"] } +napi-derive = { workspace = true, features = ["type-def"] } +par-core = { workspace = true, features = ["chili"] } +path-clean = { workspace = true } +rustc-hash = { workspace = true } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true, features = ["unbounded_depth"] } +tracing = { workspace = true, features = ["release_max_level_info"] } +swc_api_common = { path = "../../crates/swc_api_common" } swc_core = { path = "../../crates/swc_core", features = [ "allocator_node", "ecma_ast", diff --git a/bindings/binding_core_node/build.rs b/bindings/binding_core_node/build.rs index a6220cd0337c..f1e4c7b52c55 100644 --- a/bindings/binding_core_node/build.rs +++ b/bindings/binding_core_node/build.rs @@ -13,6 +13,12 @@ compile_error!("Please enable swc_v1 or swc_v2 feature"); #[cfg(all(feature = "swc_v1", feature = "swc_v2"))] compile_error!("Features swc_v1 and swc_v2 are incompatible"); +#[cfg(all(not(feature = "api_v1"), not(feature = "api_v2")))] +compile_error!("Please enable api_v1 or api_v2 feature"); + +#[cfg(all(feature = "api_v1", feature = "api_v2"))] +compile_error!("Features api_v1 and api_v2 are incompatible"); + fn main() { let out_dir = env::var("OUT_DIR").expect("Outdir should exist"); let dest_path = Path::new(&out_dir).join("triple.txt"); diff --git a/bindings/binding_core_node/src/ast_context.rs b/bindings/binding_core_node/src/ast_context.rs index 12ec25414a42..7c8ceb5d11e5 100644 --- a/bindings/binding_core_node/src/ast_context.rs +++ b/bindings/binding_core_node/src/ast_context.rs @@ -1,233 +1,21 @@ -use anyhow::{bail, Context, Error}; -use serde::{Deserialize, Serialize}; -use swc_core::{ - base::Compiler, - common::{BytePos, FileName, SourceFile, Span, DUMMY_SP}, - ecma::{ - ast::Program, - visit::{VisitMut, VisitMutWith}, - }, - node::deserialize_json, -}; +//! Version selection for the shared AST wire codec. -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct ProgramSourceContext { - real_filename: Option, - source: String, - start_pos: u32, - end_pos: u32, -} +use swc_api_common::{ + deserialize_program_input as deserialize, serialize_program as serialize, WireVersion, +}; +pub use swc_api_common::{prepare_program_with_context, ProgramInput}; +use swc_core::{common::SourceFile, ecma::ast::Program}; -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct ProgramEnvelope { - program: Program, - source_context: Option, -} +#[cfg(feature = "api_v1")] +const WIRE_VERSION: WireVersion = WireVersion::V1; -#[derive(Debug)] -pub enum ProgramInput { - Raw(Program), - WithContext { - program: Program, - source_context: ProgramSourceContext, - }, -} - -pub fn source_context_from_file(fm: &SourceFile) -> ProgramSourceContext { - ProgramSourceContext { - real_filename: match &*fm.name { - FileName::Real(path) => Some(path.display().to_string()), - _ => None, - }, - source: fm.src.as_str().to_owned(), - start_pos: fm.start_pos.0, - end_pos: fm.end_pos.0, - } -} +#[cfg(feature = "api_v2")] +const WIRE_VERSION: WireVersion = WireVersion::V2; pub fn serialize_program(program: Program, fm: &SourceFile) -> Result { - serde_json::to_string(&ProgramEnvelope { - program, - source_context: Some(source_context_from_file(fm)), - }) + serialize(WIRE_VERSION, program, fm) } pub fn deserialize_program_input(json: &str) -> Result { - if let Ok(envelope) = deserialize_json::(json) { - if let Some(source_context) = envelope.source_context { - return Ok(ProgramInput::WithContext { - program: envelope.program, - source_context, - }); - } - - return Ok(ProgramInput::Raw(envelope.program)); - } - - deserialize_json(json).map(ProgramInput::Raw) -} - -pub fn prepare_program_with_context( - c: &Compiler, - mut program: Program, - source_context: ProgramSourceContext, -) -> Result<(swc_core::common::sync::Lrc, Program), Error> { - let fm = c.cm.new_source_file( - source_context.file_name().into(), - source_context.source.clone(), - ); - - let expected_len = source_context - .end_pos - .checked_sub(source_context.start_pos) - .context("invalid source context byte range")?; - - if fm.byte_length() != expected_len { - bail!( - "failed to restore source context because the source length changed while passing AST \ - between JavaScript and native code" - ); - } - - rebase_program_spans(&mut program, &source_context, &fm) - .context("failed to rebase AST spans to restored source context")?; - - Ok((fm, program)) -} - -fn rebase_program_spans( - program: &mut Program, - source_context: &ProgramSourceContext, - fm: &SourceFile, -) -> Result<(), Error> { - let old_start = source_context.start_pos; - let old_end = source_context.end_pos; - let new_start = fm.start_pos.0; - let new_end = fm.end_pos.0; - - if old_start > old_end { - bail!("invalid source context byte range: {old_start}..{old_end}"); - } - - program.visit_mut_with(&mut SpanRebaser { - old_start, - old_end, - new_start, - new_end, - }); - - Ok(()) -} - -impl ProgramSourceContext { - fn file_name(&self) -> FileName { - match &self.real_filename { - Some(filename) => FileName::Real(filename.into()), - None => FileName::Anon, - } - } -} - -struct SpanRebaser { - old_start: u32, - old_end: u32, - new_start: u32, - new_end: u32, -} - -impl SpanRebaser { - fn rebase_pos(&self, pos: BytePos) -> Option { - if pos.is_dummy() || pos.is_reserved_for_comments() || pos == BytePos::SYNTHESIZED { - return Some(pos); - } - - if pos.0 < self.old_start || pos.0 > self.old_end { - return None; - } - - let rebased = self.new_start.checked_add(pos.0 - self.old_start)?; - if rebased > self.new_end { - return None; - } - - Some(BytePos(rebased)) - } - - fn rebase_span(&self, span: Span) -> Span { - if span.is_dummy() { - return span; - } - - let Some(lo) = self.rebase_pos(span.lo) else { - return DUMMY_SP; - }; - let Some(hi) = self.rebase_pos(span.hi) else { - return DUMMY_SP; - }; - - if lo.is_dummy() || hi.is_dummy() { - return DUMMY_SP; - } - - Span::new_with_checked(lo, hi) - } -} - -impl VisitMut for SpanRebaser { - fn visit_mut_span(&mut self, span: &mut Span) { - *span = self.rebase_span(*span); - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn rebase_span_into_new_source_range() { - let rebaser = SpanRebaser { - old_start: 100, - old_end: 140, - new_start: 1, - new_end: 41, - }; - - let span = rebaser.rebase_span(Span::new(BytePos(110), BytePos(125))); - - assert_eq!(span.lo, BytePos(11)); - assert_eq!(span.hi, BytePos(26)); - } - - #[test] - fn drops_out_of_range_span() { - let rebaser = SpanRebaser { - old_start: 100, - old_end: 140, - new_start: 1, - new_end: 41, - }; - - assert_eq!( - rebaser.rebase_span(Span::new(BytePos(90), BytePos(125))), - DUMMY_SP - ); - } - - #[test] - fn preserves_dummy_and_reserved_spans() { - let rebaser = SpanRebaser { - old_start: 100, - old_end: 140, - new_start: 1, - new_end: 41, - }; - - assert_eq!(rebaser.rebase_span(DUMMY_SP), DUMMY_SP); - assert_eq!( - rebaser.rebase_span(Span::new(BytePos::PURE, BytePos::PURE)), - Span::new(BytePos::PURE, BytePos::PURE) - ); - } + deserialize(WIRE_VERSION, json) } diff --git a/bindings/binding_core_node/src/lib.rs b/bindings/binding_core_node/src/lib.rs index afa5a1089b89..b010fdb3d251 100644 --- a/bindings/binding_core_node/src/lib.rs +++ b/bindings/binding_core_node/src/lib.rs @@ -17,9 +17,11 @@ use swc_core::{ #[cfg(feature = "plugin")] mod analyze; mod ast_context; +#[cfg(feature = "api_v1")] mod bundle; mod minify; mod parse; +#[cfg(feature = "api_v1")] mod print; mod transform; mod util; diff --git a/bindings/binding_core_node/src/print.rs b/bindings/binding_core_node/src/print.rs index 23799491777d..095e6f7a9b9e 100644 --- a/bindings/binding_core_node/src/print.rs +++ b/bindings/binding_core_node/src/print.rs @@ -43,7 +43,9 @@ fn prepare_program_for_print(c: &Compiler, program_input: ProgramInput) -> Resul ProgramInput::WithContext { program, source_context, - } => prepare_program_with_context(c, program, source_context).map(|(_, program)| program), + } => { + prepare_program_with_context(&c.cm, program, source_context).map(|(_, program)| program) + } ProgramInput::Raw(program) => Ok(program), } } diff --git a/bindings/binding_core_node/src/transform.rs b/bindings/binding_core_node/src/transform.rs index c0ac1367af02..74b6d3c90922 100644 --- a/bindings/binding_core_node/src/transform.rs +++ b/bindings/binding_core_node/src/transform.rs @@ -60,7 +60,7 @@ fn process_program_input( program, source_context, } => { - let (fm, program) = prepare_program_with_context(c, program, source_context)?; + let (fm, program) = prepare_program_with_context(&c.cm, program, source_context)?; c.process_js_with_custom_pass( fm, diff --git a/bindings/binding_core_node_v2/Cargo.toml b/bindings/binding_core_node_v2/Cargo.toml new file mode 100644 index 000000000000..001903faabf9 --- /dev/null +++ b/bindings/binding_core_node_v2/Cargo.toml @@ -0,0 +1,59 @@ +[package] +authors = ["강동윤 "] +build = "../binding_core_node/build.rs" +edition = { workspace = true } +license = { workspace = true } +name = "binding_core_node_v2" +publish = false +version = "0.1.0" + +[lib] +bench = false +crate-type = ["cdylib"] +path = "../binding_core_node/src/lib.rs" + +[features] +api_v1 = [] +api_v2 = [] +default = ["api_v2", "swc_v2", "plugin"] +plugin = [ + "swc_core/plugin_backend_wasmer", + "swc_core/plugin_transform_host_native", + "swc_core/plugin_transform_host_native_filesystem_cache", +] +swc_v1 = [] +swc_v2 = [] + +[build-dependencies] +napi-build = { workspace = true } + +[dependencies] +anyhow = { workspace = true } +backtrace = { workspace = true } +napi = { workspace = true, features = ["napi3", "serde-json"] } +napi-derive = { workspace = true, features = ["type-def"] } +par-core = { workspace = true, features = ["chili"] } +path-clean = { workspace = true } +rustc-hash = { workspace = true } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true, features = ["unbounded_depth"] } +tracing = { workspace = true, features = ["release_max_level_info"] } + +swc_api_common = { path = "../../crates/swc_api_common" } +swc_core = { path = "../../crates/swc_core", features = [ + "allocator_node", + "ecma_ast", + "ecma_minifier", + "ecma_codegen", + "ecma_ast_serde", + "common_concurrent", + "ecma_helpers_inline", + "ecma_transforms", + "ecma_visit", + "base_node", + "base_concurrent", + "base_flow", + "base_module", + "base_react_compiler", +] } +swc_malloc = { path = "../../crates/swc_malloc" } diff --git a/bindings/binding_core_wasm/Cargo.toml b/bindings/binding_core_wasm/Cargo.toml index b32d18c4df02..22959348a83d 100644 --- a/bindings/binding_core_wasm/Cargo.toml +++ b/bindings/binding_core_wasm/Cargo.toml @@ -14,7 +14,9 @@ crate-type = ["cdylib"] [features] __plugin = [] -default = ["swc_v1"] +api_v1 = [] +api_v2 = [] +default = ["api_v1", "swc_v1"] swc_v1 = [] swc_v2 = [] # This feature exists to allow cargo operations diff --git a/bindings/binding_core_wasm/src/lib.rs b/bindings/binding_core_wasm/src/lib.rs index dc789d06790c..7ede671fa51c 100644 --- a/bindings/binding_core_wasm/src/lib.rs +++ b/bindings/binding_core_wasm/src/lib.rs @@ -1,13 +1,22 @@ use swc_core::binding_macros::{ - build_minify, build_minify_sync, build_parse, build_parse_sync, build_print, build_print_sync, - build_transform, build_transform_sync, + build_minify, build_minify_sync, build_parse, build_parse_sync, build_transform, + build_transform_sync, }; +#[cfg(feature = "api_v1")] +use swc_core::binding_macros::{build_print, build_print_sync}; use wasm_bindgen::prelude::*; mod types; +#[cfg(all(not(feature = "api_v1"), not(feature = "api_v2")))] +compile_error!("Please enable api_v1 or api_v2 feature"); + +#[cfg(all(feature = "api_v1", feature = "api_v2"))] +compile_error!("Features api_v1 and api_v2 are incompatible"); + /// Custom interface definitions for the @swc/wasm's public interface instead of /// auto generated one, which is not reflecting most of types in detail. #[wasm_bindgen(typescript_custom_section)] +#[cfg(feature = "api_v1")] const INTERFACE_DEFINITIONS: &'static str = r#" export function minify(src: string, opts?: JsMinifyOptions): Promise; export function minifySync(code: string, opts?: JsMinifyOptions): Output; @@ -44,11 +53,43 @@ export function transform( export function transformSync(code: string | Program, opts?: Options, experimental_plugin_bytes_resolver?: any): Output; "#; +/// The v2 Wasm API intentionally omits the standalone print functions. Keep +/// this declaration independent from v1 so future configuration and AST changes +/// cannot leak into the stable API. +#[wasm_bindgen(typescript_custom_section)] +#[cfg(feature = "api_v2")] +const INTERFACE_DEFINITIONS_V2: &'static str = r#" +export function minify(src: string, opts?: JsMinifyOptions): Promise; +export function minifySync(code: string, opts?: JsMinifyOptions): Output; + +export function parse(src: string, options: ParseOptions & { + isModule: false | "commonjs"; +}): Promise