diff options
Diffstat (limited to 'cli')
-rw-r--r-- | cli/lockfile.rs | 148 | ||||
-rw-r--r-- | cli/main.rs | 16 | ||||
-rw-r--r-- | cli/npm/resolution.rs | 118 | ||||
-rw-r--r-- | cli/npm/resolvers/common.rs | 3 | ||||
-rw-r--r-- | cli/npm/resolvers/global.rs | 6 | ||||
-rw-r--r-- | cli/npm/resolvers/local.rs | 5 | ||||
-rw-r--r-- | cli/npm/resolvers/mod.rs | 44 | ||||
-rw-r--r-- | cli/proc_state.rs | 8 | ||||
-rw-r--r-- | cli/tests/integration/npm_tests.rs | 8 | ||||
-rw-r--r-- | cli/tests/testdata/npm/lock_file/lock.json | 164 | ||||
-rw-r--r-- | cli/tests/testdata/npm/lock_file/main.js | 5 | ||||
-rw-r--r-- | cli/tests/testdata/npm/lock_file/main.out | 4 |
12 files changed, 513 insertions, 16 deletions
diff --git a/cli/lockfile.rs b/cli/lockfile.rs index b572b9d93..53a3c1286 100644 --- a/cli/lockfile.rs +++ b/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", ); diff --git a/cli/main.rs b/cli/main.rs index 23f073a7d..1fb942963 100644 --- a/cli/main.rs +++ b/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); } } } diff --git a/cli/npm/resolution.rs b/cli/npm/resolution.rs index 8656d8a3c..7cd4df124 100644 --- a/cli/npm/resolution.rs +++ b/cli/npm/resolution.rs @@ -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)] diff --git a/cli/npm/resolvers/common.rs b/cli/npm/resolvers/common.rs index 4981d5613..e114f3f8a 100644 --- a/cli/npm/resolvers/common.rs +++ b/cli/npm/resolvers/common.rs @@ -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. diff --git a/cli/npm/resolvers/global.rs b/cli/npm/resolvers/global.rs index 8eafc19f4..996f55c2d 100644 --- a/cli/npm/resolvers/global.rs +++ b/cli/npm/resolvers/global.rs @@ -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( diff --git a/cli/npm/resolvers/local.rs b/cli/npm/resolvers/local.rs index b51593d4c..6c4c4ef6c 100644 --- a/cli/npm/resolvers/local.rs +++ b/cli/npm/resolvers/local.rs @@ -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( diff --git a/cli/npm/resolvers/mod.rs b/cli/npm/resolvers/mod.rs index 5498bbf75..3d55170ac 100644 --- a/cli/npm/resolvers/mod.rs +++ b/cli/npm/resolvers/mod.rs @@ -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 { diff --git a/cli/proc_state.rs b/cli/proc_state.rs index ac83e9459..7fc28b553 100644 --- a/cli/proc_state.rs +++ b/cli/proc_state.rs @@ -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)]) diff --git a/cli/tests/integration/npm_tests.rs b/cli/tests/integration/npm_tests.rs index 9fc817141..8fa79d06c 100644 --- a/cli/tests/integration/npm_tests.rs +++ b/cli/tests/integration/npm_tests.rs @@ -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", diff --git a/cli/tests/testdata/npm/lock_file/lock.json b/cli/tests/testdata/npm/lock_file/lock.json new file mode 100644 index 000000000..57253314e --- /dev/null +++ b/cli/tests/testdata/npm/lock_file/lock.json @@ -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" + } + } + } + } +} diff --git a/cli/tests/testdata/npm/lock_file/main.js b/cli/tests/testdata/npm/lock_file/main.js new file mode 100644 index 000000000..a7b5960ca --- /dev/null +++ b/cli/tests/testdata/npm/lock_file/main.js @@ -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); diff --git a/cli/tests/testdata/npm/lock_file/main.out b/cli/tests/testdata/npm/lock_file/main.out new file mode 100644 index 000000000..4c034d03b --- /dev/null +++ b/cli/tests/testdata/npm/lock_file/main.out @@ -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. |