feat(oxc): remove oxc_napi crate (#7634)

napi has a limitation, it cannot feature gate type exposure.

closes #7623
This commit is contained in:
Boshen 2024-12-04 10:07:32 +08:00 committed by GitHub
parent e824501a21
commit 771c698a81
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 724 additions and 779 deletions

View file

@ -285,11 +285,12 @@ 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'
- run: git diff --exit-code # Must commit everything
if: steps.filter.outputs.src == 'true'

45
Cargo.lock generated
View file

@ -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,7 @@ dependencies = [
"napi-build",
"napi-derive",
"oxc",
"oxc_napi",
"rustc-hash",
"serde_json",
]
@ -1947,6 +1931,7 @@ dependencies = [
"cow-utils",
"napi",
"napi-derive",
"rayon",
"rustc-hash",
"serde",
"serde_json",
@ -2031,8 +2016,8 @@ dependencies = [
"napi-build",
"napi-derive",
"oxc",
"oxc_napi",
"oxc_sourcemap",
"rustc-hash",
]
[[package]]

View file

@ -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"

View file

@ -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",

View file

@ -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 }

View file

@ -1,30 +0,0 @@
use napi_derive::napi;
use oxc_sourcemap::napi::SourceMap;
#[napi(object)]
pub struct IsolatedDeclarationsResult {
pub code: String,
pub map: Option<SourceMap>,
pub errors: Vec<String>,
}
#[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 <https://www.typescriptlang.org/tsconfig/#stripInternal>
pub strip_internal: Option<bool>,
pub sourcemap: Option<bool>,
}
impl From<IsolatedDeclarationsOptions> for oxc_isolated_declarations::IsolatedDeclarationsOptions {
fn from(options: IsolatedDeclarationsOptions) -> Self {
Self { strip_internal: options.strip_internal.unwrap_or_default() }
}
}

View file

@ -1,5 +0,0 @@
pub mod parse;
pub mod isolated_declarations;
pub mod transform;

View file

@ -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<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)
pub declaration: Option<String>,
/// Declaration source map. Only generated if both
/// {@link TypeScriptOptions#declaration declaration} and
/// {@link TransformOptions#sourcemap sourcemap} are set to `true`.
pub declaration_map: Option<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.
pub errors: Vec<String>,
}
/// 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<String>,
/// Treat the source text as `js`, `jsx`, `ts`, or `tsx`.
#[napi(ts_type = "'js' | 'jsx' | 'ts' | 'tsx'")]
pub lang: Option<String>,
/// The current working directory. Used to resolve relative paths in other
/// options.
pub cwd: Option<String>,
/// Enable source map generation.
///
/// When `true`, the `sourceMap` field of transform result objects will be populated.
///
/// @default false
///
/// @see {@link SourceMap}
pub sourcemap: Option<bool>,
/// Set assumptions in order to produce smaller output.
pub assumptions: Option<CompilerAssumptions>,
/// Configure how TypeScript is transformed.
pub typescript: Option<TypeScriptOptions>,
/// Configure how TSX and JSX are transformed.
pub jsx: Option<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)
pub target: Option<Either<String, Vec<String>>>,
/// Define Plugin
#[napi(ts_type = "Record<string, string>")]
pub define: Option<FxHashMap<String, String>>,
/// Inject Plugin
#[napi(ts_type = "Record<string, string | [string, string]>")]
pub inject: Option<FxHashMap<String, Either<String, Vec<String>>>>,
}
impl TryFrom<TransformOptions> for oxc_transformer::TransformOptions {
type Error = String;
fn try_from(options: TransformOptions) -> Result<Self, Self::Error> {
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<bool>,
pub no_document_all: Option<bool>,
pub object_rest_no_symbols: Option<bool>,
pub pure_getters: Option<bool>,
pub set_public_class_fields: Option<bool>,
}
impl From<CompilerAssumptions> 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<String>,
pub jsx_pragma_frag: Option<String>,
pub only_remove_type_imports: Option<bool>,
pub allow_namespaces: Option<bool>,
pub allow_declare_fields: Option<bool>,
/// 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<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
#[napi(ts_type = "'rewrite' | 'remove' | boolean")]
pub rewrite_import_extensions: Option<Either<bool, String>>,
}
impl From<TypeScriptOptions> 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<String>,
/// 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<bool>,
/// 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<bool>,
/// 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<bool>,
/// Replaces the import source when importing functions.
///
/// @default 'react'
pub import_source: Option<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'
pub pragma: Option<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'
pub pragma_frag: Option<String>,
/// 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<bool>,
/// 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<bool>,
/// 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<Either<bool, ReactRefreshOptions>>,
}
impl From<JsxOptions> 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<String>,
/// Specify the identifier of the refresh signature variable.
///
/// @default `$RefreshSig$`.
pub refresh_sig: Option<String>,
pub emit_full_signatures: Option<bool>,
}
impl From<ReactRefreshOptions> 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<bool>,
}
impl From<ArrowFunctionsOptions> 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<ArrowFunctionsOptions>,
}
impl From<Es2015Options> for oxc_transformer::ES2015Options {
fn from(options: Es2015Options) -> Self {
oxc_transformer::ES2015Options { arrow_function: options.arrow_function.map(Into::into) }
}
}

View file

@ -2,6 +2,7 @@
"name": "@oxc-minify/binding",
"private": true,
"scripts": {
"build-dev": "napi build --platform",
"build": "napi build --platform --release",
"test": "echo 'skip'"
},

View file

@ -21,12 +21,13 @@ test = false
doctest = false
[dependencies]
oxc = { workspace = true, features = ["napi", "serialize"] }
oxc_napi = { workspace = true }
oxc = { workspace = true, features = ["serialize"] }
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"]

View file

@ -16,18 +16,6 @@ export interface EcmaScriptModule {
staticExports: Array<StaticExport>
}
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
@ -81,40 +69,6 @@ export declare const enum ExportLocalNameKind {
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
@ -174,7 +128,19 @@ export declare function parseWithoutReturn(filename: string, sourceText: string,
export interface StaticExport {
start: number
end: number
entries: Array<ExportEntry>
entries: Array<StaticExportEntry>
}
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 {
@ -196,7 +162,41 @@ export interface StaticImport {
*
* Empty for `import "mod"`.
*/
entries: Array<ImportEntry>
entries: Array<StaticImportEntry>
}
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 {

View file

@ -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) {

View file

@ -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": {

View file

@ -1,208 +1,15 @@
use napi_derive::napi;
use rustc_hash::FxHashMap;
use oxc_span::Span;
use oxc_syntax::module_record::{self, ModuleRecord};
use oxc::{
span::Span,
syntax::module_record::{self, ModuleRecord},
};
#[napi(object)]
#[derive(Default)]
pub struct ParserOptions {
#[napi(ts_type = "'script' | 'module' | 'unambiguous' | undefined")]
pub source_type: Option<String>,
/// Treat the source text as `js`, `jsx`, `ts`, or `tsx`.
#[napi(ts_type = "'js' | 'jsx' | 'ts' | 'tsx'")]
pub lang: Option<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
pub preserve_parens: Option<bool>,
}
#[napi(object)]
#[derive(Default)]
pub struct ParseResult {
#[napi(ts_type = "import(\"@oxc-project/types\").Program")]
pub program: String,
pub module: EcmaScriptModule,
pub comments: Vec<Comment>,
pub errors: Vec<String>,
}
#[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<StaticImport>,
/// Export Statements.
pub static_exports: Vec<StaticExport>,
}
#[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<ImportEntry>,
}
#[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<String>,
pub start: Option<u32>,
pub end: Option<u32>,
}
#[napi(object)]
pub struct StaticExport {
pub start: u32,
pub end: u32,
pub entries: Vec<ExportEntry>,
}
#[napi(object)]
pub struct ExportEntry {
pub start: u32,
pub end: u32,
pub module_request: Option<ValueSpan>,
/// 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<String>,
pub start: Option<u32>,
pub end: Option<u32>,
}
#[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<String>,
pub start: Option<u32>,
pub end: Option<u32>,
}
#[napi(object)]
pub struct ExportLocalName {
pub kind: ExportLocalNameKind,
pub name: Option<String>,
pub start: Option<u32>,
pub end: Option<u32>,
}
#[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 +22,7 @@ impl From<&ModuleRecord<'_>> for EcmaScriptModule {
.import_entries
.iter()
.filter(|e| e.statement_span == m.statement_span)
.map(ImportEntry::from)
.map(StaticImportEntry::from)
.collect::<Vec<_>>();
{
StaticImport {
@ -239,10 +46,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::<Vec<_>>()
.into_iter()
.fold(FxHashMap::<Span, Vec<ExportEntry>>::default(), |mut acc, (span, e)| {
.fold(FxHashMap::<Span, Vec<StaticExportEntry>>::default(), |mut acc, (span, e)| {
acc.entry(span).or_default().push(e);
acc
})
@ -255,7 +62,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 +115,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 {

View file

@ -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,

198
napi/parser/src/types.rs Normal file
View file

@ -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<String>,
/// Treat the source text as `js`, `jsx`, `ts`, or `tsx`.
#[napi(ts_type = "'js' | 'jsx' | 'ts' | 'tsx'")]
pub lang: Option<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
pub preserve_parens: Option<bool>,
}
#[napi(object)]
pub struct ParseResult {
#[napi(ts_type = "import(\"@oxc-project/types\").Program")]
pub program: String,
pub module: EcmaScriptModule,
pub comments: Vec<Comment>,
pub errors: Vec<String>,
}
#[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<StaticImport>,
/// Export Statements.
pub static_exports: Vec<StaticExport>,
}
#[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<StaticImportEntry>,
}
#[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<String>,
pub start: Option<u32>,
pub end: Option<u32>,
}
#[napi(object)]
pub struct StaticExportEntry {
pub start: u32,
pub end: u32,
pub module_request: Option<ValueSpan>,
/// 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<StaticExportEntry>,
}
#[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<String>,
pub start: Option<u32>,
pub end: Option<u32>,
}
#[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<String>,
pub start: Option<u32>,
pub end: Option<u32>,
}
#[napi(object)]
pub struct ExportLocalName {
pub kind: ExportLocalNameKind,
pub name: Option<String>,
pub start: Option<u32>,
pub end: Option<u32>,
}
#[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,
}

View file

@ -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 }

View file

@ -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"
},

View file

@ -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<SourceMap>,
pub errors: Vec<String>,
}
#[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 <https://www.typescriptlang.org/tsconfig/#stripInternal>
pub strip_internal: Option<bool>,
pub sourcemap: Option<bool>,
}
impl From<IsolatedDeclarationsOptions> 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]

View file

@ -1,7 +1,5 @@
mod errors;
pub use oxc_napi::{isolated_declarations, transform};
mod isolated_declaration;
pub use isolated_declaration::*;

View file

@ -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<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)
pub declaration: Option<String>,
/// Declaration source map. Only generated if both
/// {@link TypeScriptOptions#declaration declaration} and
/// {@link TransformOptions#sourcemap sourcemap} are set to `true`.
pub declaration_map: Option<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.
pub errors: Vec<String>,
}
/// 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<String>,
/// Treat the source text as `js`, `jsx`, `ts`, or `tsx`.
#[napi(ts_type = "'js' | 'jsx' | 'ts' | 'tsx'")]
pub lang: Option<String>,
/// The current working directory. Used to resolve relative paths in other
/// options.
pub cwd: Option<String>,
/// Enable source map generation.
///
/// When `true`, the `sourceMap` field of transform result objects will be populated.
///
/// @default false
///
/// @see {@link SourceMap}
pub sourcemap: Option<bool>,
/// Set assumptions in order to produce smaller output.
pub assumptions: Option<CompilerAssumptions>,
/// Configure how TypeScript is transformed.
pub typescript: Option<TypeScriptOptions>,
/// Configure how TSX and JSX are transformed.
pub jsx: Option<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)
pub target: Option<Either<String, Vec<String>>>,
/// Define Plugin
#[napi(ts_type = "Record<string, string>")]
pub define: Option<FxHashMap<String, String>>,
/// Inject Plugin
#[napi(ts_type = "Record<string, string | [string, string]>")]
pub inject: Option<FxHashMap<String, Either<String, Vec<String>>>>,
}
impl TryFrom<TransformOptions> for oxc::transformer::TransformOptions {
type Error = String;
fn try_from(options: TransformOptions) -> Result<Self, Self::Error> {
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<bool>,
pub no_document_all: Option<bool>,
pub object_rest_no_symbols: Option<bool>,
pub pure_getters: Option<bool>,
pub set_public_class_fields: Option<bool>,
}
impl From<CompilerAssumptions> 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<String>,
pub jsx_pragma_frag: Option<String>,
pub only_remove_type_imports: Option<bool>,
pub allow_namespaces: Option<bool>,
pub allow_declare_fields: Option<bool>,
/// 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<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
#[napi(ts_type = "'rewrite' | 'remove' | boolean")]
pub rewrite_import_extensions: Option<Either<bool, String>>,
}
impl From<TypeScriptOptions> 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<String>,
/// 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<bool>,
/// 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<bool>,
/// 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<bool>,
/// Replaces the import source when importing functions.
///
/// @default 'react'
pub import_source: Option<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'
pub pragma: Option<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'
pub pragma_frag: Option<String>,
/// 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<bool>,
/// 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<bool>,
/// 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<Either<bool, ReactRefreshOptions>>,
}
impl From<JsxOptions> 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<String>,
/// Specify the identifier of the refresh signature variable.
///
/// @default `$RefreshSig$`.
pub refresh_sig: Option<String>,
pub emit_full_signatures: Option<bool>,
}
impl From<ReactRefreshOptions> 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<bool>,
}
impl From<ArrowFunctionsOptions> 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<ArrowFunctionsOptions>,
}
impl From<Es2015Options> 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<IsolatedDeclarationsOptions> {
fn isolated_declaration_options(
&self,
) -> Option<oxc::isolated_declarations::IsolatedDeclarationsOptions> {
self.isolated_declaration_options
}

View file

@ -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": {