Skip to content

Commit

Permalink
feat: support generating source map (#607)
Browse files Browse the repository at this point in the history
* feat: generate sourcemap

* chore: fix spell

* chore: fix test

* chore: fix test
  • Loading branch information
underfin committed Mar 18, 2024
1 parent 7ae8435 commit f2dcda2
Show file tree
Hide file tree
Showing 22 changed files with 252 additions and 103 deletions.
22 changes: 21 additions & 1 deletion crates/bench/benches/threejs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::path::PathBuf;

use bench::join_by_repo_root;
use codspeed_criterion_compat::{criterion_group, criterion_main, Criterion};
use rolldown::InputOptions;
use rolldown::{InputOptions, OutputOptions, SourceMapType};

#[derive(Debug)]
struct BenchItem {
Expand All @@ -21,6 +21,7 @@ fn criterion_benchmark(c: &mut Criterion) {
items.into_iter().for_each(|item| {
let scan_id = format!("{}-scan", item.name);
// let build_id = format!("{}-build", item.name);
let source_map_id = format!("{}-sourcemap", item.name);
let bundle_id = format!("{}-bundle", item.name);
group.bench_function(scan_id, |b| {
b.iter(|| {
Expand Down Expand Up @@ -73,6 +74,25 @@ fn criterion_benchmark(c: &mut Criterion) {
})
});
});

group.bench_function(source_map_id, |b| {
b.iter(|| {
tokio::runtime::Runtime::new().unwrap().block_on(async {
let mut rolldown_bundler = rolldown::Bundler::new(
InputOptions {
input: vec![rolldown::InputItem {
name: Some(item.name.to_string()),
import: item.entry_path.to_string_lossy().to_string(),
}],
cwd: join_by_repo_root("crates/bench").into(),
..Default::default()
},
OutputOptions { sourcemap: Some(SourceMapType::File), ..Default::default() },
);
rolldown_bundler.write().await.unwrap();
})
});
});
});
}

Expand Down
4 changes: 2 additions & 2 deletions crates/rolldown/examples/basic.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use rolldown::{Bundler, InputItem, InputOptions, OutputOptions};
use rolldown::{Bundler, InputItem, InputOptions, OutputOptions, SourceMapType};
use std::path::PathBuf;
use sugar_path::SugarPathBuf;

Expand All @@ -19,7 +19,7 @@ async fn main() {
cwd: cwd.into(),
..Default::default()
},
OutputOptions::default(),
OutputOptions { sourcemap: Some(SourceMapType::File), ..OutputOptions::default() },
);

let _outputs = bundler.write().await.unwrap();
Expand Down
24 changes: 19 additions & 5 deletions crates/rolldown/src/chunk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ impl Chunk {
.copied()
.map(|id| &graph.module_table.normal_modules[id])
.filter_map(|m| {
let rendered_content = render_normal_module(
let rendered_output = render_normal_module(
m,
&ModuleRenderContext {
canonical_names: &self.canonical_names,
Expand All @@ -107,16 +107,30 @@ impl Chunk {
input_options,
},
&graph.ast_table[m.id],
if output_options.sourcemap.is_hidden() {
None
} else {
Some(
m.resource_id
.expect_file()
.relative_path(&input_options.cwd)
.to_string_lossy()
.to_string(),
)
},
);
Some((
m.resource_id.expect_file().to_string(),
RenderedModule { code: None },
rendered_content,
rendered_output.as_ref().map(|v| v.code.to_string()),
if output_options.sourcemap.is_hidden() {
None
} else {
// TODO add oxc codegen sourcemap to sourcemap chain
Some(collapse_sourcemaps(&m.sourcemap_chain))
let mut sourcemap_chain = m.sourcemap_chain.iter().collect::<Vec<_>>();
if let Some(Some(sourcemap)) = rendered_output.as_ref().map(|x| x.sourcemap.as_ref()) {
sourcemap_chain.push(sourcemap);
}
Some(collapse_sourcemaps(sourcemap_chain))
},
))
})
Expand All @@ -126,7 +140,7 @@ impl Chunk {
|(module_path, rendered_module, rendered_content, map)| -> Result<(), BuildError> {
if let Some(rendered_content) = rendered_content {
content_and_sourcemaps.push((
rendered_content.code.to_string(),
rendered_content,
match map {
None => None,
Some(v) => v?,
Expand Down
12 changes: 6 additions & 6 deletions crates/rolldown/src/finalizer/impl_visit_mut_for_finalizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use oxc::{
ast::{self, SimpleAssignmentTarget},
VisitMut,
},
span::Span,
span::{Span, SPAN},
};
use rolldown_common::{ExportsKind, ModuleId, SymbolRef, WrapKind};
use rolldown_oxc_utils::{Dummy, ExpressionExt, IntoIn, StatementExt, TakeIn};
Expand Down Expand Up @@ -100,7 +100,7 @@ impl<'ast, 'me: 'ast> VisitMut<'ast> for Finalizer<'me, 'ast> {
.snippet
.call_expr_with_2arg_expr_expr(
re_export_fn_name,
self.snippet.id_ref_expr(importer_namespace_name),
self.snippet.id_ref_expr(importer_namespace_name, SPAN),
self.snippet.call_expr_with_arg_expr_expr(
to_esm_fn_name,
self.snippet.call_expr_expr(importee_wrapper_ref_name),
Expand Down Expand Up @@ -129,7 +129,7 @@ impl<'ast, 'me: 'ast> VisitMut<'ast> for Finalizer<'me, 'ast> {
if func.id.is_none() {
let canonical_name_for_default_export_ref =
self.canonical_name_for(self.ctx.module.default_export_ref);
func.id = Some(self.snippet.id(canonical_name_for_default_export_ref));
func.id = Some(self.snippet.id(canonical_name_for_default_export_ref, SPAN));
}
top_stmt = ast::Statement::Declaration(ast::Declaration::FunctionDeclaration(
func.take_in(self.alloc),
Expand All @@ -141,7 +141,7 @@ impl<'ast, 'me: 'ast> VisitMut<'ast> for Finalizer<'me, 'ast> {
if class.id.is_none() {
let canonical_name_for_default_export_ref =
self.canonical_name_for(self.ctx.module.default_export_ref);
class.id = Some(self.snippet.id(canonical_name_for_default_export_ref));
class.id = Some(self.snippet.id(canonical_name_for_default_export_ref, SPAN));
}
top_stmt = ast::Statement::Declaration(ast::Declaration::ClassDeclaration(
class.take_in(self.alloc),
Expand Down Expand Up @@ -241,7 +241,7 @@ impl<'ast, 'me: 'ast> VisitMut<'ast> for Finalizer<'me, 'ast> {
declarators.push(ast::VariableDeclarator {
id: ast::BindingPattern {
kind: ast::BindingPatternKind::BindingIdentifier(
self.snippet.id(&var_name).into_in(self.alloc),
self.snippet.id(&var_name, SPAN).into_in(self.alloc),
),
..Dummy::dummy(self.alloc)
},
Expand Down Expand Up @@ -456,7 +456,7 @@ impl<'ast, 'me: 'ast> VisitMut<'ast> for Finalizer<'me, 'ast> {
*property = ast::AssignmentTargetProperty::AssignmentTargetPropertyProperty(
ast::AssignmentTargetPropertyProperty {
name: ast::PropertyKey::Identifier(
self.snippet.id_name(&prop.binding.name).into_in(self.alloc),
self.snippet.id_name(&prop.binding.name, prop.span).into_in(self.alloc),
),
binding: if let Some(init) = prop.init.take() {
ast::AssignmentTargetMaybeDefault::AssignmentTargetWithDefault(
Expand Down
14 changes: 9 additions & 5 deletions crates/rolldown/src/finalizer/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use oxc::{
allocator::Allocator,
ast::ast::{self, IdentifierReference, Statement},
span::Atom,
span::{Atom, SPAN},
};
use rolldown_common::{AstScope, ImportRecordId, ModuleId, SymbolRef, WrapKind};
use rolldown_oxc_utils::{AstSnippet, BindingPatternExt, Dummy, IntoIn, TakeIn};
Expand Down Expand Up @@ -92,7 +92,7 @@ where
access_expr
} else {
let canonical_name = self.canonical_name_for(canonical_ref);
self.snippet.id_ref_expr(canonical_name)
self.snippet.id_ref_expr(canonical_name, SPAN)
}
}

Expand Down Expand Up @@ -141,7 +141,7 @@ where
ast::ExpressionStatement {
expression: ast::Expression::AssignmentExpression(
ast::AssignmentExpression {
left: self.snippet.simple_id_assignment_target(&cls_name),
left: self.snippet.simple_id_assignment_target(&cls_name, cls_decl.span),
right: ast::Expression::ClassExpression(cls_decl.take_in(self.alloc)),
..Dummy::dummy(self.alloc)
}
Expand Down Expand Up @@ -186,7 +186,9 @@ where
let returned = self.generate_finalized_expr_for_symbol_ref(resolved_export.symbol_ref);
arg_obj_expr.properties.push(ast::ObjectPropertyKind::ObjectProperty(
ast::ObjectProperty {
key: ast::PropertyKey::Identifier(self.snippet.id_name(prop_name).into_in(self.alloc)),
key: ast::PropertyKey::Identifier(
self.snippet.id_name(prop_name, SPAN).into_in(self.alloc),
),
value: self.snippet.only_return_arrow_expr(returned),
..Dummy::dummy(self.alloc)
}
Expand All @@ -196,7 +198,9 @@ where

// construct `__export(ns_name, { prop_name: () => returned, ... })`
let mut export_call_expr = self.snippet.call_expr(self.canonical_name_for_runtime("__export"));
export_call_expr.arguments.push(ast::Argument::Expression(self.snippet.id_ref_expr(ns_name)));
export_call_expr
.arguments
.push(ast::Argument::Expression(self.snippet.id_ref_expr(ns_name, SPAN)));
export_call_expr.arguments.push(ast::Argument::Expression(ast::Expression::ObjectExpression(
arg_obj_expr.into_in(self.alloc),
)));
Expand Down
4 changes: 2 additions & 2 deletions crates/rolldown/src/finalizer/rename.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ where

let canonical_name = self.canonical_name_for(canonical_ref);
if id_ref.name != canonical_name.as_str() {
return Some(self.snippet.id_ref_expr(canonical_name));
return Some(self.snippet.id_ref_expr(canonical_name, id_ref.span));
}

None
Expand Down Expand Up @@ -83,7 +83,7 @@ where
let canonical_name = self.canonical_name_for(canonical_ref);
if id_ref.name != canonical_name.as_str() {
return Some(ast::SimpleAssignmentTarget::AssignmentTargetIdentifier(
self.snippet.id_ref(canonical_name).into_in(self.alloc),
self.snippet.id_ref(canonical_name, id_ref.span).into_in(self.alloc),
));
}

Expand Down
2 changes: 1 addition & 1 deletion crates/rolldown/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub use crate::{
options::{
file_name_template::FileNameTemplate,
input_options::{resolve_options::ResolveOptions, External, InputOptions},
output_options::{OutputFormat, OutputOptions},
output_options::{OutputFormat, OutputOptions, SourceMapType},
types::input_item::InputItem,
},
types::rolldown_output::RolldownOutput,
Expand Down
7 changes: 5 additions & 2 deletions crates/rolldown/src/stages/bundle_stage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,18 +93,21 @@ impl<'a> BundleStage<'a> {

render_chunks(self.plugin_driver, chunks).await?.into_iter().try_for_each(
|(mut content, map, rendered_chunk)| -> Result<(), BuildError> {
if let Some(map) = map {
if let Some(mut map) = map {
map.set_file(Some(rendered_chunk.file_name.clone()));
match self.output_options.sourcemap {
SourceMapType::File => {
let map = {
let mut buf = vec![];
map.to_writer(&mut buf).map_err(|e| BuildError::sourcemap_error(e.to_string()))?;
unsafe { String::from_utf8_unchecked(buf) }
};
let map_file_name = format!("{}.map", rendered_chunk.file_name);
assets.push(Output::Asset(Box::new(OutputAsset {
file_name: format!("{}.map", rendered_chunk.file_name),
file_name: map_file_name.clone(),
source: map,
})));
content.push_str(&format!("\n//# sourceMappingURL={map_file_name}"));
}
SourceMapType::Inline => {
let data_url =
Expand Down
44 changes: 38 additions & 6 deletions crates/rolldown/src/utils/render_normal_module.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,61 @@
use oxc::codegen::CodegenReturn;
use rolldown_common::NormalModule;
use rolldown_oxc_utils::{OxcCompiler, OxcProgram};
use rolldown_sourcemap::SourceMap;
use rolldown_sourcemap::{SourceMap, SourceMapBuilder};
use string_wizard::MagicString;

use crate::types::module_render_context::ModuleRenderContext;

pub struct RenderedNormalModuleOutput {
pub code: MagicString<'static>,
pub map: Option<SourceMap>,
pub sourcemap: Option<SourceMap>,
}

#[allow(clippy::unnecessary_wraps)]
#[allow(
clippy::unnecessary_wraps,
clippy::cast_possible_truncation,
clippy::needless_pass_by_value
)]
pub fn render_normal_module(
module: &NormalModule,
_ctx: &ModuleRenderContext<'_>,
ast: &OxcProgram,
enable_sourcemap: Option<String>,
) -> Option<RenderedNormalModuleOutput> {
if ast.program().body.is_empty() {
None
} else {
let generated_code = OxcCompiler::print(ast);
let mut source = MagicString::new(generated_code);
let CodegenReturn { source_map, source_text } =
OxcCompiler::print(ast, enable_sourcemap.clone());

let mut source = MagicString::new(source_text);
source.prepend(format!("// {}\n", module.pretty_path));
// Here `MagicString` sourcemap is not valid, because it need to include valid ast token.

Some(RenderedNormalModuleOutput { code: source, map: None })
Some(RenderedNormalModuleOutput {
code: source,
sourcemap: source_map.map(|source_map| {
let mut sourcemap_builder = SourceMapBuilder::new(None);

for (id, source) in source_map.sources().enumerate() {
let source_id = sourcemap_builder.add_source(source);
sourcemap_builder
.set_source_contents(source_id, source_map.get_source_contents(id as u32));
}

for token in source_map.tokens() {
sourcemap_builder.add(
token.get_dst_line() + 1, // line offset by prepend comment
token.get_dst_col(),
token.get_src_line(),
token.get_src_col(),
token.get_source(),
token.get_name(),
);
}

sourcemap_builder.into_sourcemap()
}),
})
}
}
2 changes: 1 addition & 1 deletion crates/rolldown_binding_wasm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@
"devDependencies": {
"wasm-pack": "^0.12.1"
}
}
}
7 changes: 6 additions & 1 deletion crates/rolldown_common/src/types/file_path.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::{
borrow::Cow,
ffi::OsStr,
path::{Component, Path},
path::{Component, Path, PathBuf},
sync::Arc,
};

Expand Down Expand Up @@ -63,6 +63,11 @@ impl FilePath {
name
}

pub fn relative_path(&self, root: impl AsRef<Path>) -> PathBuf {
let path = self.0.as_path();
path.relative(root)
}

pub fn representative_name(&self) -> Cow<str> {
representative_name(&self.0)
}
Expand Down

0 comments on commit f2dcda2

Please sign in to comment.