Skip to content

Commit

Permalink
feat(napi/transform): add lang option to change source type
Browse files Browse the repository at this point in the history
  • Loading branch information
Boshen committed Oct 6, 2024
1 parent 5b5daec commit 35c5ae8
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 36 deletions.
4 changes: 4 additions & 0 deletions crates/oxc/src/napi/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ 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>,
Expand Down
6 changes: 4 additions & 2 deletions napi/transform/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export interface Es2015Options {
}

/** TypeScript Isolated Declarations for Standalone DTS Emit */
export declare function isolatedDeclaration(filename: string, sourceText: string, options?: IsolatedDeclarationsOptions | undefined | null): IsolatedDeclarationsResult
export declare function isolatedDeclaration(path: string, sourceText: string, options?: IsolatedDeclarationsOptions | undefined | null): IsolatedDeclarationsResult

export interface IsolatedDeclarationsOptions {
/**
Expand Down Expand Up @@ -173,7 +173,7 @@ export interface SourceMap {
* @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
export declare function transform(path: string, sourceText: string, options?: TransformOptions | undefined | null): TransformResult

/**
* Options for transforming a JavaScript or TypeScript file.
Expand All @@ -182,6 +182,8 @@ export declare function transform(filename: string, sourceText: string, options?
*/
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.
Expand Down
7 changes: 4 additions & 3 deletions napi/transform/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use std::sync::Arc;
use std::{path::Path, sync::Arc};

use oxc::{
diagnostics::{Error, NamedSource, OxcDiagnostic},
span::SourceType,
};

pub fn wrap_diagnostics(
filename: &str,
filename: &Path,
source_type: SourceType,
source_text: &str,
errors: Vec<OxcDiagnostic>,
Expand All @@ -28,7 +28,8 @@ pub fn wrap_diagnostics(
}
};

let ns = NamedSource::new(filename, source_text.to_string()).with_language(lang);
let ns = NamedSource::new(filename.to_string_lossy(), source_text.to_string())
.with_language(lang);
Arc::new(ns)
};

Expand Down
11 changes: 7 additions & 4 deletions napi/transform/src/isolated_declaration.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::path::Path;

use napi_derive::napi;

use oxc::{
Expand All @@ -18,11 +20,12 @@ use crate::errors::wrap_diagnostics;
#[allow(clippy::needless_pass_by_value)]
#[napi]
pub fn isolated_declaration(
filename: String,
path: String,
source_text: String,
options: Option<IsolatedDeclarationsOptions>,
) -> IsolatedDeclarationsResult {
let source_type = SourceType::from_path(&filename).unwrap_or_default().with_typescript(true);
let source_path = Path::new(&path);
let source_type = SourceType::from_path(source_path).unwrap_or_default().with_typescript(true);
let allocator = Allocator::default();
let options = options.unwrap_or_default();

Expand All @@ -44,12 +47,12 @@ pub fn isolated_declaration(
CommentOptions { preserve_annotate_comments: false },
);
if options.sourcemap == Some(true) {
codegen = codegen.enable_source_map(&filename, &source_text);
codegen = codegen.enable_source_map(&path, &source_text);
}
let codegen_ret = codegen.build(&transformed_ret.program);

let errors = ret.errors.into_iter().chain(transformed_ret.errors).collect();
let errors = wrap_diagnostics(&filename, source_type, &source_text, errors);
let errors = wrap_diagnostics(source_path, source_type, &source_text, errors);

IsolatedDeclarationsResult {
code: codegen_ret.source_text,
Expand Down
37 changes: 25 additions & 12 deletions napi/transform/src/transformer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,27 +135,40 @@ impl CompilerInterface for Compiler {
#[allow(clippy::needless_pass_by_value)]
#[napi]
pub fn transform(
filename: String,
path: String,
source_text: String,
options: Option<TransformOptions>,
) -> TransformResult {
let source_path = Path::new(&filename);
let source_type = {
let mut source_type = SourceType::from_path(source_path).unwrap_or_default();
// Force `script` or `module`
match options.as_ref().and_then(|options| options.source_type.as_deref()) {
Some("script") => source_type = source_type.with_script(true),
Some("module") => source_type = source_type.with_module(true),
_ => {}
let source_path = Path::new(&path);

let source_type = match options.as_ref().and_then(|options| options.lang.as_deref()) {
Some("js") => SourceType::mjs(),
Some("jsx") => SourceType::jsx(),
Some("ts") => SourceType::ts(),
Some("tsx") => SourceType::tsx(),
Some(lang) => {
return TransformResult {
errors: vec![format!("Incorrect lang '{lang}'")],
..Default::default()
}
}
None => {
let mut source_type = SourceType::from_path(source_path).unwrap_or_default();
// Force `script` or `module`
match options.as_ref().and_then(|options| options.source_type.as_deref()) {
Some("script") => source_type = source_type.with_script(true),
Some("module") => source_type = source_type.with_module(true),
_ => {}
}
source_type
}
source_type
};

let mut compiler = match Compiler::new(options) {
Ok(compiler) => compiler,
Err(errors) => {
return TransformResult {
errors: wrap_diagnostics(&filename, source_type, &source_text, errors),
errors: wrap_diagnostics(source_path, source_type, &source_text, errors),
..Default::default()
}
}
Expand All @@ -167,6 +180,6 @@ pub fn transform(
map: compiler.printed_sourcemap,
declaration: compiler.declaration,
declaration_map: compiler.declaration_map,
errors: wrap_diagnostics(&filename, source_type, &source_text, compiler.errors),
errors: wrap_diagnostics(source_path, source_type, &source_text, compiler.errors),
}
}
5 changes: 3 additions & 2 deletions napi/transform/test/id.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe('isolated declaration', () => {

it('matches output', () => {
const ret = oxc.isolatedDeclaration('test.ts', code, { sourcemap: true });
assert(ret, {
assert.deepEqual(ret, {
code: '/**\n' +
'* jsdoc 1\n' +
'*/\n' +
Expand All @@ -28,12 +28,13 @@ describe('isolated declaration', () => {
'\tfoo: string;\n' +
'}\n',
map: {
mappings: ';;;AAIA,OAAO,cAAM,EAAE;;;;CAIb;AACD',
mappings: ';;;AAIE,OAAO,cAAM,EAAE;;;;CAIb;AACD',
names: [],
sources: ['test.ts'],
sourcesContent: [code],
version: 3,
},
errors: [],
});
});
});
29 changes: 16 additions & 13 deletions napi/transform/test/transform.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ describe('transform', () => {

it('matches output', () => {
const ret = oxc.transform('test.ts', code, { sourcemap: true });
assert(ret, {
assert.deepEqual(ret, {
code: 'class A {}\n',
errors: [],
map: {
mappings: 'AAAA,MAAM,EAAK,CAAE',
names: [],
Expand All @@ -18,6 +19,11 @@ describe('transform', () => {
},
});
});

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

describe('react refresh plugin', () => {
Expand All @@ -29,8 +35,9 @@ describe('react refresh plugin', () => {

it('matches output', () => {
const ret = oxc.transform('test.tsx', code, { jsx: { refresh: {} } });
assert(ret, {
code: 'var _s = $RefreshSig$();\n' +
assert.equal(
ret.code,
'var _s = $RefreshSig$();\n' +
'import { useState } from "react";\n' +
'import { jsxs as _jsxs } from "react/jsx-runtime";\n' +
'export const App = () => {\n' +
Expand All @@ -45,7 +52,7 @@ describe('react refresh plugin', () => {
'_c = App;\n' +
'var _c;\n' +
'$RefreshReg$(_c, "App");\n',
});
);
});
});

Expand All @@ -58,10 +65,8 @@ describe('define plugin', () => {
'process.env.NODE_ENV': 'false',
},
});
assert(ret, {
// TODO: should be constant folded
code: 'if (false === "production") {\n\tfoo;\n}\n',
});
// TODO: should be constant folded
assert.equal(ret.code, 'if (false === "production") {\n\tfoo;\n}\n');
});
});

Expand All @@ -70,12 +75,10 @@ describe('inject plugin', () => {

it('matches output', () => {
const ret = oxc.transform('test.tsx', code, {
define: {
'process.env.NODE_ENV': 'false',
inject: {
'Object.assign': 'foo',
},
});
assert(ret, {
code: 'import $inject_Object_assign from "foo";\nlet _ = $inject_Object_assign;\n',
});
assert.equal(ret.code, 'import $inject_Object_assign from "foo";\nlet _ = $inject_Object_assign;\n');
});
});

0 comments on commit 35c5ae8

Please sign in to comment.