Skip to content

Commit

Permalink
feat(runtime): prefetch&preload (#5031)
Browse files Browse the repository at this point in the history
* feat(runtime): prefetch&preload

* feat(runtime): prefetch&preload

* fix: clippy error

* feat: support prefetch and preload magic comments

* feat: support prefetch and preload in jsonp chunk loading

* fix: clippy error

* test: reuse webpack prefetch&preload config cases

* test: add jsonp chunk loading diff cases

* test: add diff cases for prefetch&preload runtime module

* test: add diff cases for prefetch&preload runtime module

* fix: remove fetch priority

* test: add diff cases for cross-origin and script-type

* test: add diff cases for dynamicImportPrefetch&dynamicImportPreload
  • Loading branch information
LingyuCoder authored and ahabhgk committed Jan 2, 2024
1 parent 3a9fbcf commit cdcb7e2
Show file tree
Hide file tree
Showing 116 changed files with 1,696 additions and 124 deletions.
3 changes: 3 additions & 0 deletions crates/node_binding/binding.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ export const enum BuiltinPluginName {
EnableChunkLoadingPlugin = 'EnableChunkLoadingPlugin',
EnableLibraryPlugin = 'EnableLibraryPlugin',
EnableWasmLoadingPlugin = 'EnableWasmLoadingPlugin',
ChunkPrefetchPreloadPlugin = 'ChunkPrefetchPreloadPlugin',
CommonJsChunkFormatPlugin = 'CommonJsChunkFormatPlugin',
ArrayPushCallbackChunkFormatPlugin = 'ArrayPushCallbackChunkFormatPlugin',
ModuleChunkFormatPlugin = 'ModuleChunkFormatPlugin',
Expand Down Expand Up @@ -840,6 +841,8 @@ export interface RawInfo {

export interface RawJavascriptParserOptions {
dynamicImportMode: string
dynamicImportPreload: string
dynamicImportPrefetch: string
url: string
}

Expand Down
8 changes: 6 additions & 2 deletions crates/rspack_binding_options/src/options/raw_builtins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ use rspack_plugin_mf::{
};
use rspack_plugin_progress::ProgressPlugin;
use rspack_plugin_runtime::{
enable_chunk_loading_plugin, ArrayPushCallbackChunkFormatPlugin, CommonJsChunkFormatPlugin,
ModuleChunkFormatPlugin,
enable_chunk_loading_plugin, ArrayPushCallbackChunkFormatPlugin, ChunkPrefetchPreloadPlugin,
CommonJsChunkFormatPlugin, ModuleChunkFormatPlugin,
};
use rspack_plugin_swc_css_minimizer::SwcCssMinimizerRspackPlugin;
use rspack_plugin_swc_js_minimizer::SwcJsMinimizerRspackPlugin;
Expand Down Expand Up @@ -70,6 +70,7 @@ pub enum BuiltinPluginName {
EnableChunkLoadingPlugin,
EnableLibraryPlugin,
EnableWasmLoadingPlugin,
ChunkPrefetchPreloadPlugin,
CommonJsChunkFormatPlugin,
ArrayPushCallbackChunkFormatPlugin,
ModuleChunkFormatPlugin,
Expand Down Expand Up @@ -167,6 +168,9 @@ impl RawOptionsApply for BuiltinPlugin {
wasm_loading_type.as_str().into(),
));
}
BuiltinPluginName::ChunkPrefetchPreloadPlugin => {
plugins.push(ChunkPrefetchPreloadPlugin.boxed());
}
BuiltinPluginName::CommonJsChunkFormatPlugin => {
plugins.push(CommonJsChunkFormatPlugin.boxed());
}
Expand Down
10 changes: 7 additions & 3 deletions crates/rspack_binding_options/src/options/raw_module/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ use rspack_core::{
AssetGeneratorDataUrl, AssetGeneratorDataUrlOptions, AssetGeneratorOptions,
AssetInlineGeneratorOptions, AssetParserDataUrl, AssetParserDataUrlOptions, AssetParserOptions,
AssetResourceGeneratorOptions, BoxLoader, DescriptionData, DynamicImportMode, FuncUseCtx,
GeneratorOptions, GeneratorOptionsByModuleType, JavascriptParserOptions, JavascriptParserUrl,
ModuleOptions, ModuleRule, ModuleRuleEnforce, ModuleRuleUse, ModuleRuleUseLoader, ModuleType,
ParserOptions, ParserOptionsByModuleType,
GeneratorOptions, GeneratorOptionsByModuleType, JavascriptParserOptions, JavascriptParserOrder,
JavascriptParserUrl, ModuleOptions, ModuleRule, ModuleRuleEnforce, ModuleRuleUse,
ModuleRuleUseLoader, ModuleType, ParserOptions, ParserOptionsByModuleType,
};
use rspack_error::{internal_error, miette::IntoDiagnostic};
use rspack_loader_react_refresh::REACT_REFRESH_LOADER_IDENTIFIER;
Expand Down Expand Up @@ -328,13 +328,17 @@ impl From<RawParserOptions> for ParserOptions {
#[napi(object)]
pub struct RawJavascriptParserOptions {
pub dynamic_import_mode: String,
pub dynamic_import_preload: String,
pub dynamic_import_prefetch: String,
pub url: String,
}

impl From<RawJavascriptParserOptions> for JavascriptParserOptions {
fn from(value: RawJavascriptParserOptions) -> Self {
Self {
dynamic_import_mode: DynamicImportMode::from(value.dynamic_import_mode.as_str()),
dynamic_import_preload: JavascriptParserOrder::from(value.dynamic_import_preload.as_str()),
dynamic_import_prefetch: JavascriptParserOrder::from(value.dynamic_import_prefetch.as_str()),
url: JavascriptParserUrl::from(value.url.as_str()),
}
}
Expand Down
8 changes: 7 additions & 1 deletion crates/rspack_core/src/build_chunk_graph/code_splitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,16 @@ pub(super) struct CodeSplitter<'me> {
}

fn add_chunk_in_group(group_options: Option<&GroupOptions>, info: ChunkGroupInfo) -> ChunkGroup {
let options = ChunkGroupOptions::default().name_optional(
let options = ChunkGroupOptions::new(
group_options
.and_then(|x| x.name())
.map(|name| name.to_string()),
group_options
.and_then(|x| x.normal_options())
.and_then(|x| x.preload_order),
group_options
.and_then(|x| x.normal_options())
.and_then(|x| x.prefetch_order),
);
let kind = ChunkGroupKind::Normal { options };
ChunkGroup::new(kind, info)
Expand Down
175 changes: 173 additions & 2 deletions crates/rspack_core/src/chunk.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use std::cmp::Ordering;
use std::hash::BuildHasherDefault;
use std::{fmt::Debug, hash::Hash, sync::Arc};

use indexmap::IndexSet;
use indexmap::{IndexMap, IndexSet};
use itertools::Itertools;
use rspack_database::{DatabaseItem, Ukey};
use rspack_hash::{RspackHash, RspackHashDigest};
use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet, FxHasher};

use crate::{sort_group_by_index, ChunkGraph, ChunkGroup};
use crate::{compare_chunk_group, sort_group_by_index, ChunkGraph, ChunkGroup, ChunkGroupOrderKey};
use crate::{ChunkGroupByUkey, ChunkGroupUkey, ChunkUkey, SourceType};
use crate::{Compilation, EntryOptions, Filename, ModuleGraph, RuntimeSpec};

Expand Down Expand Up @@ -446,6 +447,176 @@ impl Chunk {
pub fn remove_group(&mut self, chunk_group: &ChunkGroupUkey) {
self.groups.remove(chunk_group);
}

pub fn get_children_of_type_in_order(
&self,
order_key: &ChunkGroupOrderKey,
compilation: &Compilation,
is_self_last_chunk: bool,
) -> Option<Vec<(Vec<ChunkUkey>, Vec<ChunkUkey>)>> {
let mut list = vec![];
let chunk_group_by_ukey = &compilation.chunk_group_by_ukey;
for group_ukey in self.get_sorted_groups_iter(chunk_group_by_ukey) {
let group = chunk_group_by_ukey
.get(group_ukey)
.expect("chunk group do not exists");
if let Some(last_chunk) = group.chunks.last() {
if is_self_last_chunk && !last_chunk.eq(&self.ukey) {
continue;
}
}

for child_group_ukey in group
.children
.iter()
.sorted_by(|a, b| sort_group_by_index(a, b, chunk_group_by_ukey))
{
let child_group = chunk_group_by_ukey
.get(child_group_ukey)
.expect("child group do not exists");
let order = child_group
.kind
.get_normal_options()
.and_then(|o| match order_key {
ChunkGroupOrderKey::Prefetch => o.prefetch_order,
ChunkGroupOrderKey::Preload => o.preload_order,
});
if let Some(order) = order {
list.push((order, group_ukey.to_owned(), child_group_ukey.to_owned()));
}
}
}

if list.is_empty() {
return None;
}

list.sort_by(|a, b| {
let order = b.0.cmp(&a.0);
match order {
Ordering::Equal => compare_chunk_group(&a.1, &b.1, compilation),
_ => order,
}
});

let mut result: IndexMap<
ChunkGroupUkey,
IndexSet<ChunkUkey, BuildHasherDefault<FxHasher>>,
BuildHasherDefault<FxHasher>,
> = IndexMap::default();
for (_, group_ukey, child_group_ukey) in list.iter() {
let child_group = chunk_group_by_ukey
.get(child_group_ukey)
.expect("chunk group do not exists");

result
.entry(group_ukey.to_owned())
.or_default()
.extend(child_group.chunks.iter());
}

Some(
result
.iter()
.map(|(group_ukey, chunks)| {
let group = chunk_group_by_ukey
.get(group_ukey)
.expect("chunk group do not exists");
(
group.chunks.clone(),
chunks.iter().map(|x| x.to_owned()).collect_vec(),
)
})
.collect_vec(),
)
}

pub fn get_child_ids_by_orders_map(
&self,
include_direct_children: bool,
compilation: &Compilation,
) -> HashMap<ChunkGroupOrderKey, IndexMap<String, Vec<String>, BuildHasherDefault<FxHasher>>> {
let mut result = HashMap::default();

fn add_child_ids_by_orders_to_map(
chunk_ukey: &ChunkUkey,
order: &ChunkGroupOrderKey,
result: &mut HashMap<
ChunkGroupOrderKey,
IndexMap<String, Vec<String>, BuildHasherDefault<FxHasher>>,
>,
compilation: &Compilation,
) {
let chunk = compilation
.chunk_by_ukey
.get(chunk_ukey)
.expect("chunk do not exists");
let order_children = chunk.get_children_of_type_in_order(order, compilation, true);
if let (Some(chunk_id), Some(order_children)) = (chunk.id.to_owned(), order_children) {
let child_chunk_ids = order_children
.iter()
.flat_map(|(_, child_chunks)| {
child_chunks.iter().filter_map(|chunk_ukey| {
compilation
.chunk_by_ukey
.get(chunk_ukey)
.expect("chunk do not exists")
.id
.to_owned()
})
})
.collect_vec();

result
.entry(order.clone())
.or_default()
.insert(chunk_id, child_chunk_ids);
}
}

if include_direct_children {
for chunk_ukey in self
.get_sorted_groups_iter(&compilation.chunk_group_by_ukey)
.filter_map(|chunk_group_ukey| {
compilation
.chunk_group_by_ukey
.get(chunk_group_ukey)
.map(|g| g.chunks.to_owned())
})
.flatten()
{
add_child_ids_by_orders_to_map(
&chunk_ukey,
&ChunkGroupOrderKey::Prefetch,
&mut result,
compilation,
);
add_child_ids_by_orders_to_map(
&chunk_ukey,
&ChunkGroupOrderKey::Preload,
&mut result,
compilation,
);
}
}

for chunk_ukey in self.get_all_async_chunks(&compilation.chunk_group_by_ukey) {
add_child_ids_by_orders_to_map(
&chunk_ukey,
&ChunkGroupOrderKey::Prefetch,
&mut result,
compilation,
);
add_child_ids_by_orders_to_map(
&chunk_ukey,
&ChunkGroupOrderKey::Preload,
&mut result,
compilation,
);
}

result
}
}

pub fn chunk_hash_js<'a>(
Expand Down
33 changes: 33 additions & 0 deletions crates/rspack_core/src/chunk_group.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,13 @@ impl ChunkGroupKind {
}
}

pub fn get_normal_options(&self) -> Option<&ChunkGroupOptions> {
match self {
ChunkGroupKind::Entrypoint { .. } => None,
ChunkGroupKind::Normal { options, .. } => Some(options),
}
}

pub fn name(&self) -> Option<&str> {
match self {
ChunkGroupKind::Entrypoint { options, .. } => options.name.as_deref(),
Expand All @@ -315,12 +322,31 @@ pub struct EntryOptions {
pub library: Option<LibraryOptions>,
}

#[derive(Debug, Hash, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum ChunkGroupOrderKey {
Preload,
Prefetch,
}

#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct ChunkGroupOptions {
pub name: Option<String>,
pub preload_order: Option<u32>,
pub prefetch_order: Option<u32>,
}

impl ChunkGroupOptions {
pub fn new(
name: Option<String>,
preload_order: Option<u32>,
prefetch_order: Option<u32>,
) -> Self {
Self {
name,
preload_order,
prefetch_order,
}
}
pub fn name_optional(mut self, name: Option<String>) -> Self {
self.name = name;
self
Expand All @@ -347,6 +373,13 @@ impl GroupOptions {
GroupOptions::ChunkGroup(_) => None,
}
}

pub fn normal_options(&self) -> Option<&ChunkGroupOptions> {
match self {
GroupOptions::Entrypoint(_) => None,
GroupOptions::ChunkGroup(e) => Some(e),
}
}
}

#[derive(Debug, Default, Clone)]
Expand Down
8 changes: 6 additions & 2 deletions crates/rspack_core/src/context_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -774,7 +774,9 @@ impl ContextModule {
{
let name = self.options.context_options.chunk_name.clone();
let mut block = AsyncDependenciesBlock::new(self.identifier, "", None);
block.set_group_options(GroupOptions::ChunkGroup(ChunkGroupOptions { name }));
block.set_group_options(GroupOptions::ChunkGroup(ChunkGroupOptions::new(
name, None, None,
)));
for context_element_dependency in context_element_dependencies {
block.add_dependency(Box::new(context_element_dependency));
}
Expand Down Expand Up @@ -806,7 +808,9 @@ impl ContextModule {
&context_element_dependency.user_request,
None,
);
block.set_group_options(GroupOptions::ChunkGroup(ChunkGroupOptions { name }));
block.set_group_options(GroupOptions::ChunkGroup(ChunkGroupOptions::new(
name, None, None,
)));
block.add_dependency(Box::new(context_element_dependency));
blocks.push(block);
}
Expand Down
Loading

0 comments on commit cdcb7e2

Please sign in to comment.