From 64fe33bbd9b678892e222ea753c466305cfef0ff Mon Sep 17 00:00:00 2001 From: Fy <1114550440@qq.com> Date: Tue, 7 Nov 2023 14:32:01 +0800 Subject: [PATCH] feat: should have null as placeholder in stats chunks before chunkIds (#4549) * Revert "revert: should have null as placeholder in stats chunks before chunkIds (#4492)" This reverts commit 7e923841f41e337656067b2814c387091969599b. * fix(napi): add codegen-units to 16 --- Cargo.lock | 62 +++++++++++ Cargo.toml | 5 +- crates/node_binding/Cargo.toml | 1 + crates/node_binding/binding.d.ts | 27 ++++- crates/node_binding/src/lib.rs | 10 +- crates/node_binding/src/plugins/mod.rs | 8 +- crates/rspack_binding_options/Cargo.toml | 1 + crates/rspack_binding_options/src/chunk.rs | 32 ------ crates/rspack_binding_options/src/lib.rs | 1 - .../src/options/raw_builtins/raw_banner.rs | 3 +- crates/rspack_binding_values/Cargo.toml | 62 +++++++++++ crates/rspack_binding_values/LICENSE | 22 ++++ .../src}/asset.rs | 2 + crates/rspack_binding_values/src/chunk.rs | 104 ++++++++++++++++++ .../src}/chunk_group.rs | 4 +- .../src}/compilation.rs | 9 +- .../src}/hooks.rs | 1 + .../src/lib.rs} | 9 +- .../src}/module.rs | 1 + .../src}/normal_module_factory.rs | 1 + .../src}/path_data.rs | 2 + .../src}/source.rs | 1 + .../src}/stats.rs | 17 ++- .../src/utils.rs | 22 ++-- crates/rspack_core/Cargo.toml | 1 + crates/rspack_core/src/lib.rs | 12 ++ crates/rspack_core/src/stats.rs | 38 ++++--- crates/rspack_database/src/ukey.rs | 12 ++ packages/rspack/src/Chunk.ts | 72 ++++++++++++ packages/rspack/src/Compilation.ts | 32 ++---- .../src/stats/DefaultStatsFactoryPlugin.ts | 2 +- packages/rspack/src/util/comparators.ts | 2 +- packages/rspack/tests/Stats.test.ts | 34 +++++- 33 files changed, 490 insertions(+), 122 deletions(-) delete mode 100644 crates/rspack_binding_options/src/chunk.rs create mode 100644 crates/rspack_binding_values/Cargo.toml create mode 100644 crates/rspack_binding_values/LICENSE rename crates/{node_binding/src/js_values => rspack_binding_values/src}/asset.rs (99%) create mode 100644 crates/rspack_binding_values/src/chunk.rs rename crates/{node_binding/src/js_values => rspack_binding_values/src}/chunk_group.rs (92%) rename crates/{node_binding/src/js_values => rspack_binding_values/src}/compilation.rs (97%) rename crates/{node_binding/src/js_values => rspack_binding_values/src}/hooks.rs (98%) rename crates/{node_binding/src/js_values/mod.rs => rspack_binding_values/src/lib.rs} (73%) rename crates/{node_binding/src/js_values => rspack_binding_values/src}/module.rs (97%) rename crates/{node_binding/src/js_values => rspack_binding_values/src}/normal_module_factory.rs (99%) rename crates/{node_binding/src/js_values => rspack_binding_values/src}/path_data.rs (97%) rename crates/{node_binding/src/js_values => rspack_binding_values/src}/source.rs (99%) rename crates/{node_binding/src/js_values => rspack_binding_values/src}/stats.rs (98%) rename crates/{node_binding => rspack_binding_values}/src/utils.rs (84%) create mode 100644 packages/rspack/src/Chunk.ts diff --git a/Cargo.lock b/Cargo.lock index 8998d3caeb2..ccd8252793e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2417,6 +2417,7 @@ dependencies = [ "napi", "napi-derive", "rspack_binding_macros", + "rspack_binding_values", "rspack_core", "rspack_error", "rspack_identifier", @@ -2464,6 +2465,65 @@ dependencies = [ "tracing", ] +[[package]] +name = "rspack_binding_values" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "better_scoped_tls", + "dashmap", + "derivative", + "futures", + "glob", + "napi", + "napi-derive", + "napi-sys", + "rspack_binding_macros", + "rspack_core", + "rspack_error", + "rspack_identifier", + "rspack_ids", + "rspack_loader_react_refresh", + "rspack_loader_runner", + "rspack_loader_sass", + "rspack_loader_swc", + "rspack_napi_shared", + "rspack_plugin_asset", + "rspack_plugin_banner", + "rspack_plugin_copy", + "rspack_plugin_css", + "rspack_plugin_dev_friendly_split_chunks", + "rspack_plugin_devtool", + "rspack_plugin_ensure_chunk_conditions", + "rspack_plugin_entry", + "rspack_plugin_externals", + "rspack_plugin_hmr", + "rspack_plugin_html", + "rspack_plugin_javascript", + "rspack_plugin_json", + "rspack_plugin_library", + "rspack_plugin_progress", + "rspack_plugin_real_content_hash", + "rspack_plugin_remove_empty_chunks", + "rspack_plugin_runtime", + "rspack_plugin_schemes", + "rspack_plugin_split_chunks", + "rspack_plugin_split_chunks_new", + "rspack_plugin_swc_css_minimizer", + "rspack_plugin_swc_js_minimizer", + "rspack_plugin_wasm", + "rspack_plugin_worker", + "rspack_regex", + "rspack_swc_visitors", + "rustc-hash", + "serde", + "serde_json", + "swc_core", + "tokio", + "tracing", +] + [[package]] name = "rspack_core" version = "0.1.0" @@ -2476,6 +2536,7 @@ dependencies = [ "dashmap", "derivative", "dyn-clone", + "either", "futures", "glob-match", "hashlink", @@ -2712,6 +2773,7 @@ dependencies = [ "once_cell", "rspack_binding_macros", "rspack_binding_options", + "rspack_binding_values", "rspack_core", "rspack_error", "rspack_fs_node", diff --git a/Cargo.toml b/Cargo.toml index 10ccc45f224..ae78a85a5c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,8 +68,9 @@ swc_node_comments = { version = "=0.20.2" } tikv-jemallocator = { version = "=0.5.4", features = ["disable_initial_exec_tls"] } [profile.dev] -debug = 2 -incremental = true +codegen-units = 16 # debug build will cause runtime panic if codegen-unints is default +debug = 2 +incremental = true [profile.release] codegen-units = 1 diff --git a/crates/node_binding/Cargo.toml b/crates/node_binding/Cargo.toml index afe65c95e4e..dc877eb45ef 100644 --- a/crates/node_binding/Cargo.toml +++ b/crates/node_binding/Cargo.toml @@ -12,6 +12,7 @@ crate-type = ["cdylib"] [dependencies] rspack_binding_macros = { path = "../rspack_binding_macros" } rspack_binding_options = { path = "../rspack_binding_options" } +rspack_binding_values = { path = "../rspack_binding_values" } rspack_core = { path = "../rspack_core" } rspack_error = { path = "../rspack_error" } rspack_fs_node = { path = "../rspack_fs_node" } diff --git a/crates/node_binding/binding.d.ts b/crates/node_binding/binding.d.ts index 7a4c59c0107..6561e1f1a66 100644 --- a/crates/node_binding/binding.d.ts +++ b/crates/node_binding/binding.d.ts @@ -56,7 +56,7 @@ export class JsStats { getErrors(): Array getWarnings(): Array getLogging(acceptedTypes: number): Array - getHash(): string + getHash(): string | null } export class Rspack { @@ -96,6 +96,12 @@ export class Rspack { unsafe_drop(): void } +export function __chunk_inner_can_be_initial(jsChunk: JsChunk, compilation: JsCompilation): boolean + +export function __chunk_inner_has_runtime(jsChunk: JsChunk, compilation: JsCompilation): boolean + +export function __chunk_inner_is_only_initial(jsChunk: JsChunk, compilation: JsCompilation): boolean + export interface AfterResolveData { request: string context: string @@ -198,8 +204,19 @@ export interface JsAssetInfoRelated { } export interface JsChunk { + __inner_ukey: number name?: string + id?: string + ids: Array + idNameHints: Array + filenameTemplate?: string + cssFilenameTemplate?: string files: Array + runtime: Array + hash?: string + contentHash: Record + renderedHash?: string + chunkReasons: Array } export interface JsChunkAssetArgs { @@ -333,7 +350,7 @@ export interface JsStatsAsset { type: string name: string size: number - chunks: Array + chunks: Array chunkNames: Array info: JsStatsAssetInfo emitted: boolean @@ -354,7 +371,7 @@ export interface JsStatsChunk { type: string files: Array auxiliaryFiles: Array - id: string + id?: string entry: boolean initial: boolean names: Array @@ -368,7 +385,7 @@ export interface JsStatsChunk { export interface JsStatsChunkGroup { name: string assets: Array - chunks: Array + chunks: Array assetsSize: number } @@ -406,7 +423,7 @@ export interface JsStatsModule { identifier: string name: string id?: string - chunks: Array + chunks: Array size: number issuer?: string issuerName?: string diff --git a/crates/node_binding/src/lib.rs b/crates/node_binding/src/lib.rs index c99a6b75674..76559dde34e 100644 --- a/crates/node_binding/src/lib.rs +++ b/crates/node_binding/src/lib.rs @@ -4,9 +4,6 @@ #[macro_use] extern crate napi_derive; -#[macro_use] -extern crate rspack_binding_macros; - use std::collections::HashSet; use std::pin::Pin; use std::sync::atomic::{AtomicU32, Ordering}; @@ -15,26 +12,23 @@ use std::sync::Mutex; use napi::bindgen_prelude::*; use once_cell::sync::Lazy; use rspack_binding_options::BuiltinPlugin; +use rspack_binding_values::SingleThreadedHashMap; use rspack_core::PluginExt; use rspack_fs_node::{AsyncNodeWritableFileSystem, ThreadsafeNodeFS}; use rspack_napi_shared::NAPI_ENV; mod hook; -mod js_values; mod loader; mod plugins; -mod utils; use hook::*; -use js_values::*; // Napi macro registered this successfully #[allow(unused)] use loader::*; use plugins::*; use rspack_binding_options::*; +use rspack_binding_values::*; use rspack_tracing::chrome::FlushGuard; -use utils::*; - #[cfg(not(target_os = "linux"))] #[global_allocator] static GLOBAL: mimalloc_rust::GlobalMiMalloc = mimalloc_rust::GlobalMiMalloc; diff --git a/crates/node_binding/src/plugins/mod.rs b/crates/node_binding/src/plugins/mod.rs index 619404e227f..6b71ab227fd 100644 --- a/crates/node_binding/src/plugins/mod.rs +++ b/crates/node_binding/src/plugins/mod.rs @@ -6,6 +6,10 @@ use async_trait::async_trait; pub use loader::JsLoaderResolver; use napi::{Env, Result}; use rspack_binding_macros::js_fn_into_threadsafe_fn; +use rspack_binding_values::{ + AfterResolveData, BeforeResolveData, JsAssetEmittedArgs, JsChunkAssetArgs, JsModule, + JsResolveForSchemeInput, JsResolveForSchemeResult, ToJsModule, +}; use rspack_core::{ ChunkAssetArgs, NormalModuleAfterResolveArgs, NormalModuleBeforeResolveArgs, PluginNormalModuleFactoryAfterResolveOutput, PluginNormalModuleFactoryBeforeResolveOutput, @@ -15,10 +19,6 @@ use rspack_error::internal_error; use rspack_napi_shared::threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode}; use rspack_napi_shared::NapiResultExt; -use crate::js_values::{ - AfterResolveData, BeforeResolveData, JsAssetEmittedArgs, JsChunkAssetArgs, JsModule, - JsResolveForSchemeInput, JsResolveForSchemeResult, ToJsModule, -}; use crate::{DisabledHooks, Hook, JsCompilation, JsHooks}; pub struct JsHooksAdapter { diff --git a/crates/rspack_binding_options/Cargo.toml b/crates/rspack_binding_options/Cargo.toml index c754d357fe9..c617feac715 100644 --- a/crates/rspack_binding_options/Cargo.toml +++ b/crates/rspack_binding_options/Cargo.toml @@ -7,6 +7,7 @@ version = "0.1.0" [dependencies] rspack_binding_macros = { path = "../rspack_binding_macros" } +rspack_binding_values = { path = "../rspack_binding_values" } rspack_core = { path = "../rspack_core" } rspack_error = { path = "../rspack_error" } rspack_identifier = { path = "../rspack_identifier" } diff --git a/crates/rspack_binding_options/src/chunk.rs b/crates/rspack_binding_options/src/chunk.rs deleted file mode 100644 index eaefe924633..00000000000 --- a/crates/rspack_binding_options/src/chunk.rs +++ /dev/null @@ -1,32 +0,0 @@ -use napi_derive::napi; -use rspack_core::ChunkAssetArgs; - -#[napi(object)] -pub struct JsChunk { - pub name: Option, - pub files: Vec, -} - -impl JsChunk { - pub fn from(chunk: &rspack_core::Chunk) -> Self { - let name = chunk.name.clone(); - let mut files = Vec::from_iter(chunk.files.iter().cloned()); - files.sort_unstable(); - Self { name, files } - } -} - -#[napi(object)] -pub struct JsChunkAssetArgs { - pub chunk: JsChunk, - pub filename: String, -} - -impl From<&ChunkAssetArgs<'_>> for JsChunkAssetArgs { - fn from(value: &ChunkAssetArgs) -> Self { - Self { - chunk: JsChunk::from(value.chunk), - filename: value.filename.to_string(), - } - } -} diff --git a/crates/rspack_binding_options/src/lib.rs b/crates/rspack_binding_options/src/lib.rs index 960c3f89206..4e0c9dc1bb1 100644 --- a/crates/rspack_binding_options/src/lib.rs +++ b/crates/rspack_binding_options/src/lib.rs @@ -1,3 +1,2 @@ -pub mod chunk; mod options; pub use options::*; diff --git a/crates/rspack_binding_options/src/options/raw_builtins/raw_banner.rs b/crates/rspack_binding_options/src/options/raw_builtins/raw_banner.rs index 5767c5cb949..c400b6a0139 100644 --- a/crates/rspack_binding_options/src/options/raw_builtins/raw_banner.rs +++ b/crates/rspack_binding_options/src/options/raw_builtins/raw_banner.rs @@ -3,6 +3,7 @@ use std::{fmt::Debug, sync::Arc}; use derivative::Derivative; use napi::{Env, JsFunction}; use napi_derive::napi; +use rspack_binding_values::JsChunk; use rspack_error::internal_error; use rspack_napi_shared::{ threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode}, @@ -13,8 +14,6 @@ use rspack_plugin_banner::{ }; use serde::Deserialize; -use crate::chunk::JsChunk; - #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] #[napi(object)] diff --git a/crates/rspack_binding_values/Cargo.toml b/crates/rspack_binding_values/Cargo.toml new file mode 100644 index 00000000000..689ab76b877 --- /dev/null +++ b/crates/rspack_binding_values/Cargo.toml @@ -0,0 +1,62 @@ +[package] +edition = "2021" +license = "MIT" +name = "rspack_binding_values" +repository = "https://github.com/web-infra-dev/rspack" +version = "0.1.0" + +[dependencies] +rspack_binding_macros = { path = "../rspack_binding_macros" } +rspack_core = { path = "../rspack_core" } +rspack_error = { path = "../rspack_error" } +rspack_identifier = { path = "../rspack_identifier" } +rspack_ids = { path = "../rspack_ids" } +rspack_loader_react_refresh = { path = "../rspack_loader_react_refresh" } +rspack_loader_runner = { path = "../rspack_loader_runner" } +rspack_loader_sass = { path = "../rspack_loader_sass" } +rspack_loader_swc = { path = "../rspack_loader_swc" } +rspack_napi_shared = { path = "../rspack_napi_shared" } +rspack_plugin_asset = { path = "../rspack_plugin_asset" } +rspack_plugin_banner = { path = "../rspack_plugin_banner" } +rspack_plugin_copy = { path = "../rspack_plugin_copy" } +rspack_plugin_css = { path = "../rspack_plugin_css" } +rspack_plugin_dev_friendly_split_chunks = { path = "../rspack_plugin_dev_friendly_split_chunks" } +rspack_plugin_devtool = { path = "../rspack_plugin_devtool" } +rspack_plugin_ensure_chunk_conditions = { path = "../rspack_plugin_ensure_chunk_conditions" } +rspack_plugin_entry = { path = "../rspack_plugin_entry" } +rspack_plugin_externals = { path = "../rspack_plugin_externals" } +rspack_plugin_hmr = { path = "../rspack_plugin_hmr" } +rspack_plugin_html = { path = "../rspack_plugin_html" } +rspack_plugin_javascript = { path = "../rspack_plugin_javascript" } +rspack_plugin_json = { path = "../rspack_plugin_json" } +rspack_plugin_library = { path = "../rspack_plugin_library" } +rspack_plugin_progress = { path = "../rspack_plugin_progress" } +rspack_plugin_real_content_hash = { path = "../rspack_plugin_real_content_hash" } +rspack_plugin_remove_empty_chunks = { path = "../rspack_plugin_remove_empty_chunks" } +rspack_plugin_runtime = { path = "../rspack_plugin_runtime" } +rspack_plugin_schemes = { path = "../rspack_plugin_schemes" } +rspack_plugin_split_chunks = { path = "../rspack_plugin_split_chunks" } +rspack_plugin_split_chunks_new = { path = "../rspack_plugin_split_chunks_new" } +rspack_plugin_swc_css_minimizer = { path = "../rspack_plugin_swc_css_minimizer" } +rspack_plugin_swc_js_minimizer = { path = "../rspack_plugin_swc_js_minimizer" } +rspack_plugin_wasm = { path = "../rspack_plugin_wasm" } +rspack_plugin_worker = { path = "../rspack_plugin_worker" } +rspack_regex = { path = "../rspack_regex" } +rspack_swc_visitors = { path = "../rspack_swc_visitors" } + +anyhow = { workspace = true, features = ["backtrace"] } +async-trait = { workspace = true } +better_scoped_tls = { workspace = true } +dashmap = { workspace = true } +derivative = { workspace = true } +futures = { workspace = true } +glob = { workspace = true } +napi = { workspace = true, features = ["async", "tokio_rt", "serde-json", "anyhow"] } +napi-derive = { workspace = true } +napi-sys = { workspace = true } +rustc-hash = { workspace = true } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } +swc_core = { workspace = true, default-features = false, features = ["ecma_transforms_react"] } +tokio = { workspace = true, features = ["rt", "rt-multi-thread", "macros", "test-util", "parking_lot"] } +tracing = { workspace = true } diff --git a/crates/rspack_binding_values/LICENSE b/crates/rspack_binding_values/LICENSE new file mode 100644 index 00000000000..46310101ad8 --- /dev/null +++ b/crates/rspack_binding_values/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2022-present Bytedance, Inc. and its affiliates. + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/crates/node_binding/src/js_values/asset.rs b/crates/rspack_binding_values/src/asset.rs similarity index 99% rename from crates/node_binding/src/js_values/asset.rs rename to crates/rspack_binding_values/src/asset.rs index 48a2b31635c..796265396c9 100644 --- a/crates/node_binding/src/js_values/asset.rs +++ b/crates/rspack_binding_values/src/asset.rs @@ -1,3 +1,5 @@ +use napi_derive::napi; + use super::JsCompatSource; #[napi(object)] diff --git a/crates/rspack_binding_values/src/chunk.rs b/crates/rspack_binding_values/src/chunk.rs new file mode 100644 index 00000000000..090441f3eed --- /dev/null +++ b/crates/rspack_binding_values/src/chunk.rs @@ -0,0 +1,104 @@ +use std::collections::HashMap; + +use napi_derive::napi; +use rspack_core::{Chunk, ChunkAssetArgs, ChunkUkey, Compilation}; + +use crate::JsCompilation; + +#[napi(object)] +pub struct JsChunk { + #[napi(js_name = "__inner_ukey")] + pub inner_ukey: u32, // ChunkUkey + pub name: Option, + pub id: Option, + pub ids: Vec, + pub id_name_hints: Vec, + pub filename_template: Option, + pub css_filename_template: Option, + pub files: Vec, + pub runtime: Vec, + pub hash: Option, + pub content_hash: HashMap, + pub rendered_hash: Option, + pub chunk_reasons: Vec, +} + +impl JsChunk { + pub fn from(chunk: &rspack_core::Chunk) -> Self { + let name = chunk.name.clone(); + let mut files = Vec::from_iter(chunk.files.iter().cloned()); + files.sort_unstable(); + + Self { + inner_ukey: usize::from(chunk.ukey) as u32, + name, + id: chunk.id.clone(), + ids: chunk.ids.clone(), + id_name_hints: Vec::from_iter(chunk.id_name_hints.clone()), + filename_template: chunk + .filename_template + .as_ref() + .map(|tpl| tpl.template().to_string()), + css_filename_template: chunk + .css_filename_template + .as_ref() + .map(|tpl| tpl.template().to_string()), + files, + runtime: Vec::::from_iter(chunk.runtime.clone().into_iter().map(|r| r.to_string())), + hash: chunk.hash.as_ref().map(|d| d.encoded().to_string()), + content_hash: chunk + .content_hash + .iter() + .map(|(key, v)| (key.to_string(), v.encoded().to_string())) + .collect::>(), + rendered_hash: chunk.rendered_hash.as_ref().map(|hash| hash.to_string()), + chunk_reasons: chunk.chunk_reasons.clone(), + } + } + + fn chunk<'compilation>(&self, compilation: &'compilation Compilation) -> &'compilation Chunk { + let inner_key = self.inner_ukey; + let ukey = ChunkUkey::from(inner_key as usize); + + compilation + .chunk_by_ukey + .get(&ukey) + .expect("Chunk must exist") + } +} + +#[napi(js_name = "__chunk_inner_is_only_initial")] +pub fn is_only_initial(js_chunk: JsChunk, compilation: &JsCompilation) -> bool { + let compilation = &compilation.inner; + let chunk = js_chunk.chunk(compilation); + chunk.is_only_initial(&compilation.chunk_group_by_ukey) +} + +#[napi(js_name = "__chunk_inner_can_be_initial")] +pub fn can_be_initial(js_chunk: JsChunk, compilation: &JsCompilation) -> bool { + let compilation = &compilation.inner; + let chunk = js_chunk.chunk(compilation); + chunk.can_be_initial(&compilation.chunk_group_by_ukey) +} + +#[napi(js_name = "__chunk_inner_has_runtime")] +pub fn has_runtime(js_chunk: JsChunk, compilation: &JsCompilation) -> bool { + let compilation = &compilation.inner; + let chunk = js_chunk.chunk(compilation); + chunk.has_runtime(&compilation.chunk_group_by_ukey) +} + +#[napi(object)] +pub struct JsChunkAssetArgs { + pub chunk: JsChunk, + pub filename: String, +} + +impl From<&ChunkAssetArgs<'_>> for JsChunkAssetArgs { + fn from(value: &ChunkAssetArgs) -> Self { + Self { + chunk: JsChunk::from(value.chunk), + filename: value.filename.to_string(), + } + } +} diff --git a/crates/node_binding/src/js_values/chunk_group.rs b/crates/rspack_binding_values/src/chunk_group.rs similarity index 92% rename from crates/node_binding/src/js_values/chunk_group.rs rename to crates/rspack_binding_values/src/chunk_group.rs index 557e72c0fee..48085a00f68 100644 --- a/crates/node_binding/src/js_values/chunk_group.rs +++ b/crates/rspack_binding_values/src/chunk_group.rs @@ -1,4 +1,6 @@ -use crate::js_values::JsChunk; +use napi_derive::napi; + +use crate::JsChunk; #[napi(object)] pub struct JsChunkGroup { diff --git a/crates/node_binding/src/js_values/compilation.rs b/crates/rspack_binding_values/src/compilation.rs similarity index 97% rename from crates/node_binding/src/js_values/compilation.rs rename to crates/rspack_binding_values/src/compilation.rs index a1203c896c7..b90964edb63 100644 --- a/crates/node_binding/src/js_values/compilation.rs +++ b/crates/rspack_binding_values/src/compilation.rs @@ -3,6 +3,9 @@ use std::path::PathBuf; use napi::bindgen_prelude::*; use napi::NapiRaw; +use napi_derive::napi; +use rspack_binding_macros::call_js_function_with_napi_objects; +use rspack_binding_macros::convert_raw_napi_value_to_napi_value; use rspack_core::rspack_sources::BoxSource; use rspack_core::AssetInfo; use rspack_core::ModuleIdentifier; @@ -15,13 +18,13 @@ use super::module::ToJsModule; use super::PathWithInfo; use crate::utils::callbackify; use crate::{ - js_values::{chunk::JsChunk, module::JsModule, PathData}, - CompatSource, JsAsset, JsAssetInfo, JsChunkGroup, JsCompatSource, JsStats, ToJsCompatSource, + chunk::JsChunk, module::JsModule, CompatSource, JsAsset, JsAssetInfo, JsChunkGroup, + JsCompatSource, JsStats, PathData, ToJsCompatSource, }; #[napi] pub struct JsCompilation { - inner: &'static mut rspack_core::Compilation, + pub(crate) inner: &'static mut rspack_core::Compilation, } #[napi] diff --git a/crates/node_binding/src/js_values/hooks.rs b/crates/rspack_binding_values/src/hooks.rs similarity index 98% rename from crates/node_binding/src/js_values/hooks.rs rename to crates/rspack_binding_values/src/hooks.rs index 3a401a52603..8c5dbad6dba 100644 --- a/crates/node_binding/src/js_values/hooks.rs +++ b/crates/rspack_binding_values/src/hooks.rs @@ -1,4 +1,5 @@ use napi::bindgen_prelude::*; +use napi_derive::napi; #[napi(object)] pub struct JsHooks { diff --git a/crates/node_binding/src/js_values/mod.rs b/crates/rspack_binding_values/src/lib.rs similarity index 73% rename from crates/node_binding/src/js_values/mod.rs rename to crates/rspack_binding_values/src/lib.rs index c2959a499c8..2b9df5c2119 100644 --- a/crates/node_binding/src/js_values/mod.rs +++ b/crates/rspack_binding_values/src/lib.rs @@ -1,9 +1,6 @@ -mod chunk { - // TODO: should we merge rspack_binding_options and node_binding? - pub use rspack_binding_options::chunk::*; -} - +#![feature(try_blocks)] mod asset; +mod chunk; mod chunk_group; mod compilation; mod hooks; @@ -12,6 +9,7 @@ mod normal_module_factory; mod path_data; mod source; mod stats; +mod utils; pub use asset::*; pub use chunk::*; @@ -23,3 +21,4 @@ pub use normal_module_factory::*; pub use path_data::*; pub use source::*; pub use stats::*; +pub use utils::*; diff --git a/crates/node_binding/src/js_values/module.rs b/crates/rspack_binding_values/src/module.rs similarity index 97% rename from crates/node_binding/src/js_values/module.rs rename to crates/rspack_binding_values/src/module.rs index 8ddf315f67d..ee5f7999ce4 100644 --- a/crates/node_binding/src/js_values/module.rs +++ b/crates/rspack_binding_values/src/module.rs @@ -1,4 +1,5 @@ use napi::bindgen_prelude::*; +use napi_derive::napi; use rspack_core::Module; use rspack_identifier::Identifiable; diff --git a/crates/node_binding/src/js_values/normal_module_factory.rs b/crates/rspack_binding_values/src/normal_module_factory.rs similarity index 99% rename from crates/node_binding/src/js_values/normal_module_factory.rs rename to crates/rspack_binding_values/src/normal_module_factory.rs index feb0296a2eb..2f157b672c7 100644 --- a/crates/node_binding/src/js_values/normal_module_factory.rs +++ b/crates/rspack_binding_values/src/normal_module_factory.rs @@ -1,3 +1,4 @@ +use napi_derive::napi; use rspack_core::{NormalModuleAfterResolveArgs, NormalModuleBeforeResolveArgs, ResourceData}; #[napi(object)] diff --git a/crates/node_binding/src/js_values/path_data.rs b/crates/rspack_binding_values/src/path_data.rs similarity index 97% rename from crates/node_binding/src/js_values/path_data.rs rename to crates/rspack_binding_values/src/path_data.rs index 68d5afcb66e..ae7b2c38303 100644 --- a/crates/node_binding/src/js_values/path_data.rs +++ b/crates/rspack_binding_values/src/path_data.rs @@ -1,3 +1,5 @@ +use napi_derive::napi; + use super::JsAssetInfo; #[napi(object)] diff --git a/crates/node_binding/src/js_values/source.rs b/crates/rspack_binding_values/src/source.rs similarity index 99% rename from crates/node_binding/src/js_values/source.rs rename to crates/rspack_binding_values/src/source.rs index 22d972bce11..77134bcb8b0 100644 --- a/crates/node_binding/src/js_values/source.rs +++ b/crates/rspack_binding_values/src/source.rs @@ -1,6 +1,7 @@ use std::{borrow::Cow, hash::Hash, sync::Arc}; use napi::bindgen_prelude::*; +use napi_derive::napi; use rspack_core::rspack_sources::{ stream_chunks::{stream_chunks_default, GeneratedInfo, OnChunk, OnName, OnSource, StreamChunks}, CachedSource, ConcatSource, MapOptions, OriginalSource, RawSource, ReplaceSource, Source, diff --git a/crates/node_binding/src/js_values/stats.rs b/crates/rspack_binding_values/src/stats.rs similarity index 98% rename from crates/node_binding/src/js_values/stats.rs rename to crates/rspack_binding_values/src/stats.rs index df18afce006..537462502c0 100644 --- a/crates/node_binding/src/js_values/stats.rs +++ b/crates/rspack_binding_values/src/stats.rs @@ -3,6 +3,7 @@ use napi::{ bindgen_prelude::{Result, SharedReference}, Either, }; +use napi_derive::napi; use rspack_core::Stats; use super::{JsCompilation, ToJsCompatSource}; @@ -168,7 +169,7 @@ pub struct JsStatsAsset { pub r#type: &'static str, pub name: String, pub size: f64, - pub chunks: Vec, + pub chunks: Vec>, pub chunk_names: Vec, pub info: JsStatsAssetInfo, pub emitted: bool, @@ -213,7 +214,7 @@ pub struct JsStatsModule { pub identifier: String, pub name: String, pub id: Option, - pub chunks: Vec, + pub chunks: Vec>, pub size: f64, pub issuer: Option, pub issuer_name: Option, @@ -342,7 +343,7 @@ pub struct JsStatsChunk { pub r#type: &'static str, pub files: Vec, pub auxiliary_files: Vec, - pub id: String, + pub id: Option, pub entry: bool, pub initial: bool, pub names: Vec, @@ -395,7 +396,7 @@ impl From for JsStatsChunkGroupAsset { pub struct JsStatsChunkGroup { pub name: String, pub assets: Vec, - pub chunks: Vec, + pub chunks: Vec>, pub assets_size: f64, } @@ -553,11 +554,7 @@ impl JsStats { } #[napi(catch_unwind)] - pub fn get_hash(&self) -> String { - self - .inner - .get_hash() - .expect("should have hash in stats::get_hash") - .to_string() + pub fn get_hash(&self) -> Option { + self.inner.get_hash().map(|hash| hash.to_string()) } } diff --git a/crates/node_binding/src/utils.rs b/crates/rspack_binding_values/src/utils.rs similarity index 84% rename from crates/node_binding/src/utils.rs rename to crates/rspack_binding_values/src/utils.rs index 4e1104aa16e..06b54de7858 100644 --- a/crates/node_binding/src/utils.rs +++ b/crates/rspack_binding_values/src/utils.rs @@ -125,7 +125,7 @@ where } // **Note** that Node's main thread and the worker thread share the same binding context. Using `Mutex` would cause deadlocks if multiple compilers exist. -pub(crate) struct SingleThreadedHashMap(DashMap); +pub struct SingleThreadedHashMap(DashMap); impl SingleThreadedHashMap where @@ -133,8 +133,9 @@ where { /// Acquire a mutable reference to the inner hashmap. /// - /// Safety: Mutable reference can almost let you do anything you want, this is intended to be used from the thread where the map was created. - pub(crate) unsafe fn borrow_mut(&self, key: &K, f: F) -> Result + /// # Safety + /// Mutable reference can almost let you do anything you want, this is intended to be used from the thread where the map was created. + pub unsafe fn borrow_mut(&self, key: &K, f: F) -> Result where F: FnOnce(&mut V) -> Result, { @@ -149,9 +150,10 @@ where /// Acquire a shared reference to the inner hashmap. /// - /// Safety: It's not thread-safe if a value is not safe to modify cross thread boundary, so this is intended to be used from the thread where the map was created. + /// # Safety + /// It's not thread-safe if a value is not safe to modify cross thread boundary, so this is intended to be used from the thread where the map was created. #[allow(unused)] - pub(crate) unsafe fn borrow(&self, key: &K, f: F) -> Result + pub unsafe fn borrow(&self, key: &K, f: F) -> Result where F: FnOnce(&V) -> Result, { @@ -166,8 +168,9 @@ where /// Insert a value into the map. /// - /// Safety: It's not thread-safe if a value has thread affinity, so this is intended to be used from the thread where the map was created. - pub(crate) unsafe fn insert_if_vacant(&self, key: K, value: V) -> Result<()> { + /// # Safety + /// It's not thread-safe if a value has thread affinity, so this is intended to be used from the thread where the map was created. + pub unsafe fn insert_if_vacant(&self, key: K, value: V) -> Result<()> { if let dashmap::mapref::entry::Entry::Vacant(vacant) = self.0.entry(key) { vacant.insert(value); Ok(()) @@ -182,9 +185,10 @@ where /// /// See: [DashMap::remove] for more details. https://docs.rs/dashmap/latest/dashmap/struct.DashMap.html#method.remove /// - /// Safety: It's not thread-safe if a value has thread affinity, so this is intended to be used from the thread where the map was created. + /// # Safety + /// It's not thread-safe if a value has thread affinity, so this is intended to be used from the thread where the map was created. #[allow(unused)] - pub(crate) unsafe fn remove(&self, key: &K) -> Option { + pub unsafe fn remove(&self, key: &K) -> Option { self.0.remove(key).map(|(_, v)| v) } } diff --git a/crates/rspack_core/Cargo.toml b/crates/rspack_core/Cargo.toml index 081d0d116cc..53416c851b8 100644 --- a/crates/rspack_core/Cargo.toml +++ b/crates/rspack_core/Cargo.toml @@ -14,6 +14,7 @@ bitflags = { workspace = true } dashmap = { workspace = true } derivative = { workspace = true } dyn-clone = "1.0.11" +either = "1" futures = { workspace = true } glob-match = "0.2.1" hashlink = { workspace = true } diff --git a/crates/rspack_core/src/lib.rs b/crates/rspack_core/src/lib.rs index 6c726790772..f7f094e6839 100644 --- a/crates/rspack_core/src/lib.rs +++ b/crates/rspack_core/src/lib.rs @@ -94,6 +94,18 @@ pub enum SourceType { Unknown, } +impl std::fmt::Display for SourceType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + SourceType::JavaScript => write!(f, "javascript"), + SourceType::Css => write!(f, "css"), + SourceType::Wasm => write!(f, "wasm"), + SourceType::Asset => write!(f, "asset"), + SourceType::Unknown => write!(f, "unknown"), + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ModuleType { Json, diff --git a/crates/rspack_core/src/stats.rs b/crates/rspack_core/src/stats.rs index 25c1439afe7..c12a63d8015 100644 --- a/crates/rspack_core/src/stats.rs +++ b/crates/rspack_core/src/stats.rs @@ -1,3 +1,4 @@ +use either::Either; use itertools::Itertools; use rspack_error::{ emitter::{ @@ -84,10 +85,7 @@ impl Stats<'_> { } for (name, asset) in &mut assets { if let Some(chunks) = compilation_file_to_chunks.get(name) { - asset.chunks = chunks - .iter() - .map(|chunk| chunk.id.clone().expect("Chunk should have id")) - .collect(); + asset.chunks = chunks.iter().map(|chunk| chunk.id.clone()).collect(); asset.chunks.sort_unstable(); asset.chunk_names = chunks .iter() @@ -193,7 +191,7 @@ impl Stats<'_> { r#type: "chunk", files, auxiliary_files, - id: c.expect_id().to_string(), + id: c.id.clone(), names: c.name.clone().map(|n| vec![n]).unwrap_or_default(), entry: c.has_entry_module(&self.compilation.chunk_graph), initial: c.can_be_initial(&self.compilation.chunk_group_by_ukey), @@ -208,7 +206,17 @@ impl Stats<'_> { }) }) .collect::>()?; - chunks.sort_by_cached_key(|v| v.id.to_string()); + + // make result deterministic + chunks.sort_unstable_by_key(|v| { + // chunk id only exist after chunkIds hook + if let Some(id) = &v.id { + Either::Left(id.clone()) + } else { + Either::Right(v.size as u32) + } + }); + Ok(chunks) } @@ -218,7 +226,7 @@ impl Stats<'_> { .chunk_group_by_ukey .get(ukey) .expect("compilation.chunk_group_by_ukey should have ukey from entrypoint"); - let chunks: Vec = cg + let chunks: Vec> = cg .chunks .iter() .map(|c| { @@ -228,7 +236,7 @@ impl Stats<'_> { .get(c) .expect("compilation.chunk_by_ukey should have ukey from chunk_group") }) - .map(|c| c.expect_id().to_string()) + .map(|c| c.id.clone()) .collect(); let assets = cg.chunks.iter().fold(Vec::new(), |mut acc, c| { let chunk = self @@ -397,7 +405,7 @@ impl Stats<'_> { }) .transpose()?; - let mut chunks: Vec = self + let mut chunks: Vec> = self .compilation .chunk_graph .get_chunk_graph_module(mgm.module_identifier) @@ -409,8 +417,8 @@ impl Stats<'_> { .chunk_by_ukey .get(k) .unwrap_or_else(|| panic!("Could not find chunk by ukey: {k:?}")) - .expect_id() - .to_string() + .id + .clone() }) .collect(); chunks.sort_unstable(); @@ -548,7 +556,7 @@ pub struct StatsAsset { pub r#type: &'static str, pub name: String, pub size: f64, - pub chunks: Vec, + pub chunks: Vec>, pub chunk_names: Vec, pub info: StatsAssetInfo, pub emitted: bool, @@ -575,7 +583,7 @@ pub struct StatsModule<'a> { pub name: String, pub name_for_condition: Option, pub id: Option, - pub chunks: Vec, + pub chunks: Vec>, // has id after the call of chunkIds hook pub size: f64, pub issuer: Option, pub issuer_name: Option, @@ -600,7 +608,7 @@ pub struct StatsChunk<'a> { pub r#type: &'static str, pub files: Vec, pub auxiliary_files: Vec, - pub id: String, + pub id: Option, pub entry: bool, pub initial: bool, pub names: Vec, @@ -621,7 +629,7 @@ pub struct StatsChunkGroupAsset { pub struct StatsChunkGroup { pub name: String, pub assets: Vec, - pub chunks: Vec, + pub chunks: Vec>, pub assets_size: f64, } diff --git a/crates/rspack_database/src/ukey.rs b/crates/rspack_database/src/ukey.rs index ce8af280102..a06b2fb1dad 100644 --- a/crates/rspack_database/src/ukey.rs +++ b/crates/rspack_database/src/ukey.rs @@ -84,3 +84,15 @@ impl Hash for Ukey { } impl Copy for Ukey {} + +impl From for Ukey { + fn from(value: usize) -> Self { + Self(value, std::marker::PhantomData) + } +} + +impl From> for usize { + fn from(value: Ukey) -> Self { + value.0 + } +} diff --git a/packages/rspack/src/Chunk.ts b/packages/rspack/src/Chunk.ts new file mode 100644 index 00000000000..fcd17537f19 --- /dev/null +++ b/packages/rspack/src/Chunk.ts @@ -0,0 +1,72 @@ +import { + __chunk_inner_can_be_initial, + __chunk_inner_has_runtime, + __chunk_inner_is_only_initial, + type JsChunk, + type JsCompilation +} from "@rspack/binding"; + +export class Chunk implements JsChunk { + #inner_chunk: JsChunk; + #inner_compilation: JsCompilation; + + // @ts-expect-error should not use inner_ukey in js side + __inner_ukey: never; + + name?: string; + id?: string; + ids: Array; + idNameHints: Array; + filenameTemplate?: string; + cssFilenameTemplate?: string; + files: Array; + runtime: Array; + hash?: string; + contentHash: Record; + renderedHash?: string; + chunkReasons: Array; + + static __from_binding(chunk: JsChunk, compilation: JsCompilation) { + return new Chunk(chunk, compilation); + } + + // Should not construct by user + private constructor(chunk: JsChunk, compilation: JsCompilation) { + this.#inner_chunk = chunk; + this.#inner_compilation = compilation; + + this.name = chunk.name; + this.id = chunk.id; + this.ids = chunk.ids; + this.idNameHints = chunk.idNameHints; + this.filenameTemplate = chunk.filenameTemplate; + this.cssFilenameTemplate = chunk.cssFilenameTemplate; + this.files = chunk.files; + this.runtime = chunk.runtime; + this.hash = chunk.hash; + this.contentHash = chunk.contentHash; + this.renderedHash = chunk.renderedHash; + this.chunkReasons = chunk.chunkReasons; + } + + isOnlyInitial() { + return __chunk_inner_is_only_initial( + this.#inner_chunk, + this.#inner_compilation + ); + } + + canBeInitial() { + return __chunk_inner_can_be_initial( + this.#inner_chunk, + this.#inner_compilation + ); + } + + hasRuntime() { + return __chunk_inner_has_runtime( + this.#inner_chunk, + this.#inner_compilation + ); + } +} diff --git a/packages/rspack/src/Compilation.ts b/packages/rspack/src/Compilation.ts index 8f26c2cebdb..8bc2bc68b72 100644 --- a/packages/rspack/src/Compilation.ts +++ b/packages/rspack/src/Compilation.ts @@ -54,6 +54,7 @@ import { } from "./util/fake"; import { NormalizedJsModule, normalizeJsModule } from "./util/normalization"; import MergeCaller from "./util/MergeCaller"; +import { Chunk } from "./Chunk"; export type AssetInfo = Partial & Record; export type Assets = Record; @@ -685,23 +686,7 @@ export class Compilation { // FIXME: This is not aligned with Webpack. get chunks() { - const stats = this.getStats().toJson({ - all: false, - chunks: true, - chunkModules: true, - reasons: true - }); - const chunks = stats.chunks?.map(chunk => { - return { - ...chunk, - name: chunk.names.length > 0 ? chunk.names[0] : "", - modules: this.__internal__getAssociatedModules(chunk), - isOnlyInitial: function () { - return this.initial; - } - }; - }); - return chunks; + return this.__internal__getChunks(); } /** @@ -709,14 +694,15 @@ export class Compilation { * * Note: This is a proxy for webpack internal API, only method `get` is supported now. */ - get namedChunks(): Map> { + get namedChunks(): Map> { return { get: (property: unknown) => { if (typeof property === "string") { - return this.#inner.getNamedChunk(property) ?? undefined; + const chunk = this.#inner.getNamedChunk(property); + return chunk && Chunk.__from_binding(chunk, this.#inner); } } - } as Map>; + } as Map>; } /** @@ -783,8 +769,10 @@ export class Compilation { * * @internal */ - __internal__getChunks(): JsChunk[] { - return this.#inner.getChunks(); + __internal__getChunks(): Chunk[] { + return this.#inner + .getChunks() + .map(c => Chunk.__from_binding(c, this.#inner)); } getStats() { diff --git a/packages/rspack/src/stats/DefaultStatsFactoryPlugin.ts b/packages/rspack/src/stats/DefaultStatsFactoryPlugin.ts index 84e7e0ff6af..c37384ade25 100644 --- a/packages/rspack/src/stats/DefaultStatsFactoryPlugin.ts +++ b/packages/rspack/src/stats/DefaultStatsFactoryPlugin.ts @@ -529,7 +529,7 @@ const SIMPLE_EXTRACTORS: SimpleExtractors = { } }, hash: (object, _compilation, context: KnownStatsFactoryContext) => { - object.hash = context._inner.getHash(); + object.hash = context._inner.getHash() || undefined; }, version: object => { const { version, webpackVersion } = require("../../package.json"); diff --git a/packages/rspack/src/util/comparators.ts b/packages/rspack/src/util/comparators.ts index 50a5d9c854c..8c2c73978e5 100644 --- a/packages/rspack/src/util/comparators.ts +++ b/packages/rspack/src/util/comparators.ts @@ -86,7 +86,7 @@ export const compareIds = ( }; export const compareChunksById = (a: Chunk, b: Chunk): -1 | 0 | 1 => { - return compareIds(a.id, b.id); + return compareIds(a.id || "", b.id || ""); }; const compareSelectCache: TwoKeyWeakMap< diff --git a/packages/rspack/tests/Stats.test.ts b/packages/rspack/tests/Stats.test.ts index e8b51ad2d80..b838aab0965 100644 --- a/packages/rspack/tests/Stats.test.ts +++ b/packages/rspack/tests/Stats.test.ts @@ -1,6 +1,6 @@ import * as util from "util"; import path from "path"; -import { rspack, RspackOptions, Stats } from "../src"; +import { Compiler, rspack, RspackOptions, Stats } from "../src"; import serializer from "jest-serializer-path"; expect.addSnapshotSerializer(serializer); @@ -353,4 +353,36 @@ describe("Stats", () => { ./fixtures/a.js [876] {main}" `); }); + + it("should have null as placeholders in stats before chunkIds", async () => { + let stats; + + class TestPlugin { + apply(compiler: Compiler) { + compiler.hooks.thisCompilation.tap("custom", compilation => { + compilation.hooks.optimizeModules.tap("test plugin", () => { + stats = compiler.compilation.getStats().toJson({}); + }); + }); + } + } + await compile({ + context: __dirname, + entry: "./fixtures/a", + plugins: [new TestPlugin()] + }); + + expect(stats!.entrypoints).toMatchInlineSnapshot(` + { + "main": { + "assets": [], + "assetsSize": 0, + "chunks": [ + null, + ], + "name": "main", + }, + } + `); + }); });