From 70ee29f3938e29161b62c3e3401255305bf45630 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Sun, 8 Dec 2024 18:36:36 +0900 Subject: [PATCH 1/4] test: add app chunk test --- .../app/dynamic-import-chunk/_config.json | 6 ++ .../app/dynamic-import-chunk/artifacts.snap | 40 +++++++++++++ .../format/app/dynamic-import-chunk/dep.js | 1 + .../format/app/dynamic-import-chunk/main.js | 1 + .../format/app/shared-chunk/_config.json | 16 +++++ .../format/app/shared-chunk/artifacts.snap | 59 +++++++++++++++++++ .../function/format/app/shared-chunk/dep.js | 1 + .../function/format/app/shared-chunk/main1.js | 2 + .../function/format/app/shared-chunk/main2.js | 2 + ...egration_rolldown__filename_with_hash.snap | 11 ++++ 10 files changed, 139 insertions(+) create mode 100644 crates/rolldown/tests/rolldown/function/format/app/dynamic-import-chunk/_config.json create mode 100644 crates/rolldown/tests/rolldown/function/format/app/dynamic-import-chunk/artifacts.snap create mode 100644 crates/rolldown/tests/rolldown/function/format/app/dynamic-import-chunk/dep.js create mode 100644 crates/rolldown/tests/rolldown/function/format/app/dynamic-import-chunk/main.js create mode 100644 crates/rolldown/tests/rolldown/function/format/app/shared-chunk/_config.json create mode 100644 crates/rolldown/tests/rolldown/function/format/app/shared-chunk/artifacts.snap create mode 100644 crates/rolldown/tests/rolldown/function/format/app/shared-chunk/dep.js create mode 100644 crates/rolldown/tests/rolldown/function/format/app/shared-chunk/main1.js create mode 100644 crates/rolldown/tests/rolldown/function/format/app/shared-chunk/main2.js diff --git a/crates/rolldown/tests/rolldown/function/format/app/dynamic-import-chunk/_config.json b/crates/rolldown/tests/rolldown/function/format/app/dynamic-import-chunk/_config.json new file mode 100644 index 000000000000..1042a7187452 --- /dev/null +++ b/crates/rolldown/tests/rolldown/function/format/app/dynamic-import-chunk/_config.json @@ -0,0 +1,6 @@ +{ + "config": { + "format": "app" + }, + "expectExecuted": false +} diff --git a/crates/rolldown/tests/rolldown/function/format/app/dynamic-import-chunk/artifacts.snap b/crates/rolldown/tests/rolldown/function/format/app/dynamic-import-chunk/artifacts.snap new file mode 100644 index 000000000000..fb537566a11e --- /dev/null +++ b/crates/rolldown/tests/rolldown/function/format/app/dynamic-import-chunk/artifacts.snap @@ -0,0 +1,40 @@ +--- +source: crates/rolldown_testing/src/integration_test.rs +snapshot_kind: text +--- +# Assets + +## dep.js + +```js +var __rolldown_modules = { + +"dep.js": function(__rolldown_runtime) { +//#region dep.js +__rolldown_runtime.__toCommonJS(__rolldown_runtime.exports); +__rolldown_runtime.__export(__rolldown_runtime.exports, { default: () => dep_default }); +var dep_default = "[dep ok]"; + +//#endregion +}, + +}; + +``` +## main.js + +```js +var __rolldown_modules = { + +"main.js": function(__rolldown_runtime) { +//#region main.js +__rolldown_runtime.__toCommonJS(__rolldown_runtime.exports); +__rolldown_runtime.__export(__rolldown_runtime.exports, { default: () => main_default }); +var main_default = () => import("./dep.js"); + +//#endregion +}, + +}; + +``` diff --git a/crates/rolldown/tests/rolldown/function/format/app/dynamic-import-chunk/dep.js b/crates/rolldown/tests/rolldown/function/format/app/dynamic-import-chunk/dep.js new file mode 100644 index 000000000000..4f43e2332bd5 --- /dev/null +++ b/crates/rolldown/tests/rolldown/function/format/app/dynamic-import-chunk/dep.js @@ -0,0 +1 @@ +export default "[dep ok]" diff --git a/crates/rolldown/tests/rolldown/function/format/app/dynamic-import-chunk/main.js b/crates/rolldown/tests/rolldown/function/format/app/dynamic-import-chunk/main.js new file mode 100644 index 000000000000..d7059027f228 --- /dev/null +++ b/crates/rolldown/tests/rolldown/function/format/app/dynamic-import-chunk/main.js @@ -0,0 +1 @@ +export default () => import("./dep.js") diff --git a/crates/rolldown/tests/rolldown/function/format/app/shared-chunk/_config.json b/crates/rolldown/tests/rolldown/function/format/app/shared-chunk/_config.json new file mode 100644 index 000000000000..6283b0eb3719 --- /dev/null +++ b/crates/rolldown/tests/rolldown/function/format/app/shared-chunk/_config.json @@ -0,0 +1,16 @@ +{ + "config": { + "format": "app", + "input": [ + { + "import": "./main1.js", + "name": "main1" + }, + { + "import": "./main2.js", + "name": "main2" + } + ] + }, + "expectExecuted": false +} diff --git a/crates/rolldown/tests/rolldown/function/format/app/shared-chunk/artifacts.snap b/crates/rolldown/tests/rolldown/function/format/app/shared-chunk/artifacts.snap new file mode 100644 index 000000000000..a1894158d48b --- /dev/null +++ b/crates/rolldown/tests/rolldown/function/format/app/shared-chunk/artifacts.snap @@ -0,0 +1,59 @@ +--- +source: crates/rolldown_testing/src/integration_test.rs +snapshot_kind: text +--- +# Assets + +## dep.js + +```js +var __rolldown_modules = { + +"dep.js": function(__rolldown_runtime) { +//#region dep.js +__rolldown_runtime.__toCommonJS(__rolldown_runtime.exports); +__rolldown_runtime.__export(__rolldown_runtime.exports, { default: () => dep_default }); +var dep_default = "[dep ok]"; + +//#endregion +}, + +}; + +``` +## main1.js + +```js +var __rolldown_modules = { + +"main1.js": function(__rolldown_runtime) { +//#region main1.js +__rolldown_runtime.__toCommonJS(__rolldown_runtime.exports); +__rolldown_runtime.__export(__rolldown_runtime.exports, { default: () => main1_default }); +var dep_exports = __rolldown_runtime.require("dep.js"); +var main1_default = "main1: " + dep_exports.default; + +//#endregion +}, + +}; + +``` +## main2.js + +```js +var __rolldown_modules = { + +"main2.js": function(__rolldown_runtime) { +//#region main2.js +__rolldown_runtime.__toCommonJS(__rolldown_runtime.exports); +__rolldown_runtime.__export(__rolldown_runtime.exports, { default: () => main2_default }); +var dep_exports = __rolldown_runtime.require("dep.js"); +var main2_default = "main2: " + dep_exports.default; + +//#endregion +}, + +}; + +``` diff --git a/crates/rolldown/tests/rolldown/function/format/app/shared-chunk/dep.js b/crates/rolldown/tests/rolldown/function/format/app/shared-chunk/dep.js new file mode 100644 index 000000000000..4f43e2332bd5 --- /dev/null +++ b/crates/rolldown/tests/rolldown/function/format/app/shared-chunk/dep.js @@ -0,0 +1 @@ +export default "[dep ok]" diff --git a/crates/rolldown/tests/rolldown/function/format/app/shared-chunk/main1.js b/crates/rolldown/tests/rolldown/function/format/app/shared-chunk/main1.js new file mode 100644 index 000000000000..3361832169a1 --- /dev/null +++ b/crates/rolldown/tests/rolldown/function/format/app/shared-chunk/main1.js @@ -0,0 +1,2 @@ +import dep from "./dep.js" +export default "main1: " + dep; diff --git a/crates/rolldown/tests/rolldown/function/format/app/shared-chunk/main2.js b/crates/rolldown/tests/rolldown/function/format/app/shared-chunk/main2.js new file mode 100644 index 000000000000..09dfc2473ac8 --- /dev/null +++ b/crates/rolldown/tests/rolldown/function/format/app/shared-chunk/main2.js @@ -0,0 +1,2 @@ +import dep from "./dep.js" +export default "main2: " + dep; diff --git a/crates/rolldown/tests/snapshots/integration_rolldown__filename_with_hash.snap b/crates/rolldown/tests/snapshots/integration_rolldown__filename_with_hash.snap index 6febe4490145..039bc9cef589 100644 --- a/crates/rolldown/tests/snapshots/integration_rolldown__filename_with_hash.snap +++ b/crates/rolldown/tests/snapshots/integration_rolldown__filename_with_hash.snap @@ -4125,6 +4125,11 @@ snapshot_kind: text - out.js => out.js - out.css +# tests/rolldown/function/format/app/dynamic-import-chunk + +- main-!~{000}~.js => main-CLbdS1rw.js +- dep-!~{001}~.js => dep-CpkzZlrt.js + # tests/rolldown/function/format/app/export-all - main-!~{000}~.js => main-BdghrDco.js @@ -4163,6 +4168,12 @@ snapshot_kind: text - main-!~{000}~.js => main-C1Zl5Znc.js +# tests/rolldown/function/format/app/shared-chunk + +- main1-!~{000}~.js => main1-R3nOeIoa.js +- main2-!~{001}~.js => main2-BbKbhREb.js +- dep-!~{002}~.js => dep-Bs9a6pBX.js + # tests/rolldown/function/format/cjs/conflict_exports_key - main-!~{000}~.js => main-BJs_Uc9B.js From ed64ca1024d03a9d13edece546463032bfda2b88 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Sun, 8 Dec 2024 19:07:00 +0900 Subject: [PATCH 2/4] wip: rewrite dynamic import --- .../isolating/impl_visit_mut.rs | 25 ++++++++++++++++++- .../src/module_finalizers/isolating/mod.rs | 3 +++ .../rolldown/src/stages/generate_stage/mod.rs | 1 + .../app/dynamic-import-chunk/artifacts.snap | 2 +- ...egration_rolldown__filename_with_hash.snap | 2 +- 5 files changed, 30 insertions(+), 3 deletions(-) diff --git a/crates/rolldown/src/module_finalizers/isolating/impl_visit_mut.rs b/crates/rolldown/src/module_finalizers/isolating/impl_visit_mut.rs index e36b9025a514..9a2012ca0a08 100644 --- a/crates/rolldown/src/module_finalizers/isolating/impl_visit_mut.rs +++ b/crates/rolldown/src/module_finalizers/isolating/impl_visit_mut.rs @@ -68,7 +68,7 @@ impl<'ast> VisitMut<'ast> for IsolatingModuleFinalizer<'_, 'ast> { program.body.extend(stmts); - // quick and dirty 2nd pass to rewrite globals into __rolldown_runtime.xxx + // quick and dirty extra pass to rewrite globals e.g. require -> runtime.require walk_mut::walk_program(&mut AppRuntimeRewriter { snippet: &self.snippet }, program); } @@ -103,6 +103,29 @@ impl<'ast> VisitMut<'ast> for IsolatingModuleFinalizer<'_, 'ast> { } }; } + if let Expression::ImportExpression(import_expr) = expr { + // input: import("dep.js") + // output: runtime.ensureChunk("dep-chunk").then(() => runtime.require("dep.js")) + if import_expr.source.is_string_literal() && import_expr.arguments.len() == 0 { + // copied from ScopeHoistingFinalizer::visit_import_expression + let rec_id = self.ctx.module.imports[&import_expr.span]; + let rec = &self.ctx.module.import_records[rec_id]; + let importee_id = rec.resolved_module; + match &self.ctx.modules[importee_id] { + Module::Normal(_importee) => { + let importee_chunk_id = self.ctx.chunk_graph.entry_module_to_entry_chunk[&importee_id]; + let importee_chunk = &self.ctx.chunk_graph.chunk_table[importee_chunk_id]; + let import_path = importee_chunk.name.as_ref().unwrap(); // TODO: unique chunk name + *expr = Expression::StringLiteral(self.snippet.builder.alloc_string_literal( + SPAN, + format!("TODO-ensureChunk{import_path}"), + None, + )); + } + Module::External(_) => {} + } + } + } walk_mut::walk_expression(self, expr); } diff --git a/crates/rolldown/src/module_finalizers/isolating/mod.rs b/crates/rolldown/src/module_finalizers/isolating/mod.rs index 002d28d73276..257fa072a753 100644 --- a/crates/rolldown/src/module_finalizers/isolating/mod.rs +++ b/crates/rolldown/src/module_finalizers/isolating/mod.rs @@ -7,12 +7,15 @@ use rolldown_common::{AstScopes, IndexModules, NormalModule, SymbolRefDb}; use rolldown_ecmascript_utils::AstSnippet; use rustc_hash::FxHashSet; +use crate::chunk_graph::ChunkGraph; + mod impl_visit_mut; pub struct IsolatingModuleFinalizerContext<'me> { pub module: &'me NormalModule, pub modules: &'me IndexModules, pub symbol_db: &'me SymbolRefDb, + pub chunk_graph: &'me ChunkGraph, } pub struct IsolatingModuleFinalizer<'me, 'ast> { diff --git a/crates/rolldown/src/stages/generate_stage/mod.rs b/crates/rolldown/src/stages/generate_stage/mod.rs index c5c8e053421f..88907842ffe5 100644 --- a/crates/rolldown/src/stages/generate_stage/mod.rs +++ b/crates/rolldown/src/stages/generate_stage/mod.rs @@ -130,6 +130,7 @@ impl<'a> GenerateStage<'a> { module, modules: &self.link_output.module_table.modules, symbol_db: &self.link_output.symbol_db, + chunk_graph: &chunk_graph, }, snippet: AstSnippet::new(alloc), generated_imports_set: FxHashSet::default(), diff --git a/crates/rolldown/tests/rolldown/function/format/app/dynamic-import-chunk/artifacts.snap b/crates/rolldown/tests/rolldown/function/format/app/dynamic-import-chunk/artifacts.snap index fb537566a11e..6b20a40c0996 100644 --- a/crates/rolldown/tests/rolldown/function/format/app/dynamic-import-chunk/artifacts.snap +++ b/crates/rolldown/tests/rolldown/function/format/app/dynamic-import-chunk/artifacts.snap @@ -30,7 +30,7 @@ var __rolldown_modules = { //#region main.js __rolldown_runtime.__toCommonJS(__rolldown_runtime.exports); __rolldown_runtime.__export(__rolldown_runtime.exports, { default: () => main_default }); -var main_default = () => import("./dep.js"); +var main_default = () => "TODO-ensureChunkdep"; //#endregion }, diff --git a/crates/rolldown/tests/snapshots/integration_rolldown__filename_with_hash.snap b/crates/rolldown/tests/snapshots/integration_rolldown__filename_with_hash.snap index 039bc9cef589..725e16208686 100644 --- a/crates/rolldown/tests/snapshots/integration_rolldown__filename_with_hash.snap +++ b/crates/rolldown/tests/snapshots/integration_rolldown__filename_with_hash.snap @@ -4127,7 +4127,7 @@ snapshot_kind: text # tests/rolldown/function/format/app/dynamic-import-chunk -- main-!~{000}~.js => main-CLbdS1rw.js +- main-!~{000}~.js => main-DbkMdzjC.js - dep-!~{001}~.js => dep-CpkzZlrt.js # tests/rolldown/function/format/app/export-all From 021178d05383fe857d295084e2a76a6a21d6c92f Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 9 Dec 2024 11:45:28 +0900 Subject: [PATCH 3/4] wip: rewrite dynamic import --- .../isolating/impl_visit_mut.rs | 92 ++++++++++++++++--- .../app/dynamic-import-chunk/artifacts.snap | 4 +- ...egration_rolldown__filename_with_hash.snap | 2 +- 3 files changed, 82 insertions(+), 16 deletions(-) diff --git a/crates/rolldown/src/module_finalizers/isolating/impl_visit_mut.rs b/crates/rolldown/src/module_finalizers/isolating/impl_visit_mut.rs index 9a2012ca0a08..58cf6c588899 100644 --- a/crates/rolldown/src/module_finalizers/isolating/impl_visit_mut.rs +++ b/crates/rolldown/src/module_finalizers/isolating/impl_visit_mut.rs @@ -1,6 +1,6 @@ use oxc::ast::ast::{self, ExportDefaultDeclarationKind, Expression, Statement}; use oxc::ast::visit::walk_mut; -use oxc::ast::VisitMut; +use oxc::ast::{VisitMut, NONE}; use oxc::span::{CompactStr, Span, SPAN}; use rolldown_common::{Interop, Module}; use rolldown_ecmascript_utils::{AstSnippet, TakeIn}; @@ -104,23 +104,87 @@ impl<'ast> VisitMut<'ast> for IsolatingModuleFinalizer<'_, 'ast> { }; } if let Expression::ImportExpression(import_expr) = expr { - // input: import("dep.js") - // output: runtime.ensureChunk("dep-chunk").then(() => runtime.require("dep.js")) + // input: import("importee") + // output: runtime.ensureChunk("importee-chunk").then(function() { return runtime.require("importee-id") }) if import_expr.source.is_string_literal() && import_expr.arguments.len() == 0 { - // copied from ScopeHoistingFinalizer::visit_import_expression - let rec_id = self.ctx.module.imports[&import_expr.span]; - let rec = &self.ctx.module.import_records[rec_id]; - let importee_id = rec.resolved_module; - match &self.ctx.modules[importee_id] { + let importee_module = self.get_importee_module(import_expr.span); + match importee_module { Module::Normal(_importee) => { - let importee_chunk_id = self.ctx.chunk_graph.entry_module_to_entry_chunk[&importee_id]; + let importee_chunk_id = + self.ctx.chunk_graph.entry_module_to_entry_chunk[&importee_module.idx()]; let importee_chunk = &self.ctx.chunk_graph.chunk_table[importee_chunk_id]; - let import_path = importee_chunk.name.as_ref().unwrap(); // TODO: unique chunk name - *expr = Expression::StringLiteral(self.snippet.builder.alloc_string_literal( + // TODO: unique chunk name? + let chunk_name = importee_chunk.name.as_ref().unwrap(); + *expr = self.snippet.builder.expression_call( SPAN, - format!("TODO-ensureChunk{import_path}"), - None, - )); + Expression::StaticMemberExpression( + self.snippet.builder.alloc_static_member_expression( + SPAN, + self.snippet.builder.expression_call( + SPAN, + Expression::StaticMemberExpression( + self.snippet.builder.alloc_static_member_expression( + SPAN, + self.snippet.id_ref_expr("__rolldown_runtime", SPAN), + self.snippet.builder.identifier_name(SPAN, "ensureChunk"), + false, + ), + ), + NONE, + self.snippet.builder.vec1(ast::Argument::from( + self.snippet.string_literal_expr(chunk_name, SPAN), + )), + false, + ), + self.snippet.builder.identifier_name(SPAN, "then"), + false, + ), + ), + NONE, + self.snippet.builder.vec1(ast::Argument::from( + self.snippet.builder.expression_function( + ast::FunctionType::FunctionExpression, + SPAN, + None::, + false, + false, + false, + NONE, + NONE, + self.snippet.builder.formal_parameters( + SPAN, + ast::FormalParameterKind::Signature, + self.snippet.builder.vec(), + NONE, + ), + NONE, + Some(self.snippet.builder.function_body( + SPAN, + self.snippet.builder.vec(), + self.snippet.builder.vec1(self.snippet.builder.statement_return( + SPAN, + Some(self.snippet.builder.expression_call( + SPAN, + Expression::StaticMemberExpression( + self.snippet.builder.alloc_static_member_expression( + SPAN, + self.snippet.id_ref_expr("__rolldown_runtime", SPAN), + self.snippet.builder.identifier_name(SPAN, "require"), + false, + ), + ), + NONE, + self.snippet.builder.vec1(ast::Argument::from( + self.snippet.string_literal_expr(importee_module.stable_id(), SPAN), + )), + false, + )), + )), + )), + ), + )), + false, + ); } Module::External(_) => {} } diff --git a/crates/rolldown/tests/rolldown/function/format/app/dynamic-import-chunk/artifacts.snap b/crates/rolldown/tests/rolldown/function/format/app/dynamic-import-chunk/artifacts.snap index 6b20a40c0996..fb2f30a693bc 100644 --- a/crates/rolldown/tests/rolldown/function/format/app/dynamic-import-chunk/artifacts.snap +++ b/crates/rolldown/tests/rolldown/function/format/app/dynamic-import-chunk/artifacts.snap @@ -30,7 +30,9 @@ var __rolldown_modules = { //#region main.js __rolldown_runtime.__toCommonJS(__rolldown_runtime.exports); __rolldown_runtime.__export(__rolldown_runtime.exports, { default: () => main_default }); -var main_default = () => "TODO-ensureChunkdep"; +var main_default = () => __rolldown_runtime.ensureChunk("dep").then(function() { + return __rolldown_runtime.require("dep.js"); +}); //#endregion }, diff --git a/crates/rolldown/tests/snapshots/integration_rolldown__filename_with_hash.snap b/crates/rolldown/tests/snapshots/integration_rolldown__filename_with_hash.snap index 725e16208686..d0014d98e7af 100644 --- a/crates/rolldown/tests/snapshots/integration_rolldown__filename_with_hash.snap +++ b/crates/rolldown/tests/snapshots/integration_rolldown__filename_with_hash.snap @@ -4127,7 +4127,7 @@ snapshot_kind: text # tests/rolldown/function/format/app/dynamic-import-chunk -- main-!~{000}~.js => main-DbkMdzjC.js +- main-!~{000}~.js => main-ot7EdN42.js - dep-!~{001}~.js => dep-CpkzZlrt.js # tests/rolldown/function/format/app/export-all From fdb7f281908dc5c3cab4f6102a4657f9c7ff218f Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 10 Dec 2024 16:57:43 +0900 Subject: [PATCH 4/4] fix: reset file emitter state --- crates/rolldown/src/bundler.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/rolldown/src/bundler.rs b/crates/rolldown/src/bundler.rs index 1b4cae38e8eb..1aadeec46ea2 100644 --- a/crates/rolldown/src/bundler.rs +++ b/crates/rolldown/src/bundler.rs @@ -177,7 +177,10 @@ impl Bundler { output.watch_files = self.plugin_driver.watch_files.iter().map(|f| f.clone()).collect(); - self.rebuild_manager.save_output(&output); + if self.rebuild_manager.enabled { + self.rebuild_manager.save_output(&output); + self.plugin_driver.clear(); + } Ok(output) }