1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
use std::path::Path;
use miette::{Context, IntoDiagnostic};
use super::FileSources;
pub(crate) fn configure_script_settings(
ctx: &aube_settings::ResolveCtx<'_>,
command: Option<&str>,
) {
// Fold an embedder's `NODE_OPTIONS` contribution over the resolved
// `nodeOptions` setting (append by default, so a wrapper's `--import`
// preload adds to the user's value rather than replacing it).
let node_options = crate::runtime::merge_node_options(
aube_settings::resolved::node_options(ctx).and_then(non_empty_string),
);
let script_shell = aube_settings::resolved::script_shell(ctx)
.and_then(|s| non_empty_string(s).map(Into::into));
let unsafe_perm = aube_settings::resolved::unsafe_perm(ctx);
let shell_emulator = aube_settings::resolved::shell_emulator(ctx);
// Runtime switching: `crate::runtime::ensure` must have run before
// this for lifecycle scripts to see the pinned node — the install
// driver resolves the runtime early, then configures script
// settings. When no context exists (or no switching is active)
// `node_bin_dir` stays `None` (PATH untouched); `node_program` /
// `node_execpath` still fall back to the ambient `node` on PATH so
// `NODE` / `npm_node_execpath` are populated for lifecycle scripts
// the way pnpm/npm do.
let runtime = crate::runtime::current();
// Resolve the proxy the same way the registry client does, so a
// dependency's install script honors it too. `httpProxy` inherits
// from the resolved `httpsProxy` (npm/pnpm parity: a lone
// `https-proxy=` line configures both schemes) — replicate that
// fallback here, since the generated accessors only walk each
// setting's own sources. `noProxy` has no cross-setting inheritance.
let https_proxy = aube_settings::resolved::https_proxy(ctx).and_then(non_empty_string);
let http_proxy = aube_settings::resolved::http_proxy(ctx)
.and_then(non_empty_string)
.or_else(|| https_proxy.clone());
let no_proxy = aube_settings::resolved::no_proxy(ctx).and_then(non_empty_string);
aube_scripts::set_script_settings(aube_scripts::ScriptSettings {
node_options,
script_shell,
unsafe_perm,
shell_emulator,
node_bin_dir: runtime.as_ref().and_then(|r| r.bin_dir.clone()),
node_program: runtime
.as_ref()
.and_then(|r| r.node_program.clone())
.or_else(aube_runtime::node_on_path),
node_execpath: runtime
.as_ref()
.and_then(|r| r.node_execpath.clone().or_else(|| r.node_program.clone()))
.or_else(aube_runtime::node_on_path),
extra_env: crate::runtime::embedder_extra_env(),
command: command.map(str::to_string),
// `npm_config_node_gyp` parity: hand every lifecycle script a
// runnable node-gyp stand-in. The shim is written once into
// aube's cache; a write failure here is non-fatal (the var just
// stays unset, matching pre-parity behavior).
node_gyp_js: super::install::node_gyp_bootstrap::lazy_js_shim_path().ok(),
http_proxy,
https_proxy,
no_proxy,
});
}
/// Load `.npmrc` + workspace settings for `cwd` and push them into the
/// process-wide script settings snapshot. Used by commands that run
/// lifecycle hooks (pack/publish/version) outside the install path,
/// which already does this via `configure_script_settings` directly.
/// `command` is the npm-compat command label exported as
/// `npm_command` (e.g. `"pack"`).
pub(crate) fn configure_script_settings_for_cwd(
cwd: &Path,
command: Option<&str>,
) -> miette::Result<()> {
let files = FileSources::load(cwd);
let (_, raw_workspace) = aube_manifest::workspace::load_both(cwd)
.into_diagnostic()
.wrap_err("failed to load workspace config")?;
let env_snapshot = aube_settings::values::capture_env();
let ctx = files.ctx(&raw_workspace, &env_snapshot, &[]);
configure_script_settings(&ctx, command);
Ok(())
}
fn non_empty_string(value: String) -> Option<String> {
let trimmed = value.trim();
(!trimmed.is_empty()).then(|| trimmed.to_string())
}