Skip to content

Commit

Permalink
feat: hoist exported function declaration (#73)
Browse files Browse the repository at this point in the history
  • Loading branch information
underfin committed Oct 18, 2023
1 parent 58c2786 commit f530305
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 41 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ serde_json = "1.0.87"
insta = "1.21.0"
testing_macros = "0.2.7"
scoped-tls = "1.0"
string_wizard = { version = "0.0.7" }
string_wizard = { version = "0.0.8" }
async-trait = "0.1.62"
futures = "0.3.25"
itertools = "0.10.5"
64 changes: 34 additions & 30 deletions crates/rolldown/src/bundler/visitors/esm_wrap_source_render.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use oxc::{
ast::{ast::Declaration, Visit},
formatter::{Formatter, FormatterOptions, Gen},
span::{Atom, GetSpan, Span},
};
use rolldown_oxc::BindingIdentifierExt;
Expand All @@ -10,7 +9,7 @@ use super::RendererContext;
pub struct EsmWrapSourceRender<'ast> {
ctx: RendererContext<'ast>,
hoisted_vars: Vec<Atom>,
hoisted_functions: Vec<String>,
hoisted_functions: Vec<Span>,
}

impl<'ast> EsmWrapSourceRender<'ast> {
Expand All @@ -21,13 +20,14 @@ impl<'ast> EsmWrapSourceRender<'ast> {
pub fn apply(&mut self) {
let program = self.ctx.module.ast.program();
self.visit_program(program);
self.hoisted_functions.iter().for_each(|f| {
// TODO: remove this hack
// here move end of function to the keep "\n"
self.ctx.source.relocate(f.start, f.end + 1, 0);
});
self.ctx.source.append_right(0, format!("var {};\n", self.hoisted_vars.join(",")));

let namespace_name = self.ctx.namespace_symbol_name.unwrap();
let wrap_symbol_name = self.ctx.wrap_symbol_name.unwrap();
let esm_runtime_symbol_name = self.ctx.get_runtime_symbol_final_name(&"__esm".into());
self.ctx.source.prepend(format!(
"var {wrap_symbol_name} = {esm_runtime_symbol_name}({{\n'{}'() {{\n",
self.ctx.module.resource_id.prettify(),
));
let exports: String = self
.ctx
.module
Expand All @@ -40,10 +40,28 @@ impl<'ast> EsmWrapSourceRender<'ast> {
})
.collect::<Vec<_>>()
.join(",\n");
self.ctx.source.append_right(0, format!("var {namespace_name} = {{\n{exports}\n}};\n",));

let wrap_symbol_name = self.ctx.wrap_symbol_name.unwrap();
let esm_runtime_symbol_name = self.ctx.get_runtime_symbol_final_name(&"__esm".into());
self.ctx.source.append_right(
0,
format!(
"var {wrap_symbol_name} = {esm_runtime_symbol_name}({{\n'{}'() {{\n",
self.ctx.module.resource_id.prettify(),
),
);
self.ctx.source.append("\n}\n});");
self.ctx.source.prepend(format!("\nvar {namespace_name} = {{\n{exports}\n}};\n",));
self.ctx.source.prepend(format!("var {};\n", self.hoisted_vars.join(",")));
self.ctx.source.prepend(format!("{}\n", self.hoisted_functions.join("\n")));
}

fn hoisted_function(&mut self, func: &'ast oxc::ast::ast::Function<'ast>) {
// deconflict function name
if let Some(id) = &func.id {
let name =
self.ctx.get_symbol_final_name((self.ctx.module.id, id.expect_symbol_id()).into()).unwrap();
self.ctx.overwrite(id.span.start, id.span.end, name.to_string());
}
self.hoisted_functions.push(func.span);
}
}

Expand Down Expand Up @@ -84,14 +102,8 @@ impl<'ast> Visit<'ast> for EsmWrapSourceRender<'ast> {
.remove_node(Span::new(named_decl.span.start, var_decl.declarations[0].span.start));
}
Declaration::FunctionDeclaration(func) => {
// hoisted function declaration
// TODO update symbol name with magic string move
self.ctx.remove_node(Span::new(named_decl.span.start, named_decl.span.end));
#[allow(clippy::eq_op)]
let mut formatter =
Formatter::new((func.span.end - func.span.end) as usize, FormatterOptions::default());
func.gen(&mut formatter);
self.hoisted_functions.push(formatter.into_code());
self.ctx.remove_node(Span::new(named_decl.span.start, func.span.start));
self.hoisted_function(func);
}
Declaration::ClassDeclaration(class) => {
let id = class.id.as_ref().unwrap();
Expand Down Expand Up @@ -126,17 +138,9 @@ impl<'ast> Visit<'ast> for EsmWrapSourceRender<'ast> {
self.hoisted_vars.push(default_symbol_name.clone());
self.ctx.overwrite(decl.span.start, exp.span().start, format!("{default_symbol_name} = "));
}
oxc::ast::ast::ExportDefaultDeclarationKind::FunctionDeclaration(fn_decl) => {
// hoisted function declaration
// TODO update symbol name with magic string move
self.ctx.remove_node(decl.span);
#[allow(clippy::eq_op)]
let mut formatter = Formatter::new(
(fn_decl.span.end - fn_decl.span.end) as usize,
FormatterOptions::default(),
);
fn_decl.gen(&mut formatter);
self.hoisted_functions.push(formatter.into_code());
oxc::ast::ast::ExportDefaultDeclarationKind::FunctionDeclaration(func) => {
self.ctx.remove_node(Span::new(decl.span.start, func.span.start));
self.hoisted_function(func);
}
oxc::ast::ast::ExportDefaultDeclarationKind::ClassDeclaration(class) => {
let default_symbol_name = self.ctx.default_symbol_name.unwrap();
Expand Down
12 changes: 5 additions & 7 deletions crates/rolldown/tests/fixtures/basic_commonjs/artifacts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,9 @@ input_file: crates/rolldown/tests/fixtures/basic_commonjs

```js
// esm.js
function esm_default_fn () {
}
function esm_named_fn () {
}
function esm_default_fn() {}
function esm_named_fn() {}
var esm_named_var,esm_named_class;

var esm_ns = {
get esm_named_var() { return esm_named_var },
get default() { return esm_default_fn },
Expand All @@ -21,9 +18,7 @@ var esm_ns = {
};
var init_esm = __esm({
'esm.js'() {

esm_named_var = 1;

esm_named_class = class esm_named_class {}
}
});
Expand All @@ -41,5 +36,8 @@ var _default = commonjs_ns.default;
init_esm();

console.log(_default, esm_default_fn, esm_named_var, esm_named_fn, esm_named_class)
// test commonjs warp symbol deconflict
const require_commonjs = () => {}
// test esm export function symbol deconflict
function esm_default_fn$1() {}
```
5 changes: 4 additions & 1 deletion crates/rolldown/tests/fixtures/basic_commonjs/main.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import foo from './commonjs.js'
import esm, { esm_named_var, esm_named_fn, esm_named_class } from './esm.js'
console.log(foo, esm, esm_named_var, esm_named_fn, esm_named_class)
const require_commonjs = () => {}
// test commonjs warp symbol deconflict
const require_commonjs = () => {}
// test esm export function symbol deconflict
function esm_default_fn() {}

0 comments on commit f530305

Please sign in to comment.