feat: support node built-in module imports (#17264)

Co-authored-by: David Sherret <dsherret@gmail.com>
This commit is contained in:
Bartek Iwańczuk 2023-01-24 15:05:54 +01:00 committed by GitHub
parent cadeaae045
commit fc2e00152b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 925 additions and 445 deletions

View file

@ -14,7 +14,7 @@ pub use cache::NpmCache;
pub use registry::NpmPackageVersionDistInfo;
pub use registry::NpmRegistryApi;
pub use registry::RealNpmRegistryApi;
pub use resolution::resolve_npm_package_reqs;
pub use resolution::resolve_graph_npm_info;
pub use resolution::NpmPackageId;
pub use resolution::NpmPackageReference;
pub use resolution::NpmPackageReq;

View file

@ -1081,7 +1081,7 @@ fn tag_to_version_info<'a>(
// explicit version.
if tag == "latest" && info.name == "@types/node" {
return get_resolved_package_version_and_info(
&NpmVersionReq::parse("18.0.0 - 18.8.2").unwrap(),
&NpmVersionReq::parse("18.0.0 - 18.11.18").unwrap(),
info,
parent,
);

View file

@ -28,7 +28,7 @@ mod specifier;
use graph::Graph;
pub use snapshot::NpmResolutionSnapshot;
pub use specifier::resolve_npm_package_reqs;
pub use specifier::resolve_graph_npm_info;
pub use specifier::NpmPackageReference;
pub use specifier::NpmPackageReq;

View file

@ -168,8 +168,15 @@ impl NpmVersionMatcher for NpmPackageReq {
}
}
/// Resolves the npm package requirements from the graph attempting. The order
/// returned is the order they should be resolved in.
pub struct GraphNpmInfo {
/// The order of these package requirements is the order they
/// should be resolved in.
pub package_reqs: Vec<NpmPackageReq>,
/// Gets if the graph had a built-in node specifier (ex. `node:fs`).
pub has_node_builtin_specifier: bool,
}
/// Resolves npm specific information from the graph.
///
/// This function will analyze the module graph for parent-most folder
/// specifiers of all modules, then group npm specifiers together as found in
@ -211,7 +218,7 @@ impl NpmVersionMatcher for NpmPackageReq {
///
/// Then it would resolve the npm specifiers in each of those groups according
/// to that tree going by tree depth.
pub fn resolve_npm_package_reqs(graph: &ModuleGraph) -> Vec<NpmPackageReq> {
pub fn resolve_graph_npm_info(graph: &ModuleGraph) -> GraphNpmInfo {
fn collect_specifiers<'a>(
graph: &'a ModuleGraph,
module: &'a deno_graph::Module,
@ -248,6 +255,7 @@ pub fn resolve_npm_package_reqs(graph: &ModuleGraph) -> Vec<NpmPackageReq> {
graph: &ModuleGraph,
specifier_graph: &mut SpecifierTree,
seen: &mut HashSet<ModuleSpecifier>,
has_node_builtin_specifier: &mut bool,
) {
if !seen.insert(module.specifier.clone()) {
return; // already visited
@ -267,12 +275,22 @@ pub fn resolve_npm_package_reqs(graph: &ModuleGraph) -> Vec<NpmPackageReq> {
.dependencies
.insert(get_folder_path_specifier(specifier));
}
if !*has_node_builtin_specifier && specifier.scheme() == "node" {
*has_node_builtin_specifier = true;
}
}
// now visit all the dependencies
for specifier in &specifiers {
if let Some(module) = graph.get(specifier) {
analyze_module(module, graph, specifier_graph, seen);
analyze_module(
module,
graph,
specifier_graph,
seen,
has_node_builtin_specifier,
);
}
}
}
@ -284,9 +302,16 @@ pub fn resolve_npm_package_reqs(graph: &ModuleGraph) -> Vec<NpmPackageReq> {
.collect::<Vec<_>>();
let mut seen = HashSet::new();
let mut specifier_graph = SpecifierTree::default();
let mut has_node_builtin_specifier = false;
for root in &root_specifiers {
if let Some(module) = graph.get(root) {
analyze_module(module, graph, &mut specifier_graph, &mut seen);
analyze_module(
module,
graph,
&mut specifier_graph,
&mut seen,
&mut has_node_builtin_specifier,
);
}
}
@ -324,7 +349,10 @@ pub fn resolve_npm_package_reqs(graph: &ModuleGraph) -> Vec<NpmPackageReq> {
}
}
result
GraphNpmInfo {
has_node_builtin_specifier,
package_reqs: result,
}
}
fn get_folder_path_specifier(specifier: &ModuleSpecifier) -> ModuleSpecifier {
@ -979,7 +1007,8 @@ mod tests {
},
)
.await;
let reqs = resolve_npm_package_reqs(&graph)
let reqs = resolve_graph_npm_info(&graph)
.package_reqs
.into_iter()
.map(|r| r.to_string())
.collect::<Vec<_>>();

View file

@ -95,59 +95,51 @@ impl NpmPackageResolver {
no_npm: bool,
local_node_modules_path: Option<PathBuf>,
) -> Self {
Self::new_with_maybe_snapshot(
Self::new_inner(cache, api, no_npm, local_node_modules_path, None, None)
}
pub async fn new_with_maybe_lockfile(
cache: NpmCache,
api: RealNpmRegistryApi,
no_npm: bool,
local_node_modules_path: Option<PathBuf>,
maybe_lockfile: Option<Arc<Mutex<Lockfile>>>,
) -> Result<Self, AnyError> {
let maybe_snapshot = if let Some(lockfile) = &maybe_lockfile {
if lockfile.lock().overwrite {
None
} else {
Some(
NpmResolutionSnapshot::from_lockfile(lockfile.clone(), &api)
.await
.with_context(|| {
format!(
"failed reading lockfile '{}'",
lockfile.lock().filename.display()
)
})?,
)
}
} else {
None
};
Ok(Self::new_inner(
cache,
api,
no_npm,
local_node_modules_path,
None,
)
maybe_snapshot,
maybe_lockfile,
))
}
/// This function will replace current resolver with a new one built from a
/// snapshot created out of the lockfile.
pub async fn add_lockfile_and_maybe_regenerate_snapshot(
&mut self,
lockfile: Arc<Mutex<Lockfile>>,
) -> Result<(), AnyError> {
self.maybe_lockfile = Some(lockfile.clone());
if lockfile.lock().overwrite {
return Ok(());
}
let snapshot =
NpmResolutionSnapshot::from_lockfile(lockfile.clone(), &self.api)
.await
.with_context(|| {
format!(
"failed reading lockfile '{}'",
lockfile.lock().filename.display()
)
})?;
if let Some(node_modules_folder) = &self.local_node_modules_path {
self.inner = Arc::new(LocalNpmPackageResolver::new(
self.cache.clone(),
self.api.clone(),
node_modules_folder.clone(),
Some(snapshot),
));
} else {
self.inner = Arc::new(GlobalNpmPackageResolver::new(
self.cache.clone(),
self.api.clone(),
Some(snapshot),
));
}
Ok(())
}
fn new_with_maybe_snapshot(
fn new_inner(
cache: NpmCache,
api: RealNpmRegistryApi,
no_npm: bool,
local_node_modules_path: Option<PathBuf>,
initial_snapshot: Option<NpmResolutionSnapshot>,
maybe_lockfile: Option<Arc<Mutex<Lockfile>>>,
) -> Self {
let process_npm_state = NpmProcessState::take();
let local_node_modules_path = local_node_modules_path.or_else(|| {
@ -177,7 +169,7 @@ impl NpmPackageResolver {
local_node_modules_path,
api,
cache,
maybe_lockfile: None,
maybe_lockfile,
}
}
@ -320,12 +312,13 @@ impl NpmPackageResolver {
/// Gets a new resolver with a new snapshotted state.
pub fn snapshotted(&self) -> Self {
Self::new_with_maybe_snapshot(
Self::new_inner(
self.cache.clone(),
self.api.clone(),
self.no_npm,
self.local_node_modules_path.clone(),
Some(self.snapshot()),
None,
)
}
@ -336,6 +329,19 @@ impl NpmPackageResolver {
pub fn lock(&self, lockfile: &mut Lockfile) -> Result<(), AnyError> {
self.inner.lock(lockfile)
}
pub async fn inject_synthetic_types_node_package(
&self,
) -> Result<(), AnyError> {
// add and ensure this isn't added to the lockfile
self
.inner
.add_package_reqs(vec![NpmPackageReq::from_str("@types/node").unwrap()])
.await?;
self.inner.cache_packages().await?;
Ok(())
}
}
impl RequireNpmResolver for NpmPackageResolver {