Skip to main content

rspack_plugin_javascript/plugin/
flag_dependency_exports_plugin.rs

1use std::rc::Rc;
2
3use rayon::prelude::*;
4use rspack_collections::{IdentifierMap, IdentifierSet};
5use rspack_core::{
6  AsyncModulesArtifact, BuildMetaExportsType, Compilation, CompilationFinishModules,
7  DependenciesBlock, DependencyId, EvaluatedInlinableValue, ExportInfo, ExportInfoData,
8  ExportNameOrSpec, ExportProvided, ExportSpecExports, ExportsInfo, ExportsInfoData,
9  ExportsOfExportsSpec, ExportsSpec, GetTargetResult, Logger, ModuleGraph,
10  ModuleGraphCacheArtifact, ModuleGraphConnection, ModuleIdentifier, Nullable, Plugin,
11  PrefetchExportsInfoMode, get_target,
12  incremental::{self, IncrementalPasses},
13};
14use rspack_error::Result;
15use rspack_hook::{plugin, plugin_hook};
16use rspack_util::fx_hash::{FxIndexMap, FxIndexSet};
17use rustc_hash::FxHashSet;
18use swc_core::ecma::atoms::Atom;
19
20struct FlagDependencyExportsState<'a> {
21  mg: &'a mut ModuleGraph,
22  mg_cache: &'a ModuleGraphCacheArtifact,
23}
24
25impl<'a> FlagDependencyExportsState<'a> {
26  pub fn new(mg: &'a mut ModuleGraph, mg_cache: &'a ModuleGraphCacheArtifact) -> Self {
27    Self { mg, mg_cache }
28  }
29
30  pub fn apply(&mut self, modules: IdentifierSet) {
31    // initialize the exports info data and their provided info for all modules
32    for module_id in &modules {
33      let exports_type_unset = self
34        .mg
35        .module_by_identifier(module_id)
36        .expect("should have module")
37        .build_meta()
38        .exports_type
39        == BuildMetaExportsType::Unset;
40      // for module_id in modules {
41      let exports_info = self.mg.get_exports_info_data_mut(module_id);
42      // Reset exports provide info back to initial
43      exports_info.reset_provide_info();
44      if exports_type_unset
45        && !matches!(
46          exports_info.other_exports_info().provided(),
47          Some(ExportProvided::Unknown)
48        )
49      {
50        exports_info.set_has_provide_info();
51        exports_info.set_unknown_exports_provided(false, None, None, None, None);
52        continue;
53      }
54
55      exports_info.set_has_provide_info();
56    }
57
58    // collect the exports specs from all modules and their dependencies
59    // and then merge the exports specs to exports info data
60    // and collect the dependencies which will be used to backtrack when target exports info is changed
61    let mut batch = modules;
62    let mut dependencies: IdentifierMap<IdentifierSet> = IdentifierMap::default();
63    while !batch.is_empty() {
64      let modules = std::mem::take(&mut batch);
65
66      // collect the exports specs from modules by calling `dependency.get_exports`
67      let module_exports_specs = modules
68        .into_par_iter()
69        .map(|module_id| {
70          let exports_specs =
71            collect_module_exports_specs(&module_id, self.mg, self.mg_cache).unwrap_or_default();
72          (module_id, exports_specs)
73        })
74        .collect::<Vec<_>>();
75
76      let mut changed_modules = FxHashSet::default();
77
78      // partition the exports specs into two parts:
79      // 1. if the exports info data do not have `redirect_to` and exports specs do not have nested `exports`,
80      // then the merging only affect the exports info data itself and can be done parallelly
81      // 2. if the exports info data have `redirect_to` or exports specs have nested `exports`,
82      // then the merging will affect the redirected exports info data or create a new exports info data
83      // and this merging can not be done parallelly
84      //
85      // There are two cases that the `redirect_to` or nested `exports` exist:
86      // 1. exports from json dependency which has nested json object data
87      // 2. exports from an esm reexport and the target is a commonjs module which should create a interop `default` export
88      let (non_nested_specs, has_nested_specs): (Vec<_>, Vec<_>) = module_exports_specs
89        .into_iter()
90        .partition(|(_mid, (_, has_nested_exports))| {
91          if *has_nested_exports {
92            return false;
93          }
94          true
95        });
96
97      // parallelize the merging of exports specs to exports info data
98      let non_nested_tasks = non_nested_specs
99        .into_iter()
100        .map(|(module_id, (exports_specs, _))| {
101          let exports_info = self.mg.get_exports_info_data(&module_id).clone();
102          (module_id, exports_info, exports_specs)
103        })
104        .par_bridge()
105        .map(|(module_id, mut exports_info, exports_specs)| {
106          let mut changed = false;
107          let mut dependencies = vec![];
108          for (dep_id, exports_spec) in exports_specs.into_iter() {
109            let (is_changed, changed_dependencies) = process_exports_spec_without_nested(
110              self.mg,
111              &module_id,
112              dep_id,
113              &exports_spec,
114              &mut exports_info,
115            );
116            changed |= is_changed;
117            dependencies.extend(changed_dependencies);
118          }
119          (module_id, changed, dependencies, exports_info)
120        })
121        .collect::<Vec<_>>();
122
123      // handle collected side effects and apply the merged exports info data to module graph
124      for (module_id, changed, changed_dependencies, exports_info) in non_nested_tasks {
125        if changed {
126          changed_modules.insert(module_id);
127        }
128        for (module_id, dep_id) in changed_dependencies {
129          dependencies.entry(module_id).or_default().insert(dep_id);
130        }
131        self.mg.set_exports_info(exports_info.id(), exports_info);
132      }
133
134      // serializing the merging of exports specs to nested exports info data
135      for (module_id, (exports_specs, _)) in has_nested_specs {
136        let mut changed = false;
137        for (dep_id, exports_spec) in exports_specs.into_iter() {
138          let (is_changed, changed_dependencies) =
139            process_exports_spec(self.mg, &module_id, dep_id, &exports_spec);
140          changed |= is_changed;
141          for (module_id, dep_id) in changed_dependencies {
142            dependencies.entry(module_id).or_default().insert(dep_id);
143          }
144        }
145        if changed {
146          changed_modules.insert(module_id);
147        }
148      }
149
150      // collect the dependencies which will be used to backtrack when target exports info is changed
151      batch.extend(changed_modules.into_iter().flat_map(|m| {
152        dependencies
153          .get(&m)
154          .into_iter()
155          .flat_map(|d| d.iter())
156          .copied()
157      }));
158    }
159  }
160}
161
162/// Used for reducing nums of params
163#[derive(Debug, Clone)]
164pub struct DefaultExportInfo<'a> {
165  can_mangle: Option<bool>,
166  terminal_binding: bool,
167  from: Option<&'a ModuleGraphConnection>,
168  priority: Option<u8>,
169}
170
171#[plugin]
172#[derive(Debug, Default)]
173pub struct FlagDependencyExportsPlugin;
174
175#[plugin_hook(CompilationFinishModules for FlagDependencyExportsPlugin)]
176async fn finish_modules(
177  &self,
178  compilation: &mut Compilation,
179  _async_modules_artifact: &mut AsyncModulesArtifact,
180) -> Result<()> {
181  let modules: IdentifierSet = if let Some(mutations) = compilation
182    .incremental
183    .mutations_read(IncrementalPasses::PROVIDED_EXPORTS)
184  {
185    let modules = mutations.get_affected_modules_with_module_graph(compilation.get_module_graph());
186    tracing::debug!(target: incremental::TRACING_TARGET, passes = %IncrementalPasses::PROVIDED_EXPORTS, %mutations, ?modules);
187    let logger = compilation.get_logger("rspack.incremental.providedExports");
188    logger.log(format!(
189      "{} modules are affected, {} in total",
190      modules.len(),
191      compilation.get_module_graph().modules().len()
192    ));
193    modules
194  } else {
195    compilation
196      .get_module_graph()
197      .modules()
198      .keys()
199      .copied()
200      .collect()
201  };
202  let module_graph_cache = compilation.module_graph_cache_artifact.clone();
203
204  let module_graph = compilation
205    .build_module_graph_artifact
206    .get_module_graph_mut();
207  FlagDependencyExportsState::new(module_graph, &module_graph_cache).apply(modules);
208  Ok(())
209}
210
211impl Plugin for FlagDependencyExportsPlugin {
212  fn name(&self) -> &'static str {
213    "FlagDependencyExportsPlugin"
214  }
215
216  fn apply(&self, ctx: &mut rspack_core::ApplyContext<'_>) -> Result<()> {
217    ctx
218      .compilation_hooks
219      .finish_modules
220      .tap(finish_modules::new(self));
221    Ok(())
222  }
223}
224
225/**
226 * Collect all exports specs from a module and its dependencies
227 * by calling `dependency.get_exports` for each dependency.
228 */
229fn collect_module_exports_specs(
230  module_id: &ModuleIdentifier,
231  mg: &ModuleGraph,
232  mg_cache: &ModuleGraphCacheArtifact,
233) -> Option<(FxIndexMap<DependencyId, ExportsSpec>, bool)> {
234  let mut has_nested_exports = false;
235  fn walk_block<B: DependenciesBlock + ?Sized>(
236    block: &B,
237    dep_ids: &mut FxIndexSet<DependencyId>,
238    mg: &ModuleGraph,
239  ) {
240    dep_ids.extend(block.get_dependencies().iter().copied());
241    for block_id in block.get_blocks() {
242      if let Some(block) = mg.block_by_id(block_id) {
243        walk_block(block, dep_ids, mg);
244      }
245    }
246  }
247
248  let block = mg.module_by_identifier(module_id)?.as_ref();
249  let mut dep_ids = FxIndexSet::default();
250  walk_block(block, &mut dep_ids, mg);
251
252  // There is no need to use the cache here
253  // because the `get_exports` of each dependency will only be called once
254  // mg_cache.freeze();
255  let res = dep_ids
256    .into_iter()
257    .filter_map(|id| {
258      let dep = mg.dependency_by_id(&id);
259      let exports_spec = dep.get_exports(mg, mg_cache)?;
260      has_nested_exports |= exports_spec.has_nested_exports();
261      Some((id, exports_spec))
262    })
263    .collect::<FxIndexMap<DependencyId, ExportsSpec>>();
264  // mg_cache.unfreeze();
265  Some((res, has_nested_exports))
266}
267
268/// Merge exports specs to exports info data
269/// and also collect the dependencies
270/// which will be used to backtrack when target exports info is changed
271pub fn process_exports_spec(
272  mg: &mut ModuleGraph,
273  module_id: &ModuleIdentifier,
274  dep_id: DependencyId,
275  export_desc: &ExportsSpec,
276) -> (bool, Vec<(ModuleIdentifier, ModuleIdentifier)>) {
277  let mut changed = false;
278  let mut dependencies = vec![];
279  let exports = &export_desc.exports;
280  let global_can_mangle = &export_desc.can_mangle;
281  let global_from = export_desc.from.as_ref();
282  let global_priority = &export_desc.priority;
283  let global_terminal_binding = export_desc.terminal_binding.unwrap_or(false);
284  let export_dependencies = &export_desc.dependencies;
285  if let Some(hide_export) = &export_desc.hide_export {
286    let exports_info = mg.get_exports_info_data_mut(module_id);
287    for name in hide_export.iter() {
288      exports_info.ensure_export_info(name);
289    }
290    for name in hide_export.iter() {
291      exports_info
292        .named_exports_mut(name)
293        .expect("should have named export")
294        .unset_target(&dep_id);
295    }
296  }
297  match exports {
298    ExportsOfExportsSpec::UnknownExports => {
299      changed |= mg
300        .get_exports_info_data_mut(module_id)
301        .set_unknown_exports_provided(
302          global_can_mangle.unwrap_or_default(),
303          export_desc.exclude_exports.as_ref(),
304          global_from.map(|_| dep_id),
305          global_from.map(|_| dep_id),
306          *global_priority,
307        );
308    }
309    ExportsOfExportsSpec::NoExports => {}
310    ExportsOfExportsSpec::Names(ele) => {
311      let (merge_changed, merge_dependencies) = merge_exports(
312        mg,
313        module_id,
314        mg.get_exports_info(module_id),
315        ele,
316        DefaultExportInfo {
317          can_mangle: *global_can_mangle,
318          terminal_binding: global_terminal_binding,
319          from: global_from,
320          priority: *global_priority,
321        },
322        dep_id,
323      );
324      changed |= merge_changed;
325      dependencies.extend(merge_dependencies);
326    }
327  }
328
329  if let Some(export_dependencies) = export_dependencies {
330    for export_dep in export_dependencies {
331      dependencies.push((*export_dep, *module_id));
332    }
333  }
334
335  (changed, dependencies)
336}
337
338/// Merge exports specs to exports info data
339/// and also collect the dependencies
340/// which will be used to backtrack when target exports info is changed
341/// This method is used for the case that the exports info data will not be nested modified
342/// that means this exports info can be modified parallelly
343pub fn process_exports_spec_without_nested(
344  mg: &ModuleGraph,
345  module_id: &ModuleIdentifier,
346  dep_id: DependencyId,
347  export_desc: &ExportsSpec,
348  exports_info: &mut ExportsInfoData,
349) -> (bool, Vec<(ModuleIdentifier, ModuleIdentifier)>) {
350  let mut changed = false;
351  let mut dependencies = vec![];
352
353  let exports = &export_desc.exports;
354  let global_can_mangle = &export_desc.can_mangle;
355  let global_from = export_desc.from.as_ref();
356  let global_priority = &export_desc.priority;
357  let global_terminal_binding = export_desc.terminal_binding.unwrap_or(false);
358  let export_dependencies = &export_desc.dependencies;
359  if let Some(hide_export) = &export_desc.hide_export {
360    for name in hide_export.iter() {
361      exports_info
362        .ensure_owned_export_info(name)
363        .unset_target(&dep_id);
364    }
365  }
366  match exports {
367    ExportsOfExportsSpec::UnknownExports => {
368      changed |= exports_info.set_unknown_exports_provided(
369        global_can_mangle.unwrap_or_default(),
370        export_desc.exclude_exports.as_ref(),
371        global_from.map(|_| dep_id),
372        global_from.map(|_| dep_id),
373        *global_priority,
374      );
375    }
376    ExportsOfExportsSpec::NoExports => {}
377    ExportsOfExportsSpec::Names(ele) => {
378      let (merge_changed, merge_dependencies) = merge_exports_without_nested(
379        mg,
380        module_id,
381        exports_info,
382        ele,
383        DefaultExportInfo {
384          can_mangle: *global_can_mangle,
385          terminal_binding: global_terminal_binding,
386          from: global_from,
387          priority: *global_priority,
388        },
389        dep_id,
390      );
391      changed |= merge_changed;
392      dependencies.extend(merge_dependencies);
393    }
394  }
395
396  if let Some(export_dependencies) = export_dependencies {
397    for export_dep in export_dependencies {
398      dependencies.push((*export_dep, *module_id));
399    }
400  }
401
402  (changed, dependencies)
403}
404
405struct ParsedExportSpec<'a> {
406  name: &'a Atom,
407  can_mangle: Option<bool>,
408  terminal_binding: bool,
409  exports: Option<&'a ExportSpecExports>,
410  from: Option<&'a ModuleGraphConnection>,
411  from_export: Option<&'a Nullable<Vec<Atom>>>,
412  priority: Option<u8>,
413  hidden: bool,
414  inlinable: Option<&'a EvaluatedInlinableValue>,
415}
416
417impl<'a> ParsedExportSpec<'a> {
418  pub fn new(
419    export_name_or_spec: &'a ExportNameOrSpec,
420    global_export_info: &'a DefaultExportInfo,
421  ) -> Self {
422    match export_name_or_spec {
423      ExportNameOrSpec::String(name) => Self {
424        name,
425        can_mangle: global_export_info.can_mangle,
426        terminal_binding: global_export_info.terminal_binding,
427        exports: None,
428        from: global_export_info.from,
429        from_export: None,
430        priority: global_export_info.priority,
431        hidden: false,
432        inlinable: None,
433      },
434      ExportNameOrSpec::ExportSpec(spec) => Self {
435        name: &spec.name,
436        can_mangle: spec.can_mangle.or(global_export_info.can_mangle),
437        terminal_binding: spec
438          .terminal_binding
439          .unwrap_or(global_export_info.terminal_binding),
440        exports: spec.exports.as_ref(),
441        from: spec.from.as_ref().or(global_export_info.from),
442        from_export: spec.export.as_ref(),
443        priority: spec.priority.or(global_export_info.priority),
444        hidden: spec.hidden.unwrap_or(false),
445        inlinable: spec.inlinable.as_ref(),
446      },
447    }
448  }
449}
450
451/// Do merging of exports info and create export infos from export specs
452///
453/// This method is used for the case that the exports info data will not be nested modified
454/// that means this exports info can be modified parallelly
455pub fn merge_exports_without_nested(
456  mg: &ModuleGraph,
457  module_id: &ModuleIdentifier,
458  exports_info: &mut ExportsInfoData,
459  exports: &Vec<ExportNameOrSpec>,
460  global_export_info: DefaultExportInfo,
461  dep_id: DependencyId,
462) -> (bool, Vec<(ModuleIdentifier, ModuleIdentifier)>) {
463  let mut changed = false;
464  let mut dependencies = vec![];
465  for export_name_or_spec in exports {
466    let ParsedExportSpec {
467      name,
468      can_mangle,
469      terminal_binding,
470      from,
471      from_export,
472      priority,
473      hidden,
474      inlinable,
475      ..
476    } = ParsedExportSpec::new(export_name_or_spec, &global_export_info);
477
478    let export_info = exports_info.ensure_owned_export_info(name);
479    changed |= set_export_base_info(export_info, can_mangle, terminal_binding, inlinable);
480
481    changed |= set_export_target(
482      export_info,
483      from,
484      from_export,
485      priority,
486      hidden,
487      dep_id,
488      name,
489    );
490
491    let (target_exports_info, target_dependencies) =
492      find_target_exports_info(mg, export_info, module_id);
493    dependencies.extend(target_dependencies);
494
495    if export_info.exports_info() != target_exports_info {
496      export_info.set_exports_info(target_exports_info);
497      changed = true;
498    }
499  }
500  (changed, dependencies)
501}
502
503/// Do merging of exports info and create export infos from export specs
504/// This method is used for the case that the exports info data will be nested modified
505/// that means this exports info can not be modified parallelly
506pub fn merge_exports(
507  mg: &mut ModuleGraph,
508  module_id: &ModuleIdentifier,
509  exports_info: ExportsInfo,
510  exports: &Vec<ExportNameOrSpec>,
511  global_export_info: DefaultExportInfo,
512  dep_id: DependencyId,
513) -> (bool, Vec<(ModuleIdentifier, ModuleIdentifier)>) {
514  let mut changed = false;
515  let mut dependencies = vec![];
516  for export_name_or_spec in exports {
517    let ParsedExportSpec {
518      name,
519      can_mangle,
520      terminal_binding,
521      exports,
522      from,
523      from_export,
524      priority,
525      hidden,
526      inlinable,
527    } = ParsedExportSpec::new(export_name_or_spec, &global_export_info);
528
529    let export_info = exports_info.as_data_mut(mg).ensure_export_info(name);
530    changed |= set_export_base_info(
531      export_info.as_data_mut(mg),
532      can_mangle,
533      terminal_binding,
534      inlinable,
535    );
536
537    if let Some(exports) = exports {
538      let (merge_changed, merge_dependencies) = merge_nested_exports(
539        mg,
540        module_id,
541        export_info.clone(),
542        exports,
543        global_export_info.clone(),
544        dep_id,
545      );
546      changed |= merge_changed;
547      dependencies.extend(merge_dependencies);
548    }
549
550    changed |= set_export_target(
551      export_info.as_data_mut(mg),
552      from,
553      from_export,
554      priority,
555      hidden,
556      dep_id,
557      name,
558    );
559
560    let (target_exports_info, target_dependencies) =
561      find_target_exports_info(mg, export_info.as_data(mg), module_id);
562    dependencies.extend(target_dependencies);
563
564    let export_info_data = export_info.as_data_mut(mg);
565    if export_info_data.exports_info_owned()
566      && export_info_data.exports_info() != target_exports_info
567      && let Some(target_exports_info) = target_exports_info
568    {
569      export_info_data.set_exports_info(Some(target_exports_info));
570      changed = true;
571    }
572  }
573  (changed, dependencies)
574}
575
576fn set_export_base_info(
577  export_info: &mut ExportInfoData,
578  can_mangle: Option<bool>,
579  terminal_binding: bool,
580  inlinable: Option<&EvaluatedInlinableValue>,
581) -> bool {
582  let mut changed = false;
583  if let Some(provided) = export_info.provided()
584    && matches!(
585      provided,
586      ExportProvided::NotProvided | ExportProvided::Unknown
587    )
588  {
589    export_info.set_provided(Some(ExportProvided::Provided));
590    changed = true;
591  }
592
593  if Some(false) != export_info.can_mangle_provide() && can_mangle == Some(false) {
594    export_info.set_can_mangle_provide(Some(false));
595    changed = true;
596  }
597
598  if let Some(inlined) = inlinable
599    && export_info.can_inline_provide().is_none()
600  {
601    export_info.set_can_inline_provide(Some(inlined.clone()));
602    changed = true;
603  }
604
605  if terminal_binding && !export_info.terminal_binding() {
606    export_info.set_terminal_binding(true);
607    changed = true;
608  }
609  changed
610}
611
612fn merge_nested_exports(
613  mg: &mut ModuleGraph,
614  module_id: &ModuleIdentifier,
615  export_info: ExportInfo,
616  exports: &ExportSpecExports,
617  global_export_info: DefaultExportInfo,
618  dep_id: DependencyId,
619) -> (bool, Vec<(ModuleIdentifier, ModuleIdentifier)>) {
620  let mut changed = false;
621  let mut dependencies = vec![];
622  let nested_exports_info = if export_info.as_data(mg).exports_info_owned() {
623    export_info
624      .as_data(mg)
625      .exports_info()
626      .expect("should have exports_info when exports_info is true")
627  } else {
628    let export_info = export_info.as_data_mut(mg);
629    let new_exports_info = ExportsInfoData::default();
630    let new_exports_info_id = new_exports_info.id();
631    export_info.set_exports_info(Some(new_exports_info_id));
632    export_info.set_exports_info_owned(true);
633    mg.set_exports_info(new_exports_info_id, new_exports_info);
634
635    new_exports_info_id.as_data_mut(mg).set_has_provide_info();
636    new_exports_info_id
637  };
638
639  if exports.unknown_provided {
640    nested_exports_info
641      .as_data_mut(mg)
642      .set_unknown_exports_provided(false, None, None, None, None);
643  }
644
645  let (merge_changed, merge_dependencies) = merge_exports(
646    mg,
647    module_id,
648    nested_exports_info,
649    &exports.exports,
650    global_export_info.clone(),
651    dep_id,
652  );
653  changed |= merge_changed;
654  dependencies.extend(merge_dependencies);
655
656  (changed, dependencies)
657}
658
659fn set_export_target(
660  export_info: &mut ExportInfoData,
661  from: Option<&ModuleGraphConnection>,
662  from_export: Option<&Nullable<Vec<Atom>>>,
663  priority: Option<u8>,
664  hidden: bool,
665  dep_id: DependencyId,
666  name: &Atom,
667) -> bool {
668  let mut changed = false;
669  // shadowing the previous `export_info_mut` to reduce the mut borrow life time,
670  // because `create_nested_exports_info` needs `&mut ModuleGraph`
671  if let Some(from) = from {
672    changed |= if hidden {
673      export_info.unset_target(&dep_id)
674    } else {
675      let fallback = rspack_core::Nullable::Value(vec![name.clone()]);
676      let export_name = if let Some(from) = from_export {
677        Some(from)
678      } else {
679        Some(&fallback)
680      };
681      export_info.set_target(
682        Some(dep_id),
683        Some(from.dependency_id),
684        export_name,
685        priority,
686      )
687    }
688  }
689  changed
690}
691
692fn find_target_exports_info(
693  mg: &ModuleGraph,
694  export_info: &ExportInfoData,
695  module_id: &ModuleIdentifier,
696) -> (
697  Option<ExportsInfo>,
698  Vec<(ModuleIdentifier, ModuleIdentifier)>,
699) {
700  let mut dependencies = vec![];
701  // Recalculate target exportsInfo
702  let target = get_target(export_info, mg, Rc::new(|_| true), &mut Default::default());
703
704  let mut target_exports_info = None;
705  if let Some(GetTargetResult::Target(target)) = target {
706    let target_module_exports_info = mg.get_prefetched_exports_info(
707      &target.module,
708      if let Some(names) = &target.export {
709        PrefetchExportsInfoMode::Nested(names)
710      } else {
711        PrefetchExportsInfoMode::Default
712      },
713    );
714    target_exports_info = target_module_exports_info
715      .get_nested_exports_info(target.export.as_deref())
716      .map(|data| data.id());
717
718    dependencies.push((target.module, *module_id));
719  }
720
721  (target_exports_info, dependencies)
722}