Skip to content

Commit e955037

Browse files
committed
feat: add graph.remove_npm_specifiers()
1 parent 2bb64b2 commit e955037

File tree

1 file changed

+138
-9
lines changed

1 file changed

+138
-9
lines changed

src/graph.rs

Lines changed: 138 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2494,6 +2494,22 @@ impl ModuleGraph {
24942494
self.has_node_specifier = has_node_specifier;
24952495
}
24962496

2497+
/// Removes the npm specifiers from the graph including roots and redirects.
2498+
pub fn remove_npm_specifiers(&mut self) {
2499+
self
2500+
.module_slots
2501+
.retain(|_, slot| !matches!(slot, ModuleSlot::Module(Module::Npm(_))));
2502+
self.redirects.retain(|_, to| to.scheme() != "npm");
2503+
self.roots.retain(|root| root.scheme() != "npm");
2504+
let is_npm =
2505+
|s: &Resolution| s.maybe_specifier().is_some_and(|s| s.scheme() == "npm");
2506+
for graph_import in self.imports.values_mut() {
2507+
graph_import
2508+
.dependencies
2509+
.retain(|_, dep| !is_npm(&dep.maybe_code) && !is_npm(&dep.maybe_type));
2510+
}
2511+
}
2512+
24972513
/// Iterates over all the module entries in the module graph searching from the provided roots.
24982514
pub fn walk<'a, 'options>(
24992515
&'a self,
@@ -5202,15 +5218,19 @@ impl<'a, 'graph> Builder<'a, 'graph> {
52025218
options.in_dynamic_branch,
52035219
);
52045220
} else {
5205-
// mark external
5206-
self.graph.module_slots.insert(
5207-
specifier.clone(),
5208-
ModuleSlot::Module(Module::External(ExternalModule {
5209-
maybe_cache_info: None,
5210-
specifier: specifier.clone(),
5211-
was_asset_load: false,
5212-
})),
5213-
);
5221+
self.load_pending_module(PendingModuleLoadItem {
5222+
redirect_count,
5223+
requested_specifier: specifier.clone(),
5224+
maybe_attribute_type: options.maybe_attribute_type,
5225+
maybe_range: maybe_range.cloned(),
5226+
maybe_source_phase_referrer: maybe_source_phase_referrer.cloned(),
5227+
load_specifier: specifier.clone(),
5228+
is_asset: options.is_asset,
5229+
in_dynamic_branch: options.in_dynamic_branch,
5230+
is_root: options.is_root,
5231+
maybe_checksum: None,
5232+
maybe_version_info: None,
5233+
});
52145234
}
52155235
}
52165236
Ok(LoadSpecifierKind::Node(module_name)) => {
@@ -8338,6 +8358,115 @@ mod tests {
83388358
assert_eq!(errors.len(), 1);
83398359
}
83408360

8361+
#[test]
8362+
fn remove_npm_specifiers() {
8363+
let mut graph = ModuleGraph::new(GraphKind::All);
8364+
8365+
let file_specifier = ModuleSpecifier::parse("file:///foo.js").unwrap();
8366+
let npm_specifier = ModuleSpecifier::parse("npm:chalk@1.0.0").unwrap();
8367+
let npm_redirect_from =
8368+
ModuleSpecifier::parse("file:///node_modules/chalk/index.js").unwrap();
8369+
8370+
// add a js module
8371+
graph.module_slots.insert(
8372+
file_specifier.clone(),
8373+
ModuleSlot::Module(Module::Js(JsModule {
8374+
specifier: file_specifier.clone(),
8375+
dependencies: Default::default(),
8376+
maybe_types_dependency: None,
8377+
maybe_cache_info: None,
8378+
maybe_source_map_dependency: None,
8379+
source: ModuleTextSource::new_unknown(Default::default()),
8380+
media_type: MediaType::JavaScript,
8381+
is_script: false,
8382+
mtime: None,
8383+
#[cfg(feature = "fast_check")]
8384+
fast_check: Default::default(),
8385+
})),
8386+
);
8387+
8388+
// add an npm module
8389+
graph.module_slots.insert(
8390+
npm_specifier.clone(),
8391+
ModuleSlot::Module(Module::Npm(NpmModule {
8392+
specifier: npm_specifier.clone(),
8393+
pkg_req_ref: deno_semver::npm::NpmPackageReqReference::from_str(
8394+
"npm:chalk@1.0.0",
8395+
)
8396+
.unwrap(),
8397+
})),
8398+
);
8399+
8400+
// add a redirect pointing to the npm specifier
8401+
graph
8402+
.redirects
8403+
.insert(npm_redirect_from.clone(), npm_specifier.clone());
8404+
8405+
// add npm as a root
8406+
graph.roots.insert(file_specifier.clone());
8407+
graph.roots.insert(npm_specifier.clone());
8408+
8409+
// add an import with an npm dependency
8410+
let import_referrer = ModuleSpecifier::parse("file:///deno.json").unwrap();
8411+
let mut deps = IndexMap::new();
8412+
deps.insert(
8413+
"chalk".to_string(),
8414+
Dependency {
8415+
maybe_code: Resolution::Ok(Box::new(ResolutionResolved {
8416+
specifier: npm_specifier.clone(),
8417+
range: Range {
8418+
specifier: import_referrer.clone(),
8419+
range: PositionRange::zeroed(),
8420+
resolution_mode: None,
8421+
},
8422+
})),
8423+
..Default::default()
8424+
},
8425+
);
8426+
deps.insert(
8427+
"./local.js".to_string(),
8428+
Dependency {
8429+
maybe_code: Resolution::Ok(Box::new(ResolutionResolved {
8430+
specifier: file_specifier.clone(),
8431+
range: Range {
8432+
specifier: import_referrer.clone(),
8433+
range: PositionRange::zeroed(),
8434+
resolution_mode: None,
8435+
},
8436+
})),
8437+
..Default::default()
8438+
},
8439+
);
8440+
graph
8441+
.imports
8442+
.insert(import_referrer, GraphImport { dependencies: deps });
8443+
8444+
// verify initial state
8445+
assert_eq!(graph.module_slots.len(), 2);
8446+
assert_eq!(graph.redirects.len(), 1);
8447+
assert_eq!(graph.roots.len(), 2);
8448+
assert_eq!(graph.imports[0].dependencies.len(), 2);
8449+
8450+
graph.remove_npm_specifiers();
8451+
8452+
// npm module slot removed, js module slot kept
8453+
assert_eq!(graph.module_slots.len(), 1);
8454+
assert!(graph.get(&file_specifier).is_some());
8455+
assert!(graph.get(&npm_specifier).is_none());
8456+
8457+
// npm redirect removed
8458+
assert_eq!(graph.redirects.len(), 0);
8459+
8460+
// npm root removed
8461+
assert_eq!(graph.roots.len(), 1);
8462+
assert!(graph.roots.contains(&file_specifier));
8463+
8464+
// npm import dependency removed, local kept
8465+
assert_eq!(graph.imports[0].dependencies.len(), 1);
8466+
assert!(graph.imports[0].dependencies.contains_key("./local.js"));
8467+
assert!(!graph.imports[0].dependencies.contains_key("chalk"));
8468+
}
8469+
83418470
#[test]
83428471
fn module_text_source_bom() {
83438472
let module_text_source = ModuleTextSource {

0 commit comments

Comments
 (0)