Skip to content

fix: reinstall broken packages in the store#465

Merged
zkochan merged 1 commit into
masterfrom
issue462
Nov 4, 2016
Merged

fix: reinstall broken packages in the store#465
zkochan merged 1 commit into
masterfrom
issue462

Conversation

@zkochan

@zkochan zkochan commented Nov 4, 2016

Copy link
Copy Markdown
Member

Close #462

Comment thread test/install.ts
// TODO: implement
})

test('should reinstall package to the store if it is not in the store.yml', async function (t) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏

@zkochan zkochan merged commit 11db3b8 into master Nov 4, 2016
@zkochan zkochan deleted the issue462 branch November 4, 2016 10:35
pull Bot pushed a commit to dwongdev/pnpm that referenced this pull request May 14, 2026
…slice 3 close-out) (pnpm#465)

Closes out the Slice 3 hoist-algorithm port. Two additions on top of slice 3c's peer-aware hoist:

1. **Multi-round convergence.** The DFS runs in a fixed-point loop — a peer-shadow refusal in one round can be reconsidered in a later round once the blocking ident has moved out of the ancestor chain (or a previously-empty root slot has been filled with a compatible version). Bounded by O(N) rounds since every move is one-way.
2. **`hoistingLimits` enforcement.** The upfront `UnsupportedHoistingLimits` guard is gone; the algorithm now reads `opts.hoisting_limits[root_locator]` and refuses to hoist names listed there. New `AbsorbDecision::Border` variant alongside `Free` / `SameNode` / `Conflict` / `PeerShadow`.

Plus a backfill of behavior-pinning tests ported from `@yarnpkg/nm/tests/hoist.test.ts`.

## How multi-round works

- `hoist_subtree` returns `bool` for whether it moved any node in the current round.
- `hoist_into_root` wraps it in a `loop` that resets `visited` each iteration and exits when a round makes no moves.
- Mirrors upstream `hoistTo`'s `do { hoistGraph(); } while (anotherRoundNeeded)` without the per-candidate `dependsOn` bookkeeping; pacquet's coarse re-walk catches the same unlock cases at the cost of revisiting unchanged nodes.

Canonical case (`multi_round_unlocks_peer_friendly_hoist_after_blocker_moves`): `root → app → {widget(peer: x), x@1}` with root carrying no `x`.

- **Round 1.** Alphabetical iteration visits `widget` first. App supplies `x@1`, root has no `x` → mismatch → `PeerShadow`, widget stays at app. Then `x` is evaluated: `Free`, hoist to root.
- **Round 2.** Reconsider widget. App no longer has `x`; root has `x@1`. No ancestor disagreement → `Free`. Widget hoists.
- **Round 3.** No moves; loop exits.

## How hoistingLimits works

- Computed locator: `"{ident_name}@{reference}"` — for the wrapper's root that's `".@"`, matching pnpm's keying convention.
- Border-name set: `opts.hoisting_limits.get(root_locator)`, defaulting to empty when absent.
- New `AbsorbDecision::Border` is layered between the basic decision and the peer check. Names in the set never hoist; they stay where the lockfile placed them.
- Mirrors upstream's `isHoistBorder` flag set during `cloneTree`.

## Upstream test ports

From `yarnpkg/berry@4287909fa6` `packages/yarnpkg-nm/tests/hoist.test.ts`, expressed via lockfile fixtures so they exercise the wrapper too:

- **`hoisting_limits_keeps_blocked_name_at_parent`** — `should not hoist packages past hoist boundary`.
- **`hoisting_limits_blocks_multiple_names`** — `should not hoist multiple package past nohoist root`.
- **`hoisting_limits_keyed_on_unrelated_importer_is_inert`** — non-interference sanity check.
- **`self_dependency_does_not_loop`** — `should tolerate self-dependencies`.
- **`basic_cyclic_dependency_terminates`** — `should support basic cyclic dependencies`.

## Documented gaps

The `nm_hoist` doc-comment now lists what's deferred *intentionally* rather than "not done yet":

- **Popularity-based ident preference.** Upstream's `buildPreferenceMap` picks the ident with more incoming references when two distinct deps share an alias; pacquet picks the first-visited. Affects the handful of upstream test cases that exercise the tie-break (`should honor package popularity when hoisting`, etc.).
- **Multi-importer workspace hoist trees.** Still guarded upfront by `HoistError::UnsupportedWorkspace`. Workspace-aware hoisting needs per-importer roots and a different output shape.
- **`ExternalSoftLink` descendants.** The wrapper only creates soft-links as zero-children placeholders, so upstream's "only-hoist-when-all-descendants-hoist" rule has nothing to delay today. Tests for `should not hoist portal with unhoistable dependencies` and similar were skipped.

## Upstream reference

Aligned with `yarnpkg/berry@4287909fa6`:

- [`hoist.ts:109-119`](https://github.com/yarnpkg/berry/blob/4287909fa6a0a1ec976a55776bff606864b31990/packages/yarnpkg-nm/sources/hoist.ts#L109-L119) — outer round loop in `hoist`.
- [`hoist.ts:352-367`](https://github.com/yarnpkg/berry/blob/4287909fa6a0a1ec976a55776bff606864b31990/packages/yarnpkg-nm/sources/hoist.ts#L352-L367) — inner round loop in `hoistTo`.
- [`hoist.ts:707`](https://github.com/yarnpkg/berry/blob/4287909fa6a0a1ec976a55776bff606864b31990/packages/yarnpkg-nm/sources/hoist.ts#L707) — `isHoistBorder` flag during `cloneTree`.
github-actions Bot pushed a commit to Eyalm321/pnpm that referenced this pull request May 18, 2026
…slice 3 close-out) (pnpm#465)

Closes out the Slice 3 hoist-algorithm port. Two additions on top of slice 3c's peer-aware hoist:

1. **Multi-round convergence.** The DFS runs in a fixed-point loop — a peer-shadow refusal in one round can be reconsidered in a later round once the blocking ident has moved out of the ancestor chain (or a previously-empty root slot has been filled with a compatible version). Bounded by O(N) rounds since every move is one-way.
2. **`hoistingLimits` enforcement.** The upfront `UnsupportedHoistingLimits` guard is gone; the algorithm now reads `opts.hoisting_limits[root_locator]` and refuses to hoist names listed there. New `AbsorbDecision::Border` variant alongside `Free` / `SameNode` / `Conflict` / `PeerShadow`.

Plus a backfill of behavior-pinning tests ported from `@yarnpkg/nm/tests/hoist.test.ts`.

## How multi-round works

- `hoist_subtree` returns `bool` for whether it moved any node in the current round.
- `hoist_into_root` wraps it in a `loop` that resets `visited` each iteration and exits when a round makes no moves.
- Mirrors upstream `hoistTo`'s `do { hoistGraph(); } while (anotherRoundNeeded)` without the per-candidate `dependsOn` bookkeeping; pacquet's coarse re-walk catches the same unlock cases at the cost of revisiting unchanged nodes.

Canonical case (`multi_round_unlocks_peer_friendly_hoist_after_blocker_moves`): `root → app → {widget(peer: x), x@1}` with root carrying no `x`.

- **Round 1.** Alphabetical iteration visits `widget` first. App supplies `x@1`, root has no `x` → mismatch → `PeerShadow`, widget stays at app. Then `x` is evaluated: `Free`, hoist to root.
- **Round 2.** Reconsider widget. App no longer has `x`; root has `x@1`. No ancestor disagreement → `Free`. Widget hoists.
- **Round 3.** No moves; loop exits.

## How hoistingLimits works

- Computed locator: `"{ident_name}@{reference}"` — for the wrapper's root that's `".@"`, matching pnpm's keying convention.
- Border-name set: `opts.hoisting_limits.get(root_locator)`, defaulting to empty when absent.
- New `AbsorbDecision::Border` is layered between the basic decision and the peer check. Names in the set never hoist; they stay where the lockfile placed them.
- Mirrors upstream's `isHoistBorder` flag set during `cloneTree`.

## Upstream test ports

From `yarnpkg/berry@4287909fa6` `packages/yarnpkg-nm/tests/hoist.test.ts`, expressed via lockfile fixtures so they exercise the wrapper too:

- **`hoisting_limits_keeps_blocked_name_at_parent`** — `should not hoist packages past hoist boundary`.
- **`hoisting_limits_blocks_multiple_names`** — `should not hoist multiple package past nohoist root`.
- **`hoisting_limits_keyed_on_unrelated_importer_is_inert`** — non-interference sanity check.
- **`self_dependency_does_not_loop`** — `should tolerate self-dependencies`.
- **`basic_cyclic_dependency_terminates`** — `should support basic cyclic dependencies`.

## Documented gaps

The `nm_hoist` doc-comment now lists what's deferred *intentionally* rather than "not done yet":

- **Popularity-based ident preference.** Upstream's `buildPreferenceMap` picks the ident with more incoming references when two distinct deps share an alias; pacquet picks the first-visited. Affects the handful of upstream test cases that exercise the tie-break (`should honor package popularity when hoisting`, etc.).
- **Multi-importer workspace hoist trees.** Still guarded upfront by `HoistError::UnsupportedWorkspace`. Workspace-aware hoisting needs per-importer roots and a different output shape.
- **`ExternalSoftLink` descendants.** The wrapper only creates soft-links as zero-children placeholders, so upstream's "only-hoist-when-all-descendants-hoist" rule has nothing to delay today. Tests for `should not hoist portal with unhoistable dependencies` and similar were skipped.

## Upstream reference

Aligned with `yarnpkg/berry@4287909fa6`:

- [`hoist.ts:109-119`](https://github.com/yarnpkg/berry/blob/4287909fa6a0a1ec976a55776bff606864b31990/packages/yarnpkg-nm/sources/hoist.ts#L109-L119) — outer round loop in `hoist`.
- [`hoist.ts:352-367`](https://github.com/yarnpkg/berry/blob/4287909fa6a0a1ec976a55776bff606864b31990/packages/yarnpkg-nm/sources/hoist.ts#L352-L367) — inner round loop in `hoistTo`.
- [`hoist.ts:707`](https://github.com/yarnpkg/berry/blob/4287909fa6a0a1ec976a55776bff606864b31990/packages/yarnpkg-nm/sources/hoist.ts#L707) — `isHoistBorder` flag during `cloneTree`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants