mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
feat(napi/transform): add TransformOptions::target API (#7426)
This commit is contained in:
parent
88d17b96e3
commit
59e7e4674f
8 changed files with 97 additions and 24 deletions
|
|
@ -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};
|
||||
|
||||
|
|
@ -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>>,
|
||||
|
|
@ -93,6 +107,11 @@ 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
|
||||
|
|
@ -100,6 +119,7 @@ impl TryFrom<TransformOptions> for oxc_transformer::TransformOptions {
|
|||
.map(oxc_transformer::TypeScriptOptions::from)
|
||||
.unwrap_or_default(),
|
||||
jsx: options.jsx.map(Into::into).unwrap_or_default(),
|
||||
env,
|
||||
..Self::default()
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {} }"),
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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
|
||||
----------
|
||||
|
||||
|
|
@ -65,7 +71,7 @@ a || (a = b);
|
|||
: ^^
|
||||
`----
|
||||
|
||||
########## 8 es2021
|
||||
########## 9 es2021
|
||||
class foo { static {} }
|
||||
----------
|
||||
class foo {
|
||||
|
|
|
|||
15
napi/transform/index.d.ts
vendored
15
napi/transform/index.d.ts
vendored
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { assert, describe, it } from 'vitest';
|
||||
|
||||
import oxc from './index';
|
||||
import oxc from '../index';
|
||||
|
||||
describe('isolated declaration', () => {
|
||||
const code = `
|
||||
|
|
|
|||
|
|
@ -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: [],
|
||||
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
|
@ -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, {
|
||||
|
|
@ -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";
|
||||
|
|
@ -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"',
|
||||
},
|
||||
|
|
@ -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"',
|
||||
},
|
||||
|
|
@ -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',
|
||||
},
|
||||
|
|
|
|||
7
napi/transform/tsconfig.json
Normal file
7
napi/transform/tsconfig.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"module": "Preserve",
|
||||
"moduleResolution": "Bundler",
|
||||
"target": "ESNext"
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue