Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support to install chunks before entry loaded #783

Merged
merged 2 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion crates/mako/src/chunk_pot/ast_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,10 @@ pub(crate) fn render_normal_js_chunk(
) -> Result<ChunkFile> {
mako_core::mako_profile_function!();

let module = pot_to_chunk_module(chunk_pot)?;
let module = pot_to_chunk_module(
chunk_pot,
context.config.output.chunk_loading_global.clone(),
)?;

let mut ast = GLOBALS.set(&context.meta.script.globals, || Ast {
ast: module,
Expand Down
4 changes: 3 additions & 1 deletion crates/mako/src/chunk_pot/str_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,10 @@ pub(super) fn render_normal_js_chunk(

pub fn pot_to_chunk_module_content(pot: &ChunkPot, context: &Arc<Context>) -> Result<String> {
Ok(format!(
r#"globalThis.jsonpCallback([["{}"],
r#"(globalThis['{}'] = globalThis['{}'] || []).push([['{}'],
{}]);"#,
context.config.output.chunk_loading_global,
context.config.output.chunk_loading_global,
pot.chunk_id,
pot_to_chunk_module_object_string(pot, context)?
))
Expand Down
50 changes: 33 additions & 17 deletions crates/mako/src/chunk_pot/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ use mako_core::cached::SizedCache;
use mako_core::sailfish::TemplateOnce;
use mako_core::swc_common::DUMMY_SP;
use mako_core::swc_ecma_ast::{
ArrayLit, BlockStmt, Expr, ExprOrSpread, FnExpr, Function, KeyValueProp, Module as SwcModule,
ObjectLit, Prop, PropOrSpread,
ArrayLit, AssignOp, BinaryOp, BlockStmt, Expr, ExprOrSpread, FnExpr, Function, Ident,
KeyValueProp, Module as SwcModule, ObjectLit, Prop, PropOrSpread,
};
use mako_core::swc_ecma_codegen::text_writer::JsWriter;
use mako_core::swc_ecma_codegen::{Config as JsCodegenConfig, Emitter};
use mako_core::swc_ecma_utils::{member_expr, quote_ident, quote_str, ExprFactory};
use mako_core::swc_ecma_utils::{quote_ident, quote_str, ExprFactory};
use mako_core::twox_hash::XxHash64;

use crate::chunk_pot::ChunkPot;
Expand Down Expand Up @@ -104,6 +104,7 @@ pub(crate) fn runtime_code(context: &Arc<Context>) -> Result<String> {
has_dynamic_chunks,
has_hmr,
umd,
chunk_loading_global: context.config.output.chunk_loading_global.clone(),
};
let app_runtime = app_runtime.render_once()?;
let app_runtime = app_runtime.replace(
Expand Down Expand Up @@ -178,26 +179,41 @@ pub(crate) fn pot_to_module_object(pot: &ChunkPot) -> Result<ObjectLit> {
})
}

pub(crate) fn pot_to_chunk_module(pot: &ChunkPot) -> Result<SwcModule> {
pub(crate) fn pot_to_chunk_module(pot: &ChunkPot, global: String) -> Result<SwcModule> {
mako_core::mako_profile_function!();

let module_object = pot_to_module_object(pot)?;

// globalThis.jsonpCallback([["module_id"], { module object }])
let jsonp_callback_stmt = <Expr as ExprFactory>::as_call(
*member_expr!(DUMMY_SP, globalThis.jsonpCallback),
DUMMY_SP,
// [[ "module id"], { module object }]
vec![to_array_lit(vec![
to_array_lit(vec![quote_str!(pot.chunk_id.clone()).as_arg()]).as_arg(),
module_object.as_arg(),
])
.as_arg()],
)
.into_stmt();
// (globalThis['makoChunk_global'] = globalThis['makoChunk_global'] || []).push([["module_id"], { module object }])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

globalThis['makoChunk_global'] = 赋值是需要前置加载的 chunk 需要的吧,async chunk 应该不需要。这个优化比较小,如果先不做,可以注释里记个 TODO。

Copy link
Member Author

@PeachScript PeachScript Dec 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里是打算 async chunk 和 shared chunk 用同一套 wrap,不然还得区分两种 chunk 的 runtime wrap

let chunk_global_expr =
quote_ident!("globalThis").computed_member::<Expr>(global.clone().into());
let chunk_global_obj = chunk_global_expr
.clone()
.make_bin::<Expr>(
BinaryOp::LogicalOr,
ArrayLit {
span: DUMMY_SP,
elems: vec![],
}
.into(),
)
.make_assign_to(AssignOp::Assign, chunk_global_expr.clone().as_pat_or_expr())
.wrap_with_paren()
.make_member::<Ident>(quote_ident!("push"));
let chunk_register_stmt = chunk_global_obj
.as_call(
DUMMY_SP,
// [[ "module id"], { module object }]
vec![to_array_lit(vec![
to_array_lit(vec![quote_str!(pot.chunk_id.clone()).as_arg()]).as_arg(),
module_object.as_arg(),
])
.as_arg()],
)
.into_stmt();

Ok(SwcModule {
body: vec![jsonp_callback_stmt.into()],
body: vec![chunk_register_stmt.into()],
shebang: None,
span: DUMMY_SP,
})
Expand Down
30 changes: 30 additions & 0 deletions crates/mako/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub struct OutputConfig {
pub es_version: EsVersion,
pub ascii_only: bool,
pub meta: bool,
pub chunk_loading_global: String,

pub preserve_modules: bool,
pub preserve_modules_root: PathBuf,
Expand Down Expand Up @@ -314,6 +315,7 @@ const DEFAULT_CONFIG: &str = r#"
"esVersion": "es2022",
"meta": false,
"asciiOnly": true,
"chunkLoadingGlobal": "",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我们是不是也叫 namespace 好一点

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

webpack 的默认值也是 "" 吗?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我们是不是也叫 namespace 好一点

webpack 也是叫这个名字:https://webpack.js.org/configuration/output/#outputchunkloadingglobal

webpack 的默认值也是 "" 吗?

不是,Mako 和 webpack 一样默认值是根据配置动态计算的

"preserveModules": false,
"preserveModulesRoot": ""
},
Expand Down Expand Up @@ -403,10 +405,16 @@ impl Config {
let mut ret = c.try_deserialize::<Config>();
// normalize & check
if let Ok(config) = &mut ret {
// normalize output
if config.output.path.is_relative() {
config.output.path = root.join(config.output.path.to_string_lossy().to_string());
}

if config.output.chunk_loading_global.is_empty() {
config.output.chunk_loading_global =
get_default_chunk_loading_global(config.umd.clone(), root);
}

let node_env_config_opt = config.define.get("NODE_ENV");
if let Some(node_env_config) = node_env_config_opt {
if node_env_config.as_str() != Some(config.mode.to_string().as_str()) {
Expand Down Expand Up @@ -530,6 +538,28 @@ impl Default for Config {
}
}

fn get_default_chunk_loading_global(umd: String, root: &Path) -> String {
let unique_name = if umd != "none" {
umd
} else {
let pkg_json_path = root.join("package.json");
let mut pkg_name = "global".to_string();

if pkg_json_path.exists() {
let pkg_json = std::fs::read_to_string(pkg_json_path).unwrap();
let pkg_json: serde_json::Value = serde_json::from_str(&pkg_json).unwrap();

if let Some(name) = pkg_json.get("name") {
pkg_name = name.as_str().unwrap().to_string();
}
}

pkg_name
};

format!("makoChunk_{}", unique_name)
}

#[derive(Error, Debug)]
pub enum ConfigError {
#[error("define value '{0}' is not an Expression")]
Expand Down
1 change: 1 addition & 0 deletions crates/mako/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ pub struct AppRuntimeTemplate {
pub has_dynamic_chunks: bool,
pub has_hmr: bool,
pub umd: Option<String>,
pub chunk_loading_global: String,
}
7 changes: 7 additions & 0 deletions crates/mako/templates/app_runtime.stpl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
function createRuntime(makoModules, entryModuleId) {
var global = typeof globalThis !== 'undefined' ? globalThis : self;
var modulesRegistry = {};

function requireModule(moduleId) {
Expand Down Expand Up @@ -287,6 +288,12 @@ function createRuntime(makoModules, entryModuleId) {
installedChunks[id] = 0;
}
};
var chunkLoadingGlobal = global['<%= chunk_loading_global.clone() %>'] = global['<%= chunk_loading_global.clone() %>'] || [];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这段感觉也是可以按需载入的,如果没有打包出前置加载的 chunk,应该不需要。

Copy link
Member Author

@PeachScript PeachScript Dec 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已线下沟通,先不做处理

chunkLoadingGlobal.forEach(jsonpCallback.bind(null));
chunkLoadingGlobal.push = (function(push, data) {
push(data);
jsonpCallback(data);
}).bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));
<% } %>

var registerModules = function(modules) {
Expand Down