1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
//! Dependency credit for webpack inline loaders (`!raw-loader!./file.js`).
//!
//! The resolver sends an inline loader request to its resource. The loaders
//! run at build time, so they are tooling, not runtime imports. This module
//! returns their package names so the unused-dependency pass treats them like
//! the loaders that a webpack config references. A loader that is not in
//! `package.json` is not reported as unlisted, the same as a loader in a
//! webpack config.
use std::path::Path;
use rustc_hash::FxHashSet;
use crate::discover::FileId;
use crate::graph::ModuleGraph;
use crate::resolve::{
InlineLoaderRequest, ResolvedModule, extract_package_name, is_bare_specifier,
};
/// Suffix that webpack 1 added to a loader name without it (`raw` loads
/// `raw-loader`).
const LOADER_SUFFIX: &str = "-loader";
/// Collect the package names of the inline loaders in every import source.
///
/// A loader that is a path to a local file has no package, so it is skipped.
/// An unscoped loader name without the `-loader` suffix also credits the name
/// with the suffix, because webpack 1 resolved `raw` to `raw-loader`. The
/// result is sorted, so the order is deterministic.
pub(super) fn collect_inline_loader_referenced_deps(
modules: &[ResolvedModule],
graph: &ModuleGraph,
) -> Vec<String> {
let mut packages: FxHashSet<String> = FxHashSet::default();
for module in modules {
for edge in module.all_resolved_source_edges() {
let Some(request) =
InlineLoaderRequest::resolved_to(edge.source_specifier(), edge.target(), |id| {
module_path(graph, id)
})
else {
continue;
};
for loader in request.loaders() {
if !is_bare_specifier(loader) {
continue;
}
let package = extract_package_name(loader);
if !package.starts_with('@') && !package.ends_with(LOADER_SUFFIX) {
packages.insert(format!("{package}{LOADER_SUFFIX}"));
}
packages.insert(package);
}
}
}
let mut packages: Vec<String> = packages.into_iter().collect();
packages.sort_unstable();
packages
}
/// The path of a project file by its id.
fn module_path(graph: &ModuleGraph, file_id: FileId) -> Option<&Path> {
graph
.modules
.get(file_id.0 as usize)
.map(|module| module.path.as_path())
}