mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05:00
fix(npm): add support for npm packages in lock files (#15938)
This commit adds support for npm packages in the lock file.
This commit is contained in:
parent
10c3c0ee57
commit
9835b095e5
12 changed files with 513 additions and 16 deletions
148
cli/lockfile.rs
148
cli/lockfile.rs
|
@ -1,7 +1,5 @@
|
|||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
|
@ -17,19 +15,68 @@ use std::path::PathBuf;
|
|||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::npm::NpmPackageReq;
|
||||
use crate::npm::NpmResolutionPackage;
|
||||
use crate::tools::fmt::format_json;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LockfileError(String);
|
||||
|
||||
impl std::fmt::Display for LockfileError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
f.write_str(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for LockfileError {}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct NpmPackageInfo {
|
||||
pub integrity: String,
|
||||
pub dependencies: BTreeMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct NpmContent {
|
||||
/// Mapping between requests for npm packages and resolved specifiers, eg.
|
||||
/// {
|
||||
/// "chalk": "chalk@5.0.0"
|
||||
/// "react@17": "react@17.0.1"
|
||||
/// "foo@latest": "foo@1.0.0"
|
||||
/// }
|
||||
pub specifiers: BTreeMap<String, String>,
|
||||
/// Mapping between resolved npm specifiers and their associated info, eg.
|
||||
/// {
|
||||
/// "chalk@5.0.0": {
|
||||
/// "integrity": "sha512-...",
|
||||
/// "dependencies": {
|
||||
/// "ansi-styles": "ansi-styles@4.1.0",
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
pub packages: BTreeMap<String, NpmPackageInfo>,
|
||||
}
|
||||
|
||||
impl NpmContent {
|
||||
fn is_empty(&self) -> bool {
|
||||
self.specifiers.is_empty() && self.packages.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct LockfileContent {
|
||||
version: String,
|
||||
// Mapping between URLs and their checksums
|
||||
// Mapping between URLs and their checksums for "http:" and "https:" deps
|
||||
remote: BTreeMap<String, String>,
|
||||
#[serde(skip_serializing_if = "NpmContent::is_empty")]
|
||||
#[serde(default)]
|
||||
pub npm: NpmContent,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Lockfile {
|
||||
write: bool,
|
||||
content: LockfileContent,
|
||||
pub write: bool,
|
||||
pub content: LockfileContent,
|
||||
pub filename: PathBuf,
|
||||
}
|
||||
|
||||
|
@ -40,6 +87,7 @@ impl Lockfile {
|
|||
LockfileContent {
|
||||
version: "2".to_string(),
|
||||
remote: BTreeMap::new(),
|
||||
npm: NpmContent::default(),
|
||||
}
|
||||
} else {
|
||||
let s = std::fs::read_to_string(&filename).with_context(|| {
|
||||
|
@ -60,6 +108,7 @@ impl Lockfile {
|
|||
LockfileContent {
|
||||
version: "2".to_string(),
|
||||
remote,
|
||||
npm: NpmContent::default(),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -92,7 +141,13 @@ impl Lockfile {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn check_or_insert(&mut self, specifier: &str, code: &str) -> bool {
|
||||
// TODO(bartlomieju): this function should return an error instead of a bool,
|
||||
// but it requires changes to `deno_graph`'s `Locker`.
|
||||
pub fn check_or_insert_remote(
|
||||
&mut self,
|
||||
specifier: &str,
|
||||
code: &str,
|
||||
) -> bool {
|
||||
if self.write {
|
||||
// In case --lock-write is specified check always passes
|
||||
self.insert(specifier, code);
|
||||
|
@ -102,6 +157,19 @@ impl Lockfile {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn check_or_insert_npm_package(
|
||||
&mut self,
|
||||
package: &NpmResolutionPackage,
|
||||
) -> Result<(), LockfileError> {
|
||||
if self.write {
|
||||
// In case --lock-write is specified check always passes
|
||||
self.insert_npm_package(package);
|
||||
Ok(())
|
||||
} else {
|
||||
self.check_npm_package(package)
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks the given module is included.
|
||||
/// Returns Ok(true) if check passed.
|
||||
fn check(&mut self, specifier: &str, code: &str) -> bool {
|
||||
|
@ -123,6 +191,64 @@ impl Lockfile {
|
|||
let checksum = crate::checksum::gen(&[code.as_bytes()]);
|
||||
self.content.remote.insert(specifier.to_string(), checksum);
|
||||
}
|
||||
|
||||
fn check_npm_package(
|
||||
&mut self,
|
||||
package: &NpmResolutionPackage,
|
||||
) -> Result<(), LockfileError> {
|
||||
let specifier = package.id.serialize_for_lock_file();
|
||||
if let Some(package_info) = self.content.npm.packages.get(&specifier) {
|
||||
let integrity = package
|
||||
.dist
|
||||
.integrity
|
||||
.as_ref()
|
||||
.unwrap_or(&package.dist.shasum);
|
||||
if &package_info.integrity != integrity {
|
||||
return Err(LockfileError(format!(
|
||||
"Integrity check failed for npm package: \"{}\".
|
||||
Cache has \"{}\" and lockfile has \"{}\".
|
||||
Use \"--lock-write\" flag to update the lockfile.",
|
||||
package.id, integrity, package_info.integrity
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn insert_npm_package(&mut self, package: &NpmResolutionPackage) {
|
||||
let dependencies = package
|
||||
.dependencies
|
||||
.iter()
|
||||
.map(|(name, id)| (name.to_string(), id.serialize_for_lock_file()))
|
||||
.collect::<BTreeMap<String, String>>();
|
||||
|
||||
let integrity = package
|
||||
.dist
|
||||
.integrity
|
||||
.as_ref()
|
||||
.unwrap_or(&package.dist.shasum);
|
||||
self.content.npm.packages.insert(
|
||||
package.id.serialize_for_lock_file(),
|
||||
NpmPackageInfo {
|
||||
integrity: integrity.to_string(),
|
||||
dependencies,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn insert_npm_specifier(
|
||||
&mut self,
|
||||
package_req: &NpmPackageReq,
|
||||
version: String,
|
||||
) {
|
||||
if self.write {
|
||||
self.content.npm.specifiers.insert(
|
||||
package_req.to_string(),
|
||||
format!("{}@{}", package_req.name, version),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -136,7 +262,7 @@ impl deno_graph::source::Locker for Locker {
|
|||
) -> bool {
|
||||
if let Some(lock_file) = &self.0 {
|
||||
let mut lock_file = lock_file.lock();
|
||||
lock_file.check_or_insert(specifier.as_str(), source)
|
||||
lock_file.check_or_insert_remote(specifier.as_str(), source)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
|
@ -180,6 +306,10 @@ mod tests {
|
|||
"remote": {
|
||||
"https://deno.land/std@0.71.0/textproto/mod.ts": "3118d7a42c03c242c5a49c2ad91c8396110e14acca1324e7aaefd31a999b71a4",
|
||||
"https://deno.land/std@0.71.0/async/delay.ts": "35957d585a6e3dd87706858fb1d6b551cb278271b03f52c5a2cb70e65e00c26a"
|
||||
},
|
||||
"npm": {
|
||||
"specifiers": {},
|
||||
"packages": {}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -305,13 +435,13 @@ mod tests {
|
|||
"Here is some source code",
|
||||
);
|
||||
|
||||
let check_true = lockfile.check_or_insert(
|
||||
let check_true = lockfile.check_or_insert_remote(
|
||||
"https://deno.land/std@0.71.0/textproto/mod.ts",
|
||||
"Here is some source code",
|
||||
);
|
||||
assert!(check_true);
|
||||
|
||||
let check_false = lockfile.check_or_insert(
|
||||
let check_false = lockfile.check_or_insert_remote(
|
||||
"https://deno.land/std@0.71.0/textproto/mod.ts",
|
||||
"This is new Source code",
|
||||
);
|
||||
|
|
16
cli/main.rs
16
cli/main.rs
|
@ -1060,16 +1060,22 @@ fn unwrap_or_exit<T>(result: Result<T, AnyError>) -> T {
|
|||
match result {
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
let error_string = match error.downcast_ref::<JsError>() {
|
||||
Some(e) => format_js_error(e),
|
||||
None => format!("{:?}", error),
|
||||
};
|
||||
let mut error_string = format!("{:?}", error);
|
||||
let mut error_code = 1;
|
||||
|
||||
if let Some(e) = error.downcast_ref::<JsError>() {
|
||||
error_string = format_js_error(e);
|
||||
} else if let Some(e) = error.downcast_ref::<lockfile::LockfileError>() {
|
||||
error_string = e.to_string();
|
||||
error_code = 10;
|
||||
}
|
||||
|
||||
eprintln!(
|
||||
"{}: {}",
|
||||
colors::red_bold("error"),
|
||||
error_string.trim_start_matches("error: ")
|
||||
);
|
||||
std::process::exit(1);
|
||||
std::process::exit(error_code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,9 +11,13 @@ use deno_core::anyhow::Context;
|
|||
use deno_core::error::generic_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::futures;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_core::parking_lot::RwLock;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::lockfile::Lockfile;
|
||||
|
||||
use super::cache::should_sync_download;
|
||||
use super::registry::NpmPackageInfo;
|
||||
|
@ -172,6 +176,24 @@ impl NpmPackageId {
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn serialize_for_lock_file(&self) -> String {
|
||||
format!("{}@{}", self.name, self.version)
|
||||
}
|
||||
|
||||
pub fn deserialize_from_lock_file(id: &str) -> Result<Self, AnyError> {
|
||||
let reference = NpmPackageReference::from_str(&format!("npm:{}", id))
|
||||
.with_context(|| {
|
||||
format!("Unable to deserialize npm package reference: {}", id)
|
||||
})?;
|
||||
let version =
|
||||
NpmVersion::parse(&reference.req.version_req.unwrap().to_string())
|
||||
.unwrap();
|
||||
Ok(Self {
|
||||
name: reference.req.name,
|
||||
version,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for NpmPackageId {
|
||||
|
@ -345,6 +367,88 @@ impl NpmResolutionSnapshot {
|
|||
}
|
||||
maybe_best_version.cloned()
|
||||
}
|
||||
|
||||
pub async fn from_lockfile(
|
||||
lockfile: Arc<Mutex<Lockfile>>,
|
||||
api: &NpmRegistryApi,
|
||||
) -> Result<Self, AnyError> {
|
||||
let mut package_reqs = HashMap::new();
|
||||
let mut packages_by_name: HashMap<String, Vec<NpmVersion>> = HashMap::new();
|
||||
let mut packages = HashMap::new();
|
||||
|
||||
{
|
||||
let lockfile = lockfile.lock();
|
||||
|
||||
for (key, value) in &lockfile.content.npm.specifiers {
|
||||
let reference = NpmPackageReference::from_str(&format!("npm:{}", key))
|
||||
.with_context(|| format!("Unable to parse npm specifier: {}", key))?;
|
||||
let package_id = NpmPackageId::deserialize_from_lock_file(value)?;
|
||||
package_reqs.insert(reference.req, package_id.version.clone());
|
||||
}
|
||||
|
||||
for (key, value) in &lockfile.content.npm.packages {
|
||||
let package_id = NpmPackageId::deserialize_from_lock_file(key)?;
|
||||
let mut dependencies = HashMap::default();
|
||||
|
||||
for (name, specifier) in &value.dependencies {
|
||||
dependencies.insert(
|
||||
name.to_string(),
|
||||
NpmPackageId::deserialize_from_lock_file(specifier)?,
|
||||
);
|
||||
}
|
||||
|
||||
for (name, id) in &dependencies {
|
||||
packages_by_name
|
||||
.entry(name.to_string())
|
||||
.or_default()
|
||||
.push(id.version.clone());
|
||||
}
|
||||
|
||||
let package = NpmResolutionPackage {
|
||||
id: package_id.clone(),
|
||||
// temporary dummy value
|
||||
dist: NpmPackageVersionDistInfo {
|
||||
tarball: "foobar".to_string(),
|
||||
shasum: "foobar".to_string(),
|
||||
integrity: Some("foobar".to_string()),
|
||||
},
|
||||
dependencies,
|
||||
};
|
||||
|
||||
packages.insert(package_id.clone(), package);
|
||||
}
|
||||
}
|
||||
|
||||
let mut unresolved_tasks = Vec::with_capacity(packages_by_name.len());
|
||||
|
||||
for package_id in packages.keys() {
|
||||
let package_id = package_id.clone();
|
||||
let api = api.clone();
|
||||
unresolved_tasks.push(tokio::task::spawn(async move {
|
||||
let info = api.package_info(&package_id.name).await?;
|
||||
Result::<_, AnyError>::Ok((package_id, info))
|
||||
}));
|
||||
}
|
||||
for result in futures::future::join_all(unresolved_tasks).await {
|
||||
let (package_id, info) = result??;
|
||||
|
||||
let version_and_info = get_resolved_package_version_and_info(
|
||||
&package_id.name,
|
||||
&NpmVersionReq::parse(&package_id.version.to_string()).unwrap(),
|
||||
info,
|
||||
None,
|
||||
)?;
|
||||
|
||||
let package = packages.get_mut(&package_id).unwrap();
|
||||
package.dist = version_and_info.info.dist;
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
package_reqs,
|
||||
packages_by_name,
|
||||
packages,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NpmResolution {
|
||||
|
@ -628,6 +732,20 @@ impl NpmResolution {
|
|||
pub fn snapshot(&self) -> NpmResolutionSnapshot {
|
||||
self.snapshot.read().clone()
|
||||
}
|
||||
|
||||
pub fn lock(
|
||||
&self,
|
||||
lockfile: &mut Lockfile,
|
||||
snapshot: &NpmResolutionSnapshot,
|
||||
) -> Result<(), AnyError> {
|
||||
for (package_req, version) in snapshot.package_reqs.iter() {
|
||||
lockfile.insert_npm_specifier(package_req, version.to_string());
|
||||
}
|
||||
for package in self.all_packages() {
|
||||
lockfile.check_or_insert_npm_package(&package)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
|
@ -11,6 +11,7 @@ use deno_core::futures;
|
|||
use deno_core::futures::future::BoxFuture;
|
||||
use deno_core::url::Url;
|
||||
|
||||
use crate::lockfile::Lockfile;
|
||||
use crate::npm::cache::should_sync_download;
|
||||
use crate::npm::resolution::NpmResolutionSnapshot;
|
||||
use crate::npm::NpmCache;
|
||||
|
@ -50,6 +51,8 @@ pub trait InnerNpmPackageResolver: Send + Sync {
|
|||
fn ensure_read_permission(&self, path: &Path) -> Result<(), AnyError>;
|
||||
|
||||
fn snapshot(&self) -> NpmResolutionSnapshot;
|
||||
|
||||
fn lock(&self, lockfile: &mut Lockfile) -> Result<(), AnyError>;
|
||||
}
|
||||
|
||||
/// Caches all the packages in parallel.
|
||||
|
|
|
@ -15,6 +15,7 @@ use deno_core::url::Url;
|
|||
use deno_runtime::deno_node::PackageJson;
|
||||
use deno_runtime::deno_node::TYPES_CONDITIONS;
|
||||
|
||||
use crate::lockfile::Lockfile;
|
||||
use crate::npm::resolution::NpmResolution;
|
||||
use crate::npm::resolution::NpmResolutionSnapshot;
|
||||
use crate::npm::resolvers::common::cache_packages;
|
||||
|
@ -145,6 +146,11 @@ impl InnerNpmPackageResolver for GlobalNpmPackageResolver {
|
|||
fn snapshot(&self) -> NpmResolutionSnapshot {
|
||||
self.resolution.snapshot()
|
||||
}
|
||||
|
||||
fn lock(&self, lockfile: &mut Lockfile) -> Result<(), AnyError> {
|
||||
let snapshot = self.resolution.snapshot();
|
||||
self.resolution.lock(lockfile, &snapshot)
|
||||
}
|
||||
}
|
||||
|
||||
async fn cache_packages_in_resolver(
|
||||
|
|
|
@ -22,6 +22,7 @@ use deno_runtime::deno_node::TYPES_CONDITIONS;
|
|||
use tokio::task::JoinHandle;
|
||||
|
||||
use crate::fs_util;
|
||||
use crate::lockfile::Lockfile;
|
||||
use crate::npm::cache::should_sync_download;
|
||||
use crate::npm::resolution::NpmResolution;
|
||||
use crate::npm::resolution::NpmResolutionSnapshot;
|
||||
|
@ -213,6 +214,10 @@ impl InnerNpmPackageResolver for LocalNpmPackageResolver {
|
|||
fn snapshot(&self) -> NpmResolutionSnapshot {
|
||||
self.resolution.snapshot()
|
||||
}
|
||||
|
||||
fn lock(&self, lockfile: &mut Lockfile) -> Result<(), AnyError> {
|
||||
self.resolution.lock(lockfile, &self.snapshot())
|
||||
}
|
||||
}
|
||||
|
||||
async fn sync_resolver_with_fs(
|
||||
|
|
|
@ -8,6 +8,7 @@ use deno_ast::ModuleSpecifier;
|
|||
use deno_core::anyhow::bail;
|
||||
use deno_core::error::custom_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_core::serde_json;
|
||||
use deno_runtime::deno_node::PathClean;
|
||||
use deno_runtime::deno_node::RequireNpmResolver;
|
||||
|
@ -21,6 +22,7 @@ use std::path::PathBuf;
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::fs_util;
|
||||
use crate::lockfile::Lockfile;
|
||||
|
||||
use self::common::InnerNpmPackageResolver;
|
||||
use self::local::LocalNpmPackageResolver;
|
||||
|
@ -70,6 +72,7 @@ pub struct NpmPackageResolver {
|
|||
local_node_modules_path: Option<PathBuf>,
|
||||
api: NpmRegistryApi,
|
||||
cache: NpmCache,
|
||||
maybe_lockfile: Option<Arc<Mutex<Lockfile>>>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for NpmPackageResolver {
|
||||
|
@ -101,6 +104,32 @@ impl NpmPackageResolver {
|
|||
)
|
||||
}
|
||||
|
||||
/// This function will replace current resolver with a new one built from a
|
||||
/// snapshot created out of the lockfile.
|
||||
pub async fn add_lockfile(
|
||||
&mut self,
|
||||
lockfile: Arc<Mutex<Lockfile>>,
|
||||
) -> Result<(), AnyError> {
|
||||
let snapshot =
|
||||
NpmResolutionSnapshot::from_lockfile(lockfile.clone(), &self.api).await?;
|
||||
self.maybe_lockfile = Some(lockfile);
|
||||
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(
|
||||
cache: NpmCache,
|
||||
api: NpmRegistryApi,
|
||||
|
@ -138,6 +167,7 @@ impl NpmPackageResolver {
|
|||
local_node_modules_path,
|
||||
api,
|
||||
cache,
|
||||
maybe_lockfile: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -224,7 +254,15 @@ impl NpmPackageResolver {
|
|||
));
|
||||
}
|
||||
|
||||
self.inner.add_package_reqs(packages).await
|
||||
self.inner.add_package_reqs(packages).await?;
|
||||
|
||||
// If there's a lock file, update it with all discovered npm packages
|
||||
if let Some(lockfile_mutex) = &self.maybe_lockfile {
|
||||
let mut lockfile = lockfile_mutex.lock();
|
||||
self.lock(&mut lockfile)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets package requirements to the resolver, removing old requirements and adding new ones.
|
||||
|
@ -266,6 +304,10 @@ impl NpmPackageResolver {
|
|||
Some(self.inner.snapshot()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn lock(&self, lockfile: &mut Lockfile) -> Result<(), AnyError> {
|
||||
self.inner.lock(lockfile)
|
||||
}
|
||||
}
|
||||
|
||||
impl RequireNpmResolver for NpmPackageResolver {
|
||||
|
|
|
@ -235,7 +235,8 @@ impl ProcState {
|
|||
cli_options.cache_setting(),
|
||||
progress_bar.clone(),
|
||||
);
|
||||
let npm_resolver = NpmPackageResolver::new(
|
||||
let maybe_lockfile = lockfile.as_ref().filter(|l| !l.lock().write).cloned();
|
||||
let mut npm_resolver = NpmPackageResolver::new(
|
||||
npm_cache.clone(),
|
||||
api,
|
||||
cli_options.unstable()
|
||||
|
@ -246,6 +247,9 @@ impl ProcState {
|
|||
.resolve_local_node_modules_folder()
|
||||
.with_context(|| "Resolving local node_modules folder.")?,
|
||||
);
|
||||
if let Some(lockfile) = maybe_lockfile.clone() {
|
||||
npm_resolver.add_lockfile(lockfile).await?;
|
||||
}
|
||||
let node_analysis_cache =
|
||||
NodeAnalysisCache::new(Some(dir.node_analysis_db_file_path()));
|
||||
|
||||
|
@ -464,6 +468,8 @@ impl ProcState {
|
|||
}
|
||||
|
||||
/// Add the builtin node modules to the graph data.
|
||||
// FIXME(bartlomieju): appears this function can be called more than once
|
||||
// if we have npm imports
|
||||
pub async fn prepare_node_std_graph(&self) -> Result<(), AnyError> {
|
||||
let node_std_graph = self
|
||||
.create_graph(vec![(node::MODULE_ALL_URL.clone(), ModuleKind::Esm)])
|
||||
|
|
|
@ -158,6 +158,14 @@ itest!(import_map {
|
|||
http_server: true,
|
||||
});
|
||||
|
||||
itest!(lock_file {
|
||||
args: "run --allow-read --allow-env --unstable --lock npm/lock_file/lock.json npm/lock_file/main.js",
|
||||
output: "npm/lock_file/main.out",
|
||||
envs: env_vars(),
|
||||
http_server: true,
|
||||
exit_code: 10,
|
||||
});
|
||||
|
||||
itest!(sub_paths {
|
||||
args: "run --unstable -A --quiet npm/sub_paths/main.jsx",
|
||||
output: "npm/sub_paths/main.out",
|
||||
|
|
164
cli/tests/testdata/npm/lock_file/lock.json
vendored
Normal file
164
cli/tests/testdata/npm/lock_file/lock.json
vendored
Normal file
|
@ -0,0 +1,164 @@
|
|||
{
|
||||
"version": "2",
|
||||
"remote": {},
|
||||
"npm": {
|
||||
"specifiers": { "fs-extra@10.1.0": "fs-extra@10.1.0", "vue": "vue@3.2.38" },
|
||||
"packages": {
|
||||
"@babel/parser@7.19.0": {
|
||||
"integrity": "sha512-foobar!",
|
||||
"dependencies": {}
|
||||
},
|
||||
"@vue/compiler-core@3.2.38": {
|
||||
"integrity": "sha512-/FsvnSu7Z+lkd/8KXMa4yYNUiqQrI22135gfsQYVGuh5tqEgOB0XqrUdb/KnCLa5+TmQLPwvyUnKMyCpu+SX3Q==",
|
||||
"dependencies": {
|
||||
"@babel/parser": "@babel/parser@7.19.0",
|
||||
"@vue/shared": "@vue/shared@3.2.38",
|
||||
"estree-walker": "estree-walker@2.0.2",
|
||||
"source-map": "source-map@0.6.1"
|
||||
}
|
||||
},
|
||||
"@vue/compiler-dom@3.2.38": {
|
||||
"integrity": "sha512-zqX4FgUbw56kzHlgYuEEJR8mefFiiyR3u96498+zWPsLeh1WKvgIReoNE+U7gG8bCUdvsrJ0JRmev0Ky6n2O0g==",
|
||||
"dependencies": {
|
||||
"@vue/compiler-core": "@vue/compiler-core@3.2.38",
|
||||
"@vue/shared": "@vue/shared@3.2.38"
|
||||
}
|
||||
},
|
||||
"@vue/compiler-sfc@3.2.38": {
|
||||
"integrity": "sha512-KZjrW32KloMYtTcHAFuw3CqsyWc5X6seb8KbkANSWt3Cz9p2qA8c1GJpSkksFP9ABb6an0FLCFl46ZFXx3kKpg==",
|
||||
"dependencies": {
|
||||
"@babel/parser": "@babel/parser@7.19.0",
|
||||
"@vue/compiler-core": "@vue/compiler-core@3.2.38",
|
||||
"@vue/compiler-dom": "@vue/compiler-dom@3.2.38",
|
||||
"@vue/compiler-ssr": "@vue/compiler-ssr@3.2.38",
|
||||
"@vue/reactivity-transform": "@vue/reactivity-transform@3.2.38",
|
||||
"@vue/shared": "@vue/shared@3.2.38",
|
||||
"estree-walker": "estree-walker@2.0.2",
|
||||
"magic-string": "magic-string@0.25.9",
|
||||
"postcss": "postcss@8.4.16",
|
||||
"source-map": "source-map@0.6.1"
|
||||
}
|
||||
},
|
||||
"@vue/compiler-ssr@3.2.38": {
|
||||
"integrity": "sha512-bm9jOeyv1H3UskNm4S6IfueKjUNFmi2kRweFIGnqaGkkRePjwEcfCVqyS3roe7HvF4ugsEkhf4+kIvDhip6XzQ==",
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "@vue/compiler-dom@3.2.38",
|
||||
"@vue/shared": "@vue/shared@3.2.38"
|
||||
}
|
||||
},
|
||||
"@vue/reactivity-transform@3.2.38": {
|
||||
"integrity": "sha512-3SD3Jmi1yXrDwiNJqQ6fs1x61WsDLqVk4NyKVz78mkaIRh6d3IqtRnptgRfXn+Fzf+m6B1KxBYWq1APj6h4qeA==",
|
||||
"dependencies": {
|
||||
"@babel/parser": "@babel/parser@7.19.0",
|
||||
"@vue/compiler-core": "@vue/compiler-core@3.2.38",
|
||||
"@vue/shared": "@vue/shared@3.2.38",
|
||||
"estree-walker": "estree-walker@2.0.2",
|
||||
"magic-string": "magic-string@0.25.9"
|
||||
}
|
||||
},
|
||||
"@vue/reactivity@3.2.38": {
|
||||
"integrity": "sha512-6L4myYcH9HG2M25co7/BSo0skKFHpAN8PhkNPM4xRVkyGl1K5M3Jx4rp5bsYhvYze2K4+l+pioN4e6ZwFLUVtw==",
|
||||
"dependencies": { "@vue/shared": "@vue/shared@3.2.38" }
|
||||
},
|
||||
"@vue/runtime-core@3.2.38": {
|
||||
"integrity": "sha512-kk0qiSiXUU/IKxZw31824rxmFzrLr3TL6ZcbrxWTKivadoKupdlzbQM4SlGo4MU6Zzrqv4fzyUasTU1jDoEnzg==",
|
||||
"dependencies": {
|
||||
"@vue/reactivity": "@vue/reactivity@3.2.38",
|
||||
"@vue/shared": "@vue/shared@3.2.38"
|
||||
}
|
||||
},
|
||||
"@vue/runtime-dom@3.2.38": {
|
||||
"integrity": "sha512-4PKAb/ck2TjxdMSzMsnHViOrrwpudk4/A56uZjhzvusoEU9xqa5dygksbzYepdZeB5NqtRw5fRhWIiQlRVK45A==",
|
||||
"dependencies": {
|
||||
"@vue/runtime-core": "@vue/runtime-core@3.2.38",
|
||||
"@vue/shared": "@vue/shared@3.2.38",
|
||||
"csstype": "csstype@2.6.20"
|
||||
}
|
||||
},
|
||||
"@vue/server-renderer@3.2.38": {
|
||||
"integrity": "sha512-pg+JanpbOZ5kEfOZzO2bt02YHd+ELhYP8zPeLU1H0e7lg079NtuuSB8fjLdn58c4Ou8UQ6C1/P+528nXnLPAhA==",
|
||||
"dependencies": {
|
||||
"@vue/compiler-ssr": "@vue/compiler-ssr@3.2.38",
|
||||
"@vue/shared": "@vue/shared@3.2.38"
|
||||
}
|
||||
},
|
||||
"@vue/shared@3.2.38": {
|
||||
"integrity": "sha512-dTyhTIRmGXBjxJE+skC8tTWCGLCVc4wQgRRLt8+O9p5ewBAjoBwtCAkLPrtToSr1xltoe3st21Pv953aOZ7alg==",
|
||||
"dependencies": {}
|
||||
},
|
||||
"csstype@2.6.20": {
|
||||
"integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==",
|
||||
"dependencies": {}
|
||||
},
|
||||
"estree-walker@2.0.2": {
|
||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||
"dependencies": {}
|
||||
},
|
||||
"fs-extra@10.1.0": {
|
||||
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
|
||||
"dependencies": {
|
||||
"graceful-fs": "graceful-fs@4.2.10",
|
||||
"jsonfile": "jsonfile@6.1.0",
|
||||
"universalify": "universalify@2.0.0"
|
||||
}
|
||||
},
|
||||
"graceful-fs@4.2.10": {
|
||||
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
|
||||
"dependencies": {}
|
||||
},
|
||||
"jsonfile@6.1.0": {
|
||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||
"dependencies": {
|
||||
"graceful-fs": "graceful-fs@4.2.10",
|
||||
"universalify": "universalify@2.0.0"
|
||||
}
|
||||
},
|
||||
"magic-string@0.25.9": {
|
||||
"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
|
||||
"dependencies": { "sourcemap-codec": "sourcemap-codec@1.4.8" }
|
||||
},
|
||||
"nanoid@3.3.4": {
|
||||
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
|
||||
"dependencies": {}
|
||||
},
|
||||
"picocolors@1.0.0": {
|
||||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
||||
"dependencies": {}
|
||||
},
|
||||
"postcss@8.4.16": {
|
||||
"integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==",
|
||||
"dependencies": {
|
||||
"nanoid": "nanoid@3.3.4",
|
||||
"picocolors": "picocolors@1.0.0",
|
||||
"source-map-js": "source-map-js@1.0.2"
|
||||
}
|
||||
},
|
||||
"source-map-js@1.0.2": {
|
||||
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
|
||||
"dependencies": {}
|
||||
},
|
||||
"source-map@0.6.1": {
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dependencies": {}
|
||||
},
|
||||
"sourcemap-codec@1.4.8": {
|
||||
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
|
||||
"dependencies": {}
|
||||
},
|
||||
"universalify@2.0.0": {
|
||||
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
|
||||
"dependencies": {}
|
||||
},
|
||||
"vue@3.2.38": {
|
||||
"integrity": "sha512-hHrScEFSmDAWL0cwO4B6WO7D3sALZPbfuThDsGBebthrNlDxdJZpGR3WB87VbjpPh96mep1+KzukYEhpHDFa8Q==",
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "@vue/compiler-dom@3.2.38",
|
||||
"@vue/compiler-sfc": "@vue/compiler-sfc@3.2.38",
|
||||
"@vue/runtime-dom": "@vue/runtime-dom@3.2.38",
|
||||
"@vue/server-renderer": "@vue/server-renderer@3.2.38",
|
||||
"@vue/shared": "@vue/shared@3.2.38"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
5
cli/tests/testdata/npm/lock_file/main.js
vendored
Normal file
5
cli/tests/testdata/npm/lock_file/main.js
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
import fsx from "npm:fs-extra@10.1.0";
|
||||
import { createApp } from "npm:vue";
|
||||
|
||||
console.log(fsx.access);
|
||||
console.log(createApp);
|
4
cli/tests/testdata/npm/lock_file/main.out
vendored
Normal file
4
cli/tests/testdata/npm/lock_file/main.out
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
Download [WILDCARD]
|
||||
error: Integrity check failed for npm package: "@babel/parser@7.19.0".
|
||||
Cache has "sha512-74bEXKX2h+8rrfQUfsBfuZZHzsEs6Eql4pqy/T4Nn6Y9wNPggQOqD6z6pn5Bl8ZfysKouFZT/UXEH94ummEeQw==" and lockfile has "sha512-foobar!".
|
||||
Use "--lock-write" flag to update the lockfile.
|
Loading…
Reference in a new issue