Skip to content

Commit

Permalink
feat(napi/transform): add TransformOptions::target API
Browse files Browse the repository at this point in the history
  • Loading branch information
Boshen committed Nov 23, 2024
1 parent bb2c0c2 commit 5f64b35
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 24 deletions.
22 changes: 21 additions & 1 deletion crates/oxc/src/napi/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use napi::Either;
use napi_derive::napi;
use rustc_hash::FxHashMap;

use oxc_transformer::{JsxRuntime, RewriteExtensionsMode};
use oxc_transformer::{EnvOptions, JsxRuntime, RewriteExtensionsMode};

use super::{isolated_declarations::IsolatedDeclarationsOptions, source_map::SourceMap};

Expand Down Expand Up @@ -80,6 +80,20 @@ pub struct TransformOptions {
/// 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>>,
Expand All @@ -93,13 +107,19 @@ 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(),
typescript: options
.typescript
.map(oxc_transformer::TypeScriptOptions::from)
.unwrap_or_default(),
jsx: options.jsx.map(Into::into).unwrap_or_default(),
env,
..Self::default()
})
}
Expand Down
5 changes: 3 additions & 2 deletions crates/oxc_transformer/tests/integrations/es_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ fn es_target() {
("es2015", "a ** b"),
("es2016", "async function foo() {}"),
("es2017", "({ ...x })"),
("es2017", "try {} catch {}"),
("es2018", "try {} catch {}"),
("es2019", "a?.b"),
("es2019", "a ?? b"),
("es2019", "a ||= b"),
("es2020", "a ||= b"),
("es2019", "1n ** 2n"), // test target error
("es2021", "class foo { static {} }"),
];
Expand Down
14 changes: 10 additions & 4 deletions crates/oxc_transformer/tests/integrations/snapshots/es_target.snap
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,29 @@ function _foo() {
import _objectSpread from '@babel/runtime/helpers/objectSpread2';
_objectSpread({}, x);

########## 4 es2017
########## 4 es2018
try {} catch {}
----------
try {} catch (_unused) {}

########## 5 es2019
a?.b
----------
var _a;
(_a = a) === null || _a === void 0 ? void 0 : _a.b;

########## 6 es2019
a ?? b
----------
var _a;
(_a = a) !== null && _a !== void 0 ? _a : b;

########## 6 es2019
########## 7 es2020
a ||= b
----------
a || (a = b);

########## 7 es2019
########## 8 es2019
1n ** 2n
----------

Expand All @@ -65,7 +71,7 @@ a || (a = b);
: ^^
`----

########## 8 es2021
########## 9 es2021
class foo { static {} }
----------
class foo {
Expand Down
15 changes: 15 additions & 0 deletions napi/transform/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,21 @@ export interface TransformOptions {
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<string>
/** Define Plugin */
define?: Record<string, string>
/** Inject Plugin */
Expand Down
5 changes: 1 addition & 4 deletions napi/transform/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
"private": true,
"scripts": {
"build": "napi build --platform --release",
"test": "vitest run ./test"
},
"engines": {
"node": ">=14.*"
"test": "vitest --typecheck run ./test"
},
"napi": {
"binaryName": "transform",
Expand Down
2 changes: 1 addition & 1 deletion napi/transform/test/id.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { assert, describe, it } from 'vitest';

import oxc from './index';
import oxc from '../index';

describe('isolated declaration', () => {
const code = `
Expand Down
51 changes: 39 additions & 12 deletions napi/transform/test/transform.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { assert, describe, it } from 'vitest';
import { assert, describe, it, test } from 'vitest';

import oxc from './index';
import { transform } from '../index';

describe('simple', () => {
const code = 'export class A<T> {}';

it('matches output', () => {
const ret = oxc.transform('test.ts', code, { sourcemap: true });
const ret = transform('test.ts', code, { sourcemap: true });
assert.deepEqual(ret, {
code: 'export class A {}\n',
errors: [],
Expand All @@ -21,12 +21,12 @@ describe('simple', () => {
});

it('uses the `lang` option', () => {
const ret = oxc.transform('test.vue', code, { lang: 'ts' });
const ret = transform('test.vue', code, { lang: 'ts' });
assert.equal(ret.code, 'export class A {}\n');
});

it('uses the `declaration option`', () => {
const ret = oxc.transform('test.ts', code, { typescript: { declaration: true } });
const ret = transform('test.ts', code, { typescript: { declaration: {} } });
assert.equal(ret.declaration, 'export declare class A<T> {}\n');
});
});
Expand All @@ -44,21 +44,48 @@ describe('transform', () => {
'class foo {\n\tstatic {}\n}',
];
for (const code of cases) {
const ret = oxc.transform('test.ts', code);
const ret = transform('test.ts', code);
assert.equal(ret.code.trim(), code);
}
});
});

describe('target', () => {
const data = [
['es2015', 'a ** b;\n'],
['es2016', 'async function foo() {}\n'],
['es2017', '({ ...x });\n'],
['es2017', 'try {} catch {}\n'],
['es2019', 'a?.b;\n'],
['es2019', 'a ?? b;\n'],
['es2021', 'class foo {\n\tstatic {}\n}\n'],
];

test.each(data)('transform %s', (target, code) => {
// Also test array syntax.
const ret = transform('test.js', code, { target: [target] });
assert(ret.errors.length == 0);
assert(ret.code);
assert.notEqual(ret.code, code);
});

test.each(data)('no transform esnext: %s', (_target, code) => {
const ret = transform('test.js', code, { target: 'esnext' });
assert(ret.errors.length == 0);
assert(ret.code);
assert.equal(ret.code, code);
});
});

describe('modules', () => {
it('should transform export = and import ', () => {
const code = `
export = function foo (): void {}
import bar = require('bar')
`;
const ret = oxc.transform('test.ts', code, {
const ret = transform('test.ts', code, {
typescript: {
declaration: true,
declaration: {},
},
});
assert.deepEqual(ret, {
Expand All @@ -77,7 +104,7 @@ describe('react refresh plugin', () => {
};`;

it('matches output', () => {
const ret = oxc.transform('test.tsx', code, { jsx: { refresh: {} } });
const ret = transform('test.tsx', code, { jsx: { refresh: {} } });
assert.equal(
ret.code,
`import { useState } from "react";
Expand All @@ -103,7 +130,7 @@ $RefreshReg$(_c, "App");
describe('define plugin', () => {
it('matches output', () => {
const code = 'if (process.env.NODE_ENV === "production") { foo; }';
const ret = oxc.transform('test.tsx', code, {
const ret = transform('test.tsx', code, {
define: {
'process.env.NODE_ENV': '"development"',
},
Expand All @@ -113,7 +140,7 @@ describe('define plugin', () => {

it('handles typescript declare global', () => {
const code = 'declare let __TEST_DEFINE__: string; console.log({ __TEST_DEFINE__ });';
const ret = oxc.transform('test.ts', code, {
const ret = transform('test.ts', code, {
define: {
'__TEST_DEFINE__': '"replaced"',
},
Expand All @@ -126,7 +153,7 @@ describe('inject plugin', () => {
const code = 'let _ = Object.assign';

it('matches output', () => {
const ret = oxc.transform('test.tsx', code, {
const ret = transform('test.tsx', code, {
inject: {
'Object.assign': 'foo',
},
Expand Down
7 changes: 7 additions & 0 deletions napi/transform/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"compilerOptions": {
"module": "Preserve",
"moduleResolution": "Bundler",
"target": "ESNext"
}
}

0 comments on commit 5f64b35

Please sign in to comment.