mirror of
https://github.com/denoland/deno.git
synced 2024-11-24 15:19:26 -05:00
fix(install): re-setup bin entries after running lifecycle scripts (#26752)
Fixes #26677 Some packages (like supabase) declare bin entries that don't exist until lifecycle scripts are run. For instance, the lifecycle script downloads a binary file which serves as a bin entrypoint. Unfortunately you can't just defer setting up the bin entries until after lifecycle scripts have run, because the scripts may rely on them. I looked into this, and PNPM just re-links bin entries after running lifecycle scripts. I think that's about the best we can do as well. Note that we'll only re-setup bin entries for packages whose lifecycle scripts we run. This should limit the performance cost, as typically a given project will not have many lifecycle scripts (and of those, many of them probably don't have bin entries to set up).
This commit is contained in:
parent
15b6baff33
commit
c371b2a492
11 changed files with 253 additions and 47 deletions
|
@ -18,6 +18,7 @@ pub struct BinEntries<'a> {
|
||||||
seen_names: HashMap<&'a str, &'a NpmPackageId>,
|
seen_names: HashMap<&'a str, &'a NpmPackageId>,
|
||||||
/// The bin entries
|
/// The bin entries
|
||||||
entries: Vec<(&'a NpmResolutionPackage, PathBuf)>,
|
entries: Vec<(&'a NpmResolutionPackage, PathBuf)>,
|
||||||
|
sorted: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the name of the default binary for the given package.
|
/// Returns the name of the default binary for the given package.
|
||||||
|
@ -31,6 +32,20 @@ fn default_bin_name(package: &NpmResolutionPackage) -> &str {
|
||||||
.map_or(package.id.nv.name.as_str(), |(_, name)| name)
|
.map_or(package.id.nv.name.as_str(), |(_, name)| name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn warn_missing_entrypoint(
|
||||||
|
bin_name: &str,
|
||||||
|
package_path: &Path,
|
||||||
|
entrypoint: &Path,
|
||||||
|
) {
|
||||||
|
log::warn!(
|
||||||
|
"{} Trying to set up '{}' bin for \"{}\", but the entry point \"{}\" doesn't exist.",
|
||||||
|
deno_terminal::colors::yellow("Warning"),
|
||||||
|
bin_name,
|
||||||
|
package_path.display(),
|
||||||
|
entrypoint.display()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> BinEntries<'a> {
|
impl<'a> BinEntries<'a> {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
|
@ -42,6 +57,7 @@ impl<'a> BinEntries<'a> {
|
||||||
package: &'a NpmResolutionPackage,
|
package: &'a NpmResolutionPackage,
|
||||||
package_path: PathBuf,
|
package_path: PathBuf,
|
||||||
) {
|
) {
|
||||||
|
self.sorted = false;
|
||||||
// check for a new collision, if we haven't already
|
// check for a new collision, if we haven't already
|
||||||
// found one
|
// found one
|
||||||
match package.bin.as_ref().unwrap() {
|
match package.bin.as_ref().unwrap() {
|
||||||
|
@ -79,16 +95,21 @@ impl<'a> BinEntries<'a> {
|
||||||
&str, // bin name
|
&str, // bin name
|
||||||
&str, // bin script
|
&str, // bin script
|
||||||
) -> Result<(), AnyError>,
|
) -> Result<(), AnyError>,
|
||||||
|
mut filter: impl FnMut(&NpmResolutionPackage) -> bool,
|
||||||
) -> Result<(), AnyError> {
|
) -> Result<(), AnyError> {
|
||||||
if !self.collisions.is_empty() {
|
if !self.collisions.is_empty() && !self.sorted {
|
||||||
// walking the dependency tree to find out the depth of each package
|
// walking the dependency tree to find out the depth of each package
|
||||||
// is sort of expensive, so we only do it if there's a collision
|
// is sort of expensive, so we only do it if there's a collision
|
||||||
sort_by_depth(snapshot, &mut self.entries, &mut self.collisions);
|
sort_by_depth(snapshot, &mut self.entries, &mut self.collisions);
|
||||||
|
self.sorted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut seen = HashSet::new();
|
let mut seen = HashSet::new();
|
||||||
|
|
||||||
for (package, package_path) in &self.entries {
|
for (package, package_path) in &self.entries {
|
||||||
|
if !filter(package) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if let Some(bin_entries) = &package.bin {
|
if let Some(bin_entries) = &package.bin {
|
||||||
match bin_entries {
|
match bin_entries {
|
||||||
deno_npm::registry::NpmPackageVersionBinEntry::String(script) => {
|
deno_npm::registry::NpmPackageVersionBinEntry::String(script) => {
|
||||||
|
@ -118,8 +139,8 @@ impl<'a> BinEntries<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Collect the bin entries into a vec of (name, script path)
|
/// Collect the bin entries into a vec of (name, script path)
|
||||||
pub fn into_bin_files(
|
pub fn collect_bin_files(
|
||||||
mut self,
|
&mut self,
|
||||||
snapshot: &NpmResolutionSnapshot,
|
snapshot: &NpmResolutionSnapshot,
|
||||||
) -> Vec<(String, PathBuf)> {
|
) -> Vec<(String, PathBuf)> {
|
||||||
let mut bins = Vec::new();
|
let mut bins = Vec::new();
|
||||||
|
@ -131,17 +152,18 @@ impl<'a> BinEntries<'a> {
|
||||||
bins.push((name.to_string(), package_path.join(script)));
|
bins.push((name.to_string(), package_path.join(script)));
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
|
|_| true,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
bins
|
bins
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finish setting up the bin entries, writing the necessary files
|
fn set_up_entries_filtered(
|
||||||
/// to disk.
|
|
||||||
pub fn finish(
|
|
||||||
mut self,
|
mut self,
|
||||||
snapshot: &NpmResolutionSnapshot,
|
snapshot: &NpmResolutionSnapshot,
|
||||||
bin_node_modules_dir_path: &Path,
|
bin_node_modules_dir_path: &Path,
|
||||||
|
filter: impl FnMut(&NpmResolutionPackage) -> bool,
|
||||||
|
mut handler: impl FnMut(&EntrySetupOutcome<'_>),
|
||||||
) -> Result<(), AnyError> {
|
) -> Result<(), AnyError> {
|
||||||
if !self.entries.is_empty() && !bin_node_modules_dir_path.exists() {
|
if !self.entries.is_empty() && !bin_node_modules_dir_path.exists() {
|
||||||
std::fs::create_dir_all(bin_node_modules_dir_path).with_context(
|
std::fs::create_dir_all(bin_node_modules_dir_path).with_context(
|
||||||
|
@ -160,18 +182,54 @@ impl<'a> BinEntries<'a> {
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
|package, package_path, name, script| {
|
|package, package_path, name, script| {
|
||||||
set_up_bin_entry(
|
let outcome = set_up_bin_entry(
|
||||||
package,
|
package,
|
||||||
name,
|
name,
|
||||||
script,
|
script,
|
||||||
package_path,
|
package_path,
|
||||||
bin_node_modules_dir_path,
|
bin_node_modules_dir_path,
|
||||||
)
|
)?;
|
||||||
|
handler(&outcome);
|
||||||
|
Ok(())
|
||||||
},
|
},
|
||||||
|
filter,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Finish setting up the bin entries, writing the necessary files
|
||||||
|
/// to disk.
|
||||||
|
pub fn finish(
|
||||||
|
self,
|
||||||
|
snapshot: &NpmResolutionSnapshot,
|
||||||
|
bin_node_modules_dir_path: &Path,
|
||||||
|
handler: impl FnMut(&EntrySetupOutcome<'_>),
|
||||||
|
) -> Result<(), AnyError> {
|
||||||
|
self.set_up_entries_filtered(
|
||||||
|
snapshot,
|
||||||
|
bin_node_modules_dir_path,
|
||||||
|
|_| true,
|
||||||
|
handler,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finish setting up the bin entries, writing the necessary files
|
||||||
|
/// to disk.
|
||||||
|
pub fn finish_only(
|
||||||
|
self,
|
||||||
|
snapshot: &NpmResolutionSnapshot,
|
||||||
|
bin_node_modules_dir_path: &Path,
|
||||||
|
handler: impl FnMut(&EntrySetupOutcome<'_>),
|
||||||
|
only: &HashSet<&NpmPackageId>,
|
||||||
|
) -> Result<(), AnyError> {
|
||||||
|
self.set_up_entries_filtered(
|
||||||
|
snapshot,
|
||||||
|
bin_node_modules_dir_path,
|
||||||
|
|package| only.contains(&package.id),
|
||||||
|
handler,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// walk the dependency tree to find out the depth of each package
|
// walk the dependency tree to find out the depth of each package
|
||||||
|
@ -233,16 +291,17 @@ fn sort_by_depth(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_up_bin_entry(
|
pub fn set_up_bin_entry<'a>(
|
||||||
package: &NpmResolutionPackage,
|
package: &'a NpmResolutionPackage,
|
||||||
bin_name: &str,
|
bin_name: &'a str,
|
||||||
#[allow(unused_variables)] bin_script: &str,
|
#[allow(unused_variables)] bin_script: &str,
|
||||||
#[allow(unused_variables)] package_path: &Path,
|
#[allow(unused_variables)] package_path: &'a Path,
|
||||||
bin_node_modules_dir_path: &Path,
|
bin_node_modules_dir_path: &Path,
|
||||||
) -> Result<(), AnyError> {
|
) -> Result<EntrySetupOutcome<'a>, AnyError> {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
{
|
{
|
||||||
set_up_bin_shim(package, bin_name, bin_node_modules_dir_path)?;
|
set_up_bin_shim(package, bin_name, bin_node_modules_dir_path)?;
|
||||||
|
Ok(EntrySetupOutcome::Success)
|
||||||
}
|
}
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
|
@ -252,9 +311,8 @@ pub fn set_up_bin_entry(
|
||||||
bin_script,
|
bin_script,
|
||||||
package_path,
|
package_path,
|
||||||
bin_node_modules_dir_path,
|
bin_node_modules_dir_path,
|
||||||
)?;
|
)
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
|
@ -301,14 +359,39 @@ fn make_executable_if_exists(path: &Path) -> Result<bool, AnyError> {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum EntrySetupOutcome<'a> {
|
||||||
|
#[cfg_attr(windows, allow(dead_code))]
|
||||||
|
MissingEntrypoint {
|
||||||
|
bin_name: &'a str,
|
||||||
|
package_path: &'a Path,
|
||||||
|
entrypoint: PathBuf,
|
||||||
|
package: &'a NpmResolutionPackage,
|
||||||
|
},
|
||||||
|
Success,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> EntrySetupOutcome<'a> {
|
||||||
|
pub fn warn_if_failed(&self) {
|
||||||
|
match self {
|
||||||
|
EntrySetupOutcome::MissingEntrypoint {
|
||||||
|
bin_name,
|
||||||
|
package_path,
|
||||||
|
entrypoint,
|
||||||
|
..
|
||||||
|
} => warn_missing_entrypoint(bin_name, package_path, entrypoint),
|
||||||
|
EntrySetupOutcome::Success => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn symlink_bin_entry(
|
fn symlink_bin_entry<'a>(
|
||||||
_package: &NpmResolutionPackage,
|
package: &'a NpmResolutionPackage,
|
||||||
bin_name: &str,
|
bin_name: &'a str,
|
||||||
bin_script: &str,
|
bin_script: &str,
|
||||||
package_path: &Path,
|
package_path: &'a Path,
|
||||||
bin_node_modules_dir_path: &Path,
|
bin_node_modules_dir_path: &Path,
|
||||||
) -> Result<(), AnyError> {
|
) -> Result<EntrySetupOutcome<'a>, AnyError> {
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::os::unix::fs::symlink;
|
use std::os::unix::fs::symlink;
|
||||||
let link = bin_node_modules_dir_path.join(bin_name);
|
let link = bin_node_modules_dir_path.join(bin_name);
|
||||||
|
@ -318,14 +401,12 @@ fn symlink_bin_entry(
|
||||||
format!("Can't set up '{}' bin at {}", bin_name, original.display())
|
format!("Can't set up '{}' bin at {}", bin_name, original.display())
|
||||||
})?;
|
})?;
|
||||||
if !found {
|
if !found {
|
||||||
log::warn!(
|
return Ok(EntrySetupOutcome::MissingEntrypoint {
|
||||||
"{} Trying to set up '{}' bin for \"{}\", but the entry point \"{}\" doesn't exist.",
|
|
||||||
deno_terminal::colors::yellow("Warning"),
|
|
||||||
bin_name,
|
bin_name,
|
||||||
package_path.display(),
|
package_path,
|
||||||
original.display()
|
entrypoint: original,
|
||||||
);
|
package,
|
||||||
return Ok(());
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let original_relative =
|
let original_relative =
|
||||||
|
@ -348,7 +429,7 @@ fn symlink_bin_entry(
|
||||||
original_relative.display()
|
original_relative.display()
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
return Ok(());
|
return Ok(EntrySetupOutcome::Success);
|
||||||
}
|
}
|
||||||
return Err(err).with_context(|| {
|
return Err(err).with_context(|| {
|
||||||
format!(
|
format!(
|
||||||
|
@ -359,5 +440,5 @@ fn symlink_bin_entry(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(EntrySetupOutcome::Success)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ use deno_runtime::deno_io::FromRawIoHandle;
|
||||||
use deno_semver::package::PackageNv;
|
use deno_semver::package::PackageNv;
|
||||||
use deno_semver::Version;
|
use deno_semver::Version;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::collections::HashSet;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
@ -61,7 +62,7 @@ impl<'a> LifecycleScripts<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_lifecycle_scripts(
|
pub fn has_lifecycle_scripts(
|
||||||
package: &NpmResolutionPackage,
|
package: &NpmResolutionPackage,
|
||||||
package_path: &Path,
|
package_path: &Path,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
@ -83,7 +84,7 @@ fn is_broken_default_install_script(script: &str, package_path: &Path) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> LifecycleScripts<'a> {
|
impl<'a> LifecycleScripts<'a> {
|
||||||
fn can_run_scripts(&self, package_nv: &PackageNv) -> bool {
|
pub fn can_run_scripts(&self, package_nv: &PackageNv) -> bool {
|
||||||
if !self.strategy.can_run_scripts() {
|
if !self.strategy.can_run_scripts() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -98,6 +99,9 @@ impl<'a> LifecycleScripts<'a> {
|
||||||
PackagesAllowedScripts::None => false,
|
PackagesAllowedScripts::None => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn has_run_scripts(&self, package: &NpmResolutionPackage) -> bool {
|
||||||
|
self.strategy.has_run(package)
|
||||||
|
}
|
||||||
/// Register a package for running lifecycle scripts, if applicable.
|
/// Register a package for running lifecycle scripts, if applicable.
|
||||||
///
|
///
|
||||||
/// `package_path` is the path containing the package's code (its root dir).
|
/// `package_path` is the path containing the package's code (its root dir).
|
||||||
|
@ -110,12 +114,12 @@ impl<'a> LifecycleScripts<'a> {
|
||||||
) {
|
) {
|
||||||
if has_lifecycle_scripts(package, &package_path) {
|
if has_lifecycle_scripts(package, &package_path) {
|
||||||
if self.can_run_scripts(&package.id.nv) {
|
if self.can_run_scripts(&package.id.nv) {
|
||||||
if !self.strategy.has_run(package) {
|
if !self.has_run_scripts(package) {
|
||||||
self
|
self
|
||||||
.packages_with_scripts
|
.packages_with_scripts
|
||||||
.push((package, package_path.into_owned()));
|
.push((package, package_path.into_owned()));
|
||||||
}
|
}
|
||||||
} else if !self.strategy.has_run(package)
|
} else if !self.has_run_scripts(package)
|
||||||
&& (self.config.explicit_install || !self.strategy.has_warned(package))
|
&& (self.config.explicit_install || !self.strategy.has_warned(package))
|
||||||
{
|
{
|
||||||
// Skip adding `esbuild` as it is known that it can work properly without lifecycle script
|
// Skip adding `esbuild` as it is known that it can work properly without lifecycle script
|
||||||
|
@ -149,22 +153,32 @@ impl<'a> LifecycleScripts<'a> {
|
||||||
self,
|
self,
|
||||||
snapshot: &NpmResolutionSnapshot,
|
snapshot: &NpmResolutionSnapshot,
|
||||||
packages: &[NpmResolutionPackage],
|
packages: &[NpmResolutionPackage],
|
||||||
root_node_modules_dir_path: Option<&Path>,
|
root_node_modules_dir_path: &Path,
|
||||||
progress_bar: &ProgressBar,
|
progress_bar: &ProgressBar,
|
||||||
) -> Result<(), AnyError> {
|
) -> Result<(), AnyError> {
|
||||||
self.warn_not_run_scripts()?;
|
self.warn_not_run_scripts()?;
|
||||||
let get_package_path =
|
let get_package_path =
|
||||||
|p: &NpmResolutionPackage| self.strategy.package_path(p);
|
|p: &NpmResolutionPackage| self.strategy.package_path(p);
|
||||||
let mut failed_packages = Vec::new();
|
let mut failed_packages = Vec::new();
|
||||||
|
let mut bin_entries = BinEntries::new();
|
||||||
if !self.packages_with_scripts.is_empty() {
|
if !self.packages_with_scripts.is_empty() {
|
||||||
|
let package_ids = self
|
||||||
|
.packages_with_scripts
|
||||||
|
.iter()
|
||||||
|
.map(|(p, _)| &p.id)
|
||||||
|
.collect::<HashSet<_>>();
|
||||||
// get custom commands for each bin available in the node_modules dir (essentially
|
// get custom commands for each bin available in the node_modules dir (essentially
|
||||||
// the scripts that are in `node_modules/.bin`)
|
// the scripts that are in `node_modules/.bin`)
|
||||||
let base =
|
let base = resolve_baseline_custom_commands(
|
||||||
resolve_baseline_custom_commands(snapshot, packages, get_package_path)?;
|
&mut bin_entries,
|
||||||
|
snapshot,
|
||||||
|
packages,
|
||||||
|
get_package_path,
|
||||||
|
)?;
|
||||||
let init_cwd = &self.config.initial_cwd;
|
let init_cwd = &self.config.initial_cwd;
|
||||||
let process_state = crate::npm::managed::npm_process_state(
|
let process_state = crate::npm::managed::npm_process_state(
|
||||||
snapshot.as_valid_serialized(),
|
snapshot.as_valid_serialized(),
|
||||||
root_node_modules_dir_path,
|
Some(root_node_modules_dir_path),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut env_vars = crate::task_runner::real_env_vars();
|
let mut env_vars = crate::task_runner::real_env_vars();
|
||||||
|
@ -221,7 +235,7 @@ impl<'a> LifecycleScripts<'a> {
|
||||||
custom_commands: custom_commands.clone(),
|
custom_commands: custom_commands.clone(),
|
||||||
init_cwd,
|
init_cwd,
|
||||||
argv: &[],
|
argv: &[],
|
||||||
root_node_modules_dir: root_node_modules_dir_path,
|
root_node_modules_dir: Some(root_node_modules_dir_path),
|
||||||
stdio: Some(crate::task_runner::TaskIo {
|
stdio: Some(crate::task_runner::TaskIo {
|
||||||
stderr: TaskStdio::piped(),
|
stderr: TaskStdio::piped(),
|
||||||
stdout: TaskStdio::piped(),
|
stdout: TaskStdio::piped(),
|
||||||
|
@ -262,6 +276,17 @@ impl<'a> LifecycleScripts<'a> {
|
||||||
}
|
}
|
||||||
self.strategy.did_run_scripts(package)?;
|
self.strategy.did_run_scripts(package)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// re-set up bin entries for the packages which we've run scripts for.
|
||||||
|
// lifecycle scripts can create files that are linked to by bin entries,
|
||||||
|
// and the only reliable way to handle this is to re-link bin entries
|
||||||
|
// (this is what PNPM does as well)
|
||||||
|
bin_entries.finish_only(
|
||||||
|
snapshot,
|
||||||
|
&root_node_modules_dir_path.join(".bin"),
|
||||||
|
|outcome| outcome.warn_if_failed(),
|
||||||
|
&package_ids,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
if failed_packages.is_empty() {
|
if failed_packages.is_empty() {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -281,9 +306,10 @@ impl<'a> LifecycleScripts<'a> {
|
||||||
// take in all (non copy) packages from snapshot,
|
// take in all (non copy) packages from snapshot,
|
||||||
// and resolve the set of available binaries to create
|
// and resolve the set of available binaries to create
|
||||||
// custom commands available to the task runner
|
// custom commands available to the task runner
|
||||||
fn resolve_baseline_custom_commands(
|
fn resolve_baseline_custom_commands<'a>(
|
||||||
snapshot: &NpmResolutionSnapshot,
|
bin_entries: &mut BinEntries<'a>,
|
||||||
packages: &[NpmResolutionPackage],
|
snapshot: &'a NpmResolutionSnapshot,
|
||||||
|
packages: &'a [NpmResolutionPackage],
|
||||||
get_package_path: impl Fn(&NpmResolutionPackage) -> PathBuf,
|
get_package_path: impl Fn(&NpmResolutionPackage) -> PathBuf,
|
||||||
) -> Result<crate::task_runner::TaskCustomCommands, AnyError> {
|
) -> Result<crate::task_runner::TaskCustomCommands, AnyError> {
|
||||||
let mut custom_commands = crate::task_runner::TaskCustomCommands::new();
|
let mut custom_commands = crate::task_runner::TaskCustomCommands::new();
|
||||||
|
@ -306,6 +332,7 @@ fn resolve_baseline_custom_commands(
|
||||||
// doing it for packages that are set up already.
|
// doing it for packages that are set up already.
|
||||||
// realistically, scripts won't be run very often so it probably isn't too big of an issue.
|
// realistically, scripts won't be run very often so it probably isn't too big of an issue.
|
||||||
resolve_custom_commands_from_packages(
|
resolve_custom_commands_from_packages(
|
||||||
|
bin_entries,
|
||||||
custom_commands,
|
custom_commands,
|
||||||
snapshot,
|
snapshot,
|
||||||
packages,
|
packages,
|
||||||
|
@ -320,12 +347,12 @@ fn resolve_custom_commands_from_packages<
|
||||||
'a,
|
'a,
|
||||||
P: IntoIterator<Item = &'a NpmResolutionPackage>,
|
P: IntoIterator<Item = &'a NpmResolutionPackage>,
|
||||||
>(
|
>(
|
||||||
|
bin_entries: &mut BinEntries<'a>,
|
||||||
mut commands: crate::task_runner::TaskCustomCommands,
|
mut commands: crate::task_runner::TaskCustomCommands,
|
||||||
snapshot: &'a NpmResolutionSnapshot,
|
snapshot: &'a NpmResolutionSnapshot,
|
||||||
packages: P,
|
packages: P,
|
||||||
get_package_path: impl Fn(&'a NpmResolutionPackage) -> PathBuf,
|
get_package_path: impl Fn(&'a NpmResolutionPackage) -> PathBuf,
|
||||||
) -> Result<crate::task_runner::TaskCustomCommands, AnyError> {
|
) -> Result<crate::task_runner::TaskCustomCommands, AnyError> {
|
||||||
let mut bin_entries = BinEntries::new();
|
|
||||||
for package in packages {
|
for package in packages {
|
||||||
let package_path = get_package_path(package);
|
let package_path = get_package_path(package);
|
||||||
|
|
||||||
|
@ -333,7 +360,7 @@ fn resolve_custom_commands_from_packages<
|
||||||
bin_entries.add(package, package_path);
|
bin_entries.add(package, package_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let bins = bin_entries.into_bin_files(snapshot);
|
let bins: Vec<(String, PathBuf)> = bin_entries.collect_bin_files(snapshot);
|
||||||
for (bin_name, script_path) in bins {
|
for (bin_name, script_path) in bins {
|
||||||
commands.insert(
|
commands.insert(
|
||||||
bin_name.clone(),
|
bin_name.clone(),
|
||||||
|
@ -356,7 +383,9 @@ fn resolve_custom_commands_from_deps(
|
||||||
snapshot: &NpmResolutionSnapshot,
|
snapshot: &NpmResolutionSnapshot,
|
||||||
get_package_path: impl Fn(&NpmResolutionPackage) -> PathBuf,
|
get_package_path: impl Fn(&NpmResolutionPackage) -> PathBuf,
|
||||||
) -> Result<crate::task_runner::TaskCustomCommands, AnyError> {
|
) -> Result<crate::task_runner::TaskCustomCommands, AnyError> {
|
||||||
|
let mut bin_entries = BinEntries::new();
|
||||||
resolve_custom_commands_from_packages(
|
resolve_custom_commands_from_packages(
|
||||||
|
&mut bin_entries,
|
||||||
baseline,
|
baseline,
|
||||||
snapshot,
|
snapshot,
|
||||||
package
|
package
|
||||||
|
|
|
@ -55,6 +55,7 @@ use crate::util::progress_bar::ProgressMessagePrompt;
|
||||||
use super::super::cache::NpmCache;
|
use super::super::cache::NpmCache;
|
||||||
use super::super::cache::TarballCache;
|
use super::super::cache::TarballCache;
|
||||||
use super::super::resolution::NpmResolution;
|
use super::super::resolution::NpmResolution;
|
||||||
|
use super::common::bin_entries;
|
||||||
use super::common::NpmPackageFsResolver;
|
use super::common::NpmPackageFsResolver;
|
||||||
use super::common::RegistryReadPermissionChecker;
|
use super::common::RegistryReadPermissionChecker;
|
||||||
|
|
||||||
|
@ -329,8 +330,7 @@ async fn sync_resolution_with_fs(
|
||||||
let mut cache_futures = FuturesUnordered::new();
|
let mut cache_futures = FuturesUnordered::new();
|
||||||
let mut newest_packages_by_name: HashMap<&String, &NpmResolutionPackage> =
|
let mut newest_packages_by_name: HashMap<&String, &NpmResolutionPackage> =
|
||||||
HashMap::with_capacity(package_partitions.packages.len());
|
HashMap::with_capacity(package_partitions.packages.len());
|
||||||
let bin_entries =
|
let bin_entries = Rc::new(RefCell::new(bin_entries::BinEntries::new()));
|
||||||
Rc::new(RefCell::new(super::common::bin_entries::BinEntries::new()));
|
|
||||||
let mut lifecycle_scripts =
|
let mut lifecycle_scripts =
|
||||||
super::common::lifecycle_scripts::LifecycleScripts::new(
|
super::common::lifecycle_scripts::LifecycleScripts::new(
|
||||||
lifecycle_scripts,
|
lifecycle_scripts,
|
||||||
|
@ -658,7 +658,28 @@ async fn sync_resolution_with_fs(
|
||||||
// 7. Set up `node_modules/.bin` entries for packages that need it.
|
// 7. Set up `node_modules/.bin` entries for packages that need it.
|
||||||
{
|
{
|
||||||
let bin_entries = std::mem::take(&mut *bin_entries.borrow_mut());
|
let bin_entries = std::mem::take(&mut *bin_entries.borrow_mut());
|
||||||
bin_entries.finish(snapshot, &bin_node_modules_dir_path)?;
|
bin_entries.finish(
|
||||||
|
snapshot,
|
||||||
|
&bin_node_modules_dir_path,
|
||||||
|
|setup_outcome| {
|
||||||
|
match setup_outcome {
|
||||||
|
bin_entries::EntrySetupOutcome::MissingEntrypoint {
|
||||||
|
package,
|
||||||
|
package_path,
|
||||||
|
..
|
||||||
|
} if super::common::lifecycle_scripts::has_lifecycle_scripts(
|
||||||
|
package,
|
||||||
|
package_path,
|
||||||
|
) && lifecycle_scripts.can_run_scripts(&package.id.nv)
|
||||||
|
&& !lifecycle_scripts.has_run_scripts(package) =>
|
||||||
|
{
|
||||||
|
// ignore, it might get fixed when the lifecycle scripts run.
|
||||||
|
// if not, we'll warn then
|
||||||
|
}
|
||||||
|
outcome => outcome.warn_if_failed(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8. Create symlinks for the workspace packages
|
// 8. Create symlinks for the workspace packages
|
||||||
|
@ -708,7 +729,7 @@ async fn sync_resolution_with_fs(
|
||||||
.finish(
|
.finish(
|
||||||
snapshot,
|
snapshot,
|
||||||
&package_partitions.packages,
|
&package_partitions.packages,
|
||||||
Some(root_node_modules_dir_path),
|
root_node_modules_dir_path,
|
||||||
progress_bar,
|
progress_bar,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
import * as fs from "node:fs";
|
||||||
|
|
||||||
|
fs.writeFileSync("./testbin.js", "#!/usr/bin/env node\nconsole.log('run testbin');");
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"name": "@denotest/bin-created-by-lifecycle",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"scripts": {
|
||||||
|
"install": "node install.mjs"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"testbin": "testbin.js"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"tempDir": true,
|
||||||
|
"tests": {
|
||||||
|
"all_at_once": {
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"args": "install --allow-scripts",
|
||||||
|
"output": "all_at_once_install.out"
|
||||||
|
},
|
||||||
|
{ "args": "task run-testbin", "output": "run_testbin.out" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"separate_steps": {
|
||||||
|
"steps": [
|
||||||
|
{ "if": "unix", "args": "install", "output": "install_warn.out" },
|
||||||
|
{
|
||||||
|
"if": "windows",
|
||||||
|
"args": "install",
|
||||||
|
"output": "install_warn_windows.out"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"args": "install --allow-scripts",
|
||||||
|
"output": "Initialize @denotest/bin-created-by-lifecycle@1.0.0: running 'install' script\n"
|
||||||
|
},
|
||||||
|
{ "args": "task run-testbin", "output": "run_testbin.out" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
Download http://localhost:4260/@denotest%2fbin-created-by-lifecycle
|
||||||
|
Download http://localhost:4260/@denotest/bin-created-by-lifecycle/1.0.0.tgz
|
||||||
|
Initialize @denotest/bin-created-by-lifecycle@1.0.0
|
||||||
|
Initialize @denotest/bin-created-by-lifecycle@1.0.0: running 'install' script
|
|
@ -0,0 +1,10 @@
|
||||||
|
Download http://localhost:4260/@denotest%2fbin-created-by-lifecycle
|
||||||
|
Download http://localhost:4260/@denotest/bin-created-by-lifecycle/1.0.0.tgz
|
||||||
|
Initialize @denotest/bin-created-by-lifecycle@1.0.0
|
||||||
|
Warning Trying to set up 'testbin' bin for "[WILDCARD]bin-created-by-lifecycle", but the entry point "[WILDCARD]testbin.js" doesn't exist.
|
||||||
|
Warning The following packages contained npm lifecycle scripts (preinstall/install/postinstall) that were not executed:
|
||||||
|
┠─ npm:@denotest/bin-created-by-lifecycle@1.0.0
|
||||||
|
┃
|
||||||
|
┠─ This may cause the packages to not work correctly.
|
||||||
|
┖─ To run lifecycle scripts, use the `--allow-scripts` flag with `deno install`:
|
||||||
|
deno install --allow-scripts=npm:@denotest/bin-created-by-lifecycle@1.0.0
|
|
@ -0,0 +1,9 @@
|
||||||
|
Download http://localhost:4260/@denotest%2fbin-created-by-lifecycle
|
||||||
|
Download http://localhost:4260/@denotest/bin-created-by-lifecycle/1.0.0.tgz
|
||||||
|
Initialize @denotest/bin-created-by-lifecycle@1.0.0
|
||||||
|
Warning The following packages contained npm lifecycle scripts (preinstall/install/postinstall) that were not executed:
|
||||||
|
┠─ npm:@denotest/bin-created-by-lifecycle@1.0.0
|
||||||
|
┃
|
||||||
|
┠─ This may cause the packages to not work correctly.
|
||||||
|
┖─ To run lifecycle scripts, use the `--allow-scripts` flag with `deno install`:
|
||||||
|
deno install --allow-scripts=npm:@denotest/bin-created-by-lifecycle@1.0.0
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"@denotest/bin-created-by-lifecycle": "1.0.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"run-testbin": "testbin"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
Task run-testbin testbin
|
||||||
|
run testbin
|
Loading…
Reference in a new issue