Skip to main content

Linker

Struct Linker 

Source
pub struct Linker { /* private fields */ }
Expand description

Links packages from the global store into a project’s node_modules/.

Uses pnpm-compatible symlink layout backed by a global virtual store:

  • Packages are materialized once in ~/.cache/aube/virtual-store/ (or $XDG_CACHE_HOME/aube/virtual-store/)
  • Per-project .aube/ entries are symlinks into the global virtual store
  • Top-level node_modules/<name> entries are symlinks to .aube/<dep_path>/node_modules/<name> (matching pnpm)
  • Transitive deps live as sibling symlinks inside .aube/<dep_path>/node_modules/ so Node’s directory walk finds them when resolving from inside the package

Implementations§

Source§

impl Linker

Source

pub fn new(store: &Store, strategy: LinkStrategy) -> Self

Source

pub fn with_node_linker(self, node_linker: NodeLinker) -> Self

Select the layout mode. Defaults to NodeLinker::Isolated (pnpm’s .aube/-backed virtual-store layout); Hoisted dispatches link_all / link_workspace to the flat node_modules materializer in crate::hoisted.

Source

pub fn node_linker(&self) -> NodeLinker

Current layout mode. The install driver reads this after linking to decide how to resolve per-package directories for bin linking and lifecycle scripts — isolated uses the .aube/<dep_path> convention, hoisted consults the HoistedPlacements returned on LinkStats.

Source

pub fn with_modules_dir_name(self, name: impl Into<String>) -> Self

Override the name of the project-level node_modules directory (pnpm’s modules-dir setting). Empty strings are coerced back to the default so a .npmrc typo can’t make the linker write into the project root itself. The setting only affects the outer directory name — the inner virtual-store layout still uses the literal node_modules that Node’s resolver expects when walking up from inside a package.

Source

pub fn modules_dir_name(&self) -> &str

Project-level modules directory name. aube reads this when it needs the same path the linker writes into — keeping the computation DRY with whatever the linker was built with.

Source

pub fn with_aube_dir_override(self, path: PathBuf) -> Self

Override the per-project virtual-store path (pnpm’s virtualStoreDir). The supplied path should be absoluteaube resolves relative .npmrc / pnpm-workspace.yaml values against the project dir before handing them here. When not set, the linker derives the virtual store path as <project_dir>/<modules_dir_name>/.aube at link time, which matches the historical behavior.

Source

pub fn aube_dir_for(&self, project_dir: &Path) -> PathBuf

Compute the effective virtual-store path for project_dir. Consults the override installed by with_aube_dir_override if any; otherwise falls back to <project_dir>/<modules_dir>/.<name>. Used internally by link_all; also called by the install driver’s “already linked” fast path so both sites land on the same directory when the user has overridden virtualStoreDir.

Override the package-level linker worker count. Values below 1 are ignored by the install driver before they reach this point.

Source

pub fn with_use_global_virtual_store(self, enabled: bool) -> Self

Override the global-virtual-store toggle set by Linker::new (which looks at CI). Callers use this to force per-project materialization when they’ve detected a consumer that breaks on directory symlinks escaping the project root — e.g. Next.js / Turbopack, which canonicalizes node_modules/<pkg> and rejects anything that lands outside its declared filesystem root.

Source

pub fn with_project_local_dep_paths( self, dep_paths: impl IntoIterator<Item = String>, ) -> Self

Materialize selected dep paths into the project-local virtual store while all other packages continue to use the GVS.

Callers use this for package-specific compatibility transforms that must never mutate a shared store entry.

Source

pub fn with_shamefully_hoist(self, shamefully_hoist: bool) -> Self

Enable pnpm’s shamefully-hoist mode. When true, every package in the graph gets a top-level node_modules/<name> symlink in addition to the direct-dep entries, producing npm’s flat layout at the cost of phantom-dep correctness. First-write-wins on duplicate names, so root deps always take precedence.

Source

pub fn with_public_hoist_pattern(self, patterns: &[String]) -> Self

Configure pnpm’s public-hoist-pattern. Each input is a glob matched against package names; a leading ! flips it into a negation. After the usual direct-dep symlinks, every non-local package whose name matches at least one positive pattern and no negation gets a top-level node_modules/<name> symlink. Invalid patterns are silently dropped (same tolerance as pnpm), so a typo in .npmrc degrades to “not hoisted” instead of failing the install.

Source

pub fn with_hoist(self, hoist: bool) -> Self

Toggle pnpm’s hoist setting. When true (the default), the hidden modules tree at node_modules/.aube/node_modules/ is populated via with_hoist_pattern. When false, that tree is skipped and any existing directory is swept so stale symlinks from a previous hoist=true run don’t keep resolving.

Source

pub fn with_hoist_pattern(self, patterns: &[String]) -> Self

Configure pnpm’s hoist-pattern. Each input is a glob matched against package names; a leading ! flips it into a negation. Every non-local package in the graph whose name matches at least one positive pattern (and no negation) gets a node_modules/.aube/node_modules/<name> symlink — the hidden fallback dir for Node’s parent-directory walk. Invalid patterns are silently dropped (pnpm parity). Supplying an empty list or only-negation list means “hoist nothing”; leaving this unconfigured keeps the default * match.

Source

pub fn with_hoist_workspace_packages(self, on: bool) -> Self

Toggle pnpm’s hoist-workspace-packages. When false, the linker skips creating node_modules/<ws-pkg> symlinks for workspace packages in every importer, including the root. Cross-importer workspace: deps already resolve through the lockfile, so only direct require('<ws-pkg>') from a package that doesn’t declare it stops working. Default true (pnpm parity).

Source

pub fn with_hoisting_limits(self, limits: HoistingLimits) -> Self

Configure pnpm’s hoistingLimits for node-linker=hoisted. No-op for the default isolated linker.

Source

pub fn with_dedupe_direct_deps(self, on: bool) -> Self

Toggle pnpm’s dedupe-direct-deps. When true, the linker skips creating a per-importer node_modules/<name> symlink for any direct dep whose root importer already declares the same package at the same resolved version — Node’s parent-directory walk from inside the workspace package still resolves the same copy via the root-level symlink, so consumer code is unaffected. Default false (pnpm parity). No-op under virtualStoreOnly=true (no per-importer symlink pass runs) and under NodeLinker::Hoisted (its workspace-wide placement plan deduplicates compatible packages independently).

Source

pub fn with_virtual_store_dir_max_length(self, max_length: usize) -> Self

Override the virtual-store directory name length cap. Primarily a hook for tests and for parity with pnpm’s virtual-store-dir-max-length config; most callers should leave it at the default.

Source

pub fn with_virtual_store_only(self, only: bool) -> Self

Toggle pnpm’s virtual-store-only. When enabled, link_all / link_workspace still populate .aube/<dep_path>/node_modules (and the shared global virtual store under ~/.cache/aube/virtual-store/) but skip the pass that writes top-level node_modules/<name> symlinks and the hoisting passes that target the same directory. No-op under NodeLinker::Hoisted — that layout is inherently a flat top-level materialization.

Source

pub fn virtual_store_only(&self) -> bool

Whether this linker will skip the top-level node_modules/<name> symlink pass. Exposed so the install driver can omit root-level bin linking and lifecycle-script invocations when the user has asked for a virtual-store-only install — both operate on the top-level tree that won’t exist.

Source

pub fn with_graph_hashes(self, hashes: GraphHashes) -> Self

Install a set of pre-computed graph hashes. Every virtual-store path the linker constructs after this point will use the hashed subdir name for the matching dep_path. Callers normally derive the hashes once per install via aube_lockfile::graph_hash::compute_graph_hashes and pass the result in here.

Source

pub fn uses_global_virtual_store(&self) -> bool

Whether this linker populates the project’s .aube/ entries as symlinks into the shared virtual store (true) or materializes a per-project copy (false). Callers that want to mutate package directories after linking — e.g. running allowBuilds lifecycle scripts — need to know because shared-store writes leak across projects.

Source

pub fn with_patches(self, patches: Patches) -> Self

Install a set of patch contents to apply at materialize time. Replaces any previously installed patches. Pair with with_graph_hashes whose patch_hash callback returns the same per-(name, version) digest, so the patched bytes land at a distinct virtual-store path from the unpatched ones.

Source§

impl Linker

Link all packages into node_modules for the given project.

Link all packages for a workspace (multiple importers).

Creates the shared .aube/ virtual store at root, then for each workspace package creates node_modules/ with its direct deps linked from the root .aube/. Workspace packages that depend on each other get symlinks to the package directory.

Source§

impl Linker

Source

pub fn detect_strategy(path: &Path) -> LinkStrategy

Detect the best linking strategy for the filesystem at the given path.

One-arg form. Probes within one dir. Fine when store and project node_modules share the same mount. Use the two-arg form for installs where the store lives on a different filesystem than the project (USB drives, bind mounts, Docker volumes, cross-drive Windows installs). Otherwise the probe reports hardlink based on project-FS self-test, then every real link call crosses an FS boundary and hits EXDEV. Runtime falls back to fs::copy per file silently, thousands of wasted syscalls, user thinks they got hardlinks.

Returns the same-filesystem strategy auto resolves to when the probe succeeds, Copy otherwise. The same-FS strategy is OS-specific: on macOS auto resolves to ReflinkAuto (APFS clonefile benchmarks ~1.91x faster than hardlink), on Linux and other targets it resolves to Hardlink (btrfs/xfs hardlink benchmarks ~2.4-2.6x faster than FICLONE reflink).

The macOS auto resolution is conservative on non-APFS volumes: clonefile is APFS-only, so on an HFS+ volume (external drives, Fusion/older disks) the same-FS hardlink probe succeeds and resolves ReflinkAuto, but the real clonefile then fails. link_file_fresh handles this by falling the reflink back to a hardlink (which HFS+ supports) before copy, so a non-APFS same-FS target still gets zero-cost links rather than a per-file copy. This probe never yields the plain Reflink strategy — that is reachable only through explicit packageImportMethod = clone / clone-or-copy, which keep a plain copy fallback.

Source

pub fn detect_strategy_cross(src_dir: &Path, dst_dir: &Path) -> LinkStrategy

Two-arg probe. src is the store shard (or any dir on the store FS), dst is the project modules dir (or any dir on the destination FS). Probe creates a real cross-mount src file and tries to hardlink into dst, which catches EXDEV up front. A successful hardlink proves src and dst share a mount, so it doubles as the same-FS probe for reflink too (APFS clonefile / btrfs FICLONE require the same FS). Returns the OS-specific same-FS strategy when the probe succeeds (ReflinkAuto on macOS, Hardlink elsewhere), Copy otherwise.

Source

pub fn ensure_in_virtual_store( &self, dep_path: &str, pkg: &LockedPackage, index: &PackageIndex, stats: &mut LinkStats, nested_link_targets: Option<&BTreeMap<String, PathBuf>>, ) -> Result<(), Error>

Materialize a package in the global virtual store if not already present.

Materialize dep_path into the shared global virtual store.

Uses atomic rename to avoid TOCTOU races: materializes into a PID-stamped temp directory, then renames into place. If another process wins the race, its result is kept and the temp dir is cleaned up.

Exposed so the install driver can pipeline GVS population into the fetch phase: as each tarball finishes importing into the CAS, the driver calls this to reflink the package into its ~/.cache/aube/virtual-store/<subdir> entry. Link step 1 then hits the pkg_nm_dir.exists() fast path and only creates the per-project .aube/<dep_path> symlink.

Source

pub fn ensure_in_aube_dir( &self, aube_dir: &Path, dep_path: &str, pkg: &LockedPackage, index: &PackageIndex, stats: &mut LinkStats, nested_link_targets: Option<&BTreeMap<String, PathBuf>>, ) -> Result<(), Error>

Materialize a single package directly into the per-project virtual store at aube_dir/<dep_path>/node_modules/<name>/.

Idempotent: if the entry already exists, counts as cached and returns. Otherwise materializes into a unique temp directory and atomically renames that entry into place so duplicate in-process fetch events for the same dep-path cannot race while writing node_modules/.aube/<dep_path>/. Used by the install-time materializer to pipeline the link work into the fetch phase under non-GVS mode, so the dedicated link phase only has to create top-level node_modules/<name> symlinks.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<D> OwoColorize for D

Source§

fn fg<C>(&self) -> FgColorDisplay<'_, C, Self>
where C: Color,

Set the foreground color generically Read more
Source§

fn bg<C>(&self) -> BgColorDisplay<'_, C, Self>
where C: Color,

Set the background color generically. Read more
Source§

fn black(&self) -> FgColorDisplay<'_, Black, Self>

Change the foreground color to black
Source§

fn on_black(&self) -> BgColorDisplay<'_, Black, Self>

Change the background color to black
Source§

fn red(&self) -> FgColorDisplay<'_, Red, Self>

Change the foreground color to red
Source§

fn on_red(&self) -> BgColorDisplay<'_, Red, Self>

Change the background color to red
Source§

fn green(&self) -> FgColorDisplay<'_, Green, Self>

Change the foreground color to green
Source§

fn on_green(&self) -> BgColorDisplay<'_, Green, Self>

Change the background color to green
Source§

fn yellow(&self) -> FgColorDisplay<'_, Yellow, Self>

Change the foreground color to yellow
Source§

fn on_yellow(&self) -> BgColorDisplay<'_, Yellow, Self>

Change the background color to yellow
Source§

fn blue(&self) -> FgColorDisplay<'_, Blue, Self>

Change the foreground color to blue
Source§

fn on_blue(&self) -> BgColorDisplay<'_, Blue, Self>

Change the background color to blue
Source§

fn magenta(&self) -> FgColorDisplay<'_, Magenta, Self>

Change the foreground color to magenta
Source§

fn on_magenta(&self) -> BgColorDisplay<'_, Magenta, Self>

Change the background color to magenta
Source§

fn purple(&self) -> FgColorDisplay<'_, Magenta, Self>

Change the foreground color to purple
Source§

fn on_purple(&self) -> BgColorDisplay<'_, Magenta, Self>

Change the background color to purple
Source§

fn cyan(&self) -> FgColorDisplay<'_, Cyan, Self>

Change the foreground color to cyan
Source§

fn on_cyan(&self) -> BgColorDisplay<'_, Cyan, Self>

Change the background color to cyan
Source§

fn white(&self) -> FgColorDisplay<'_, White, Self>

Change the foreground color to white
Source§

fn on_white(&self) -> BgColorDisplay<'_, White, Self>

Change the background color to white
Source§

fn default_color(&self) -> FgColorDisplay<'_, Default, Self>

Change the foreground color to the terminal default
Source§

fn on_default_color(&self) -> BgColorDisplay<'_, Default, Self>

Change the background color to the terminal default
Source§

fn bright_black(&self) -> FgColorDisplay<'_, BrightBlack, Self>

Change the foreground color to bright black
Source§

fn on_bright_black(&self) -> BgColorDisplay<'_, BrightBlack, Self>

Change the background color to bright black
Source§

fn bright_red(&self) -> FgColorDisplay<'_, BrightRed, Self>

Change the foreground color to bright red
Source§

fn on_bright_red(&self) -> BgColorDisplay<'_, BrightRed, Self>

Change the background color to bright red
Source§

fn bright_green(&self) -> FgColorDisplay<'_, BrightGreen, Self>

Change the foreground color to bright green
Source§

fn on_bright_green(&self) -> BgColorDisplay<'_, BrightGreen, Self>

Change the background color to bright green
Source§

fn bright_yellow(&self) -> FgColorDisplay<'_, BrightYellow, Self>

Change the foreground color to bright yellow
Source§

fn on_bright_yellow(&self) -> BgColorDisplay<'_, BrightYellow, Self>

Change the background color to bright yellow
Source§

fn bright_blue(&self) -> FgColorDisplay<'_, BrightBlue, Self>

Change the foreground color to bright blue
Source§

fn on_bright_blue(&self) -> BgColorDisplay<'_, BrightBlue, Self>

Change the background color to bright blue
Source§

fn bright_magenta(&self) -> FgColorDisplay<'_, BrightMagenta, Self>

Change the foreground color to bright magenta
Source§

fn on_bright_magenta(&self) -> BgColorDisplay<'_, BrightMagenta, Self>

Change the background color to bright magenta
Source§

fn bright_purple(&self) -> FgColorDisplay<'_, BrightMagenta, Self>

Change the foreground color to bright purple
Source§

fn on_bright_purple(&self) -> BgColorDisplay<'_, BrightMagenta, Self>

Change the background color to bright purple
Source§

fn bright_cyan(&self) -> FgColorDisplay<'_, BrightCyan, Self>

Change the foreground color to bright cyan
Source§

fn on_bright_cyan(&self) -> BgColorDisplay<'_, BrightCyan, Self>

Change the background color to bright cyan
Source§

fn bright_white(&self) -> FgColorDisplay<'_, BrightWhite, Self>

Change the foreground color to bright white
Source§

fn on_bright_white(&self) -> BgColorDisplay<'_, BrightWhite, Self>

Change the background color to bright white
Source§

fn bold(&self) -> BoldDisplay<'_, Self>

Make the text bold
Source§

fn dimmed(&self) -> DimDisplay<'_, Self>

Make the text dim
Source§

fn italic(&self) -> ItalicDisplay<'_, Self>

Make the text italicized
Source§

fn underline(&self) -> UnderlineDisplay<'_, Self>

Make the text underlined
Make the text blink
Make the text blink (but fast!)
Source§

fn reversed(&self) -> ReversedDisplay<'_, Self>

Swap the foreground and background colors
Source§

fn hidden(&self) -> HiddenDisplay<'_, Self>

Hide the text
Source§

fn strikethrough(&self) -> StrikeThroughDisplay<'_, Self>

Cross out the text
Source§

fn color<Color>(&self, color: Color) -> FgDynColorDisplay<'_, Color, Self>
where Color: DynColor,

Set the foreground color at runtime. Only use if you do not know which color will be used at compile-time. If the color is constant, use either OwoColorize::fg or a color-specific method, such as OwoColorize::green, Read more
Source§

fn on_color<Color>(&self, color: Color) -> BgDynColorDisplay<'_, Color, Self>
where Color: DynColor,

Set the background color at runtime. Only use if you do not know what color to use at compile-time. If the color is constant, use either OwoColorize::bg or a color-specific method, such as OwoColorize::on_yellow, Read more
Source§

fn fg_rgb<const R: u8, const G: u8, const B: u8>( &self, ) -> FgColorDisplay<'_, CustomColor<R, G, B>, Self>

Set the foreground color to a specific RGB value.
Source§

fn bg_rgb<const R: u8, const G: u8, const B: u8>( &self, ) -> BgColorDisplay<'_, CustomColor<R, G, B>, Self>

Set the background color to a specific RGB value.
Source§

fn truecolor(&self, r: u8, g: u8, b: u8) -> FgDynColorDisplay<'_, Rgb, Self>

Sets the foreground color to an RGB value.
Source§

fn on_truecolor(&self, r: u8, g: u8, b: u8) -> BgDynColorDisplay<'_, Rgb, Self>

Sets the background color to an RGB value.
Source§

fn style(&self, style: Style) -> Styled<&Self>

Apply a runtime-determined style
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more