Skip to content

build: Swapping NPM for PNPM Step 2 of 5#3040

Draft
scotttjob wants to merge 2 commits intoscott_t/pnpm-upgrade-chain-1from
scott_t/pnpm-upgrade-chain-2
Draft

build: Swapping NPM for PNPM Step 2 of 5#3040
scotttjob wants to merge 2 commits intoscott_t/pnpm-upgrade-chain-1from
scott_t/pnpm-upgrade-chain-2

Conversation

@scotttjob
Copy link
Copy Markdown
Contributor

Motivations

  1. We've long wanted to explore pnpm, and between some of the new features they're rolling out, the fact that Storybook is embracing it fully, and some of our internal packages have as well.
  2. This PR Chain is in 5 steps, to make it easier to review.
  3. This second PR is making all the local package change updates that we've gotten away with for a long time because of how npm hoists packages. pnpm doesn't do that work magically, so we have to be much more explicit about how our packages get used.
  4. This PR needs to be reviewed the closest along with the 4th in the chain (the actual PNPM upgrade).

Changes

I've attached codex comments to code directly, for why certain things are changing.

Testing

The application should still work as-is.

Changes can be
tested via Pre-release


In Atlantis we use Github's built in pull request reviews.

"require": "./dist/index.cjs"
}
},
"devDependencies": {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

formatters was building only because Rollup plugins were available via hoisting. pnpm requires them to be declared where they’re used.

"dist/*"
],
"devDependencies": {
"@apollo/react-testing": "^4.0.0",
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

hooks had the same problem as formatters: Rollup plugins and Apollo test utilities were being resolved transitively. pnpm surfaces that as missing direct deps.

],
"dependencies": {
"@floating-ui/react": "^0.27.5",
"@floating-ui/utils": "0.2.11",
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

components was relying on transitive/workspace deps like @jobber/hooks, @apollo/client, @floating-ui/utils, and tslib. pnpm requires those to be explicit because package boundaries are enforced more strictly.

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.

Explicit dependencies 😍

},
"devDependencies": {
"@gorhom/bottom-sheet": "^5.2.8",
"@jobber/hooks": "*",
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

components-native uses @jobber/hooks but wasn’t declaring it itself. pnpm exposed that hidden workspace dependency, so we made it explicit in both dev/peer declarations.

@@ -21,6 +21,7 @@
"visual:test:single": "node scripts/visualTestHelper.mjs test"
},
"dependencies": {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

site was also picking up several runtime/build deps transitively from other packages or the repo root. pnpm makes those missing declarations fail in isolation.

import multiInput from "rollup-plugin-multi-input";
import nodePolyfills from "rollup-plugin-polyfill-node";

const createMultiInput =
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The Rollup multi-input plugin shape is less forgiving once each package resolves its own installed copy. This change makes the config work with the module export shape pnpm exposes.

@@ -2,15 +2,14 @@
module.exports = {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

pnpm’s layout makes deep ../../node_modules/... assumptions brittle. require.resolve(...), explicit roots, and looser transform ignores make Jest resolve packages correctly without hoisting assumptions.

import { DocumentNode } from "@apollo/client";
import { MockedProvider, MockedResponse } from "@apollo/react-testing";
// eslint-disable-next-line import/no-internal-modules
import { MockedProvider } from "@apollo/client/testing";
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@apollo/react-testing was the old/indirect path. With pnpm’s stricter graph, we now import Apollo testing helpers from the package that is actually declared and used.

cursor: pageInfo.endCursor,
},
updateQuery: (prev, { fetchMoreResult }) =>
fetchMoreUpdateQueryHandler(prev, fetchMoreResult, getCollectionByPath),
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Once hooks typechecks against its own resolved Apollo/TS versions, generic inference is stricter. These casts are there to keep fetchMore/subscribeToMore update callbacks type-safe under the newer isolated compile.

const elements = [
...ref.querySelectorAll<HTMLElement>(focusables.join(", ")),
];
const elements = Array.from(
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Minor DOM typing cleanup surfaced by stricter package-local TS checking.

readonly [index: string]: boolean | string | undefined;
}

type KeyComparator = XOR<VerboseKeyComparator, SimpleKeyComparator>;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ts-xor was only needed to express a stricter utility type, but under pnpm isolation that dependency path becomes unnecessary friction. Replacing it with a plain union removes an avoidable type-only coupling.

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.

I really like this especially because I have noticed that XOR is really silly when dealing with array types which useOnKeyDown uses in its parameter keys: KeyComparator[] | KeyComparator

startedInsideRef?: MutableRefObject<boolean> | undefined;
}

export interface UseModalProps {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Explicit hook return types stabilize typechecking once the package is compiled in isolation against its own resolved React/TS types, instead of inheriting broader hoisted inference.

direction: KeyboardAction,
) => void;
}) {
useOnKeyDown((event: KeyboardEvent) => {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Small callback typing cleanup needed after the useOnKeyDown type simplification above.

} from "../../DataList.types";

const defaultLayout = (children: object) => (
<>{children as unknown as React.ReactNode}</>
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

React’s fragment child typing became stricter under the currently resolved types, and pnpm exposed that by removing ambient type leakage. The cast keeps the existing fallback behavior while making the default context value typecheck.

I tried to keep this without a horrible cast, but it tried to unwind half of DataList and I'd rather not dive into a deprecated component.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages bot commented Apr 15, 2026

Deploying atlantis with  Cloudflare Pages  Cloudflare Pages

Latest commit: 24a603d
Status:🚫  Build failed.

View logs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants