Skip to main content

rspack_core/
normal_module.rs

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