Skip to content

Commit b68c5a2

Browse files
authored
feat(playground): add inject and replace plugins (#13805)
1 parent f56c8a3 commit b68c5a2

File tree

5 files changed

+111
-7
lines changed

5 files changed

+111
-7
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

napi/playground/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ oxc_formatter = { workspace = true }
2727
oxc_index = { workspace = true }
2828
oxc_linter = { workspace = true }
2929
oxc_napi = { workspace = true }
30+
oxc_transformer_plugins = { workspace = true }
3031

3132
napi = { workspace = true, features = ["async"] }
3233
napi-derive = { workspace = true }

napi/playground/index.d.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,16 @@ export interface OxcControlFlowOptions {
6363
verbose?: boolean
6464
}
6565

66+
export interface OxcDefineOptions {
67+
/** Map of variable name to value for replacement */
68+
define: Record<string, string>
69+
}
70+
71+
export interface OxcInjectOptions {
72+
/** Map of variable name to module source or [source, specifier] */
73+
inject: Record<string, string | [string, string]>
74+
}
75+
6676
export interface OxcIsolatedDeclarationsOptions {
6777
stripInternal: boolean
6878
}
@@ -86,6 +96,8 @@ export interface OxcOptions {
8696
compress?: OxcCompressOptions
8797
mangle?: OxcMangleOptions
8898
controlFlow?: OxcControlFlowOptions
99+
inject?: OxcInjectOptions
100+
define?: OxcDefineOptions
89101
}
90102

91103
export interface OxcParserOptions {

napi/playground/src/lib.rs

Lines changed: 77 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::{
77
use options::OxcCodegenOptions;
88
use rustc_hash::FxHashMap;
99

10+
use napi::Either;
1011
use napi_derive::napi;
1112
use serde::Serialize;
1213

@@ -35,8 +36,15 @@ use oxc_linter::{
3536
ModuleRecord, Oxlintrc,
3637
};
3738
use oxc_napi::{Comment, OxcError, convert_utf8_to_utf16};
39+
use oxc_transformer_plugins::{
40+
InjectGlobalVariables, InjectGlobalVariablesConfig, InjectImport, ReplaceGlobalDefines,
41+
ReplaceGlobalDefinesConfig,
42+
};
3843

39-
use crate::options::{OxcLinterOptions, OxcOptions, OxcRunOptions};
44+
use crate::options::{
45+
OxcControlFlowOptions, OxcDefineOptions, OxcInjectOptions, OxcIsolatedDeclarationsOptions,
46+
OxcLinterOptions, OxcOptions, OxcParserOptions, OxcRunOptions, OxcTransformerOptions,
47+
};
4048

4149
mod options;
4250

@@ -97,12 +105,16 @@ impl Oxc {
97105
control_flow: ref control_flow_options,
98106
isolated_declarations: ref isolated_declarations_options,
99107
codegen: ref codegen_options,
108+
inject: ref inject_options,
109+
define: ref define_options,
100110
..
101111
} = options;
102112
let linter_options = linter_options.clone().unwrap_or_default();
103113
let transform_options = transform_options.clone().unwrap_or_default();
104114
let control_flow_options = control_flow_options.clone().unwrap_or_default();
105115
let codegen_options = codegen_options.clone().unwrap_or_default();
116+
let inject_options = inject_options.clone();
117+
let define_options = define_options.clone();
106118

107119
let allocator = Allocator::default();
108120

@@ -139,7 +151,7 @@ impl Oxc {
139151
};
140152
self.run_formatter(run_options, parse_options, &source_text, source_type);
141153

142-
let scoping = semantic.into_scoping();
154+
let mut scoping = semantic.into_scoping();
143155

144156
// Extract scope and symbol information if needed
145157
if !source_type.is_typescript_definition() {
@@ -164,9 +176,17 @@ impl Oxc {
164176
return Ok(());
165177
}
166178

179+
// Phase 5.5: Apply ReplaceGlobalDefines (before transformations)
180+
if let Some(define_opts) = define_options {
181+
let define_config = Self::build_define_config(&define_opts)?;
182+
let ret =
183+
ReplaceGlobalDefines::new(&allocator, define_config).build(scoping, &mut program);
184+
scoping = ret.scoping;
185+
}
186+
167187
// Phase 6: Apply transformations
168188
if run_options.transform {
169-
self.apply_transformations(
189+
scoping = self.apply_transformations(
170190
&allocator,
171191
&path,
172192
&mut program,
@@ -175,6 +195,13 @@ impl Oxc {
175195
);
176196
}
177197

198+
// Phase 6.5: Apply InjectGlobalVariables (after transformations)
199+
if let Some(inject_opts) = inject_options {
200+
let inject_config = Self::build_inject_config(&inject_opts)?;
201+
let _ =
202+
InjectGlobalVariables::new(&allocator, inject_config).build(scoping, &mut program);
203+
}
204+
178205
// Phase 7: Apply minification
179206
let scoping = Self::apply_minification(&allocator, &mut program, &options);
180207

@@ -192,7 +219,7 @@ impl Oxc {
192219
allocator: &'a Allocator,
193220
source_text: &'a str,
194221
source_type: SourceType,
195-
parser_options: &crate::options::OxcParserOptions,
222+
parser_options: &OxcParserOptions,
196223
) -> (Program<'a>, oxc::syntax::module_record::ModuleRecord<'a>) {
197224
let parser_options = ParseOptions {
198225
parse_regular_expression: true,
@@ -210,7 +237,7 @@ impl Oxc {
210237
&mut self,
211238
program: &'a Program<'a>,
212239
run_options: &OxcRunOptions,
213-
control_flow_options: &crate::options::OxcControlFlowOptions,
240+
control_flow_options: &OxcControlFlowOptions,
214241
) -> oxc::semantic::Semantic<'a> {
215242
let mut semantic_builder = SemanticBuilder::new();
216243
if run_options.transform {
@@ -238,7 +265,7 @@ impl Oxc {
238265
program: &Program<'a>,
239266
run_options: &OxcRunOptions,
240267
codegen_options: &OxcCodegenOptions,
241-
isolated_declarations_options: Option<crate::options::OxcIsolatedDeclarationsOptions>,
268+
isolated_declarations_options: Option<OxcIsolatedDeclarationsOptions>,
242269
) {
243270
let id_options = isolated_declarations_options
244271
.map(|o| IsolatedDeclarationsOptions { strip_internal: o.strip_internal })
@@ -259,7 +286,7 @@ impl Oxc {
259286
path: &Path,
260287
program: &mut Program<'a>,
261288
scoping: Scoping,
262-
transform_options: &crate::options::OxcTransformerOptions,
289+
transform_options: &OxcTransformerOptions,
263290
) -> Scoping {
264291
let mut options = transform_options
265292
.target
@@ -493,6 +520,49 @@ impl Oxc {
493520
writer.scope_text
494521
}
495522

523+
fn build_inject_config(
524+
inject_opts: &OxcInjectOptions,
525+
) -> napi::Result<InjectGlobalVariablesConfig> {
526+
let mut imports = Vec::new();
527+
528+
for (local, value) in &inject_opts.inject {
529+
let import = match value {
530+
Either::A(source) => InjectImport::default_specifier(source, local),
531+
Either::B(v) => {
532+
if v.len() != 2 {
533+
return Err(napi::Error::from_reason(
534+
"Inject plugin did not receive a tuple [string, string].",
535+
));
536+
}
537+
let source = &v[0];
538+
if v[1] == "*" {
539+
InjectImport::namespace_specifier(source, local)
540+
} else {
541+
InjectImport::named_specifier(source, Some(&v[1]), local)
542+
}
543+
}
544+
};
545+
imports.push(import);
546+
}
547+
548+
Ok(InjectGlobalVariablesConfig::new(imports))
549+
}
550+
551+
fn build_define_config(
552+
define_opts: &OxcDefineOptions,
553+
) -> napi::Result<ReplaceGlobalDefinesConfig> {
554+
let define_pairs: Vec<(String, String)> =
555+
define_opts.define.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
556+
557+
ReplaceGlobalDefinesConfig::new(&define_pairs).map_err(|errors| {
558+
let error_messages: Vec<String> = errors.iter().map(ToString::to_string).collect();
559+
napi::Error::from_reason(format!(
560+
"Invalid define config: {}",
561+
error_messages.join(", ")
562+
))
563+
})
564+
}
565+
496566
fn get_symbols_text(scoping: &Scoping) -> napi::Result<String> {
497567
#[derive(Serialize)]
498568
struct Data {

napi/playground/src/options.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
use napi::Either;
12
use napi_derive::napi;
3+
use rustc_hash::FxHashMap;
24

35
#[napi(object)]
46
#[derive(Default)]
@@ -12,6 +14,8 @@ pub struct OxcOptions {
1214
pub compress: Option<OxcCompressOptions>,
1315
pub mangle: Option<OxcMangleOptions>,
1416
pub control_flow: Option<OxcControlFlowOptions>,
17+
pub inject: Option<OxcInjectOptions>,
18+
pub define: Option<OxcDefineOptions>,
1519
}
1620

1721
#[napi(object)]
@@ -53,6 +57,22 @@ pub struct OxcTransformerOptions {
5357
pub emit_decorator_metadata: bool,
5458
}
5559

60+
#[napi(object)]
61+
#[derive(Default, Clone)]
62+
pub struct OxcInjectOptions {
63+
/// Map of variable name to module source or [source, specifier]
64+
#[napi(ts_type = "Record<string, string | [string, string]>")]
65+
pub inject: FxHashMap<String, Either<String, Vec<String>>>,
66+
}
67+
68+
#[napi(object)]
69+
#[derive(Default, Clone)]
70+
pub struct OxcDefineOptions {
71+
/// Map of variable name to value for replacement
72+
#[napi(ts_type = "Record<string, string>")]
73+
pub define: FxHashMap<String, String>,
74+
}
75+
5676
#[napi(object)]
5777
#[derive(Default, Clone)]
5878
pub struct OxcIsolatedDeclarationsOptions {

0 commit comments

Comments
 (0)