From d0ce62c7928b533962ee1c71c4a655cf15820d6e Mon Sep 17 00:00:00 2001 From: Boshen Date: Wed, 4 Dec 2024 08:14:27 +0800 Subject: [PATCH] feat(oxc): remove `oxc_napi` crate napi has a limitation, it cannot feature gate type exposure. --- .github/workflows/ci.yml | 3 +- Cargo.lock | 47 +- Cargo.toml | 5 +- crates/oxc/Cargo.toml | 2 - crates/oxc_napi/Cargo.toml | 33 - crates/oxc_napi/src/isolated_declarations.rs | 30 - crates/oxc_napi/src/lib.rs | 5 - crates/oxc_napi/src/transform.rs | 385 -------- napi/minify/package.json | 1 + napi/parser/Cargo.toml | 9 +- napi/parser/bindings.js | 4 + napi/parser/index.d.ts | 230 +++++ napi/parser/index.js | 2 - napi/parser/package.json | 2 +- .../parse.rs => napi/parser/src/convert.rs | 239 +---- napi/parser/src/lib.rs | 10 +- napi/parser/src/types.rs | 198 ++++ napi/transform/Cargo.toml | 7 +- napi/transform/index.d.ts | 927 ++++++++++++++++-- napi/transform/index.js | 4 + napi/transform/package.json | 1 + napi/transform/src/isolated_declaration.rs | 31 +- napi/transform/src/lib.rs | 2 - napi/transform/src/transformer.rs | 390 +++++++- package.json | 1 + 25 files changed, 1744 insertions(+), 824 deletions(-) delete mode 100644 crates/oxc_napi/Cargo.toml delete mode 100644 crates/oxc_napi/src/isolated_declarations.rs delete mode 100644 crates/oxc_napi/src/lib.rs delete mode 100644 crates/oxc_napi/src/transform.rs rename crates/oxc_napi/src/parse.rs => napi/parser/src/convert.rs (54%) create mode 100644 napi/parser/src/types.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 33bfe186f0686..fb641f34f3fcc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -285,11 +285,10 @@ jobs: - uses: Boshen/setup-rust@main if: steps.filter.outputs.src == 'true' with: - save-cache: ${{ github.ref_name == 'main' }} cache-key: warm - uses: ./.github/actions/pnpm if: steps.filter.outputs.src == 'true' - - run: pnpm run build + - run: pnpm run build-dev if: steps.filter.outputs.src == 'true' - run: pnpm run test if: steps.filter.outputs.src == 'true' diff --git a/Cargo.lock b/Cargo.lock index b855c27de600a..620d00ac67fd4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -403,9 +403,9 @@ dependencies = [ [[package]] name = "ctor" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" +checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ "quote", "syn", @@ -1067,9 +1067,9 @@ checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" [[package]] name = "libloading" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", "windows-targets 0.52.6", @@ -1206,9 +1206,9 @@ dependencies = [ [[package]] name = "napi" -version = "3.0.0-alpha.14" +version = "3.0.0-alpha.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b27505341e98aa6126eb9f56a6ea5d8608959f19f124b9d1f1a694633641e5a" +checksum = "04aea9dbe75cd1a1abecf8a66fba1e694c12ce6e6e4e11824dc274e141a6c251" dependencies = [ "bitflags 2.6.0", "ctor", @@ -1225,9 +1225,9 @@ checksum = "e1c0f5d67ee408a4685b61f5ab7e58605c8ae3f2b4189f0127d804ff13d5560a" [[package]] name = "napi-derive" -version = "3.0.0-alpha.14" +version = "3.0.0-alpha.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c98bc3e1aef12e58d9ec48325790838a736f8428ae562716c4df1893b65be22" +checksum = "c12428d113f2b64cf827a144dddaf2df50c4d93d655d57d83745c2a281e6ec62" dependencies = [ "convert_case", "napi-derive-backend", @@ -1238,23 +1238,22 @@ dependencies = [ [[package]] name = "napi-derive-backend" -version = "2.0.0-alpha.14" +version = "2.0.0-alpha.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7fbd5478c5ac30408e4599af0b532726477e347e45fc8b20d95ab1b589057" +checksum = "7a5122d26b6f849e524f1b92107364f2b4e9a2e8d41a77b3d6c5b3af75801c60" dependencies = [ "convert_case", "proc-macro2", "quote", - "regex", "semver", "syn", ] [[package]] name = "napi-sys" -version = "2.4.0" +version = "3.0.0-alpha.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427802e8ec3a734331fec1035594a210ce1ff4dc5bc1950530920ab717964ea3" +checksum = "ab9d950ea3a522a8cb9e9237ba7cf977eeca1fadaec182163be6b0feebfc7361" dependencies = [ "libloading", ] @@ -1377,7 +1376,6 @@ checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56" name = "oxc" version = "0.38.0" dependencies = [ - "napi", "oxc_allocator", "oxc_ast", "oxc_cfg", @@ -1787,20 +1785,6 @@ dependencies = [ "rustc-hash", ] -[[package]] -name = "oxc_napi" -version = "0.38.0" -dependencies = [ - "napi", - "napi-derive", - "oxc_isolated_declarations", - "oxc_sourcemap", - "oxc_span", - "oxc_syntax", - "oxc_transformer", - "rustc-hash", -] - [[package]] name = "oxc_parser" version = "0.38.0" @@ -1832,7 +1816,9 @@ dependencies = [ "napi-build", "napi-derive", "oxc", - "oxc_napi", + "oxc_span", + "oxc_syntax", + "rustc-hash", "serde_json", ] @@ -1947,6 +1933,7 @@ dependencies = [ "cow-utils", "napi", "napi-derive", + "rayon", "rustc-hash", "serde", "serde_json", @@ -2031,8 +2018,8 @@ dependencies = [ "napi-build", "napi-derive", "oxc", - "oxc_napi", "oxc_sourcemap", + "rustc-hash", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 8789215baef53..e575a459ab5fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -91,7 +91,6 @@ oxc_estree = { version = "0.38.0", path = "crates/oxc_estree" } oxc_isolated_declarations = { version = "0.38.0", path = "crates/oxc_isolated_declarations" } oxc_mangler = { version = "0.38.0", path = "crates/oxc_mangler" } oxc_minifier = { version = "0.38.0", path = "crates/oxc_minifier" } -oxc_napi = { version = "0.38.0", path = "crates/oxc_napi" } oxc_parser = { version = "0.38.0", path = "crates/oxc_parser" } oxc_regular_expression = { version = "0.38.0", path = "crates/oxc_regular_expression" } oxc_semantic = { version = "0.38.0", path = "crates/oxc_semantic" } @@ -109,9 +108,9 @@ oxc_tasks_common = { path = "tasks/common" } oxc_tasks_transform_checker = { path = "tasks/transform_checker" } # Relaxed version so the user can decide which version to use. -napi = "3.0.0-alpha" +napi = "3.0.0-alpha.11" napi-build = "2.1.3" -napi-derive = "3.0.0-alpha" +napi-derive = "3.0.0-alpha.11" # Relaxed version so the user can decide which version to use. proc-macro2 = "1" diff --git a/crates/oxc/Cargo.toml b/crates/oxc/Cargo.toml index be2e600cb7a8a..ce6863bb5adc6 100644 --- a/crates/oxc/Cargo.toml +++ b/crates/oxc/Cargo.toml @@ -42,8 +42,6 @@ oxc_span = { workspace = true } oxc_syntax = { workspace = true } oxc_transformer = { workspace = true, optional = true } -napi = { workspace = true, optional = true, features = ["async"] } - [features] full = [ "codegen", diff --git a/crates/oxc_napi/Cargo.toml b/crates/oxc_napi/Cargo.toml deleted file mode 100644 index 43ed04239cc04..0000000000000 --- a/crates/oxc_napi/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "oxc_napi" -version = "0.38.0" -authors.workspace = true -categories.workspace = true -edition.workspace = true -homepage.workspace = true -include = ["/src"] -keywords.workspace = true -license.workspace = true -publish = false -repository.workspace = true -rust-version.workspace = true -description.workspace = true - -[lints] -workspace = true - -[lib] -test = false -doctest = false - -[dependencies] -oxc_isolated_declarations = { workspace = true } -oxc_sourcemap = { workspace = true, features = ["napi"] } -oxc_span = { workspace = true } -oxc_syntax = { workspace = true } -oxc_transformer = { workspace = true } - -napi = { workspace = true } -napi-derive = { workspace = true } - -rustc-hash = { workspace = true } diff --git a/crates/oxc_napi/src/isolated_declarations.rs b/crates/oxc_napi/src/isolated_declarations.rs deleted file mode 100644 index 11a4a0dbb0e06..0000000000000 --- a/crates/oxc_napi/src/isolated_declarations.rs +++ /dev/null @@ -1,30 +0,0 @@ -use napi_derive::napi; - -use oxc_sourcemap::napi::SourceMap; - -#[napi(object)] -pub struct IsolatedDeclarationsResult { - pub code: String, - pub map: Option, - pub errors: Vec, -} - -#[napi(object)] -#[derive(Debug, Default, Clone, Copy)] -pub struct IsolatedDeclarationsOptions { - /// Do not emit declarations for code that has an @internal annotation in its JSDoc comment. - /// This is an internal compiler option; use at your own risk, because the compiler does not check that the result is valid. - /// - /// Default: `false` - /// - /// See - pub strip_internal: Option, - - pub sourcemap: Option, -} - -impl From for oxc_isolated_declarations::IsolatedDeclarationsOptions { - fn from(options: IsolatedDeclarationsOptions) -> Self { - Self { strip_internal: options.strip_internal.unwrap_or_default() } - } -} diff --git a/crates/oxc_napi/src/lib.rs b/crates/oxc_napi/src/lib.rs deleted file mode 100644 index c341153196592..0000000000000 --- a/crates/oxc_napi/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod parse; - -pub mod isolated_declarations; - -pub mod transform; diff --git a/crates/oxc_napi/src/transform.rs b/crates/oxc_napi/src/transform.rs deleted file mode 100644 index 6586a6cd51f6e..0000000000000 --- a/crates/oxc_napi/src/transform.rs +++ /dev/null @@ -1,385 +0,0 @@ -// NOTE: Types must be aligned with [@types/babel__core](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/b5dc32740d9b45d11cff9b025896dd333c795b39/types/babel__core/index.d.ts). - -#![allow(rustdoc::bare_urls)] - -use std::path::PathBuf; - -use napi::Either; -use napi_derive::napi; -use rustc_hash::FxHashMap; - -use oxc_sourcemap::napi::SourceMap; -use oxc_transformer::{EnvOptions, JsxRuntime, RewriteExtensionsMode}; - -use super::isolated_declarations::IsolatedDeclarationsOptions; - -#[derive(Default)] -#[napi(object)] -pub struct TransformResult { - /// The transformed code. - /// - /// If parsing failed, this will be an empty string. - pub code: String, - - /// The source map for the transformed code. - /// - /// This will be set if {@link TransformOptions#sourcemap} is `true`. - pub map: Option, - - /// The `.d.ts` declaration file for the transformed code. Declarations are - /// only generated if `declaration` is set to `true` and a TypeScript file - /// is provided. - /// - /// If parsing failed and `declaration` is set, this will be an empty string. - /// - /// @see {@link TypeScriptOptions#declaration} - /// @see [declaration tsconfig option](https://www.typescriptlang.org/tsconfig/#declaration) - pub declaration: Option, - - /// Declaration source map. Only generated if both - /// {@link TypeScriptOptions#declaration declaration} and - /// {@link TransformOptions#sourcemap sourcemap} are set to `true`. - pub declaration_map: Option, - - /// Parse and transformation errors. - /// - /// Oxc's parser recovers from common syntax errors, meaning that - /// transformed code may still be available even if there are errors in this - /// list. - pub errors: Vec, -} - -/// Options for transforming a JavaScript or TypeScript file. -/// -/// @see {@link transform} -#[napi(object)] -#[derive(Default)] -pub struct TransformOptions { - #[napi(ts_type = "'script' | 'module' | 'unambiguous' | undefined")] - pub source_type: Option, - - /// Treat the source text as `js`, `jsx`, `ts`, or `tsx`. - #[napi(ts_type = "'js' | 'jsx' | 'ts' | 'tsx'")] - pub lang: Option, - - /// The current working directory. Used to resolve relative paths in other - /// options. - pub cwd: Option, - - /// Enable source map generation. - /// - /// When `true`, the `sourceMap` field of transform result objects will be populated. - /// - /// @default false - /// - /// @see {@link SourceMap} - pub sourcemap: Option, - - /// Set assumptions in order to produce smaller output. - pub assumptions: Option, - - /// Configure how TypeScript is transformed. - pub typescript: Option, - - /// Configure how TSX and JSX are transformed. - pub jsx: Option, - - /// Sets the target environment for the generated JavaScript. - /// - /// The lowest target is `es2015`. - /// - /// Example: - /// - /// * 'es2015' - /// * ['es2020', 'chrome58', 'edge16', 'firefox57', 'node12', 'safari11'] - /// - /// @default `esnext` (No transformation) - /// - /// @see [esbuild#target](https://esbuild.github.io/api/#target) - pub target: Option>>, - - /// Define Plugin - #[napi(ts_type = "Record")] - pub define: Option>, - - /// Inject Plugin - #[napi(ts_type = "Record")] - pub inject: Option>>>, -} - -impl TryFrom for oxc_transformer::TransformOptions { - type Error = String; - - fn try_from(options: TransformOptions) -> Result { - let env = match options.target { - Some(Either::A(s)) => EnvOptions::from_target(&s)?, - Some(Either::B(list)) => EnvOptions::from_target_list(&list)?, - _ => EnvOptions::default(), - }; - Ok(Self { - cwd: options.cwd.map(PathBuf::from).unwrap_or_default(), - assumptions: options.assumptions.map(Into::into).unwrap_or_default(), - typescript: options - .typescript - .map(oxc_transformer::TypeScriptOptions::from) - .unwrap_or_default(), - jsx: options.jsx.map(Into::into).unwrap_or_default(), - env, - ..Self::default() - }) - } -} - -#[napi(object)] -#[derive(Default, Debug)] -pub struct CompilerAssumptions { - pub ignore_function_length: Option, - pub no_document_all: Option, - pub object_rest_no_symbols: Option, - pub pure_getters: Option, - pub set_public_class_fields: Option, -} - -impl From for oxc_transformer::CompilerAssumptions { - fn from(value: CompilerAssumptions) -> Self { - let ops = oxc_transformer::CompilerAssumptions::default(); - Self { - ignore_function_length: value - .ignore_function_length - .unwrap_or(ops.ignore_function_length), - no_document_all: value.no_document_all.unwrap_or(ops.no_document_all), - object_rest_no_symbols: value - .object_rest_no_symbols - .unwrap_or(ops.object_rest_no_symbols), - pure_getters: value.pure_getters.unwrap_or(ops.pure_getters), - set_public_class_fields: value - .set_public_class_fields - .unwrap_or(ops.set_public_class_fields), - ..ops - } - } -} - -#[napi(object)] -#[derive(Default)] -pub struct TypeScriptOptions { - pub jsx_pragma: Option, - pub jsx_pragma_frag: Option, - pub only_remove_type_imports: Option, - pub allow_namespaces: Option, - pub allow_declare_fields: Option, - /// Also generate a `.d.ts` declaration file for TypeScript files. - /// - /// The source file must be compliant with all - /// [`isolatedDeclarations`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-5.html#isolated-declarations) - /// requirements. - /// - /// @default false - pub declaration: Option, - /// Rewrite or remove TypeScript import/export declaration extensions. - /// - /// - When set to `rewrite`, it will change `.ts`, `.mts`, `.cts` extensions to `.js`, `.mjs`, `.cjs` respectively. - /// - When set to `remove`, it will remove `.ts`/`.mts`/`.cts`/`.tsx` extension entirely. - /// - When set to `true`, it's equivalent to `rewrite`. - /// - When set to `false` or omitted, no changes will be made to the extensions. - /// - /// @default false - #[napi(ts_type = "'rewrite' | 'remove' | boolean")] - pub rewrite_import_extensions: Option>, -} - -impl From for oxc_transformer::TypeScriptOptions { - fn from(options: TypeScriptOptions) -> Self { - let ops = oxc_transformer::TypeScriptOptions::default(); - oxc_transformer::TypeScriptOptions { - jsx_pragma: options.jsx_pragma.map(Into::into).unwrap_or(ops.jsx_pragma), - jsx_pragma_frag: options.jsx_pragma_frag.map(Into::into).unwrap_or(ops.jsx_pragma_frag), - only_remove_type_imports: options - .only_remove_type_imports - .unwrap_or(ops.only_remove_type_imports), - allow_namespaces: options.allow_namespaces.unwrap_or(ops.allow_namespaces), - allow_declare_fields: options.allow_declare_fields.unwrap_or(ops.allow_declare_fields), - optimize_const_enums: false, - rewrite_import_extensions: options.rewrite_import_extensions.and_then(|value| { - match value { - Either::A(v) => { - if v { - Some(RewriteExtensionsMode::Rewrite) - } else { - None - } - } - Either::B(v) => match v.as_str() { - "rewrite" => Some(RewriteExtensionsMode::Rewrite), - "remove" => Some(RewriteExtensionsMode::Remove), - _ => None, - }, - } - }), - } - } -} - -/// Configure how TSX and JSX are transformed. -/// -/// @see {@link https://babeljs.io/docs/babel-plugin-transform-react-jsx#options} -#[napi(object)] -pub struct JsxOptions { - /// Decides which runtime to use. - /// - /// - 'automatic' - auto-import the correct JSX factories - /// - 'classic' - no auto-import - /// - /// @default 'automatic' - #[napi(ts_type = "'classic' | 'automatic'")] - pub runtime: Option, - - /// Emit development-specific information, such as `__source` and `__self`. - /// - /// @default false - /// - /// @see {@link https://babeljs.io/docs/babel-plugin-transform-react-jsx-development} - pub development: Option, - - /// Toggles whether or not to throw an error if an XML namespaced tag name - /// is used. - /// - /// Though the JSX spec allows this, it is disabled by default since React's - /// JSX does not currently have support for it. - /// - /// @default true - pub throw_if_namespace: Option, - - /// Enables `@babel/plugin-transform-react-pure-annotations`. - /// - /// It will mark top-level React method calls as pure for tree shaking. - /// - /// @see {@link https://babeljs.io/docs/en/babel-plugin-transform-react-pure-annotations} - /// - /// @default true - pub pure: Option, - - /// Replaces the import source when importing functions. - /// - /// @default 'react' - pub import_source: Option, - - /// Replace the function used when compiling JSX expressions. It should be a - /// qualified name (e.g. `React.createElement`) or an identifier (e.g. - /// `createElement`). - /// - /// Only used for `classic` {@link runtime}. - /// - /// @default 'React.createElement' - pub pragma: Option, - - /// Replace the component used when compiling JSX fragments. It should be a - /// valid JSX tag name. - /// - /// Only used for `classic` {@link runtime}. - /// - /// @default 'React.Fragment' - pub pragma_frag: Option, - - /// When spreading props, use `Object.assign` directly instead of an extend helper. - /// - /// Only used for `classic` {@link runtime}. - /// - /// @default false - pub use_built_ins: Option, - - /// When spreading props, use inline object with spread elements directly - /// instead of an extend helper or Object.assign. - /// - /// Only used for `classic` {@link runtime}. - /// - /// @default false - pub use_spread: Option, - - /// Enable React Fast Refresh . - /// - /// Conforms to the implementation in {@link https://github.com/facebook/react/tree/v18.3.1/packages/react-refresh} - /// - /// @default false - pub refresh: Option>, -} - -impl From for oxc_transformer::JsxOptions { - fn from(options: JsxOptions) -> Self { - let ops = oxc_transformer::JsxOptions::default(); - oxc_transformer::JsxOptions { - runtime: match options.runtime.as_deref() { - Some("classic") => JsxRuntime::Classic, - /* "automatic" */ _ => JsxRuntime::Automatic, - }, - development: options.development.unwrap_or(ops.development), - throw_if_namespace: options.throw_if_namespace.unwrap_or(ops.throw_if_namespace), - pure: options.pure.unwrap_or(ops.pure), - import_source: options.import_source, - pragma: options.pragma, - pragma_frag: options.pragma_frag, - use_built_ins: options.use_built_ins, - use_spread: options.use_spread, - refresh: options.refresh.and_then(|value| match value { - Either::A(b) => b.then(oxc_transformer::ReactRefreshOptions::default), - Either::B(options) => Some(oxc_transformer::ReactRefreshOptions::from(options)), - }), - ..Default::default() - } - } -} - -#[napi(object)] -pub struct ReactRefreshOptions { - /// Specify the identifier of the refresh registration variable. - /// - /// @default `$RefreshReg$`. - pub refresh_reg: Option, - - /// Specify the identifier of the refresh signature variable. - /// - /// @default `$RefreshSig$`. - pub refresh_sig: Option, - - pub emit_full_signatures: Option, -} - -impl From for oxc_transformer::ReactRefreshOptions { - fn from(options: ReactRefreshOptions) -> Self { - let ops = oxc_transformer::ReactRefreshOptions::default(); - oxc_transformer::ReactRefreshOptions { - refresh_reg: options.refresh_reg.unwrap_or(ops.refresh_reg), - refresh_sig: options.refresh_sig.unwrap_or(ops.refresh_sig), - emit_full_signatures: options.emit_full_signatures.unwrap_or(ops.emit_full_signatures), - } - } -} - -#[napi(object)] -pub struct ArrowFunctionsOptions { - /// This option enables the following: - /// * Wrap the generated function in .bind(this) and keeps uses of this inside the function as-is, instead of using a renamed this. - /// * Add a runtime check to ensure the functions are not instantiated. - /// * Add names to arrow functions. - /// - /// @default false - pub spec: Option, -} - -impl From for oxc_transformer::ArrowFunctionsOptions { - fn from(options: ArrowFunctionsOptions) -> Self { - oxc_transformer::ArrowFunctionsOptions { spec: options.spec.unwrap_or_default() } - } -} - -#[napi(object)] -pub struct Es2015Options { - /// Transform arrow functions into function expressions. - pub arrow_function: Option, -} - -impl From for oxc_transformer::ES2015Options { - fn from(options: Es2015Options) -> Self { - oxc_transformer::ES2015Options { arrow_function: options.arrow_function.map(Into::into) } - } -} diff --git a/napi/minify/package.json b/napi/minify/package.json index 72cb6768a3c0e..3693e036d8b4d 100644 --- a/napi/minify/package.json +++ b/napi/minify/package.json @@ -2,6 +2,7 @@ "name": "@oxc-minify/binding", "private": true, "scripts": { + "build-dev": "napi build --platform", "build": "napi build --platform --release", "test": "echo 'skip'" }, diff --git a/napi/parser/Cargo.toml b/napi/parser/Cargo.toml index 1a26303e4892f..d327e1acf6456 100644 --- a/napi/parser/Cargo.toml +++ b/napi/parser/Cargo.toml @@ -21,12 +21,15 @@ test = false doctest = false [dependencies] -oxc = { workspace = true, features = ["napi", "serialize"] } -oxc_napi = { workspace = true } +oxc = { workspace = true, features = ["serialize"] } +oxc_span = { workspace = true } +oxc_syntax = { workspace = true } + +rustc-hash = { workspace = true } +serde_json = { workspace = true } napi = { workspace = true, features = ["async"] } napi-derive = { workspace = true } -serde_json = { workspace = true } [package.metadata.cargo-shear] ignored = ["napi"] diff --git a/napi/parser/bindings.js b/napi/parser/bindings.js index e2a65710d14fc..9f5e323636e98 100644 --- a/napi/parser/bindings.js +++ b/napi/parser/bindings.js @@ -362,8 +362,12 @@ if (!nativeBinding) { } module.exports.ExportExportNameKind = nativeBinding.ExportExportNameKind +module.exports.ExportExportNameKind = nativeBinding.ExportExportNameKind +module.exports.ExportImportNameKind = nativeBinding.ExportImportNameKind module.exports.ExportImportNameKind = nativeBinding.ExportImportNameKind module.exports.ExportLocalNameKind = nativeBinding.ExportLocalNameKind +module.exports.ExportLocalNameKind = nativeBinding.ExportLocalNameKind +module.exports.ImportNameKind = nativeBinding.ImportNameKind module.exports.ImportNameKind = nativeBinding.ImportNameKind module.exports.parseAsync = nativeBinding.parseAsync module.exports.parseSync = nativeBinding.parseSync diff --git a/napi/parser/index.d.ts b/napi/parser/index.d.ts index ab2164ce2c59e..40a1df1e945fb 100644 --- a/napi/parser/index.d.ts +++ b/napi/parser/index.d.ts @@ -9,6 +9,27 @@ export interface Comment { end: number } +export interface Comment { + type: 'Line' | 'Block' + value: string + start: number + end: number +} + +export interface Comment { + type: 'Line' | 'Block' + value: string + start: number + end: number +} + +export interface EcmaScriptModule { + /** Import Statements. */ + staticImports: Array + /** Export Statements. */ + staticExports: Array +} + export interface EcmaScriptModule { /** Import Statements. */ staticImports: Array @@ -35,6 +56,22 @@ export interface ExportExportName { end?: number } +export interface ExportExportName { + kind: ExportExportNameKind + name?: string + start?: number + end?: number +} + +export declare const enum ExportExportNameKind { + /** `export { name } */ + Name = 'Name', + /** `export default expression` */ + Default = 'Default', + /** `export * from "mod" */ + None = 'None' +} + export declare const enum ExportExportNameKind { /** `export { name } */ Name = 'Name', @@ -51,6 +88,13 @@ export interface ExportImportName { end?: number } +export interface ExportImportName { + kind: ExportImportNameKind + name?: string + start?: number + end?: number +} + export declare const enum ExportImportNameKind { /** `export { name } */ Name = 'Name', @@ -62,6 +106,24 @@ export declare const enum ExportImportNameKind { None = 'None' } +export declare const enum ExportImportNameKind { + /** `export { name } */ + Name = 'Name', + /** `export * as ns from "mod"` */ + All = 'All', + /** `export * from "mod"` */ + AllButDefault = 'AllButDefault', + /** Does not have a specifier. */ + None = 'None' +} + +export interface ExportLocalName { + kind: ExportLocalNameKind + name?: string + start?: number + end?: number +} + export interface ExportLocalName { kind: ExportLocalNameKind name?: string @@ -81,6 +143,18 @@ export declare const enum ExportLocalNameKind { None = 'None' } +export declare const enum ExportLocalNameKind { + /** `export { name } */ + Name = 'Name', + /** `export default expression` */ + Default = 'Default', + /** + * If the exported value is not locally accessible from within the module. + * `export default function () {}` + */ + None = 'None' +} + export interface ImportEntry { /** * The name under which the desired binding is exported by the module. @@ -122,6 +196,22 @@ export interface ImportName { end?: number } +export interface ImportName { + kind: ImportNameKind + name?: string + start?: number + end?: number +} + +export declare const enum ImportNameKind { + /** `import { x } from "mod"` */ + Name = 'Name', + /** `import * as ns from "mod"` */ + NamespaceObject = 'NamespaceObject', + /** `import defaultExport from "mod"` */ + Default = 'Default' +} + export declare const enum ImportNameKind { /** `import { x } from "mod"` */ Name = 'Name', @@ -138,6 +228,12 @@ export declare const enum ImportNameKind { */ export declare function parseAsync(filename: string, sourceText: string, options?: ParserOptions | undefined | null): Promise +export interface ParseResult { + program: import("@oxc-project/types").Program + comments: Array + errors: Array +} + export interface ParseResult { program: import("@oxc-project/types").Program module: EcmaScriptModule @@ -145,6 +241,49 @@ export interface ParseResult { errors: Array } +export interface ParseResult { + program: import("@oxc-project/types").Program + module: EcmaScriptModule + comments: Array + errors: Array +} + +/** + * Babel Parser Options + * + * + */ +export interface ParserOptions { + sourceType?: 'script' | 'module' | 'unambiguous' | undefined + sourceFilename?: string + /** + * Emit `ParenthesizedExpression` in AST. + * + * If this option is true, parenthesized expressions are represented by + * (non-standard) `ParenthesizedExpression` nodes that have a single `expression` property + * containing the expression inside parentheses. + * + * Default: true + */ + preserveParens?: boolean +} + +export interface ParserOptions { + sourceType?: 'script' | 'module' | 'unambiguous' | undefined + /** Treat the source text as `js`, `jsx`, `ts`, or `tsx`. */ + lang?: 'js' | 'jsx' | 'ts' | 'tsx' + /** + * Emit `ParenthesizedExpression` in AST. + * + * If this option is true, parenthesized expressions are represented by + * (non-standard) `ParenthesizedExpression` nodes that have a single `expression` property + * containing the expression inside parentheses. + * + * Default: true + */ + preserveParens?: boolean +} + export interface ParserOptions { sourceType?: 'script' | 'module' | 'unambiguous' | undefined /** Treat the source text as `js`, `jsx`, `ts`, or `tsx`. */ @@ -171,12 +310,41 @@ export declare function parseSync(filename: string, sourceText: string, options? */ export declare function parseWithoutReturn(filename: string, sourceText: string, options?: ParserOptions | undefined | null): void +export interface SourceMap { + file?: string + mappings: string + names: Array + sourceRoot?: string + sources: Array + sourcesContent?: Array + version: number + x_google_ignoreList?: Array +} + export interface StaticExport { start: number end: number entries: Array } +export interface StaticExport { + start: number + end: number + entries: Array +} + +export interface StaticExportEntry { + start: number + end: number + moduleRequest?: ValueSpan + /** The name under which the desired binding is exported by the module`. */ + importName: ExportImportName + /** The name used to export this binding by this module. */ + exportName: ExportExportName + /** The name that is used to locally access the exported value from within the importing module. */ + localName: ExportLocalName +} + export interface StaticImport { /** Start of import statement. */ start: number @@ -199,6 +367,68 @@ export interface StaticImport { entries: Array } +export interface StaticImport { + /** Start of import statement. */ + start: number + /** End of import statement. */ + end: number + /** + * Import source. + * + * ```js + * import { foo } from "mod"; + * // ^^^ + * ``` + */ + moduleRequest: ValueSpan + /** + * Import specifiers. + * + * Empty for `import "mod"`. + */ + entries: Array +} + +export interface StaticImportEntry { + /** + * The name under which the desired binding is exported by the module. + * + * ```js + * import { foo } from "mod"; + * // ^^^ + * import { foo as bar } from "mod"; + * // ^^^ + * ``` + */ + importName: ImportName + /** + * The name that is used to locally access the imported value from within the importing module. + * ```js + * import { foo } from "mod"; + * // ^^^ + * import { foo as bar } from "mod"; + * // ^^^ + * ``` + */ + localName: ValueSpan + /** + * Whether this binding is for a TypeScript type-only import. + * + * `true` for the following imports: + * ```ts + * import type { foo } from "mod"; + * import { type foo } from "mod"; + * ``` + */ + isType: boolean +} + +export interface ValueSpan { + value: string + start: number + end: number +} + export interface ValueSpan { value: string start: number diff --git a/napi/parser/index.js b/napi/parser/index.js index fcedfd9cf3a0f..ff5b26bdbda4d 100644 --- a/napi/parser/index.js +++ b/napi/parser/index.js @@ -1,7 +1,5 @@ const bindings = require('./bindings.js'); -module.exports.moduleLexerAsync = bindings.moduleLexerAsync; -module.exports.moduleLexerSync = bindings.moduleLexerSync; module.exports.parseWithoutReturn = bindings.parseWithoutReturn; module.exports.parseAsync = async function parseAsync(...args) { diff --git a/napi/parser/package.json b/napi/parser/package.json index 571cfc061572f..085ff9202d595 100644 --- a/napi/parser/package.json +++ b/napi/parser/package.json @@ -2,8 +2,8 @@ "name": "@oxc-parser/binding", "private": true, "scripts": { - "build": "napi build --platform --release --js bindings.js", "build-dev": "napi build --platform --js bindings.js", + "build": "napi build --platform --js bindings.js --release", "test": "vitest --typecheck run ./test" }, "napi": { diff --git a/crates/oxc_napi/src/parse.rs b/napi/parser/src/convert.rs similarity index 54% rename from crates/oxc_napi/src/parse.rs rename to napi/parser/src/convert.rs index 9fc35d8c3f07b..334c0b0bb4f26 100644 --- a/crates/oxc_napi/src/parse.rs +++ b/napi/parser/src/convert.rs @@ -1,208 +1,13 @@ -use napi_derive::napi; - use rustc_hash::FxHashMap; use oxc_span::Span; use oxc_syntax::module_record::{self, ModuleRecord}; -#[napi(object)] -#[derive(Default)] -pub struct ParserOptions { - #[napi(ts_type = "'script' | 'module' | 'unambiguous' | undefined")] - pub source_type: Option, - - /// Treat the source text as `js`, `jsx`, `ts`, or `tsx`. - #[napi(ts_type = "'js' | 'jsx' | 'ts' | 'tsx'")] - pub lang: Option, - - /// Emit `ParenthesizedExpression` in AST. - /// - /// If this option is true, parenthesized expressions are represented by - /// (non-standard) `ParenthesizedExpression` nodes that have a single `expression` property - /// containing the expression inside parentheses. - /// - /// Default: true - pub preserve_parens: Option, -} - -#[napi(object)] -#[derive(Default)] -pub struct ParseResult { - #[napi(ts_type = "import(\"@oxc-project/types\").Program")] - pub program: String, - pub module: EcmaScriptModule, - pub comments: Vec, - pub errors: Vec, -} - -#[napi(object)] -pub struct Comment { - #[napi(ts_type = "'Line' | 'Block'")] - pub r#type: &'static str, - pub value: String, - pub start: u32, - pub end: u32, -} - -#[napi(object)] -#[derive(Default)] -pub struct EcmaScriptModule { - /// Import Statements. - pub static_imports: Vec, - /// Export Statements. - pub static_exports: Vec, -} - -#[napi(object)] -pub struct ValueSpan { - pub value: String, - pub start: u32, - pub end: u32, -} - -#[napi(object)] -pub struct StaticImport { - /// Start of import statement. - pub start: u32, - /// End of import statement. - pub end: u32, - /// Import source. - /// - /// ```js - /// import { foo } from "mod"; - /// // ^^^ - /// ``` - pub module_request: ValueSpan, - /// Import specifiers. - /// - /// Empty for `import "mod"`. - pub entries: Vec, -} - -#[napi(object)] -pub struct ImportEntry { - /// The name under which the desired binding is exported by the module. - /// - /// ```js - /// import { foo } from "mod"; - /// // ^^^ - /// import { foo as bar } from "mod"; - /// // ^^^ - /// ``` - pub import_name: ImportName, - /// The name that is used to locally access the imported value from within the importing module. - /// ```js - /// import { foo } from "mod"; - /// // ^^^ - /// import { foo as bar } from "mod"; - /// // ^^^ - /// ``` - pub local_name: ValueSpan, - - /// Whether this binding is for a TypeScript type-only import. - /// - /// `true` for the following imports: - /// ```ts - /// import type { foo } from "mod"; - /// import { type foo } from "mod"; - /// ``` - pub is_type: bool, -} - -#[napi(string_enum)] -pub enum ImportNameKind { - /// `import { x } from "mod"` - Name, - /// `import * as ns from "mod"` - NamespaceObject, - /// `import defaultExport from "mod"` - Default, -} - -#[napi(object)] -pub struct ImportName { - pub kind: ImportNameKind, - pub name: Option, - pub start: Option, - pub end: Option, -} - -#[napi(object)] -pub struct StaticExport { - pub start: u32, - pub end: u32, - pub entries: Vec, -} - -#[napi(object)] -pub struct ExportEntry { - pub start: u32, - pub end: u32, - pub module_request: Option, - /// The name under which the desired binding is exported by the module`. - pub import_name: ExportImportName, - /// The name used to export this binding by this module. - pub export_name: ExportExportName, - /// The name that is used to locally access the exported value from within the importing module. - pub local_name: ExportLocalName, -} - -#[napi(string_enum)] -pub enum ExportImportNameKind { - /// `export { name } - Name, - /// `export * as ns from "mod"` - All, - /// `export * from "mod"` - AllButDefault, - /// Does not have a specifier. - None, -} - -#[napi(object)] -pub struct ExportImportName { - pub kind: ExportImportNameKind, - pub name: Option, - pub start: Option, - pub end: Option, -} - -#[napi(string_enum)] -pub enum ExportExportNameKind { - /// `export { name } - Name, - /// `export default expression` - Default, - /// `export * from "mod" - None, -} - -#[napi(object)] -pub struct ExportExportName { - pub kind: ExportExportNameKind, - pub name: Option, - pub start: Option, - pub end: Option, -} - -#[napi(object)] -pub struct ExportLocalName { - pub kind: ExportLocalNameKind, - pub name: Option, - pub start: Option, - pub end: Option, -} - -#[napi(string_enum)] -pub enum ExportLocalNameKind { - /// `export { name } - Name, - /// `export default expression` - Default, - /// If the exported value is not locally accessible from within the module. - /// `export default function () {}` - None, -} +use crate::types::{ + EcmaScriptModule, ExportExportName, ExportExportNameKind, ExportImportName, + ExportImportNameKind, ExportLocalName, ExportLocalNameKind, ImportName, ImportNameKind, + StaticExport, StaticExportEntry, StaticImport, StaticImportEntry, ValueSpan, +}; impl From<&ModuleRecord<'_>> for EcmaScriptModule { fn from(record: &ModuleRecord<'_>) -> Self { @@ -215,7 +20,7 @@ impl From<&ModuleRecord<'_>> for EcmaScriptModule { .import_entries .iter() .filter(|e| e.statement_span == m.statement_span) - .map(ImportEntry::from) + .map(StaticImportEntry::from) .collect::>(); { StaticImport { @@ -239,10 +44,10 @@ impl From<&ModuleRecord<'_>> for EcmaScriptModule { .iter() .chain(record.indirect_export_entries.iter()) .chain(record.star_export_entries.iter()) - .map(|e| (e.statement_span, ExportEntry::from(e))) + .map(|e| (e.statement_span, StaticExportEntry::from(e))) .collect::>() .into_iter() - .fold(FxHashMap::>::default(), |mut acc, (span, e)| { + .fold(FxHashMap::>::default(), |mut acc, (span, e)| { acc.entry(span).or_default().push(e); acc }) @@ -255,7 +60,20 @@ impl From<&ModuleRecord<'_>> for EcmaScriptModule { } } -impl From<&module_record::ImportEntry<'_>> for ImportEntry { +impl From<&module_record::ExportEntry<'_>> for StaticExportEntry { + fn from(e: &module_record::ExportEntry) -> Self { + Self { + start: e.span.start, + end: e.span.end, + module_request: e.module_request.as_ref().map(ValueSpan::from), + import_name: ExportImportName::from(&e.import_name), + export_name: ExportExportName::from(&e.export_name), + local_name: ExportLocalName::from(&e.local_name), + } + } +} + +impl From<&module_record::ImportEntry<'_>> for StaticImportEntry { fn from(e: &module_record::ImportEntry<'_>) -> Self { Self { import_name: ImportName::from(&e.import_name), @@ -295,19 +113,6 @@ impl From<&module_record::NameSpan<'_>> for ValueSpan { } } -impl From<&module_record::ExportEntry<'_>> for ExportEntry { - fn from(e: &module_record::ExportEntry) -> Self { - Self { - start: e.span.start, - end: e.span.end, - module_request: e.module_request.as_ref().map(ValueSpan::from), - import_name: ExportImportName::from(&e.import_name), - export_name: ExportExportName::from(&e.export_name), - local_name: ExportLocalName::from(&e.local_name), - } - } -} - impl From<&module_record::ExportImportName<'_>> for ExportImportName { fn from(e: &module_record::ExportImportName<'_>) -> Self { let (kind, name, start, end) = match e { diff --git a/napi/parser/src/lib.rs b/napi/parser/src/lib.rs index cdf78570f30a9..459a9929720b1 100644 --- a/napi/parser/src/lib.rs +++ b/napi/parser/src/lib.rs @@ -2,6 +2,9 @@ clippy::needless_pass_by_value // Napi value need to be passed as value )] +mod convert; +mod types; + use std::sync::Arc; use napi::{bindgen_prelude::AsyncTask, Task}; @@ -14,7 +17,8 @@ use oxc::{ parser::{ParseOptions, Parser, ParserReturn}, span::SourceType, }; -use oxc_napi::parse::{Comment, EcmaScriptModule, ParseResult, ParserOptions}; + +pub use crate::types::{Comment, EcmaScriptModule, ParseResult, ParserOptions}; fn get_source_type(filename: &str, options: &ParserOptions) -> SourceType { match options.lang.as_deref() { @@ -83,8 +87,8 @@ fn parse_with_return(filename: &str, source_text: &str, options: &ParserOptions) .iter() .map(|comment| Comment { r#type: match comment.kind { - CommentKind::Line => "Line", - CommentKind::Block => "Block", + CommentKind::Line => String::from("Line"), + CommentKind::Block => String::from("Block"), }, value: comment.content_span().source_text(source_text).to_string(), start: comment.span.start, diff --git a/napi/parser/src/types.rs b/napi/parser/src/types.rs new file mode 100644 index 0000000000000..81cb0018404ba --- /dev/null +++ b/napi/parser/src/types.rs @@ -0,0 +1,198 @@ +use napi_derive::napi; + +#[napi(object)] +#[derive(Default)] +pub struct ParserOptions { + #[napi(ts_type = "'script' | 'module' | 'unambiguous' | undefined")] + pub source_type: Option, + + /// Treat the source text as `js`, `jsx`, `ts`, or `tsx`. + #[napi(ts_type = "'js' | 'jsx' | 'ts' | 'tsx'")] + pub lang: Option, + + /// Emit `ParenthesizedExpression` in AST. + /// + /// If this option is true, parenthesized expressions are represented by + /// (non-standard) `ParenthesizedExpression` nodes that have a single `expression` property + /// containing the expression inside parentheses. + /// + /// Default: true + pub preserve_parens: Option, +} + +#[napi(object)] +pub struct ParseResult { + #[napi(ts_type = "import(\"@oxc-project/types\").Program")] + pub program: String, + pub module: EcmaScriptModule, + pub comments: Vec, + pub errors: Vec, +} + +#[napi(object)] +pub struct Comment { + #[napi(ts_type = "'Line' | 'Block'")] + pub r#type: String, + pub value: String, + pub start: u32, + pub end: u32, +} + +#[napi(object)] +pub struct EcmaScriptModule { + /// Import Statements. + pub static_imports: Vec, + /// Export Statements. + pub static_exports: Vec, +} + +#[napi(object)] +pub struct ValueSpan { + pub value: String, + pub start: u32, + pub end: u32, +} + +#[napi(object)] +pub struct StaticImport { + /// Start of import statement. + pub start: u32, + /// End of import statement. + pub end: u32, + /// Import source. + /// + /// ```js + /// import { foo } from "mod"; + /// // ^^^ + /// ``` + pub module_request: ValueSpan, + /// Import specifiers. + /// + /// Empty for `import "mod"`. + pub entries: Vec, +} + +#[napi(object)] +pub struct StaticImportEntry { + /// The name under which the desired binding is exported by the module. + /// + /// ```js + /// import { foo } from "mod"; + /// // ^^^ + /// import { foo as bar } from "mod"; + /// // ^^^ + /// ``` + pub import_name: ImportName, + /// The name that is used to locally access the imported value from within the importing module. + /// ```js + /// import { foo } from "mod"; + /// // ^^^ + /// import { foo as bar } from "mod"; + /// // ^^^ + /// ``` + pub local_name: ValueSpan, + + /// Whether this binding is for a TypeScript type-only import. + /// + /// `true` for the following imports: + /// ```ts + /// import type { foo } from "mod"; + /// import { type foo } from "mod"; + /// ``` + pub is_type: bool, +} + +#[napi(string_enum)] +pub enum ImportNameKind { + /// `import { x } from "mod"` + Name, + /// `import * as ns from "mod"` + NamespaceObject, + /// `import defaultExport from "mod"` + Default, +} + +#[napi(object)] +pub struct ImportName { + pub kind: ImportNameKind, + pub name: Option, + pub start: Option, + pub end: Option, +} + +#[napi(object)] +pub struct StaticExportEntry { + pub start: u32, + pub end: u32, + pub module_request: Option, + /// The name under which the desired binding is exported by the module`. + pub import_name: ExportImportName, + /// The name used to export this binding by this module. + pub export_name: ExportExportName, + /// The name that is used to locally access the exported value from within the importing module. + pub local_name: ExportLocalName, +} + +#[napi(object)] +pub struct StaticExport { + pub start: u32, + pub end: u32, + pub entries: Vec, +} + +#[napi(string_enum)] +pub enum ExportImportNameKind { + /// `export { name } + Name, + /// `export * as ns from "mod"` + All, + /// `export * from "mod"` + AllButDefault, + /// Does not have a specifier. + None, +} + +#[napi(object)] +pub struct ExportImportName { + pub kind: ExportImportNameKind, + pub name: Option, + pub start: Option, + pub end: Option, +} + +#[napi(string_enum)] +pub enum ExportExportNameKind { + /// `export { name } + Name, + /// `export default expression` + Default, + /// `export * from "mod" + None, +} + +#[napi(object)] +pub struct ExportExportName { + pub kind: ExportExportNameKind, + pub name: Option, + pub start: Option, + pub end: Option, +} + +#[napi(object)] +pub struct ExportLocalName { + pub kind: ExportLocalNameKind, + pub name: Option, + pub start: Option, + pub end: Option, +} + +#[napi(string_enum)] +pub enum ExportLocalNameKind { + /// `export { name } + Name, + /// `export default expression` + Default, + /// If the exported value is not locally accessible from within the module. + /// `export default function () {}` + None, +} diff --git a/napi/transform/Cargo.toml b/napi/transform/Cargo.toml index a525b17d61e4c..f3793b80477b7 100644 --- a/napi/transform/Cargo.toml +++ b/napi/transform/Cargo.toml @@ -21,9 +21,10 @@ test = false doctest = false [dependencies] -oxc = { workspace = true, features = ["full", "napi"] } -oxc_napi = { workspace = true } -oxc_sourcemap = { workspace = true, features = ["napi"] } +oxc = { workspace = true, features = ["full"] } +oxc_sourcemap = { workspace = true, features = ["napi", "rayon"] } + +rustc-hash = { workspace = true } napi = { workspace = true } napi-derive = { workspace = true } diff --git a/napi/transform/index.d.ts b/napi/transform/index.d.ts index 155b71a0a1d13..bb1cc618a1990 100644 --- a/napi/transform/index.d.ts +++ b/napi/transform/index.d.ts @@ -12,6 +12,45 @@ export interface ArrowFunctionsOptions { spec?: boolean } +export interface ArrowFunctionsOptions { + /** + * This option enables the following: + * * Wrap the generated function in .bind(this) and keeps uses of this inside the function as-is, instead of using a renamed this. + * * Add a runtime check to ensure the functions are not instantiated. + * * Add names to arrow functions. + * + * @default false + */ + spec?: boolean +} + +export interface ArrowFunctionsOptions { + /** + * This option enables the following: + * * Wrap the generated function in .bind(this) and keeps uses of this inside the function as-is, instead of using a renamed this. + * * Add a runtime check to ensure the functions are not instantiated. + * * Add names to arrow functions. + * + * @default false + */ + spec?: boolean +} + +export interface Comment { + type: 'Line' | 'Block' + value: string + start: number + end: number +} + +export interface CompilerAssumptions { + ignoreFunctionLength?: boolean + noDocumentAll?: boolean + objectRestNoSymbols?: boolean + pureGetters?: boolean + setPublicClassFields?: boolean +} + export interface CompilerAssumptions { ignoreFunctionLength?: boolean noDocumentAll?: boolean @@ -20,11 +59,143 @@ export interface CompilerAssumptions { setPublicClassFields?: boolean } +export interface EcmaScriptModule { + /** Import Statements. */ + staticImports: Array + /** Export Statements. */ + staticExports: Array +} + +export interface Es2015Options { + /** Transform arrow functions into function expressions. */ + arrowFunction?: ArrowFunctionsOptions +} + +export interface Es2015Options { + /** Transform arrow functions into function expressions. */ + arrowFunction?: ArrowFunctionsOptions +} + export interface Es2015Options { /** Transform arrow functions into function expressions. */ arrowFunction?: ArrowFunctionsOptions } +export interface ExportEntry { + start: number + end: number + moduleRequest?: ValueSpan + /** The name under which the desired binding is exported by the module`. */ + importName: ExportImportName + /** The name used to export this binding by this module. */ + exportName: ExportExportName + /** The name that is used to locally access the exported value from within the importing module. */ + localName: ExportLocalName +} + +export interface ExportExportName { + kind: ExportExportNameKind + name?: string + start?: number + end?: number +} + +export declare const enum ExportExportNameKind { + /** `export { name } */ + Name = 'Name', + /** `export default expression` */ + Default = 'Default', + /** `export * from "mod" */ + None = 'None' +} + +export interface ExportImportName { + kind: ExportImportNameKind + name?: string + start?: number + end?: number +} + +export declare const enum ExportImportNameKind { + /** `export { name } */ + Name = 'Name', + /** `export * as ns from "mod"` */ + All = 'All', + /** `export * from "mod"` */ + AllButDefault = 'AllButDefault', + /** Does not have a specifier. */ + None = 'None' +} + +export interface ExportLocalName { + kind: ExportLocalNameKind + name?: string + start?: number + end?: number +} + +export declare const enum ExportLocalNameKind { + /** `export { name } */ + Name = 'Name', + /** `export default expression` */ + Default = 'Default', + /** + * If the exported value is not locally accessible from within the module. + * `export default function () {}` + */ + None = 'None' +} + +export interface ImportEntry { + /** + * The name under which the desired binding is exported by the module. + * + * ```js + * import { foo } from "mod"; + * // ^^^ + * import { foo as bar } from "mod"; + * // ^^^ + * ``` + */ + importName: ImportName + /** + * The name that is used to locally access the imported value from within the importing module. + * ```js + * import { foo } from "mod"; + * // ^^^ + * import { foo as bar } from "mod"; + * // ^^^ + * ``` + */ + localName: ValueSpan + /** + * Whether this binding is for a TypeScript type-only import. + * + * `true` for the following imports: + * ```ts + * import type { foo } from "mod"; + * import { type foo } from "mod"; + * ``` + */ + isType: boolean +} + +export interface ImportName { + kind: ImportNameKind + name?: string + start?: number + end?: number +} + +export declare const enum ImportNameKind { + /** `import { x } from "mod"` */ + Name = 'Name', + /** `import * as ns from "mod"` */ + NamespaceObject = 'NamespaceObject', + /** `import defaultExport from "mod"` */ + Default = 'Default' +} + /** TypeScript Isolated Declarations for Standalone DTS Emit */ export declare function isolatedDeclaration(filename: string, sourceText: string, options?: IsolatedDeclarationsOptions | undefined | null): IsolatedDeclarationsResult @@ -41,6 +212,44 @@ export interface IsolatedDeclarationsOptions { sourcemap?: boolean } +export interface IsolatedDeclarationsOptions { + /** + * Do not emit declarations for code that has an @internal annotation in its JSDoc comment. + * This is an internal compiler option; use at your own risk, because the compiler does not check that the result is valid. + * + * Default: `false` + * + * See + */ + stripInternal?: boolean + sourcemap?: boolean +} + +export interface IsolatedDeclarationsOptions { + /** + * Do not emit declarations for code that has an @internal annotation in its JSDoc comment. + * This is an internal compiler option; use at your own risk, because the compiler does not check that the result is valid. + * + * Default: `false` + * + * See + */ + stripInternal?: boolean + sourcemap?: boolean +} + +export interface IsolatedDeclarationsResult { + code: string + map?: SourceMap + errors: Array +} + +export interface IsolatedDeclarationsResult { + code: string + map?: SourceMap + errors: Array +} + export interface IsolatedDeclarationsResult { code: string map?: SourceMap @@ -142,131 +351,595 @@ export interface JsxOptions { refresh?: boolean | ReactRefreshOptions } -export interface ReactRefreshOptions { - /** - * Specify the identifier of the refresh registration variable. - * - * @default `$RefreshReg$`. - */ - refreshReg?: string - /** - * Specify the identifier of the refresh signature variable. - * - * @default `$RefreshSig$`. - */ - refreshSig?: string - emitFullSignatures?: boolean -} - -export interface SourceMap { - file?: string - mappings: string - names: Array - sourceRoot?: string - sources: Array - sourcesContent?: Array - version: number - x_google_ignoreList?: Array -} - -/** - * Transpile a JavaScript or TypeScript into a target ECMAScript version. - * - * @param filename The name of the file being transformed. If this is a - * relative path, consider setting the {@link TransformOptions#cwd} option.. - * @param sourceText the source code itself - * @param options The options for the transformation. See {@link - * TransformOptions} for more information. - * - * @returns an object containing the transformed code, source maps, and any - * errors that occurred during parsing or transformation. - */ -export declare function transform(filename: string, sourceText: string, options?: TransformOptions | undefined | null): TransformResult - /** - * Options for transforming a JavaScript or TypeScript file. + * Configure how TSX and JSX are transformed. * - * @see {@link transform} + * @see {@link https://babeljs.io/docs/babel-plugin-transform-react-jsx#options} */ -export interface TransformOptions { - sourceType?: 'script' | 'module' | 'unambiguous' | undefined - /** Treat the source text as `js`, `jsx`, `ts`, or `tsx`. */ - lang?: 'js' | 'jsx' | 'ts' | 'tsx' +export interface JsxOptions { /** - * The current working directory. Used to resolve relative paths in other - * options. + * Decides which runtime to use. + * + * - 'automatic' - auto-import the correct JSX factories + * - 'classic' - no auto-import + * + * @default 'automatic' */ - cwd?: string + runtime?: 'classic' | 'automatic' /** - * Enable source map generation. - * - * When `true`, the `sourceMap` field of transform result objects will be populated. + * Emit development-specific information, such as `__source` and `__self`. * * @default false * - * @see {@link SourceMap} + * @see {@link https://babeljs.io/docs/babel-plugin-transform-react-jsx-development} */ - sourcemap?: boolean - /** Set assumptions in order to produce smaller output. */ - assumptions?: CompilerAssumptions - /** Configure how TypeScript is transformed. */ - typescript?: TypeScriptOptions - /** Configure how TSX and JSX are transformed. */ - jsx?: JsxOptions + development?: boolean /** - * Sets the target environment for the generated JavaScript. + * Toggles whether or not to throw an error if an XML namespaced tag name + * is used. * - * The lowest target is `es2015`. + * Though the JSX spec allows this, it is disabled by default since React's + * JSX does not currently have support for it. * - * Example: + * @default true + */ + throwIfNamespace?: boolean + /** + * Enables `@babel/plugin-transform-react-pure-annotations`. * - * * 'es2015' - * * ['es2020', 'chrome58', 'edge16', 'firefox57', 'node12', 'safari11'] + * It will mark top-level React method calls as pure for tree shaking. * - * @default `esnext` (No transformation) + * @see {@link https://babeljs.io/docs/en/babel-plugin-transform-react-pure-annotations} * - * @see [esbuild#target](https://esbuild.github.io/api/#target) + * @default true */ - target?: string | Array - /** Define Plugin */ - define?: Record - /** Inject Plugin */ - inject?: Record -} - -export interface TransformResult { + pure?: boolean /** - * The transformed code. + * Replaces the import source when importing functions. * - * If parsing failed, this will be an empty string. + * @default 'react' */ - code: string + importSource?: string /** - * The source map for the transformed code. + * Replace the function used when compiling JSX expressions. It should be a + * qualified name (e.g. `React.createElement`) or an identifier (e.g. + * `createElement`). * - * This will be set if {@link TransformOptions#sourcemap} is `true`. + * Only used for `classic` {@link runtime}. + * + * @default 'React.createElement' */ - map?: SourceMap + pragma?: string /** - * The `.d.ts` declaration file for the transformed code. Declarations are - * only generated if `declaration` is set to `true` and a TypeScript file - * is provided. + * Replace the component used when compiling JSX fragments. It should be a + * valid JSX tag name. * - * If parsing failed and `declaration` is set, this will be an empty string. + * Only used for `classic` {@link runtime}. * - * @see {@link TypeScriptOptions#declaration} - * @see [declaration tsconfig option](https://www.typescriptlang.org/tsconfig/#declaration) + * @default 'React.Fragment' */ - declaration?: string + pragmaFrag?: string /** - * Declaration source map. Only generated if both - * {@link TypeScriptOptions#declaration declaration} and - * {@link TransformOptions#sourcemap sourcemap} are set to `true`. + * When spreading props, use `Object.assign` directly instead of an extend helper. + * + * Only used for `classic` {@link runtime}. + * + * @default false */ - declarationMap?: SourceMap + useBuiltIns?: boolean /** - * Parse and transformation errors. - * + * When spreading props, use inline object with spread elements directly + * instead of an extend helper or Object.assign. + * + * Only used for `classic` {@link runtime}. + * + * @default false + */ + useSpread?: boolean + /** + * Enable React Fast Refresh . + * + * Conforms to the implementation in {@link https://github.com/facebook/react/tree/v18.3.1/packages/react-refresh} + * + * @default false + */ + refresh?: boolean | ReactRefreshOptions +} + +/** + * Configure how TSX and JSX are transformed. + * + * @see {@link https://babeljs.io/docs/babel-plugin-transform-react-jsx#options} + */ +export interface JsxOptions { + /** + * Decides which runtime to use. + * + * - 'automatic' - auto-import the correct JSX factories + * - 'classic' - no auto-import + * + * @default 'automatic' + */ + runtime?: 'classic' | 'automatic' + /** + * Emit development-specific information, such as `__source` and `__self`. + * + * @default false + * + * @see {@link https://babeljs.io/docs/babel-plugin-transform-react-jsx-development} + */ + development?: boolean + /** + * Toggles whether or not to throw an error if an XML namespaced tag name + * is used. + * + * Though the JSX spec allows this, it is disabled by default since React's + * JSX does not currently have support for it. + * + * @default true + */ + throwIfNamespace?: boolean + /** + * Enables `@babel/plugin-transform-react-pure-annotations`. + * + * It will mark top-level React method calls as pure for tree shaking. + * + * @see {@link https://babeljs.io/docs/en/babel-plugin-transform-react-pure-annotations} + * + * @default true + */ + pure?: boolean + /** + * Replaces the import source when importing functions. + * + * @default 'react' + */ + importSource?: string + /** + * Replace the function used when compiling JSX expressions. It should be a + * qualified name (e.g. `React.createElement`) or an identifier (e.g. + * `createElement`). + * + * Only used for `classic` {@link runtime}. + * + * @default 'React.createElement' + */ + pragma?: string + /** + * Replace the component used when compiling JSX fragments. It should be a + * valid JSX tag name. + * + * Only used for `classic` {@link runtime}. + * + * @default 'React.Fragment' + */ + pragmaFrag?: string + /** + * When spreading props, use `Object.assign` directly instead of an extend helper. + * + * Only used for `classic` {@link runtime}. + * + * @default false + */ + useBuiltIns?: boolean + /** + * When spreading props, use inline object with spread elements directly + * instead of an extend helper or Object.assign. + * + * Only used for `classic` {@link runtime}. + * + * @default false + */ + useSpread?: boolean + /** + * Enable React Fast Refresh . + * + * Conforms to the implementation in {@link https://github.com/facebook/react/tree/v18.3.1/packages/react-refresh} + * + * @default false + */ + refresh?: boolean | ReactRefreshOptions +} + +export interface ParseResult { + program: import("@oxc-project/types").Program + module: EcmaScriptModule + comments: Array + errors: Array +} + +export interface ParserOptions { + sourceType?: 'script' | 'module' | 'unambiguous' | undefined + /** Treat the source text as `js`, `jsx`, `ts`, or `tsx`. */ + lang?: 'js' | 'jsx' | 'ts' | 'tsx' + /** + * Emit `ParenthesizedExpression` in AST. + * + * If this option is true, parenthesized expressions are represented by + * (non-standard) `ParenthesizedExpression` nodes that have a single `expression` property + * containing the expression inside parentheses. + * + * Default: true + */ + preserveParens?: boolean +} + +export interface ReactRefreshOptions { + /** + * Specify the identifier of the refresh registration variable. + * + * @default `$RefreshReg$`. + */ + refreshReg?: string + /** + * Specify the identifier of the refresh signature variable. + * + * @default `$RefreshSig$`. + */ + refreshSig?: string + emitFullSignatures?: boolean +} + +export interface ReactRefreshOptions { + /** + * Specify the identifier of the refresh registration variable. + * + * @default `$RefreshReg$`. + */ + refreshReg?: string + /** + * Specify the identifier of the refresh signature variable. + * + * @default `$RefreshSig$`. + */ + refreshSig?: string + emitFullSignatures?: boolean +} + +export interface ReactRefreshOptions { + /** + * Specify the identifier of the refresh registration variable. + * + * @default `$RefreshReg$`. + */ + refreshReg?: string + /** + * Specify the identifier of the refresh signature variable. + * + * @default `$RefreshSig$`. + */ + refreshSig?: string + emitFullSignatures?: boolean +} + +export interface SourceMap { + file?: string + mappings: string + names: Array + sourceRoot?: string + sources: Array + sourcesContent?: Array + version: number + x_google_ignoreList?: Array +} + +export interface SourceMap { + file?: string + mappings: string + names: Array + sourceRoot?: string + sources: Array + sourcesContent?: Array + version: number + x_google_ignoreList?: Array +} + +export interface StaticExport { + start: number + end: number + entries: Array +} + +export interface StaticImport { + /** Start of import statement. */ + start: number + /** End of import statement. */ + end: number + /** + * Import source. + * + * ```js + * import { foo } from "mod"; + * // ^^^ + * ``` + */ + moduleRequest: ValueSpan + /** + * Import specifiers. + * + * Empty for `import "mod"`. + */ + entries: Array +} + +/** + * Transpile a JavaScript or TypeScript into a target ECMAScript version. + * + * @param filename The name of the file being transformed. If this is a + * relative path, consider setting the {@link TransformOptions#cwd} option.. + * @param sourceText the source code itself + * @param options The options for the transformation. See {@link + * TransformOptions} for more information. + * + * @returns an object containing the transformed code, source maps, and any + * errors that occurred during parsing or transformation. + */ +export declare function transform(filename: string, sourceText: string, options?: TransformOptions | undefined | null): TransformResult + +/** + * Options for transforming a JavaScript or TypeScript file. + * + * @see {@link transform} + */ +export interface TransformOptions { + sourceType?: 'script' | 'module' | 'unambiguous' | undefined + /** Treat the source text as `js`, `jsx`, `ts`, or `tsx`. */ + lang?: 'js' | 'jsx' | 'ts' | 'tsx' + /** + * The current working directory. Used to resolve relative paths in other + * options. + */ + cwd?: string + /** + * Enable source map generation. + * + * When `true`, the `sourceMap` field of transform result objects will be populated. + * + * @default false + * + * @see {@link SourceMap} + */ + sourcemap?: boolean + /** Configure how TypeScript is transformed. */ + typescript?: TypeScriptOptions + /** Configure how TSX and JSX are transformed. */ + jsx?: JsxOptions + /** + * Sets the target environment for the generated JavaScript. + * + * The lowest target is `es2015`. + * + * Example: + * + * * 'es2015' + * * ['es2020', 'chrome58', 'edge16', 'firefox57', 'node12', 'safari11'] + * + * @default `esnext` (No transformation) + * + * @see [esbuild#target](https://esbuild.github.io/api/#target) + */ + target?: string | Array + /** Define Plugin */ + define?: Record + /** Inject Plugin */ + inject?: Record +} + +/** + * Options for transforming a JavaScript or TypeScript file. + * + * @see {@link transform} + */ +export interface TransformOptions { + sourceType?: 'script' | 'module' | 'unambiguous' | undefined + /** Treat the source text as `js`, `jsx`, `ts`, or `tsx`. */ + lang?: 'js' | 'jsx' | 'ts' | 'tsx' + /** + * The current working directory. Used to resolve relative paths in other + * options. + */ + cwd?: string + /** + * Enable source map generation. + * + * When `true`, the `sourceMap` field of transform result objects will be populated. + * + * @default false + * + * @see {@link SourceMap} + */ + sourcemap?: boolean + /** Set assumptions in order to produce smaller output. */ + assumptions?: CompilerAssumptions + /** Configure how TypeScript is transformed. */ + typescript?: TypeScriptOptions + /** Configure how TSX and JSX are transformed. */ + jsx?: JsxOptions + /** + * Sets the target environment for the generated JavaScript. + * + * The lowest target is `es2015`. + * + * Example: + * + * * 'es2015' + * * ['es2020', 'chrome58', 'edge16', 'firefox57', 'node12', 'safari11'] + * + * @default `esnext` (No transformation) + * + * @see [esbuild#target](https://esbuild.github.io/api/#target) + */ + target?: string | Array + /** Define Plugin */ + define?: Record + /** Inject Plugin */ + inject?: Record +} + +/** + * Options for transforming a JavaScript or TypeScript file. + * + * @see {@link transform} + */ +export interface TransformOptions { + sourceType?: 'script' | 'module' | 'unambiguous' | undefined + /** Treat the source text as `js`, `jsx`, `ts`, or `tsx`. */ + lang?: 'js' | 'jsx' | 'ts' | 'tsx' + /** + * The current working directory. Used to resolve relative paths in other + * options. + */ + cwd?: string + /** + * Enable source map generation. + * + * When `true`, the `sourceMap` field of transform result objects will be populated. + * + * @default false + * + * @see {@link SourceMap} + */ + sourcemap?: boolean + /** Set assumptions in order to produce smaller output. */ + assumptions?: CompilerAssumptions + /** Configure how TypeScript is transformed. */ + typescript?: TypeScriptOptions + /** Configure how TSX and JSX are transformed. */ + jsx?: JsxOptions + /** + * Sets the target environment for the generated JavaScript. + * + * The lowest target is `es2015`. + * + * Example: + * + * * 'es2015' + * * ['es2020', 'chrome58', 'edge16', 'firefox57', 'node12', 'safari11'] + * + * @default `esnext` (No transformation) + * + * @see [esbuild#target](https://esbuild.github.io/api/#target) + */ + target?: string | Array + /** Define Plugin */ + define?: Record + /** Inject Plugin */ + inject?: Record +} + +export interface TransformResult { + /** + * The transformed code. + * + * If parsing failed, this will be an empty string. + */ + code: string + /** + * The source map for the transformed code. + * + * This will be set if {@link TransformOptions#sourcemap} is `true`. + */ + map?: SourceMap + /** + * The `.d.ts` declaration file for the transformed code. Declarations are + * only generated if `declaration` is set to `true` and a TypeScript file + * is provided. + * + * If parsing failed and `declaration` is set, this will be an empty string. + * + * @see {@link TypeScriptOptions#declaration} + * @see [declaration tsconfig option](https://www.typescriptlang.org/tsconfig/#declaration) + */ + declaration?: string + /** + * Declaration source map. Only generated if both + * {@link TypeScriptOptions#declaration declaration} and + * {@link TransformOptions#sourcemap sourcemap} are set to `true`. + */ + declarationMap?: SourceMap + /** + * Parse and transformation errors. + * + * Oxc's parser recovers from common syntax errors, meaning that + * transformed code may still be available even if there are errors in this + * list. + */ + errors: Array +} + +export interface TransformResult { + /** + * The transformed code. + * + * If parsing failed, this will be an empty string. + */ + code: string + /** + * The source map for the transformed code. + * + * This will be set if {@link TransformOptions#sourcemap} is `true`. + */ + map?: SourceMap + /** + * The `.d.ts` declaration file for the transformed code. Declarations are + * only generated if `declaration` is set to `true` and a TypeScript file + * is provided. + * + * If parsing failed and `declaration` is set, this will be an empty string. + * + * @see {@link TypeScriptOptions#declaration} + * @see [declaration tsconfig option](https://www.typescriptlang.org/tsconfig/#declaration) + */ + declaration?: string + /** + * Declaration source map. Only generated if both + * {@link TypeScriptOptions#declaration declaration} and + * {@link TransformOptions#sourcemap sourcemap} are set to `true`. + */ + declarationMap?: SourceMap + /** + * Parse and transformation errors. + * + * Oxc's parser recovers from common syntax errors, meaning that + * transformed code may still be available even if there are errors in this + * list. + */ + errors: Array +} + +export interface TransformResult { + /** + * The transformed code. + * + * If parsing failed, this will be an empty string. + */ + code: string + /** + * The source map for the transformed code. + * + * This will be set if {@link TransformOptions#sourcemap} is `true`. + */ + map?: SourceMap + /** + * The `.d.ts` declaration file for the transformed code. Declarations are + * only generated if `declaration` is set to `true` and a TypeScript file + * is provided. + * + * If parsing failed and `declaration` is set, this will be an empty string. + * + * @see {@link TypeScriptOptions#declaration} + * @see [declaration tsconfig option](https://www.typescriptlang.org/tsconfig/#declaration) + */ + declaration?: string + /** + * Declaration source map. Only generated if both + * {@link TypeScriptOptions#declaration declaration} and + * {@link TransformOptions#sourcemap sourcemap} are set to `true`. + */ + declarationMap?: SourceMap + /** + * Parse and transformation errors. + * * Oxc's parser recovers from common syntax errors, meaning that * transformed code may still be available even if there are errors in this * list. @@ -303,3 +976,67 @@ export interface TypeScriptOptions { rewriteImportExtensions?: 'rewrite' | 'remove' | boolean } +export interface TypeScriptOptions { + jsxPragma?: string + jsxPragmaFrag?: string + onlyRemoveTypeImports?: boolean + allowNamespaces?: boolean + allowDeclareFields?: boolean + /** + * Also generate a `.d.ts` declaration file for TypeScript files. + * + * The source file must be compliant with all + * [`isolatedDeclarations`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-5.html#isolated-declarations) + * requirements. + * + * @default false + */ + declaration?: IsolatedDeclarationsOptions + /** + * Rewrite or remove TypeScript import/export declaration extensions. + * + * - When set to `rewrite`, it will change `.ts`, `.mts`, `.cts` extensions to `.js`, `.mjs`, `.cjs` respectively. + * - When set to `remove`, it will remove `.ts`/`.mts`/`.cts`/`.tsx` extension entirely. + * - When set to `true`, it's equivalent to `rewrite`. + * - When set to `false` or omitted, no changes will be made to the extensions. + * + * @default false + */ + rewriteImportExtensions?: 'rewrite' | 'remove' | boolean +} + +export interface TypeScriptOptions { + jsxPragma?: string + jsxPragmaFrag?: string + onlyRemoveTypeImports?: boolean + allowNamespaces?: boolean + allowDeclareFields?: boolean + /** + * Also generate a `.d.ts` declaration file for TypeScript files. + * + * The source file must be compliant with all + * [`isolatedDeclarations`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-5.html#isolated-declarations) + * requirements. + * + * @default false + */ + declaration?: IsolatedDeclarationsOptions + /** + * Rewrite or remove TypeScript import/export declaration extensions. + * + * - When set to `rewrite`, it will change `.ts`, `.mts`, `.cts` extensions to `.js`, `.mjs`, `.cjs` respectively. + * - When set to `remove`, it will remove `.ts`/`.mts`/`.cts`/`.tsx` extension entirely. + * - When set to `true`, it's equivalent to `rewrite`. + * - When set to `false` or omitted, no changes will be made to the extensions. + * + * @default false + */ + rewriteImportExtensions?: 'rewrite' | 'remove' | boolean +} + +export interface ValueSpan { + value: string + start: number + end: number +} + diff --git a/napi/transform/index.js b/napi/transform/index.js index e1092b640ae6c..e72b4b135afe9 100644 --- a/napi/transform/index.js +++ b/napi/transform/index.js @@ -361,5 +361,9 @@ if (!nativeBinding) { throw new Error(`Failed to load native binding`) } +module.exports.ExportExportNameKind = nativeBinding.ExportExportNameKind +module.exports.ExportImportNameKind = nativeBinding.ExportImportNameKind +module.exports.ExportLocalNameKind = nativeBinding.ExportLocalNameKind +module.exports.ImportNameKind = nativeBinding.ImportNameKind module.exports.isolatedDeclaration = nativeBinding.isolatedDeclaration module.exports.transform = nativeBinding.transform diff --git a/napi/transform/package.json b/napi/transform/package.json index c7efd9a2953c1..08b414f6a5417 100644 --- a/napi/transform/package.json +++ b/napi/transform/package.json @@ -2,6 +2,7 @@ "name": "@oxc-transform/binding", "private": true, "scripts": { + "build-dev": "napi build --platform", "build": "napi build --platform --release", "test": "vitest --typecheck run ./test" }, diff --git a/napi/transform/src/isolated_declaration.rs b/napi/transform/src/isolated_declaration.rs index babd87f31703e..49f33cd8b2437 100644 --- a/napi/transform/src/isolated_declaration.rs +++ b/napi/transform/src/isolated_declaration.rs @@ -9,11 +9,38 @@ use oxc::{ parser::Parser, span::SourceType, }; -use oxc_napi::isolated_declarations::{IsolatedDeclarationsOptions, IsolatedDeclarationsResult}; -use oxc_sourcemap::napi::SourceMap; use crate::errors::wrap_diagnostics; +use oxc_sourcemap::napi::SourceMap; + +#[napi(object)] +pub struct IsolatedDeclarationsResult { + pub code: String, + pub map: Option, + pub errors: Vec, +} + +#[napi(object)] +#[derive(Debug, Default, Clone, Copy)] +pub struct IsolatedDeclarationsOptions { + /// Do not emit declarations for code that has an @internal annotation in its JSDoc comment. + /// This is an internal compiler option; use at your own risk, because the compiler does not check that the result is valid. + /// + /// Default: `false` + /// + /// See + pub strip_internal: Option, + + pub sourcemap: Option, +} + +impl From for oxc::isolated_declarations::IsolatedDeclarationsOptions { + fn from(options: IsolatedDeclarationsOptions) -> Self { + Self { strip_internal: options.strip_internal.unwrap_or_default() } + } +} + /// TypeScript Isolated Declarations for Standalone DTS Emit #[allow(clippy::needless_pass_by_value)] #[napi] diff --git a/napi/transform/src/lib.rs b/napi/transform/src/lib.rs index a4568decfefcd..eff0ebcfd8893 100644 --- a/napi/transform/src/lib.rs +++ b/napi/transform/src/lib.rs @@ -1,7 +1,5 @@ mod errors; -pub use oxc_napi::{isolated_declarations, transform}; - mod isolated_declaration; pub use isolated_declaration::*; diff --git a/napi/transform/src/transformer.rs b/napi/transform/src/transformer.rs index ce5e5854f3566..30af9f9753783 100644 --- a/napi/transform/src/transformer.rs +++ b/napi/transform/src/transformer.rs @@ -1,20 +1,396 @@ -use std::path::Path; +// NOTE: Types must be aligned with [@types/babel__core](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/b5dc32740d9b45d11cff9b025896dd333c795b39/types/babel__core/index.d.ts). +#![allow(rustdoc::bare_urls)] + +use std::path::{Path, PathBuf}; use napi::Either; use napi_derive::napi; +use rustc_hash::FxHashMap; use oxc::{ codegen::CodegenReturn, diagnostics::OxcDiagnostic, - isolated_declarations::IsolatedDeclarationsOptions, span::SourceType, - transformer::{InjectGlobalVariablesConfig, InjectImport, ReplaceGlobalDefinesConfig}, + transformer::{ + EnvOptions, InjectGlobalVariablesConfig, InjectImport, JsxRuntime, + ReplaceGlobalDefinesConfig, RewriteExtensionsMode, + }, CompilerInterface, }; -use oxc_napi::transform::{TransformOptions, TransformResult}; use oxc_sourcemap::napi::SourceMap; -use crate::errors::wrap_diagnostics; +use crate::{errors::wrap_diagnostics, IsolatedDeclarationsOptions}; + +#[derive(Default)] +#[napi(object)] +pub struct TransformResult { + /// The transformed code. + /// + /// If parsing failed, this will be an empty string. + pub code: String, + + /// The source map for the transformed code. + /// + /// This will be set if {@link TransformOptions#sourcemap} is `true`. + pub map: Option, + + /// The `.d.ts` declaration file for the transformed code. Declarations are + /// only generated if `declaration` is set to `true` and a TypeScript file + /// is provided. + /// + /// If parsing failed and `declaration` is set, this will be an empty string. + /// + /// @see {@link TypeScriptOptions#declaration} + /// @see [declaration tsconfig option](https://www.typescriptlang.org/tsconfig/#declaration) + pub declaration: Option, + + /// Declaration source map. Only generated if both + /// {@link TypeScriptOptions#declaration declaration} and + /// {@link TransformOptions#sourcemap sourcemap} are set to `true`. + pub declaration_map: Option, + + /// Parse and transformation errors. + /// + /// Oxc's parser recovers from common syntax errors, meaning that + /// transformed code may still be available even if there are errors in this + /// list. + pub errors: Vec, +} + +/// Options for transforming a JavaScript or TypeScript file. +/// +/// @see {@link transform} +#[napi(object)] +#[derive(Default)] +pub struct TransformOptions { + #[napi(ts_type = "'script' | 'module' | 'unambiguous' | undefined")] + pub source_type: Option, + + /// Treat the source text as `js`, `jsx`, `ts`, or `tsx`. + #[napi(ts_type = "'js' | 'jsx' | 'ts' | 'tsx'")] + pub lang: Option, + + /// The current working directory. Used to resolve relative paths in other + /// options. + pub cwd: Option, + + /// Enable source map generation. + /// + /// When `true`, the `sourceMap` field of transform result objects will be populated. + /// + /// @default false + /// + /// @see {@link SourceMap} + pub sourcemap: Option, + + /// Set assumptions in order to produce smaller output. + pub assumptions: Option, + + /// Configure how TypeScript is transformed. + pub typescript: Option, + + /// Configure how TSX and JSX are transformed. + pub jsx: Option, + + /// Sets the target environment for the generated JavaScript. + /// + /// The lowest target is `es2015`. + /// + /// Example: + /// + /// * 'es2015' + /// * ['es2020', 'chrome58', 'edge16', 'firefox57', 'node12', 'safari11'] + /// + /// @default `esnext` (No transformation) + /// + /// @see [esbuild#target](https://esbuild.github.io/api/#target) + pub target: Option>>, + + /// Define Plugin + #[napi(ts_type = "Record")] + pub define: Option>, + + /// Inject Plugin + #[napi(ts_type = "Record")] + pub inject: Option>>>, +} + +impl TryFrom for oxc::transformer::TransformOptions { + type Error = String; + + fn try_from(options: TransformOptions) -> Result { + let env = match options.target { + Some(Either::A(s)) => EnvOptions::from_target(&s)?, + Some(Either::B(list)) => EnvOptions::from_target_list(&list)?, + _ => EnvOptions::default(), + }; + Ok(Self { + cwd: options.cwd.map(PathBuf::from).unwrap_or_default(), + assumptions: options.assumptions.map(Into::into).unwrap_or_default(), + typescript: options + .typescript + .map(oxc::transformer::TypeScriptOptions::from) + .unwrap_or_default(), + jsx: options.jsx.map(Into::into).unwrap_or_default(), + env, + ..Self::default() + }) + } +} + +#[napi(object)] +#[derive(Default, Debug)] +pub struct CompilerAssumptions { + pub ignore_function_length: Option, + pub no_document_all: Option, + pub object_rest_no_symbols: Option, + pub pure_getters: Option, + pub set_public_class_fields: Option, +} + +impl From for oxc::transformer::CompilerAssumptions { + fn from(value: CompilerAssumptions) -> Self { + let ops = oxc::transformer::CompilerAssumptions::default(); + Self { + ignore_function_length: value + .ignore_function_length + .unwrap_or(ops.ignore_function_length), + no_document_all: value.no_document_all.unwrap_or(ops.no_document_all), + object_rest_no_symbols: value + .object_rest_no_symbols + .unwrap_or(ops.object_rest_no_symbols), + pure_getters: value.pure_getters.unwrap_or(ops.pure_getters), + set_public_class_fields: value + .set_public_class_fields + .unwrap_or(ops.set_public_class_fields), + ..ops + } + } +} + +#[napi(object)] +#[derive(Default)] +pub struct TypeScriptOptions { + pub jsx_pragma: Option, + pub jsx_pragma_frag: Option, + pub only_remove_type_imports: Option, + pub allow_namespaces: Option, + pub allow_declare_fields: Option, + /// Also generate a `.d.ts` declaration file for TypeScript files. + /// + /// The source file must be compliant with all + /// [`isolatedDeclarations`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-5.html#isolated-declarations) + /// requirements. + /// + /// @default false + pub declaration: Option, + /// Rewrite or remove TypeScript import/export declaration extensions. + /// + /// - When set to `rewrite`, it will change `.ts`, `.mts`, `.cts` extensions to `.js`, `.mjs`, `.cjs` respectively. + /// - When set to `remove`, it will remove `.ts`/`.mts`/`.cts`/`.tsx` extension entirely. + /// - When set to `true`, it's equivalent to `rewrite`. + /// - When set to `false` or omitted, no changes will be made to the extensions. + /// + /// @default false + #[napi(ts_type = "'rewrite' | 'remove' | boolean")] + pub rewrite_import_extensions: Option>, +} + +impl From for oxc::transformer::TypeScriptOptions { + fn from(options: TypeScriptOptions) -> Self { + let ops = oxc::transformer::TypeScriptOptions::default(); + oxc::transformer::TypeScriptOptions { + jsx_pragma: options.jsx_pragma.map(Into::into).unwrap_or(ops.jsx_pragma), + jsx_pragma_frag: options.jsx_pragma_frag.map(Into::into).unwrap_or(ops.jsx_pragma_frag), + only_remove_type_imports: options + .only_remove_type_imports + .unwrap_or(ops.only_remove_type_imports), + allow_namespaces: options.allow_namespaces.unwrap_or(ops.allow_namespaces), + allow_declare_fields: options.allow_declare_fields.unwrap_or(ops.allow_declare_fields), + optimize_const_enums: false, + rewrite_import_extensions: options.rewrite_import_extensions.and_then(|value| { + match value { + Either::A(v) => { + if v { + Some(RewriteExtensionsMode::Rewrite) + } else { + None + } + } + Either::B(v) => match v.as_str() { + "rewrite" => Some(RewriteExtensionsMode::Rewrite), + "remove" => Some(RewriteExtensionsMode::Remove), + _ => None, + }, + } + }), + } + } +} + +/// Configure how TSX and JSX are transformed. +/// +/// @see {@link https://babeljs.io/docs/babel-plugin-transform-react-jsx#options} +#[napi(object)] +pub struct JsxOptions { + /// Decides which runtime to use. + /// + /// - 'automatic' - auto-import the correct JSX factories + /// - 'classic' - no auto-import + /// + /// @default 'automatic' + #[napi(ts_type = "'classic' | 'automatic'")] + pub runtime: Option, + + /// Emit development-specific information, such as `__source` and `__self`. + /// + /// @default false + /// + /// @see {@link https://babeljs.io/docs/babel-plugin-transform-react-jsx-development} + pub development: Option, + + /// Toggles whether or not to throw an error if an XML namespaced tag name + /// is used. + /// + /// Though the JSX spec allows this, it is disabled by default since React's + /// JSX does not currently have support for it. + /// + /// @default true + pub throw_if_namespace: Option, + + /// Enables `@babel/plugin-transform-react-pure-annotations`. + /// + /// It will mark top-level React method calls as pure for tree shaking. + /// + /// @see {@link https://babeljs.io/docs/en/babel-plugin-transform-react-pure-annotations} + /// + /// @default true + pub pure: Option, + + /// Replaces the import source when importing functions. + /// + /// @default 'react' + pub import_source: Option, + + /// Replace the function used when compiling JSX expressions. It should be a + /// qualified name (e.g. `React.createElement`) or an identifier (e.g. + /// `createElement`). + /// + /// Only used for `classic` {@link runtime}. + /// + /// @default 'React.createElement' + pub pragma: Option, + + /// Replace the component used when compiling JSX fragments. It should be a + /// valid JSX tag name. + /// + /// Only used for `classic` {@link runtime}. + /// + /// @default 'React.Fragment' + pub pragma_frag: Option, + + /// When spreading props, use `Object.assign` directly instead of an extend helper. + /// + /// Only used for `classic` {@link runtime}. + /// + /// @default false + pub use_built_ins: Option, + + /// When spreading props, use inline object with spread elements directly + /// instead of an extend helper or Object.assign. + /// + /// Only used for `classic` {@link runtime}. + /// + /// @default false + pub use_spread: Option, + + /// Enable React Fast Refresh . + /// + /// Conforms to the implementation in {@link https://github.com/facebook/react/tree/v18.3.1/packages/react-refresh} + /// + /// @default false + pub refresh: Option>, +} + +impl From for oxc::transformer::JsxOptions { + fn from(options: JsxOptions) -> Self { + let ops = oxc::transformer::JsxOptions::default(); + oxc::transformer::JsxOptions { + runtime: match options.runtime.as_deref() { + Some("classic") => JsxRuntime::Classic, + /* "automatic" */ _ => JsxRuntime::Automatic, + }, + development: options.development.unwrap_or(ops.development), + throw_if_namespace: options.throw_if_namespace.unwrap_or(ops.throw_if_namespace), + pure: options.pure.unwrap_or(ops.pure), + import_source: options.import_source, + pragma: options.pragma, + pragma_frag: options.pragma_frag, + use_built_ins: options.use_built_ins, + use_spread: options.use_spread, + refresh: options.refresh.and_then(|value| match value { + Either::A(b) => b.then(oxc::transformer::ReactRefreshOptions::default), + Either::B(options) => Some(oxc::transformer::ReactRefreshOptions::from(options)), + }), + ..Default::default() + } + } +} + +#[napi(object)] +pub struct ReactRefreshOptions { + /// Specify the identifier of the refresh registration variable. + /// + /// @default `$RefreshReg$`. + pub refresh_reg: Option, + + /// Specify the identifier of the refresh signature variable. + /// + /// @default `$RefreshSig$`. + pub refresh_sig: Option, + + pub emit_full_signatures: Option, +} + +impl From for oxc::transformer::ReactRefreshOptions { + fn from(options: ReactRefreshOptions) -> Self { + let ops = oxc::transformer::ReactRefreshOptions::default(); + oxc::transformer::ReactRefreshOptions { + refresh_reg: options.refresh_reg.unwrap_or(ops.refresh_reg), + refresh_sig: options.refresh_sig.unwrap_or(ops.refresh_sig), + emit_full_signatures: options.emit_full_signatures.unwrap_or(ops.emit_full_signatures), + } + } +} + +#[napi(object)] +pub struct ArrowFunctionsOptions { + /// This option enables the following: + /// * Wrap the generated function in .bind(this) and keeps uses of this inside the function as-is, instead of using a renamed this. + /// * Add a runtime check to ensure the functions are not instantiated. + /// * Add names to arrow functions. + /// + /// @default false + pub spec: Option, +} + +impl From for oxc::transformer::ArrowFunctionsOptions { + fn from(options: ArrowFunctionsOptions) -> Self { + oxc::transformer::ArrowFunctionsOptions { spec: options.spec.unwrap_or_default() } + } +} + +#[napi(object)] +pub struct Es2015Options { + /// Transform arrow functions into function expressions. + pub arrow_function: Option, +} + +impl From for oxc::transformer::ES2015Options { + fn from(options: Es2015Options) -> Self { + oxc::transformer::ES2015Options { arrow_function: options.arrow_function.map(Into::into) } + } +} #[derive(Default)] struct Compiler { @@ -116,7 +492,9 @@ impl CompilerInterface for Compiler { Some(&self.transform_options) } - fn isolated_declaration_options(&self) -> Option { + fn isolated_declaration_options( + &self, + ) -> Option { self.isolated_declaration_options } diff --git a/package.json b/package.json index 006deb61eeccf..2b896e0ea9d63 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ }, "scripts": { "build": "pnpm --workspace-concurrency=1 --filter './napi/*' build", + "build-dev": "pnpm --workspace-concurrency=1 --filter './napi/*' build-dev", "test": "pnpm --workspace-concurrency=1 --filter './napi/*' test" }, "devDependencies": {