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:
Bartek Iwańczuk 2022-10-25 18:20:07 +02:00 committed by GitHub
parent 10c3c0ee57
commit 9835b095e5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 513 additions and 16 deletions

View file

@ -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",
);

View file

@ -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);
}
}
}

View file

@ -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)]

View file

@ -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.

View file

@ -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(

View file

@ -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(

View file

@ -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 {

View file

@ -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)])

View file

@ -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",

View 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"
}
}
}
}
}

View 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);

View 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.