From 71811aa3ea9c660074a0f1b7e155612c14d4b1ce Mon Sep 17 00:00:00 2001 From: GiveMe-A-Name Date: Fri, 8 Nov 2024 10:34:34 +0800 Subject: [PATCH] fix: resolved some issues - dll-reference-plugin should given a manifest struct, rather than json-string - we should not add a null module_graph_module_connection like webpack --- crates/node_binding/binding.d.ts | 36 +++++- .../src/options/raw_builtins/raw_dll.rs | 67 ++++++++-- crates/rspack_binding_values/src/module.rs | 121 +++++++++++++++++- crates/rspack_core/src/module.rs | 18 ++- .../src/module_graph/connection.rs | 18 --- crates/rspack_core/src/module_graph/mod.rs | 17 --- crates/rspack_core/src/module_graph/module.rs | 6 + .../dll_reference_agency_plugin.rs | 26 ++-- .../src/flag_all_modules_as_used_plugin.rs | 6 +- crates/rspack_plugin_dll/src/lib.rs | 12 +- .../src/plugin/module_concatenation_plugin.rs | 42 ++++-- packages/rspack/etc/core.api.md | 5 +- packages/rspack/src/lib/DllPlugin.ts | 4 +- packages/rspack/src/lib/DllReferencePlugin.ts | 109 +++++++++++----- 14 files changed, 360 insertions(+), 127 deletions(-) diff --git a/crates/node_binding/binding.d.ts b/crates/node_binding/binding.d.ts index 8fdac3a7c91..18ee5ccfd14 100644 --- a/crates/node_binding/binding.d.ts +++ b/crates/node_binding/binding.d.ts @@ -450,6 +450,22 @@ export interface JsBeforeResolveArgs { issuer: string } +export interface JsBuildMeta { + strictEsmModule: boolean + hasTopLevelAwait: boolean + esm: boolean + exportsType: 'unset' | 'default' | 'namespace' | 'flagged' | 'dynamic' + defaultObject: 'false' | 'redirect' | JsBuildMetaDefaultObjectRedirectWarn + moduleArgument: 'module' | 'webpackModule' + exportsArgument: 'exports' | 'webpackExports' + sideEffectFree?: boolean + exportsFinalName?: Array<[string, string]> | undefined +} + +export interface JsBuildMetaDefaultObjectRedirectWarn { + redirectWarn: JsDefaultObjectRedirectWarnObject +} + export interface JsBuildTimeExecutionOption { publicPath?: string baseUri?: string @@ -523,6 +539,10 @@ export interface JsCreateData { resource: string } +export interface JsDefaultObjectRedirectWarnObject { + ignore: boolean +} + export interface JsDiagnostic { message: string help?: string @@ -1279,6 +1299,18 @@ export interface RawDllEntryPluginOptions { name: string } +export interface RawDllManifest { + content: RawDllManifestContent + name?: string + type?: string +} + +export interface RawDllManifestContentItem { + buildMeta?: JsBuildMeta + exports?: Array + id?: string +} + export interface RawDllReferenceAgencyPluginOptions { context?: string name?: string @@ -1286,8 +1318,8 @@ export interface RawDllReferenceAgencyPluginOptions { scope?: string sourceType?: string type: string - content?: string - manifest?: string + content?: RawDllManifestContent + manifest?: RawDllManifest } export interface RawDraft { diff --git a/crates/rspack_binding_options/src/options/raw_builtins/raw_dll.rs b/crates/rspack_binding_options/src/options/raw_builtins/raw_dll.rs index bbff839ef38..d48a7c20cfb 100644 --- a/crates/rspack_binding_options/src/options/raw_builtins/raw_dll.rs +++ b/crates/rspack_binding_options/src/options/raw_builtins/raw_dll.rs @@ -1,8 +1,11 @@ use napi_derive::napi; -use rspack_binding_values::JsFilename; +use rspack_binding_values::{JsBuildMeta, JsFilename}; use rspack_plugin_dll::{ - DllEntryPluginOptions, DllReferenceAgencyPluginOptions, LibManifestPluginOptions, + DllEntryPluginOptions, DllManifest, DllManifestContent, DllManifestContentItem, + DllReferenceAgencyPluginOptions, LibManifestPluginOptions, }; +use rustc_hash::FxHashMap as HashMap; +use swc_core::atoms::Atom; #[derive(Debug)] #[napi(object)] @@ -61,8 +64,7 @@ impl From for LibManifestPluginOptions { } } -#[derive(Debug)] -#[napi(object)] +#[napi(object, object_to_js = false)] pub struct RawDllReferenceAgencyPluginOptions { pub context: Option, pub name: Option, @@ -70,8 +72,53 @@ pub struct RawDllReferenceAgencyPluginOptions { pub scope: Option, pub source_type: Option, pub r#type: String, - pub content: Option, - pub manifest: Option, + pub content: Option, + pub manifest: Option, +} + +type RawDllManifestContent = HashMap; + +#[napi(object, object_to_js = false)] +pub struct RawDllManifestContentItem { + pub build_meta: Option, + pub exports: Option>, + pub id: Option, +} + +impl From for DllManifestContentItem { + fn from(value: RawDllManifestContentItem) -> Self { + Self { + build_meta: value.build_meta.map(|meta| meta.into()), + exports: value.exports.map(|exports| { + exports + .into_iter() + .map(|export| Atom::from(export)) + .collect::>() + }), + id: value.id.into(), + } + } +} + +#[napi(object, object_to_js = false)] +pub struct RawDllManifest { + pub content: RawDllManifestContent, + pub name: Option, + pub r#type: Option, +} + +impl From for DllManifest { + fn from(value: RawDllManifest) -> Self { + Self { + content: value + .content + .into_iter() + .map(|(k, v)| (k, v.into())) + .collect::(), + name: value.name.map(|n| n.into()), + r#type: value.r#type.into(), + } + } } impl From for DllReferenceAgencyPluginOptions { @@ -94,8 +141,12 @@ impl From for DllReferenceAgencyPluginOption scope, source_type, r#type, - content, - manifest, + content: content.map(|c| { + c.into_iter() + .map(|(k, v)| (k, v.into())) + .collect::() + }), + manifest: manifest.map(|m| m.into()), } } } diff --git a/crates/rspack_binding_values/src/module.rs b/crates/rspack_binding_values/src/module.rs index ff027732f9f..974145da189 100644 --- a/crates/rspack_binding_values/src/module.rs +++ b/crates/rspack_binding_values/src/module.rs @@ -1,10 +1,12 @@ use std::{cell::RefCell, ptr::NonNull, sync::Arc}; +use napi::bindgen_prelude::ToNapiValue; use napi_derive::napi; use rspack_collections::IdentifierMap; use rspack_core::{ - AsyncDependenciesBlock, AsyncDependenciesBlockIdentifier, Compilation, CompilationId, - DependenciesBlock, Module, ModuleGraph, ModuleIdentifier, RuntimeModuleStage, SourceType, + AsyncDependenciesBlock, AsyncDependenciesBlockIdentifier, BuildMeta, BuildMetaDefaultObject, + BuildMetaExportsType, Compilation, CompilationId, DependenciesBlock, ExportsArgument, Module, + ModuleArgument, ModuleGraph, ModuleIdentifier, RuntimeModuleStage, SourceType, }; use rspack_napi::{napi::bindgen_prelude::*, threadsafe_function::ThreadsafeFunction, OneShotRef}; use rspack_plugin_runtime::RuntimeModuleFromJs; @@ -534,3 +536,118 @@ impl From for RuntimeModuleFromJs { } } } + +#[napi(object, object_to_js = false)] +pub struct JsBuildMeta { + pub strict_esm_module: bool, + pub has_top_level_await: bool, + pub esm: bool, + #[napi(ts_type = "'unset' | 'default' | 'namespace' | 'flagged' | 'dynamic'")] + pub exports_type: String, + #[napi(ts_type = "'false' | 'redirect' | JsBuildMetaDefaultObjectRedirectWarn")] + pub default_object: JsBuildMetaDefaultObject, + #[napi(ts_type = "'module' | 'webpackModule'")] + pub module_argument: String, + #[napi(ts_type = "'exports' | 'webpackExports'")] + pub exports_argument: String, + pub side_effect_free: Option, + #[napi(ts_type = "Array<[string, string]> | undefined")] + pub exports_final_name: Option>>, +} + +impl From for BuildMeta { + fn from(value: JsBuildMeta) -> Self { + let JsBuildMeta { + strict_esm_module, + has_top_level_await, + esm, + exports_argument: raw_exports_argument, + default_object: raw_default_object, + module_argument: raw_module_argument, + exports_final_name: raw_exports_final_name, + side_effect_free, + exports_type: raw_exports_type, + } = value; + + let default_object = match raw_default_object { + Either::A(s) => match s.as_str() { + "false" => BuildMetaDefaultObject::False, + "redirect" => BuildMetaDefaultObject::Redirect, + _ => unreachable!(), + }, + Either::B(default_object) => BuildMetaDefaultObject::RedirectWarn { + ignore: default_object.redirect_warn.ignore, + }, + }; + + let exports_type = match raw_exports_type.as_str() { + "unset" => BuildMetaExportsType::Unset, + "default" => BuildMetaExportsType::Default, + "namespace" => BuildMetaExportsType::Namespace, + "flagged" => BuildMetaExportsType::Flagged, + "dynamic" => BuildMetaExportsType::Dynamic, + _ => unreachable!(), + }; + + let module_argument = match raw_module_argument.as_str() { + "module" => ModuleArgument::Module, + "webpackModule" => ModuleArgument::WebpackModule, + _ => unreachable!(), + }; + + let exports_argument = match raw_exports_argument.as_str() { + "exports" => ExportsArgument::Exports, + "webpackExports" => ExportsArgument::WebpackExports, + _ => unreachable!(), + }; + + let exports_final_name = raw_exports_final_name.map(|exports_name| { + exports_name + .into_iter() + .map(|export_name| { + let first = export_name + .get(0) + .expect("The buildMeta exportsFinalName item should have first value") + .clone(); + let second = export_name + .get(1) + .expect("The buildMeta exportsFinalName item should have second value") + .clone(); + (first, second) + }) + .collect::>() + }); + + Self { + strict_esm_module, + has_top_level_await, + esm, + exports_type, + default_object, + module_argument, + exports_argument, + side_effect_free, + exports_final_name, + } + } +} + +#[napi(object)] +pub struct JsBuildMetaDefaultObjectRedirectWarn { + pub redirect_warn: JsDefaultObjectRedirectWarnObject, +} + +impl From for BuildMetaDefaultObject { + fn from(value: JsBuildMetaDefaultObjectRedirectWarn) -> Self { + Self::RedirectWarn { + ignore: value.redirect_warn.ignore, + } + } +} + +#[napi(object)] +pub struct JsDefaultObjectRedirectWarnObject { + pub ignore: bool, +} + +pub type JsBuildMetaDefaultObject = Either; diff --git a/crates/rspack_core/src/module.rs b/crates/rspack_core/src/module.rs index af14e62360f..bbeae61ecf6 100644 --- a/crates/rspack_core/src/module.rs +++ b/crates/rspack_core/src/module.rs @@ -15,7 +15,7 @@ use rspack_util::atom::Atom; use rspack_util::ext::{AsAny, DynHash}; use rspack_util::source_map::ModuleSourceMapConfig; use rustc_hash::FxHashSet as HashSet; -use serde::{Deserialize, Serialize}; +use serde::Serialize; use crate::concatenated_module::ConcatenatedModule; use crate::dependencies_block::dependencies_block_update_hash; @@ -79,7 +79,8 @@ impl Default for BuildInfo { } } -#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, Serialize)] +#[serde(rename_all = "camelCase")] pub enum BuildMetaExportsType { #[default] Unset, @@ -97,7 +98,8 @@ pub enum ExportsType { Dynamic, } -#[derive(Debug, Default, Clone, Copy, Hash, Serialize, Deserialize)] +#[derive(Debug, Default, Clone, Copy, Hash, Serialize)] +#[serde(rename_all = "camelCase")] pub enum BuildMetaDefaultObject { #[default] False, @@ -111,7 +113,8 @@ pub enum BuildMetaDefaultObject { }, } -#[derive(Debug, Default, Clone, Copy, Hash, Serialize, Deserialize)] +#[derive(Debug, Default, Clone, Copy, Hash, Serialize)] +#[serde(rename_all = "camelCase")] pub enum ModuleArgument { #[default] Module, @@ -127,7 +130,8 @@ impl Display for ModuleArgument { } } -#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, Serialize)] +#[serde(rename_all = "camelCase")] pub enum ExportsArgument { #[default] Exports, @@ -143,7 +147,7 @@ impl Display for ExportsArgument { } } -#[derive(Debug, Default, Clone, Hash, Serialize, Deserialize)] +#[derive(Debug, Default, Clone, Hash, Serialize)] #[serde(rename_all = "camelCase")] pub struct BuildMeta { pub strict_esm_module: bool, @@ -153,7 +157,9 @@ pub struct BuildMeta { pub default_object: BuildMetaDefaultObject, pub module_argument: ModuleArgument, pub exports_argument: ExportsArgument, + #[serde(skip_serializing_if = "Option::is_none")] pub side_effect_free: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub exports_final_name: Option>, } diff --git a/crates/rspack_core/src/module_graph/connection.rs b/crates/rspack_core/src/module_graph/connection.rs index bf96f96b1c9..c86fca4c3a2 100644 --- a/crates/rspack_core/src/module_graph/connection.rs +++ b/crates/rspack_core/src/module_graph/connection.rs @@ -1,8 +1,5 @@ use std::hash::Hash; -use itertools::Itertools; -use rustc_hash::FxHashSet as HashSet; - use crate::{DependencyId, ModuleGraph, ModuleIdentifier, RuntimeSpec}; #[derive(Debug, Clone, Eq)] @@ -17,8 +14,6 @@ pub struct ModuleGraphConnection { pub active: bool, pub conditional: bool, - - explanations: HashSet, } impl Hash for ModuleGraphConnection { @@ -48,19 +43,6 @@ impl ModuleGraphConnection { active, conditional, resolved_original_module_identifier: original_module_identifier, - explanations: Default::default(), - } - } - - pub fn add_explanation(&mut self, explanation: String) { - self.explanations.insert(explanation); - } - - pub fn explanation(&self) -> Option { - if self.explanations.is_empty() { - None - } else { - Some(self.explanations.iter().join(" ")) } } diff --git a/crates/rspack_core/src/module_graph/mod.rs b/crates/rspack_core/src/module_graph/mod.rs index f76fd7c8c38..f435426c83a 100644 --- a/crates/rspack_core/src/module_graph/mod.rs +++ b/crates/rspack_core/src/module_graph/mod.rs @@ -467,23 +467,6 @@ impl<'a> ModuleGraph<'a> { } } - pub fn add_extra_reason(&mut self, module_identifier: &ModuleIdentifier, explanation: String) { - let dependency_id = DependencyId::new(); - - let mut connection = - ModuleGraphConnection::new(dependency_id, None, *module_identifier, true, false); - - connection.add_explanation(explanation); - - let mg = self - .module_graph_module_by_identifier_mut(module_identifier) - .expect("Should have moduleGraphModule"); - - mg.add_incoming_connection(dependency_id); - - self.add_connection(connection, None); - } - pub fn get_depth(&self, module_id: &ModuleIdentifier) -> Option { self .module_graph_module_by_identifier(module_id) diff --git a/crates/rspack_core/src/module_graph/module.rs b/crates/rspack_core/src/module_graph/module.rs index ef8004fe826..e0442fc8bb6 100644 --- a/crates/rspack_core/src/module_graph/module.rs +++ b/crates/rspack_core/src/module_graph/module.rs @@ -22,6 +22,7 @@ pub struct ModuleGraphModule { pub profile: Option>, pub depth: Option, pub optimization_bailout: Vec, + pub concatenation_bail: String, } impl ModuleGraphModule { @@ -39,6 +40,7 @@ impl ModuleGraphModule { profile: None, depth: None, optimization_bailout: vec![], + concatenation_bail: Default::default(), } } @@ -79,6 +81,10 @@ impl ModuleGraphModule { self.profile.as_deref() } + pub fn add_concatenation_bail_reason(&mut self, reason: &str) { + self.concatenation_bail += &format!(", {}", reason); + } + pub fn set_issuer_if_unset(&mut self, issuer: Option) { if matches!(self.issuer, ModuleIssuer::Unset) { self.issuer = ModuleIssuer::from_identifier(issuer); diff --git a/crates/rspack_plugin_dll/src/dll_reference/dll_reference_agency_plugin.rs b/crates/rspack_plugin_dll/src/dll_reference/dll_reference_agency_plugin.rs index 8f947d8e88a..98e2cb4445d 100644 --- a/crates/rspack_plugin_dll/src/dll_reference/dll_reference_agency_plugin.rs +++ b/crates/rspack_plugin_dll/src/dll_reference/dll_reference_agency_plugin.rs @@ -14,8 +14,8 @@ use crate::{DllManifest, DllManifestContent}; pub struct DllReferenceAgencyPluginOptions { pub context: Option, pub name: Option, - pub content: Option, - pub manifest: Option, + pub content: Option, + pub manifest: Option, pub extensions: Vec, pub scope: Option, pub source_type: Option, @@ -42,29 +42,19 @@ impl Plugin for DllReferenceAgencyPlugin { fn apply(&self, ctx: PluginContext<&mut ApplyContext>, options: &CompilerOptions) -> Result<()> { let mut name = self.options.name.clone(); let mut source_type = self.options.source_type.clone(); - let mut resolved_content = self - .options - .content - .as_ref() - .and_then(|content| serde_json::from_str::(content).ok()); - - let manifest = self - .options - .manifest - .as_ref() - .and_then(|manifest| serde_json::from_str::(manifest).ok()); - - if let Some(manifest) = manifest { + let mut resolved_content = self.options.content.clone(); + + if let Some(manifest) = &self.options.manifest { if name.is_none() { - name = manifest.name; + name = manifest.name.clone(); } if source_type.is_none() { - source_type = manifest.r#type; + source_type = manifest.r#type.clone(); } if resolved_content.is_none() { - resolved_content = Some(manifest.content); + resolved_content = Some(manifest.content.clone()); } } diff --git a/crates/rspack_plugin_dll/src/flag_all_modules_as_used_plugin.rs b/crates/rspack_plugin_dll/src/flag_all_modules_as_used_plugin.rs index 6f118877031..7298e221501 100644 --- a/crates/rspack_plugin_dll/src/flag_all_modules_as_used_plugin.rs +++ b/crates/rspack_plugin_dll/src/flag_all_modules_as_used_plugin.rs @@ -58,7 +58,9 @@ fn optimize_dependencies(&self, compilation: &mut Compilation) -> Result Result<()> { - // set all modules have effects. To avoid any module remove by tree shakeing. + // set all modules have effects. To avoid any module remove by tree shaking. // see: https://github.com/webpack/webpack/blob/4b4ca3bb53f36a5b8fc6bc1bd976ed7af161bd80/lib/FlagAllModulesAsUsedPlugin.js#L43-L47 module.set_factory_meta(FactoryMeta { side_effect_free: Some(false), diff --git a/crates/rspack_plugin_dll/src/lib.rs b/crates/rspack_plugin_dll/src/lib.rs index 2d54982be7a..02cac598ef0 100644 --- a/crates/rspack_plugin_dll/src/lib.rs +++ b/crates/rspack_plugin_dll/src/lib.rs @@ -1,18 +1,18 @@ use rspack_core::{BuildMeta, LibraryType}; use rspack_util::atom::Atom; use rustc_hash::FxHashMap as HashMap; -use serde::{Deserialize, Serialize}; +use serde::Serialize; mod dll_entry; mod dll_reference; mod flag_all_modules_as_used_plugin; mod lib_manifest_plugin; -pub(crate) type DllManifestContent = HashMap; +pub type DllManifestContent = HashMap; -#[derive(Debug, Default, Clone, Deserialize, Serialize)] +#[derive(Debug, Default, Clone, Serialize)] #[serde(rename_all = "camelCase")] -pub(crate) struct DllManifestContentItem { +pub struct DllManifestContentItem { #[serde(skip_serializing_if = "Option::is_none")] pub build_meta: Option, @@ -23,8 +23,8 @@ pub(crate) struct DllManifestContentItem { pub id: Option, } -#[derive(Debug, Clone, Deserialize, Serialize)] -pub(crate) struct DllManifest { +#[derive(Debug, Clone, Serialize)] +pub struct DllManifest { pub content: DllManifestContent, #[serde(skip_serializing_if = "Option::is_none")] diff --git a/crates/rspack_plugin_javascript/src/plugin/module_concatenation_plugin.rs b/crates/rspack_plugin_javascript/src/plugin/module_concatenation_plugin.rs index 50e62529bd2..e36c8c65924 100644 --- a/crates/rspack_plugin_javascript/src/plugin/module_concatenation_plugin.rs +++ b/crates/rspack_plugin_javascript/src/plugin/module_concatenation_plugin.rs @@ -278,22 +278,23 @@ impl ModuleConcatenationPlugin { .filter(|&connection| connection.is_active(&module_graph, runtime)) .collect::>(); + // TODO: ADD module connection explanations if !active_non_modules_connections.is_empty() { let problem = { - let importing_explanations = active_non_modules_connections - .iter() - .flat_map(|&c| c.explanation()) - .collect::>(); - let mut explanations: Vec<_> = importing_explanations.into_iter().collect(); - explanations.sort(); + // let importing_explanations = active_non_modules_connections + // .iter() + // .flat_map(|&c| c.explanation()) + // .collect::>(); + // let mut explanations: Vec<_> = importing_explanations.into_iter().collect(); + // explanations.sort(); format!( - "Module {} is referenced {}", + "Module {} is referenced", module_readable_identifier, - if !explanations.is_empty() { - format!("by: {}", explanations.join(", ")) - } else { - "in an unsupported way".to_string() - } + // if !explanations.is_empty() { + // format!("by: {}", explanations.join(", ")) + // } else { + // "in an unsupported way".to_string() + // } ) }; let problem = Warning::Problem(problem); @@ -302,6 +303,23 @@ impl ModuleConcatenationPlugin { return Some(problem); } } + + // Every module_graph_module that tags concatenaion_bail should be return a warning problem to avoid this module concate. + if let Some(concatenation_bail) = module_graph + .module_graph_module_by_identifier(module_id) + .map(|mgm| &mgm.concatenation_bail) + { + if !concatenation_bail.is_empty() { + let problem = Warning::Problem(format!( + "Module {} is bail by: {}", + module_readable_identifier, concatenation_bail, + )); + statistics.incorrect_dependency += 1; + failure_cache.insert(*module_id, problem.clone()); + return Some(problem); + } + }; + let mut incoming_connections_from_modules = HashMap::default(); for (origin_module, connections) in incoming_connections.iter() { if let Some(origin_module) = origin_module { diff --git a/packages/rspack/etc/core.api.md b/packages/rspack/etc/core.api.md index 082ec86bbc8..0899115ab72 100644 --- a/packages/rspack/etc/core.api.md +++ b/packages/rspack/etc/core.api.md @@ -32,6 +32,7 @@ import { JsAlterAssetTagsData } from '@rspack/binding'; import type { JsAssetInfo } from '@rspack/binding'; import { JsBeforeAssetTagGenerationData } from '@rspack/binding'; import { JsBeforeEmitData } from '@rspack/binding'; +import type { JsBuildMeta } from '@rspack/binding'; import { JsChunk } from '@rspack/binding'; import { JsChunkGroup } from '@rspack/binding'; import { JsChunkGroupOrigin } from '@rspack/binding'; @@ -1448,9 +1449,7 @@ export type DllReferencePluginOptions = { // @public export interface DllReferencePluginOptionsContent { [k: string]: { - buildMeta?: { - [k: string]: any; - }; + buildMeta?: JsBuildMeta; exports?: string[] | true; id: number | string; }; diff --git a/packages/rspack/src/lib/DllPlugin.ts b/packages/rspack/src/lib/DllPlugin.ts index 1ec0f480137..26ab533342f 100644 --- a/packages/rspack/src/lib/DllPlugin.ts +++ b/packages/rspack/src/lib/DllPlugin.ts @@ -57,8 +57,6 @@ const dllPluginOptions = z.object({ type: z.string().optional() }) satisfies z.ZodType; -const DLL_PLUGIN_NAME = "DllPlugin"; - export class DllPlugin { private options: DllPluginOptions; @@ -71,7 +69,7 @@ export class DllPlugin { } apply(compiler: Compiler) { - compiler.hooks.entryOption.tap(DLL_PLUGIN_NAME, (context, entry) => { + compiler.hooks.entryOption.tap(DllPlugin.name, (context, entry) => { if (typeof entry === "function") { throw new Error( "DllPlugin doesn't support dynamic entry (function) yet" diff --git a/packages/rspack/src/lib/DllReferencePlugin.ts b/packages/rspack/src/lib/DllReferencePlugin.ts index 42d78a0d4c3..95a82e333a2 100644 --- a/packages/rspack/src/lib/DllReferencePlugin.ts +++ b/packages/rspack/src/lib/DllReferencePlugin.ts @@ -8,10 +8,14 @@ * https://github.com/webpack/webpack/blob/main/LICENSE */ +import type { JsBuildMeta } from "@rspack/binding"; import z from "zod"; +import type { CompilationParams } from "../Compilation"; import type { Compiler } from "../Compiler"; import { DllReferenceAgencyPlugin } from "../builtin-plugin"; +import { makePathsRelative } from "../util/identifier"; import { validate } from "../util/validate"; +import WebpackError from "./WebpackError"; export type DllReferencePluginOptions = | { @@ -121,9 +125,7 @@ export interface DllReferencePluginOptionsContent { /** * Meta information about the module. */ - buildMeta?: { - [k: string]: any; - }; + buildMeta?: JsBuildMeta; /** * Information about the provided exports of the module. */ @@ -136,7 +138,7 @@ export interface DllReferencePluginOptionsContent { } const dllReferencePluginOptionsContentItem = z.object({ - buildMeta: z.record(z.any()).optional(), + buildMeta: z.custom().optional(), exports: z.array(z.string()).or(z.literal(true)).optional(), id: z.union([z.number(), z.string()]) }); @@ -189,40 +191,75 @@ const dllReferencePluginOptions = z.union([ }) ]) satisfies z.ZodType; -const DLL_REFERENCE_PLUGIN_NAME = "DllReferencePlugin"; - export class DllReferencePlugin { private options: DllReferencePluginOptions; + private errors: WeakMap; + constructor(options: DllReferencePluginOptions) { validate(options, dllReferencePluginOptions); this.options = options; + this.errors = new WeakMap(); } apply(compiler: Compiler) { - compiler.hooks.beforeCompile.tapAsync( - DLL_REFERENCE_PLUGIN_NAME, - (_, callback) => { - if ("manifest" in this.options) { - const manifest = this.options.manifest; - - let manifest_content: string | undefined = undefined; - - if (typeof manifest === "string") { - compiler.inputFileSystem?.readFile( - manifest, - "utf8", - (err, result) => { - if (err) return callback(err); - - manifest_content = result; - } - ); + compiler.hooks.beforeCompile.tapPromise( + DllReferencePlugin.name, + async params => { + const manifest = await new Promise< + DllReferencePluginOptionsManifest | undefined + >((resolve, reject) => { + if ("manifest" in this.options) { + const manifest = this.options.manifest; + + if (typeof manifest === "string") { + const manifestParameter = manifest; + + compiler.inputFileSystem?.readFile( + manifestParameter, + "utf8", + (err, result) => { + if (err) return reject(err); + + if (!result) + return reject( + new DllManifestError( + manifestParameter, + `Can't read anything from ${manifestParameter}` + ) + ); + + try { + const manifest: DllReferencePluginOptionsManifest = + JSON.parse(result); + resolve(manifest); + } catch (parseError) { + const manifestPath = makePathsRelative( + compiler.context, + manifestParameter, + compiler.root + ); + + this.errors.set( + params, + new DllManifestError( + manifestPath, + (parseError as Error).message + ) + ); + } + } + ); + } else { + resolve(manifest); + } } else { - manifest_content = JSON.stringify(manifest); + resolve(undefined); } + }); + if (!this.errors.has(params)) { new DllReferenceAgencyPlugin({ ...this.options, type: this.options.type || "require", @@ -232,24 +269,36 @@ export class DllReferencePlugin { ".json", ".wasm" ], - manifest: manifest_content + manifest }).apply(compiler); - - callback(); } } ); compiler.hooks.compilation.tap( - DLL_REFERENCE_PLUGIN_NAME, - (compilation, _) => { + DllReferencePlugin.name, + (compilation, params) => { if ( "manifest" in this.options && typeof this.options.manifest === "string" ) { + const error = this.errors.get(params); + if (error) { + compilation.errors.push(error); + } + compilation.fileDependencies.add(this.options.manifest); } } ); } } + +class DllManifestError extends WebpackError { + constructor(filename: string, message: string) { + super(); + + this.name = "DllManifestError"; + this.message = `Dll manifest ${filename}\n${message}`; + } +}