Skip to content

Commit

Permalink
feat(app): use IsolatingModuleFinalizer for app output (#1317)
Browse files Browse the repository at this point in the history
  • Loading branch information
hyf0 authored Jun 1, 2024
1 parent 75c69b3 commit 92bfe2d
Show file tree
Hide file tree
Showing 14 changed files with 234 additions and 61 deletions.
10 changes: 10 additions & 0 deletions crates/rolldown/src/module_finalizers/isolating/impl_visit_mut.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use oxc::ast::ast;
use oxc::ast::VisitMut;

use super::IsolatingModuleFinalizer;

impl<'me, 'ast> VisitMut<'ast> for IsolatingModuleFinalizer<'me, 'ast> {
fn visit_program(&mut self, _program: &mut ast::Program<'ast>) {
// wrap the program within a function
}
}
9 changes: 9 additions & 0 deletions crates/rolldown/src/module_finalizers/isolating/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use oxc::allocator::Allocator;
use rolldown_common::AstScope;

mod impl_visit_mut;

pub struct IsolatingModuleFinalizer<'me, 'ast> {
pub scope: &'me AstScope,
pub alloc: &'ast Allocator,
}
1 change: 1 addition & 0 deletions crates/rolldown/src/module_finalizers/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod isolating;
pub mod scope_hoisting;
45 changes: 28 additions & 17 deletions crates/rolldown/src/stages/generate_stage/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use anyhow::Result;
use oxc::ast::VisitMut;
use rustc_hash::FxHashSet;

use futures::future::try_join_all;
Expand All @@ -16,7 +17,9 @@ use sugar_path::SugarPath;

use crate::{
chunk_graph::ChunkGraph,
module_finalizers::scope_hoisting::ScopeHoistingFinalizerContext,
module_finalizers::{
isolating::IsolatingModuleFinalizer, scope_hoisting::ScopeHoistingFinalizerContext,
},
stages::link_stage::LinkStageOutput,
type_alias::IndexNormalModules,
utils::{
Expand Down Expand Up @@ -74,23 +77,31 @@ impl<'a> GenerateStage<'a> {
let chunk_id = chunk_graph.module_to_chunk[module.id].unwrap();
let chunk = &chunk_graph.chunks[chunk_id];
let linking_info = &self.link_output.metas[module.id];
finalize_normal_module(
module,
ScopeHoistingFinalizerContext {
canonical_names: &chunk.canonical_names,
id: module.id,
symbols: &self.link_output.symbols,
linking_info,
if self.options.format.requires_scope_hoisting() {
finalize_normal_module(
module,
modules: &self.link_output.module_table.normal_modules,
external_modules: &self.link_output.module_table.external_modules,
linking_infos: &self.link_output.metas,
runtime: &self.link_output.runtime,
chunk_graph: &chunk_graph,
options: self.options,
},
ast,
);
ScopeHoistingFinalizerContext {
canonical_names: &chunk.canonical_names,
id: module.id,
symbols: &self.link_output.symbols,
linking_info,
module,
modules: &self.link_output.module_table.normal_modules,
external_modules: &self.link_output.module_table.external_modules,
linking_infos: &self.link_output.metas,
runtime: &self.link_output.runtime,
chunk_graph: &chunk_graph,
options: self.options,
},
ast,
);
} else {
ast.with_mut(|fields| {
let (oxc_program, alloc) = (fields.program, fields.allocator);
let mut finalizer = IsolatingModuleFinalizer { alloc, scope: &module.scope };
finalizer.visit_program(oxc_program);
});
}
});

let chunks = try_join_all(
Expand Down
136 changes: 92 additions & 44 deletions crates/rolldown/src/utils/chunk/render_chunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,48 +40,90 @@ pub async fn render_chunk(
let mut rendered_modules = FxHashMap::default();
let mut concat_source = ConcatSource::default();

concat_source.add_source(Box::new(RawSource::new(render_chunk_imports(
this,
graph,
chunk_graph,
options,
))));

this
.modules
.par_iter()
.copied()
.map(|id| &graph.module_table.normal_modules[id])
.filter_map(|m| {
render_normal_module(m, &graph.ast_table[m.id], m.resource_id.as_ref(), options)
})
.collect::<Vec<_>>()
.into_iter()
.for_each(|module_render_output| {
let ModuleRenderOutput {
module_path,
module_pretty_path,
rendered_module,
rendered_content,
sourcemap,
lines_count,
} = module_render_output;
concat_source.add_source(Box::new(RawSource::new(format!("// {module_pretty_path}",))));
if let Some(sourcemap) = sourcemap {
concat_source.add_source(Box::new(SourceMapSource::new(
rendered_content,
sourcemap,
lines_count,
)));
} else {
concat_source.add_source(Box::new(RawSource::new(rendered_content)));
}
// FIXME: NAPI-RS used CStr under the hood, so it can't handle null byte in the string.
if !module_path.starts_with('\0') {
rendered_modules.insert(module_path, rendered_module);
}
});
let rendered_chunk = generate_rendered_chunk(this, graph, options, rendered_modules, chunk_graph);
let rendered_chunk = match options.format {
OutputFormat::Esm | OutputFormat::Cjs => {
concat_source.add_source(Box::new(RawSource::new(render_chunk_imports(
this,
graph,
chunk_graph,
options,
))));

this
.modules
.par_iter()
.copied()
.map(|id| &graph.module_table.normal_modules[id])
.filter_map(|m| {
render_normal_module(m, &graph.ast_table[m.id], m.resource_id.as_ref(), options)
})
.collect::<Vec<_>>()
.into_iter()
.for_each(|module_render_output| {
let ModuleRenderOutput {
module_path,
module_pretty_path,
rendered_module,
rendered_content,
sourcemap,
lines_count,
} = module_render_output;
concat_source.add_source(Box::new(RawSource::new(format!("// {module_pretty_path}",))));
if let Some(sourcemap) = sourcemap {
concat_source.add_source(Box::new(SourceMapSource::new(
rendered_content,
sourcemap,
lines_count,
)));
} else {
concat_source.add_source(Box::new(RawSource::new(rendered_content)));
}
// FIXME: NAPI-RS used CStr under the hood, so it can't handle null byte in the string.
if !module_path.starts_with('\0') {
rendered_modules.insert(module_path, rendered_module);
}
});

generate_rendered_chunk(this, graph, options, rendered_modules, chunk_graph)
}
OutputFormat::App => {
this
.modules
.par_iter()
.copied()
.map(|id| &graph.module_table.normal_modules[id])
.filter_map(|m| {
render_normal_module(m, &graph.ast_table[m.id], m.resource_id.as_ref(), options)
})
.collect::<Vec<_>>()
.into_iter()
.for_each(|module_render_output| {
let ModuleRenderOutput {
module_path,
module_pretty_path,
rendered_module,
rendered_content,
sourcemap,
lines_count,
} = module_render_output;
concat_source.add_source(Box::new(RawSource::new(format!("// {module_pretty_path}",))));
if let Some(sourcemap) = sourcemap {
concat_source.add_source(Box::new(SourceMapSource::new(
rendered_content,
sourcemap,
lines_count,
)));
} else {
concat_source.add_source(Box::new(RawSource::new(rendered_content)));
}
// FIXME: NAPI-RS used CStr under the hood, so it can't handle null byte in the string.
if !module_path.starts_with('\0') {
rendered_modules.insert(module_path, rendered_module);
}
});
generate_rendered_chunk(this, graph, options, rendered_modules, chunk_graph)
}
};

// add banner
if let Some(banner) = options.banner.as_ref() {
Expand Down Expand Up @@ -135,8 +177,14 @@ pub async fn render_chunk(
}
}

if let Some(exports) = render_chunk_exports(this, &graph.runtime, graph, options) {
concat_source.add_source(Box::new(RawSource::new(exports)));
match options.format {
OutputFormat::Esm | OutputFormat::Cjs => {
if let Some(exports) = render_chunk_exports(this, &graph.runtime, graph, options) {
concat_source.add_source(Box::new(RawSource::new(exports)));
}
}

OutputFormat::App => {}
}

// add footer
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"config": {
"input": [
{
"name": "main",
"import": "./main.js"
},
{
"name": "other-entry",
"import": "./other-entry.js"
}
],
"format": "app"
},
"expectExecuted": false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
source: crates/rolldown/tests/common/case.rs
expression: content
input_file: crates/rolldown/tests/fixtures/function/format/app/multiple_entry_modules
---
# Assets

## cube.mjs

```js
// square.js
export default function square(x) {
return x * x;
}
// cube.js
import square from './square.js';
export default function cube(x) {
return square(x) * x;
}
```
## main.mjs
```js
// hyper-cube.js
import cube from './cube.js';
export default function hyperCube(x) {
return cube(x) * x;
}
// main.js
import hyperCube from './hyper-cube.js';
console.log(hyperCube(5));
```
## other-entry.mjs
```js
// other-entry.js
import cube from './cube.js';
console.log(cube(5));
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import square from './square.js';

// Everything used by both entry modules will become
// a separate chunk that is imported by both entry
// chunks to avoid code duplication
export default function cube(x) {
return square(x) * x;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import cube from './cube.js';

// This is only imported by one entry module and
// shares a chunk with that module
export default function hyperCube(x) {
return cube(x) * x;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// MULTIPLE ENTRY MODULES
import hyperCube from './hyper-cube.js';

console.log(hyperCube(5));
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Additional entry modules create new chunks
import cube from './cube.js';

console.log(cube(5));
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// This is also shared between the entry chunks
export default function square(x) {
return x * x;
}
Original file line number Diff line number Diff line change
Expand Up @@ -1387,6 +1387,12 @@ expression: "snapshot_outputs.join(\"\\n\")"
- main-!~{000}~.mjs => main-LeJVwZoF.mjs
- share-!~{002}~.mjs => share-eIGIrVeP.mjs

# tests/fixtures/function/format/app/multiple_entry_modules

- cube-!~{002}~.mjs => cube-uhjZ_rWA.mjs
- main-!~{000}~.mjs => main-unBf2xtz.mjs
- other-entry-!~{001}~.mjs => other-entry-08LFZhc1.mjs

# tests/fixtures/function/format/cjs/import_export_unicode

- $runtime$-!~{001}~.cjs => $runtime$-kixSD92L.cjs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,9 @@ pub enum OutputFormat {
Cjs,
App,
}

impl OutputFormat {
pub fn requires_scope_hoisting(&self) -> bool {
matches!(self, Self::Esm | Self::Cjs)
}
}

0 comments on commit 92bfe2d

Please sign in to comment.