1use std::{
2 borrow::Cow,
3 hash::{BuildHasherDefault, Hash},
4 sync::{
5 Arc,
6 atomic::{AtomicUsize, Ordering},
7 },
8};
9
10use dashmap::DashMap;
11use derive_more::Debug;
12use futures::future::BoxFuture;
13use rspack_cacheable::{
14 cacheable, cacheable_dyn,
15 with::{AsMap, AsOption, AsPreset},
16};
17use rspack_collections::{Identifiable, IdentifierMap, IdentifierSet};
18use rspack_error::{Diagnosable, Diagnostic, Result, error};
19use rspack_fs::ReadableFileSystem;
20use rspack_hash::{RspackHash, RspackHashDigest};
21use rspack_hook::define_hook;
22use rspack_loader_runner::{AdditionalData, Content, LoaderContext, ResourceData, run_loaders};
23use rspack_sources::{
24 BoxSource, CachedSource, OriginalSource, RawBufferSource, RawStringSource, SourceExt, SourceMap,
25 SourceMapSource, WithoutOriginalOptions,
26};
27use rspack_util::{
28 ext::DynHash,
29 source_map::{ModuleSourceMapConfig, SourceMapKind},
30};
31use rustc_hash::FxHasher;
32use serde_json::json;
33use tracing::{Instrument, info_span};
34
35use crate::{
36 AsyncDependenciesBlockIdentifier, BoxDependencyTemplate, BoxLoader, BoxModule,
37 BoxModuleDependency, BuildContext, BuildInfo, BuildMeta, BuildResult, ChunkGraph,
38 CodeGenerationResult, Compilation, ConcatenationScope, ConnectionState, Context,
39 DependenciesBlock, DependencyId, FactoryMeta, GenerateContext, GeneratorOptions, LibIdentOptions,
40 Module, ModuleGraph, ModuleGraphCacheArtifact, ModuleIdentifier, ModuleLayer, ModuleType,
41 OutputOptions, ParseContext, ParseResult, ParserAndGenerator, ParserOptions, Resolve,
42 RspackLoaderRunnerPlugin, RunnerContext, RuntimeGlobals, RuntimeSpec, SourceType, contextify,
43 diagnostics::ModuleBuildError, get_context, module_update_hash,
44};
45
46#[cacheable]
47#[derive(Debug, Clone)]
48pub enum ModuleIssuer {
49 Unset,
50 None,
51 Some(ModuleIdentifier),
52}
53
54impl ModuleIssuer {
55 pub fn from_identifier(identifier: Option<ModuleIdentifier>) -> Self {
56 match identifier {
57 Some(id) => Self::Some(id),
58 None => Self::None,
59 }
60 }
61
62 pub fn identifier(&self) -> Option<&ModuleIdentifier> {
63 match self {
64 ModuleIssuer::Some(id) => Some(id),
65 _ => None,
66 }
67 }
68
69 pub fn get_module<'a>(&self, module_graph: &'a ModuleGraph) -> Option<&'a BoxModule> {
70 if let Some(id) = self.identifier()
71 && let Some(module) = module_graph.module_by_identifier(id)
72 {
73 Some(module)
74 } else {
75 None
76 }
77 }
78}
79
80define_hook!(NormalModuleReadResource: SeriesBail(resource_data: &ResourceData, fs: &Arc<dyn ReadableFileSystem>) -> Content,tracing=false);
81define_hook!(NormalModuleLoader: Series(loader_context: &mut LoaderContext<RunnerContext>),tracing=false);
82define_hook!(NormalModuleLoaderShouldYield: SeriesBail(loader_context: &LoaderContext<RunnerContext>) -> bool,tracing=false);
83define_hook!(NormalModuleLoaderStartYielding: Series(loader_context: &mut LoaderContext<RunnerContext>),tracing=false);
84define_hook!(NormalModuleBeforeLoaders: Series(module: &mut NormalModule),tracing=false);
85define_hook!(NormalModuleAdditionalData: Series(additional_data: &mut Option<&mut AdditionalData>),tracing=false);
86
87#[derive(Debug, Default)]
88pub struct NormalModuleHooks {
89 pub read_resource: NormalModuleReadResourceHook,
90 pub loader: NormalModuleLoaderHook,
91 pub loader_should_yield: NormalModuleLoaderShouldYieldHook,
92 pub loader_yield: NormalModuleLoaderStartYieldingHook,
93 pub before_loaders: NormalModuleBeforeLoadersHook,
94 pub additional_data: NormalModuleAdditionalDataHook,
95}
96
97#[cacheable]
98#[derive(Debug)]
99pub struct NormalModuleInner {
100 blocks: Vec<AsyncDependenciesBlockIdentifier>,
101 dependencies: Vec<DependencyId>,
102
103 id: ModuleIdentifier,
104 context: Box<Context>,
106 request: String,
108 user_request: String,
110 raw_request: String,
112 module_type: ModuleType,
114 layer: Option<ModuleLayer>,
116 parser_and_generator: Box<dyn ParserAndGenerator>,
118 match_resource: Option<ResourceData>,
120 resource_data: Arc<ResourceData>,
122 #[debug(skip)]
124 loaders: Vec<BoxLoader>,
125
126 #[cacheable(with=AsOption<AsPreset>)]
128 source: Option<BoxSource>,
129
130 resolve_options: Option<Arc<Resolve>>,
132 parser_options: Option<ParserOptions>,
134 generator_options: Option<GeneratorOptions>,
136 extract_source_map: Option<bool>,
138
139 #[allow(unused)]
140 debug_id: usize,
141 #[cacheable(with=AsMap)]
142 cached_source_sizes: DashMap<SourceType, f64, BuildHasherDefault<FxHasher>>,
143 diagnostics: Vec<Diagnostic>,
144
145 code_generation_dependencies: Option<Vec<BoxModuleDependency>>,
146 presentational_dependencies: Option<Vec<BoxDependencyTemplate>>,
147
148 factory_meta: Option<FactoryMeta>,
149 build_info: BuildInfo,
150 build_meta: BuildMeta,
151 parsed: bool,
152
153 source_map_kind: SourceMapKind,
154}
155
156#[cacheable]
157#[derive(Debug)]
158pub enum NormalModule {
159 Owned(Box<NormalModuleInner>),
160 Transferred,
161}
162
163static DEBUG_ID: AtomicUsize = AtomicUsize::new(1);
164
165impl NormalModule {
166 #[inline]
167 fn inner(&self) -> &NormalModuleInner {
168 match self {
169 NormalModule::Owned(inner) => inner,
170 NormalModule::Transferred => {
171 unreachable!("NormalModule ownership has been transferred to loader execution context")
172 }
173 }
174 }
175
176 #[inline]
177 fn inner_mut(&mut self) -> &mut NormalModuleInner {
178 match self {
179 NormalModule::Owned(inner) => inner,
180 NormalModule::Transferred => {
181 unreachable!("NormalModule ownership has been transferred to loader execution context")
182 }
183 }
184 }
185
186 #[inline]
187 async fn with_ownership<R>(
188 &mut self,
189 operation: impl FnOnce(Self) -> BoxFuture<'static, R>,
190 extract_module: impl FnOnce(&mut R) -> Self,
191 ) -> R {
192 let module = std::mem::replace(self, NormalModule::Transferred);
193 let mut result = operation(module).await;
194 *self = extract_module(&mut result);
195 result
196 }
197
198 fn create_id<'request>(
199 module_type: &ModuleType,
200 layer: Option<&ModuleLayer>,
201 request: &'request str,
202 ) -> Cow<'request, str> {
203 if let Some(layer) = layer {
204 format!("{module_type}|{request}|{layer}").into()
205 } else if *module_type == ModuleType::JsAuto {
206 request.into()
207 } else {
208 format!("{module_type}|{request}").into()
209 }
210 }
211
212 #[allow(clippy::too_many_arguments)]
213 pub fn new(
214 request: String,
215 user_request: String,
216 raw_request: String,
217 module_type: impl Into<ModuleType>,
218 layer: Option<ModuleLayer>,
219 parser_and_generator: Box<dyn ParserAndGenerator>,
220 parser_options: Option<ParserOptions>,
221 generator_options: Option<GeneratorOptions>,
222 match_resource: Option<ResourceData>,
223 resource_data: Arc<ResourceData>,
224 resolve_options: Option<Arc<Resolve>>,
225 loaders: Vec<BoxLoader>,
226 context: Option<Context>,
227 extract_source_map: Option<bool>,
228 ) -> Self {
229 let module_type = module_type.into();
230 let id = Self::create_id(&module_type, layer.as_ref(), &request);
231 Self::Owned(Box::new(NormalModuleInner {
232 blocks: Vec::new(),
233 dependencies: Vec::new(),
234 id: ModuleIdentifier::from(id.as_ref()),
235 context: Box::new(context.unwrap_or_else(|| get_context(&resource_data))),
236 request,
237 user_request,
238 raw_request,
239 module_type,
240 layer,
241 parser_and_generator,
242 parser_options,
243 generator_options,
244 match_resource,
245 resource_data,
246 resolve_options,
247 loaders,
248 source: None,
249 debug_id: DEBUG_ID.fetch_add(1, Ordering::Relaxed),
250 extract_source_map,
251
252 cached_source_sizes: DashMap::default(),
253 diagnostics: Default::default(),
254 code_generation_dependencies: None,
255 presentational_dependencies: None,
256 factory_meta: None,
257 build_info: Default::default(),
258 build_meta: Default::default(),
259 parsed: false,
260 source_map_kind: SourceMapKind::empty(),
261 }))
262 }
263
264 pub fn id(&self) -> ModuleIdentifier {
265 self.inner().id
266 }
267
268 pub fn match_resource(&self) -> Option<&ResourceData> {
269 self.inner().match_resource.as_ref()
270 }
271
272 pub fn match_resource_mut(&mut self) -> &mut Option<ResourceData> {
273 &mut self.inner_mut().match_resource
274 }
275
276 pub fn resource_resolved_data(&self) -> &Arc<ResourceData> {
277 &self.inner().resource_data
278 }
279
280 pub fn request(&self) -> &str {
281 &self.inner().request
282 }
283
284 pub fn user_request(&self) -> &str {
285 &self.inner().user_request
286 }
287
288 pub fn user_request_mut(&mut self) -> &mut String {
289 &mut self.inner_mut().user_request
290 }
291
292 pub fn raw_request(&self) -> &str {
293 &self.inner().raw_request
294 }
295
296 pub fn loaders(&self) -> &[BoxLoader] {
297 &self.inner().loaders
298 }
299
300 pub fn parser_and_generator(&self) -> &dyn ParserAndGenerator {
301 &*self.inner().parser_and_generator
302 }
303
304 pub fn code_generation_dependencies(&self) -> &Option<Vec<BoxModuleDependency>> {
305 &self.inner().code_generation_dependencies
306 }
307
308 pub fn presentational_dependencies(&self) -> &Option<Vec<BoxDependencyTemplate>> {
309 &self.inner().presentational_dependencies
310 }
311
312 #[tracing::instrument(
313 "NormalModule:build_hash", skip_all,fields(
314 resource = self.inner().resource_data.resource()
315 )
316 )]
317 fn init_build_hash(
318 &self,
319 output_options: &OutputOptions,
320 build_meta: &BuildMeta,
321 ) -> RspackHashDigest {
322 let mut hasher = RspackHash::from(output_options);
323 "source".hash(&mut hasher);
324 if let Some(error) = self.first_error() {
325 error.message.hash(&mut hasher);
326 } else if let Some(s) = &self.inner().source {
327 s.hash(&mut hasher);
328 }
329 "meta".hash(&mut hasher);
330 build_meta.hash(&mut hasher);
331 hasher.digest(&output_options.hash_digest)
332 }
333
334 pub fn get_parser_options(&self) -> Option<&ParserOptions> {
335 self.inner().parser_options.as_ref()
336 }
337
338 pub fn get_generator_options(&self) -> Option<&GeneratorOptions> {
339 self.inner().generator_options.as_ref()
340 }
341}
342
343impl Identifiable for NormalModule {
344 #[inline]
345 fn identifier(&self) -> ModuleIdentifier {
346 self.inner().id
347 }
348}
349
350impl DependenciesBlock for NormalModule {
351 fn add_block_id(&mut self, block: AsyncDependenciesBlockIdentifier) {
352 self.inner_mut().blocks.push(block)
353 }
354
355 fn get_blocks(&self) -> &[AsyncDependenciesBlockIdentifier] {
356 &self.inner().blocks
357 }
358
359 fn add_dependency_id(&mut self, dependency: DependencyId) {
360 self.inner_mut().dependencies.push(dependency)
361 }
362
363 fn remove_dependency_id(&mut self, dependency: DependencyId) {
364 self.inner_mut().dependencies.retain(|d| d != &dependency)
365 }
366
367 fn get_dependencies(&self) -> &[DependencyId] {
368 &self.inner().dependencies
369 }
370}
371
372#[cacheable_dyn]
373#[async_trait::async_trait]
374impl Module for NormalModule {
375 fn module_type(&self) -> &ModuleType {
376 &self.inner().module_type
377 }
378
379 fn source_types(&self, module_graph: &ModuleGraph) -> &[SourceType] {
380 self
381 .inner()
382 .parser_and_generator
383 .source_types(self, module_graph)
384 }
385
386 fn source(&self) -> Option<&BoxSource> {
387 self.inner().source.as_ref()
388 }
389
390 fn readable_identifier(&self, context: &Context) -> Cow<'_, str> {
391 Cow::Owned(context.shorten(&self.inner().user_request))
392 }
393
394 fn size(&self, source_type: Option<&SourceType>, _compilation: Option<&Compilation>) -> f64 {
395 if let Some(size_ref) = source_type.and_then(|st| self.inner().cached_source_sizes.get(st)) {
396 *size_ref
397 } else {
398 let size = f64::max(
399 1.0,
400 self.inner().parser_and_generator.size(self, source_type),
401 );
402 source_type.and_then(|st| self.inner().cached_source_sizes.insert(*st, size));
403 size
404 }
405 }
406
407 #[tracing::instrument("NormalModule:build", skip_all, fields(
408 perfetto.track_name = format!("Module Build"),
409 perfetto.process_name = format!("Rspack Build Detail"),
410 module.resource = self.resource_resolved_data().resource(),
411 module.identifier = self.identifier().as_str(),
412 module.loaders = ?self.inner().loaders.iter().map(|l| l.identifier().as_str()).collect::<Vec<_>>())
413 )]
414 async fn build(
415 &mut self,
416 build_context: BuildContext,
417 _compilation: Option<&Compilation>,
418 ) -> Result<BuildResult> {
419 let inner = self.inner_mut();
420 inner.parsed = true;
422
423 let no_parse = if let Some(no_parse) = build_context.compiler_options.module.no_parse.as_ref() {
424 no_parse.try_match(inner.request.as_str()).await?
425 } else {
426 false
427 };
428
429 build_context
430 .plugin_driver
431 .normal_module_hooks
432 .before_loaders
433 .call(self)
434 .await?;
435
436 let plugin = Arc::new(RspackLoaderRunnerPlugin {
437 plugin_driver: build_context.plugin_driver.clone(),
438 extract_source_map: self.inner().extract_source_map,
439 });
440
441 let compiler_id = build_context.compiler_id;
442 let compilation_id = build_context.compilation_id;
443 let compiler_options = build_context.compiler_options.clone();
444 let resolver_factory = build_context.resolver_factory.clone();
445 let fs = build_context.fs.clone();
446 let (mut loader_result, err) = self
447 .with_ownership(
448 |mut module| {
449 Box::pin(async move {
450 let inner = module.inner_mut();
451 let (loader_result, err) = run_loaders(
452 inner.loaders.clone(),
453 inner.resource_data.clone(),
454 Some(plugin.clone()),
455 RunnerContext {
456 compiler_id,
457 compilation_id,
458 options: compiler_options,
459 resolver_factory,
460 source_map_kind: inner.source_map_kind,
461 module,
462 },
463 fs,
464 )
465 .instrument(info_span!("NormalModule:run_loaders",))
466 .await;
467 (loader_result, err)
468 })
469 },
470 |(loader_result, _)| {
471 std::mem::replace(&mut loader_result.context.module, NormalModule::Transferred)
472 },
473 )
474 .await;
475
476 let inner = self.inner_mut();
477 if let Some(err) = err {
478 inner.build_info.cacheable = loader_result.cacheable;
479 inner.build_info.file_dependencies = loader_result
480 .file_dependencies
481 .into_iter()
482 .map(Into::into)
483 .collect();
484 inner.build_info.context_dependencies = loader_result
485 .context_dependencies
486 .into_iter()
487 .map(Into::into)
488 .collect();
489 inner.build_info.missing_dependencies = loader_result
490 .missing_dependencies
491 .into_iter()
492 .map(Into::into)
493 .collect();
494 inner.build_info.build_dependencies = loader_result
495 .build_dependencies
496 .into_iter()
497 .map(Into::into)
498 .collect();
499
500 inner.source = None;
501
502 let current_loader = loader_result.current_loader.map(|current_loader| {
503 contextify(
504 build_context.compiler_options.context.as_path(),
505 current_loader.as_str(),
506 )
507 });
508 let diagnostic = Diagnostic::from(rspack_error::Error::from(ModuleBuildError::new(
509 err,
510 current_loader,
511 )));
512 inner.diagnostics.push(diagnostic);
513
514 self.inner_mut().build_info.hash = Some(self.init_build_hash(
515 &build_context.compiler_options.output,
516 &self.inner().build_meta,
517 ));
518 return Ok(BuildResult {
519 dependencies: Vec::new(),
520 blocks: Vec::new(),
521 optimization_bailouts: vec![],
522 });
523 };
524
525 build_context
526 .plugin_driver
527 .normal_module_hooks
528 .additional_data
529 .call(&mut loader_result.additional_data.as_mut())
530 .await?;
531 self.add_diagnostics(loader_result.diagnostics);
532
533 let inner = self.inner_mut();
534 let is_binary = inner
535 .generator_options
536 .as_ref()
537 .and_then(|g| match g {
538 GeneratorOptions::Asset(g) => g.binary,
539 GeneratorOptions::AssetInline(g) => g.binary,
540 GeneratorOptions::AssetResource(g) => g.binary,
541 _ => None,
542 })
543 .unwrap_or(inner.module_type.is_binary());
544
545 let content = if is_binary {
546 Content::Buffer(loader_result.content.into_bytes())
547 } else {
548 Content::String(loader_result.content.into_string_lossy())
549 };
550 let source = self.create_source(content, loader_result.source_map)?;
551
552 let inner = self.inner_mut();
553 inner.build_info.cacheable = loader_result.cacheable;
554 inner.build_info.file_dependencies = loader_result
555 .file_dependencies
556 .into_iter()
557 .map(Into::into)
558 .collect();
559 inner.build_info.context_dependencies = loader_result
560 .context_dependencies
561 .into_iter()
562 .map(Into::into)
563 .collect();
564 inner.build_info.missing_dependencies = loader_result
565 .missing_dependencies
566 .into_iter()
567 .map(Into::into)
568 .collect();
569 inner.build_info.build_dependencies = loader_result
570 .build_dependencies
571 .into_iter()
572 .map(Into::into)
573 .collect();
574
575 if no_parse {
576 inner.parsed = false;
577 inner.source = Some(source);
578 inner.code_generation_dependencies = Some(Vec::new());
579 inner.presentational_dependencies = Some(Vec::new());
580
581 self.inner_mut().build_info.hash = Some(self.init_build_hash(
582 &build_context.compiler_options.output,
583 &self.inner().build_meta,
584 ));
585
586 return Ok(BuildResult {
587 dependencies: Vec::new(),
588 blocks: Vec::new(),
589 optimization_bailouts: Vec::new(),
590 });
591 }
592
593 let (
594 ParseResult {
595 source,
596 dependencies,
597 blocks,
598 presentational_dependencies,
599 code_generation_dependencies,
600 side_effects_bailout,
601 },
602 diagnostics,
603 ) = inner
604 .parser_and_generator
605 .parse(ParseContext {
606 source: source.clone(),
607 module_context: &inner.context,
608 module_identifier: inner.id,
609 module_parser_options: inner.parser_options.as_ref(),
610 module_type: &inner.module_type,
611 module_layer: inner.layer.as_ref(),
612 module_user_request: &inner.user_request,
613 module_match_resource: inner.match_resource.as_ref(),
614 module_source_map_kind: inner.source_map_kind,
615 loaders: &inner.loaders,
616 resource_data: &inner.resource_data,
617 compiler_options: &build_context.compiler_options,
618 additional_data: loader_result.additional_data,
619 factory_meta: inner.factory_meta.as_ref(),
620 build_info: &mut inner.build_info,
621 build_meta: &mut inner.build_meta,
622 parse_meta: loader_result.parse_meta,
623 runtime_template: &build_context.runtime_template,
624 })
625 .await?
626 .split_into_parts();
627 if diagnostics.iter().any(|d| d.is_error()) {
628 inner.build_meta = Default::default();
629 }
630 if !diagnostics.is_empty() {
631 self.add_diagnostics(diagnostics);
632 }
633 let optimization_bailouts = if let Some(side_effects_bailout) = side_effects_bailout {
634 let short_id = self.readable_identifier(&build_context.compiler_options.context);
635 vec![format!(
636 "{} with side_effects in source code at {short_id}:{}",
637 side_effects_bailout.ty, side_effects_bailout.msg
638 )]
639 } else {
640 vec![]
641 };
642 let inner = self.inner_mut();
645 inner.source = Some(source);
646 inner.code_generation_dependencies = Some(code_generation_dependencies);
647 inner.presentational_dependencies = Some(presentational_dependencies);
648
649 self.inner_mut().build_info.hash = Some(self.init_build_hash(
650 &build_context.compiler_options.output,
651 &self.inner().build_meta,
652 ));
653
654 Ok(BuildResult {
655 dependencies,
656 blocks,
657 optimization_bailouts,
658 })
659 }
660
661 async fn code_generation(
663 &self,
664 compilation: &Compilation,
665 runtime: Option<&RuntimeSpec>,
666 mut concatenation_scope: Option<ConcatenationScope>,
667 ) -> Result<CodeGenerationResult> {
668 if let Some(error) = self.first_error() {
669 let mut code_generation_result = CodeGenerationResult::default();
670 let module_graph = compilation.get_module_graph();
671
672 if self
675 .source_types(module_graph)
676 .contains(&SourceType::JavaScript)
677 {
678 let error = error.render_report(compilation.options.stats.colors)?;
679 code_generation_result.add(
680 SourceType::JavaScript,
681 RawStringSource::from(format!("throw new Error({});\n", json!(error))).boxed(),
682 );
683 code_generation_result.concatenation_scope = concatenation_scope;
684 }
685 return Ok(code_generation_result);
686 }
687 let inner = self.inner();
688 let Some(source) = &inner.source else {
689 return Err(error!(
690 "Failed to generate code because ast or source is not set for module {}",
691 inner.request
692 ));
693 };
694
695 let mut code_generation_result = CodeGenerationResult::default();
696 if !inner.parsed {
697 code_generation_result
698 .runtime_requirements
699 .insert(RuntimeGlobals::MODULE);
700 code_generation_result
701 .runtime_requirements
702 .insert(RuntimeGlobals::EXPORTS);
703 code_generation_result
704 .runtime_requirements
705 .insert(RuntimeGlobals::THIS_AS_EXPORTS);
706 }
707
708 let module_graph = compilation.get_module_graph();
709 for source_type in self.source_types(module_graph) {
710 let generation_result = inner
711 .parser_and_generator
712 .generate(
713 source,
714 self,
715 &mut GenerateContext {
716 compilation,
717 runtime_requirements: &mut code_generation_result.runtime_requirements,
718 data: &mut code_generation_result.data,
719 requested_source_type: *source_type,
720 runtime,
721 concatenation_scope: concatenation_scope.as_mut(),
722 },
723 )
724 .await?;
725 code_generation_result.add(*source_type, CachedSource::new(generation_result).boxed());
726 }
727 code_generation_result.concatenation_scope = concatenation_scope;
728 Ok(code_generation_result)
729 }
730
731 async fn get_runtime_hash(
732 &self,
733 compilation: &Compilation,
734 runtime: Option<&RuntimeSpec>,
735 ) -> Result<RspackHashDigest> {
736 let inner = self.inner();
737 let mut hasher = RspackHash::from(&compilation.options.output);
738 inner.build_info.hash.dyn_hash(&mut hasher);
739 if inner.source.is_some() {
741 inner
742 .parser_and_generator
743 .get_runtime_hash(self, compilation, runtime)
744 .await?
745 .dyn_hash(&mut hasher);
746 }
747 module_update_hash(self, &mut hasher, compilation, runtime);
748 Ok(hasher.digest(&compilation.options.output.hash_digest))
749 }
750
751 fn name_for_condition(&self) -> Option<Box<str>> {
752 let resource = self
754 .match_resource()
755 .unwrap_or_else(|| &self.inner().resource_data)
756 .resource();
757 let idx = resource.find('?');
758 if let Some(idx) = idx {
759 Some(resource[..idx].into())
760 } else {
761 Some(resource.into())
762 }
763 }
764
765 fn lib_ident(&self, options: LibIdentOptions) -> Option<Cow<'_, str>> {
766 let mut ident = String::new();
767 if let Some(layer) = &self.inner().layer {
768 ident += "(";
769 ident += layer;
770 ident += ")/";
771 }
772 ident += &contextify(options.context, self.user_request());
773 Some(Cow::Owned(ident))
774 }
775
776 fn get_resolve_options(&self) -> Option<Arc<Resolve>> {
777 self.inner().resolve_options.clone()
778 }
779
780 fn get_code_generation_dependencies(&self) -> Option<&[BoxModuleDependency]> {
781 if let Some(deps) = self.inner().code_generation_dependencies.as_deref()
782 && !deps.is_empty()
783 {
784 Some(deps)
785 } else {
786 None
787 }
788 }
789
790 fn get_presentational_dependencies(&self) -> Option<&[BoxDependencyTemplate]> {
791 if let Some(deps) = self.inner().presentational_dependencies.as_deref()
792 && !deps.is_empty()
793 {
794 Some(deps)
795 } else {
796 None
797 }
798 }
799
800 fn get_context(&self) -> Option<Box<Context>> {
801 Some(self.inner().context.clone())
802 }
803
804 fn get_layer(&self) -> Option<&ModuleLayer> {
805 self.inner().layer.as_ref()
806 }
807
808 fn get_side_effects_connection_state(
810 &self,
811 module_graph: &ModuleGraph,
812 module_graph_cache: &ModuleGraphCacheArtifact,
813 module_chain: &mut IdentifierSet,
814 connection_state_cache: &mut IdentifierMap<ConnectionState>,
815 ) -> ConnectionState {
816 module_graph_cache.cached_get_side_effects_connection_state(self.id(), || {
817 if let Some(state) = connection_state_cache.get(&self.inner().id) {
818 return *state;
819 }
820
821 if let Some(side_effect_free) = self.factory_meta().and_then(|m| m.side_effect_free) {
822 return ConnectionState::Active(!side_effect_free);
823 }
824 if Some(true) == self.build_meta().side_effect_free {
825 if module_chain.contains(&self.identifier()) {
827 return ConnectionState::CircularConnection;
828 }
829 module_chain.insert(self.identifier());
830 let mut current = ConnectionState::Active(false);
831 for dependency_id in self.get_dependencies().iter() {
832 let dependency = module_graph.dependency_by_id(dependency_id);
833 let state = dependency.get_module_evaluation_side_effects_state(
834 module_graph,
835 module_graph_cache,
836 module_chain,
837 connection_state_cache,
838 );
839 if matches!(state, ConnectionState::Active(true)) {
840 module_chain.remove(&self.identifier());
842 connection_state_cache.insert(self.inner().id, ConnectionState::Active(true));
843 return ConnectionState::Active(true);
844 } else if !matches!(state, ConnectionState::CircularConnection) {
845 current = current + state;
846 }
847 }
848 module_chain.remove(&self.identifier());
849 connection_state_cache.insert(self.inner().id, current);
850 return current;
851 }
852 ConnectionState::Active(true)
853 })
854 }
855
856 fn get_concatenation_bailout_reason(
857 &self,
858 mg: &ModuleGraph,
859 cg: &ChunkGraph,
860 ) -> Option<Cow<'static, str>> {
861 self
862 .inner()
863 .parser_and_generator
864 .get_concatenation_bailout_reason(self, mg, cg)
865 }
866
867 fn factory_meta(&self) -> Option<&FactoryMeta> {
868 self.inner().factory_meta.as_ref()
869 }
870
871 fn set_factory_meta(&mut self, factory_meta: FactoryMeta) {
872 self.inner_mut().factory_meta = Some(factory_meta);
873 }
874
875 fn build_info(&self) -> &BuildInfo {
876 &self.inner().build_info
877 }
878
879 fn build_info_mut(&mut self) -> &mut BuildInfo {
880 &mut self.inner_mut().build_info
881 }
882
883 fn build_meta(&self) -> &BuildMeta {
884 &self.inner().build_meta
885 }
886
887 fn build_meta_mut(&mut self) -> &mut BuildMeta {
888 &mut self.inner_mut().build_meta
889 }
890}
891
892impl ModuleSourceMapConfig for NormalModule {
893 fn get_source_map_kind(&self) -> &SourceMapKind {
894 &self.inner().source_map_kind
895 }
896
897 fn set_source_map_kind(&mut self, source_map_kind: SourceMapKind) {
898 self.inner_mut().source_map_kind = source_map_kind;
899 }
900}
901
902impl Diagnosable for NormalModule {
903 fn add_diagnostic(&mut self, diagnostic: Diagnostic) {
904 self.inner_mut().diagnostics.push(diagnostic);
905 }
906
907 fn add_diagnostics(&mut self, mut diagnostics: Vec<Diagnostic>) {
908 self.inner_mut().diagnostics.append(&mut diagnostics);
909 }
910
911 fn diagnostics(&self) -> Cow<'_, [Diagnostic]> {
912 Cow::Borrowed(&self.inner().diagnostics)
913 }
914}
915
916impl NormalModule {
917 fn create_source(&self, content: Content, source_map: Option<SourceMap>) -> Result<BoxSource> {
918 if content.is_buffer() {
919 return Ok(RawBufferSource::from(content.into_bytes()).boxed());
920 }
921 let source_map_kind = self.get_source_map_kind();
922 if source_map_kind.enabled()
923 && let Some(source_map) = source_map
924 {
925 let content = content.into_string_lossy();
926 return Ok(
927 SourceMapSource::new(WithoutOriginalOptions {
928 value: content,
929 name: self.request(),
930 source_map,
931 })
932 .boxed(),
933 );
934 }
935 if source_map_kind.enabled()
936 && let Content::String(content) = content
937 {
938 return Ok(OriginalSource::new(content, self.request()).boxed());
939 }
940 Ok(RawStringSource::from(content.into_string_lossy()).boxed())
941 }
942}