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