mirror of
https://github.com/denoland/deno.git
synced 2025-09-28 13:14:48 +00:00

This adds support for using a local copy of an npm package. ```js // deno.json { "patch": [ "../path/to/local_npm_package" ], // required until Deno 2.3, but it will still be considered unstable "unstable": ["npm-patch"] } ``` 1. Requires using a node_modules folder. 2. When using `"nodeModulesDir": "auto"`, it recreates the folder in the node_modules directory on each run which will slightly increase startup time. 3. When using the default with a package.json (`"nodeModulesDir": "manual"`), updating the package requires running `deno install`. This is to get the package into the node_modules directory of the current workspace. This is necessary instead of linking because packages can have multiple "copy packages" due to peer dep resolution. Caveat: Specifying a local copy of an npm package or making changes to its dependencies will purge npm packages from the lockfile. This might cause npm resolution to resolve differently and it may end up not using the local copy of the npm package. It's very difficult to only invalidate resolution midway through the graph and then only rebuild that part of the resolution, so this is just a first pass that can be improved in the future. In practice, this probably won't be an issue for most people. Another limitation is this also requires the npm package name to exist in the registry at the moment.
170 lines
4.7 KiB
Rust
170 lines
4.7 KiB
Rust
// Copyright 2018-2025 the Deno authors. MIT license.
|
|
|
|
use std::io::ErrorKind;
|
|
use std::path::Path;
|
|
use std::path::PathBuf;
|
|
use std::time::Duration;
|
|
|
|
use sys_traits::FsCreateDirAll;
|
|
use sys_traits::FsDirEntry;
|
|
use sys_traits::FsHardLink;
|
|
use sys_traits::FsReadDir;
|
|
use sys_traits::FsRemoveFile;
|
|
use sys_traits::ThreadSleep;
|
|
|
|
#[derive(Debug, thiserror::Error, deno_error::JsError)]
|
|
pub enum HardLinkDirRecursiveError {
|
|
#[class(inherit)]
|
|
#[error(transparent)]
|
|
Io(#[from] std::io::Error),
|
|
#[class(inherit)]
|
|
#[error("Creating {path}")]
|
|
Creating {
|
|
path: PathBuf,
|
|
#[source]
|
|
#[inherit]
|
|
source: std::io::Error,
|
|
},
|
|
#[class(inherit)]
|
|
#[error("Creating {path}")]
|
|
Reading {
|
|
path: PathBuf,
|
|
#[source]
|
|
#[inherit]
|
|
source: std::io::Error,
|
|
},
|
|
#[class(inherit)]
|
|
#[error("Dir {from} to {to}")]
|
|
Dir {
|
|
from: PathBuf,
|
|
to: PathBuf,
|
|
#[source]
|
|
#[inherit]
|
|
source: Box<Self>,
|
|
},
|
|
#[class(inherit)]
|
|
#[error(transparent)]
|
|
HardLinkFile(#[from] HardLinkFileError),
|
|
}
|
|
|
|
/// Hardlinks the files in one directory to another directory.
|
|
///
|
|
/// Note: Does not handle symlinks.
|
|
pub fn hard_link_dir_recursive<
|
|
TSys: FsCreateDirAll + FsHardLink + FsReadDir + FsRemoveFile + ThreadSleep,
|
|
>(
|
|
sys: &TSys,
|
|
from: &Path,
|
|
to: &Path,
|
|
) -> Result<(), HardLinkDirRecursiveError> {
|
|
sys.fs_create_dir_all(to).map_err(|source| {
|
|
HardLinkDirRecursiveError::Creating {
|
|
path: to.to_path_buf(),
|
|
source,
|
|
}
|
|
})?;
|
|
let read_dir = sys.fs_read_dir(from).map_err(|source| {
|
|
HardLinkDirRecursiveError::Reading {
|
|
path: from.to_path_buf(),
|
|
source,
|
|
}
|
|
})?;
|
|
|
|
for entry in read_dir {
|
|
let entry = entry?;
|
|
let file_type = entry.file_type()?;
|
|
let new_from = from.join(entry.file_name());
|
|
let new_to = to.join(entry.file_name());
|
|
|
|
if file_type.is_dir() {
|
|
hard_link_dir_recursive(sys, &new_from, &new_to).map_err(|source| {
|
|
HardLinkDirRecursiveError::Dir {
|
|
from: new_from.to_path_buf(),
|
|
to: new_to.to_path_buf(),
|
|
source: Box::new(source),
|
|
}
|
|
})?;
|
|
} else if file_type.is_file() {
|
|
hard_link_file(sys, &new_from, &new_to)?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[derive(Debug, thiserror::Error, deno_error::JsError)]
|
|
pub enum HardLinkFileError {
|
|
#[class(inherit)]
|
|
#[error("Removing file to hard link {from} to {to}")]
|
|
RemoveFileToHardLink {
|
|
from: PathBuf,
|
|
to: PathBuf,
|
|
#[source]
|
|
#[inherit]
|
|
source: std::io::Error,
|
|
},
|
|
#[class(inherit)]
|
|
#[error("Hard linking {from} to {to}")]
|
|
HardLinking {
|
|
from: PathBuf,
|
|
to: PathBuf,
|
|
#[source]
|
|
#[inherit]
|
|
source: std::io::Error,
|
|
},
|
|
}
|
|
|
|
/// Hardlinks a file from one location to another.
|
|
pub fn hard_link_file<TSys: FsHardLink + FsRemoveFile + ThreadSleep>(
|
|
sys: &TSys,
|
|
from: &Path,
|
|
to: &Path,
|
|
) -> Result<(), HardLinkFileError> {
|
|
// note: chance for race conditions here between attempting to create,
|
|
// then removing, then attempting to create. There doesn't seem to be
|
|
// a way to hard link with overwriting in Rust, but maybe there is some
|
|
// way with platform specific code. The workaround here is to handle
|
|
// scenarios where something else might create or remove files.
|
|
if let Err(err) = sys.fs_hard_link(from, to) {
|
|
if err.kind() == ErrorKind::AlreadyExists {
|
|
if let Err(err) = sys.fs_remove_file(to) {
|
|
if err.kind() == ErrorKind::NotFound {
|
|
// Assume another process/thread created this hard link to the file we are wanting
|
|
// to remove then sleep a little bit to let the other process/thread move ahead
|
|
// faster to reduce contention.
|
|
sys.thread_sleep(Duration::from_millis(10));
|
|
} else {
|
|
return Err(HardLinkFileError::RemoveFileToHardLink {
|
|
from: from.to_path_buf(),
|
|
to: to.to_path_buf(),
|
|
source: err,
|
|
});
|
|
}
|
|
}
|
|
|
|
// Always attempt to recreate the hardlink. In contention scenarios, the other process
|
|
// might have been killed or exited after removing the file, but before creating the hardlink
|
|
if let Err(err) = sys.fs_hard_link(from, to) {
|
|
// Assume another process/thread created this hard link to the file we are wanting
|
|
// to now create then sleep a little bit to let the other process/thread move ahead
|
|
// faster to reduce contention.
|
|
if err.kind() == ErrorKind::AlreadyExists {
|
|
sys.thread_sleep(Duration::from_millis(10));
|
|
} else {
|
|
return Err(HardLinkFileError::HardLinking {
|
|
from: from.to_path_buf(),
|
|
to: to.to_path_buf(),
|
|
source: err,
|
|
});
|
|
}
|
|
}
|
|
} else {
|
|
return Err(HardLinkFileError::HardLinking {
|
|
from: from.to_path_buf(),
|
|
to: to.to_path_buf(),
|
|
source: err,
|
|
});
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|