Skip to content

Commit

Permalink
feat(napi/transform): add inject plugin (#6250)
Browse files Browse the repository at this point in the history
  • Loading branch information
Boshen committed Oct 2, 2024
1 parent d085c2c commit f98e12c
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 20 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions napi/transform/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ oxc_sourcemap = { workspace = true }
oxc_span = { workspace = true }
oxc_transformer = { workspace = true }

rustc-hash = { workspace = true }

napi = { workspace = true }
napi-derive = { workspace = true }

Expand Down
2 changes: 2 additions & 0 deletions napi/transform/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ export interface TransformOptions {
es2015?: ES2015BindingOptions
/** Define Plugin */
define?: Record<string, string>
/** Inject Plugin */
inject?: Record<string, string | [string, string]>
}

export interface TransformResult {
Expand Down
11 changes: 8 additions & 3 deletions napi/transform/src/options.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#![allow(rustdoc::bare_urls)]
#![allow(clippy::disallowed_types)] // allow HashMap

use std::collections::HashMap;
use std::path::PathBuf;

use napi::Either;
use napi_derive::napi;
use rustc_hash::FxHashMap;

use oxc_transformer::{ArrowFunctionsOptions, ES2015Options, JsxRuntime, RewriteExtensionsMode};

use crate::IsolatedDeclarationsOptions;
Expand Down Expand Up @@ -42,7 +42,12 @@ pub struct TransformOptions {
pub es2015: Option<ES2015BindingOptions>,

/// Define Plugin
pub define: Option<HashMap<String, String>>,
#[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 From<TransformOptions> for oxc_transformer::TransformOptions {
Expand Down
91 changes: 74 additions & 17 deletions napi/transform/src/transformer.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
use napi::Either;
use napi_derive::napi;
use rustc_hash::FxHashMap;

use oxc_allocator::Allocator;
use oxc_codegen::CodegenReturn;
use oxc_semantic::SemanticBuilder;
use oxc_semantic::{ScopeTree, SemanticBuilder, SymbolTable};
use oxc_span::SourceType;
use oxc_transformer::{ReplaceGlobalDefines, ReplaceGlobalDefinesConfig, Transformer};
use oxc_transformer::{
InjectGlobalVariables, InjectGlobalVariablesConfig, InjectImport, ReplaceGlobalDefines,
ReplaceGlobalDefinesConfig, Transformer,
};

use crate::{context::TransformContext, isolated_declaration, SourceMap, TransformOptions};

Expand Down Expand Up @@ -107,6 +113,7 @@ fn transpile(ctx: &TransformContext<'_>, options: Option<TransformOptions>) -> C

let mut options = options;
let define = options.as_mut().and_then(|options| options.define.take());
let inject = options.as_mut().and_then(|options| options.inject.take());

let options = options.map(oxc_transformer::TransformOptions::from).unwrap_or_default();

Expand All @@ -126,22 +133,72 @@ fn transpile(ctx: &TransformContext<'_>, options: Option<TransformOptions>) -> C
scopes = ret.scopes;

if let Some(define) = define {
let define = define.into_iter().collect::<Vec<_>>();
match ReplaceGlobalDefinesConfig::new(&define) {
Ok(config) => {
let _ret = ReplaceGlobalDefines::new(ctx.allocator, config).build(
symbols,
scopes,
&mut ctx.program_mut(),
);
// symbols = ret.symbols;
// scopes = ret.scopes;
}
Err(errors) => {
ctx.add_diagnostics(errors);
}
}
(symbols, scopes) = define_plugin(ctx, define, symbols, scopes);
}

if let Some(inject) = inject {
_ = inject_plugin(ctx, inject, symbols, scopes);
}

ctx.codegen().build(&ctx.program())
}

fn define_plugin(
ctx: &TransformContext<'_>,
define: FxHashMap<String, String>,
symbols: SymbolTable,
scopes: ScopeTree,
) -> (SymbolTable, ScopeTree) {
let define = define.into_iter().collect::<Vec<_>>();
match ReplaceGlobalDefinesConfig::new(&define) {
Ok(config) => {
let ret = ReplaceGlobalDefines::new(ctx.allocator, config).build(
symbols,
scopes,
&mut ctx.program_mut(),
);
(ret.symbols, ret.scopes)
}
Err(errors) => {
ctx.add_diagnostics(errors);
(symbols, scopes)
}
}
}

fn inject_plugin(
ctx: &TransformContext<'_>,
inject: FxHashMap<String, Either<String, Vec<String>>>,
symbols: SymbolTable,
scopes: ScopeTree,
) -> (SymbolTable, ScopeTree) {
let Ok(injects) = inject
.into_iter()
.map(|(local, value)| match value {
Either::A(source) => Ok(InjectImport::default_specifier(&source, &local)),
Either::B(v) => {
if v.len() != 2 {
return Err(());
}
let source = v[0].to_string();
Ok(if v[1] == "*" {
InjectImport::namespace_specifier(&source, &local)
} else {
InjectImport::named_specifier(&source, Some(&v[1]), &local)
})
}
})
.collect::<Result<Vec<_>, ()>>()
else {
return (symbols, scopes);
};

let config = InjectGlobalVariablesConfig::new(injects);
let ret = InjectGlobalVariables::new(ctx.allocator, config).build(
symbols,
scopes,
&mut ctx.program_mut(),
);

(ret.symbols, ret.scopes)
}
25 changes: 25 additions & 0 deletions napi/transform/test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,29 @@ test(
},
);

// Test define plugin
// TODO: should be constant folded
test(
oxc.transform('test.ts', 'if (process.env.NODE_ENV === "production") { foo; }', {
define: {
'process.env.NODE_ENV': 'false',
},
}),
{
code: 'if (false === "production") {\n\tfoo;\n}\n',
},
);

// Test inject plugin
test(
oxc.transform('test.ts', 'let _ = Object.assign', {
inject: {
'Object.assign': 'foo',
},
}),
{
code: 'import $inject_Object_assign from "foo";\nlet _ = $inject_Object_assign;\n',
},
);

console.log('Success.');

0 comments on commit f98e12c

Please sign in to comment.