From feecc82620f628013781a1dda5cac8dcca6b0b53 Mon Sep 17 00:00:00 2001 From: Boshen Date: Sun, 6 Oct 2024 11:57:10 +0800 Subject: [PATCH] feat(napi/transform): add `lang` option to change source type --- crates/oxc/src/napi/transform.rs | 4 +++ napi/transform/index.d.ts | 2 ++ napi/transform/src/errors.rs | 7 +++-- napi/transform/src/isolated_declaration.rs | 7 +++-- napi/transform/src/transformer.rs | 33 +++++++++++++++------- napi/transform/test/id.test.mjs | 5 ++-- napi/transform/test/transform.test.mjs | 29 ++++++++++--------- 7 files changed, 57 insertions(+), 30 deletions(-) diff --git a/crates/oxc/src/napi/transform.rs b/crates/oxc/src/napi/transform.rs index 81c7e222c0e352..c1c6b2925891df 100644 --- a/crates/oxc/src/napi/transform.rs +++ b/crates/oxc/src/napi/transform.rs @@ -57,6 +57,10 @@ 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, diff --git a/napi/transform/index.d.ts b/napi/transform/index.d.ts index 88a5cd146d76fe..e18b5bd3b88972 100644 --- a/napi/transform/index.d.ts +++ b/napi/transform/index.d.ts @@ -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. diff --git a/napi/transform/src/errors.rs b/napi/transform/src/errors.rs index 339184c4392979..15d76b4ae7a438 100644 --- a/napi/transform/src/errors.rs +++ b/napi/transform/src/errors.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{path::Path, sync::Arc}; use oxc::{ diagnostics::{Error, NamedSource, OxcDiagnostic}, @@ -6,7 +6,7 @@ use oxc::{ }; pub fn wrap_diagnostics( - filename: &str, + filename: &Path, source_type: SourceType, source_text: &str, errors: Vec, @@ -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) }; diff --git a/napi/transform/src/isolated_declaration.rs b/napi/transform/src/isolated_declaration.rs index e8b531d24d013e..3c78170594e94e 100644 --- a/napi/transform/src/isolated_declaration.rs +++ b/napi/transform/src/isolated_declaration.rs @@ -1,3 +1,5 @@ +use std::path::Path; + use napi_derive::napi; use oxc::{ @@ -22,7 +24,8 @@ pub fn isolated_declaration( source_text: String, options: Option, ) -> IsolatedDeclarationsResult { - let source_type = SourceType::from_path(&filename).unwrap_or_default().with_typescript(true); + let source_path = Path::new(&filename); + let source_type = SourceType::from_path(source_path).unwrap_or_default().with_typescript(true); let allocator = Allocator::default(); let options = options.unwrap_or_default(); @@ -49,7 +52,7 @@ pub fn isolated_declaration( 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, diff --git a/napi/transform/src/transformer.rs b/napi/transform/src/transformer.rs index 2fe315d5d6be4f..893a738bccc2ba 100644 --- a/napi/transform/src/transformer.rs +++ b/napi/transform/src/transformer.rs @@ -140,22 +140,35 @@ pub fn transform( options: Option, ) -> 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_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() } } @@ -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), } } diff --git a/napi/transform/test/id.test.mjs b/napi/transform/test/id.test.mjs index dd1e416cc0a07d..5ac5ef1b4c83a9 100644 --- a/napi/transform/test/id.test.mjs +++ b/napi/transform/test/id.test.mjs @@ -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' + @@ -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: [], }); }); }); diff --git a/napi/transform/test/transform.test.mjs b/napi/transform/test/transform.test.mjs index 2a038d3ae8770e..9e5db4a0743605 100644 --- a/napi/transform/test/transform.test.mjs +++ b/napi/transform/test/transform.test.mjs @@ -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: [], @@ -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', () => { @@ -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' + @@ -45,7 +52,7 @@ describe('react refresh plugin', () => { '_c = App;\n' + 'var _c;\n' + '$RefreshReg$(_c, "App");\n', - }); + ); }); }); @@ -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'); }); }); @@ -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'); }); });