chore: migrate from yarn 1 to pnpm - #211
Merged
Merged
Conversation
Replaces yarn 1 workspaces with pnpm. Three latent bugs that yarn's flat node_modules had been masking had to be fixed for a clean install: - Cross-workspace deps used plain semver ranges. pnpm 10+ defaults link-workspace-packages to false, so those resolved from the registry instead of linking locally — test-app-vite was building against the published @docfy/ember-vite. Converted all 9 declarations to the workspace: protocol. - packages/plugin-with-prose/tsconfig.json hardcoded typeRoots to the root node_modules/@types, which only exists under a hoisted layout. Removed the override; TypeScript's default resolution finds the package's own @types. The "types" dir it also listed does not exist. - @docfy/ember-cli imports from 'unist' but declared no @types at all, relying on a sibling's hoisted copy. Declared @types/unist directly. pnpm 11 reads `overrides` from pnpm-workspace.yaml, not package.json, so yarn's `resolutions` pin moved there. Without it @types/unist resolves to 2.0.11, whose generic Node breaks @docfy/ember-cli's compile. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Root scripts move to pnpm's native recursive runner; lerna.json switches npmClient to pnpm and drops the dead command.bootstrap block (lerna 9 has no bootstrap command). Two more phantom dependencies had to be declared before `pnpm compile` would succeed — both were satisfied by accident under yarn's flat tree: - @docfy/ember imports @docfy/core/lib/types in four source files but declared @docfy/core nowhere. Added to dependencies rather than devDependencies because its published declarations/ reference those types, so consumers must be able to resolve them. - @docfy/ember-vite imports from 'unist' in src/types.ts without declaring @types/unist. Added to devDependencies, matching how @docfy/core already declares it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two more phantom dependencies: @docfy/ember-vite and @docfy/ember-cli both import '@eslint/js' in their eslint.config.mjs without declaring it. Every other package in the repo declares it at ^9.32.0; these two relied on yarn hoisting it from a sibling. pnpm also blocks dependency build scripts by default. esbuild's postinstall installs its platform binary (vite cannot build without it) and nx is lerna's task engine, so both are allowed via allowBuilds. core-js 2.x's postinstall only prints a funding banner and stays off. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three tsconfigs hardcoded typeRoots to the repo-root node_modules/@types, which only exists under a hoisted layout. Under pnpm this left @types/jest unresolvable, so @docfy/core's 11 jest suites failed to run with "Cannot find name 'describe'". Each now points at its own package's node_modules/@types. The local "types" entries are preserved. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
pnpm/action-setup precedes setup-node so `cache: pnpm` can resolve the store path, and takes its version from the root packageManager field. Drops `npm install -g yarn` and gains dependency caching. This is the change the migration exists for: yarn 1 fetches from registry.yarnpkg.com, which the runners could not reach; pnpm fetches from registry.npmjs.org. Test steps stay explicit per package rather than `pnpm -r run test`, which would pull in the two browser suites — one of which is red for an unrelated, undiagnosed reason. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Consumer-facing install docs are deliberately left on npm/yarn — how a user installs @docfy/* into their own project is not this repo's concern. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@docfy/ember-vite and @docfy/plugin-with-prose both import eslint-plugin-jest in their eslint.config.mjs without declaring it, relying on yarn hoisting it from @docfy/core. Found by auditing every package's eslint config imports against its declared dependencies rather than fixing them one failure at a time; that audit now reports all imports declared. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Migrates the monorepo from yarn 1 to pnpm: workspace definition, lockfile, task running, and GitHub Actions. Targets
feat/static-docs-export, notmain.Important
The original motivation for this PR has evaporated — read this before merging.
This was opened because CI on #210 failed five consecutive runs at
yarn installwithECONNREFUSED, and yarn 1 fetches fromregistry.yarnpkg.comwhile pnpm fetches fromregistry.npmjs.org. That reasoning fit the evidence, but it was wrong: the yarn installhas since started working again with no change to that branch, so the failure was transient
infrastructure. pnpm is not needed to fix CI.
What this PR earned on its own merits is below: pnpm's strict resolution surfaced nine real
defects that yarn's flat
node_moduleshad been hiding — including that all 11 of@docfy/core's jest suites were silently failing to run, and thattest-app-vitewasbuilding against the published
@docfy/ember-viterather than the local workspace package.That is the case for merging it, and it is a genuine one. But the urgency is gone, so this
is now a judgement call rather than a fix.
The interesting part: 9 latent bugs that yarn's flat
node_moduleswas hidingpnpm's strict resolution surfaced nine real defects. Every one was fixed by declaring the dependency that was actually being relied on — no
shamefully-hoist, nonode-linker=hoisted, no.npmrcat all.Phantom dependencies — imported but never declared, satisfied by accident from a sibling:
@docfy/ember@docfy/coredependencies, notdevDependencies, because its publisheddeclarations/reference those types, so consumers must resolve them.@docfy/ember-cli@types/unist@types/*at all while importing from'unist'.@docfy/ember-vite@types/unist,@eslint/js,eslint-plugin-jest@docfy/ember-cli@eslint/js@docfy/plugin-with-proseeslint-plugin-jestThe eslint ones were found by auditing every package's config imports against its declared deps in one pass, rather than fixing them one failure at a time. That audit now reports clean.
Workspace deps resolved from the registry. All 9 cross-workspace declarations used plain semver (
"^0.11.0"). pnpm 10+ defaultslink-workspace-packagestofalse, so those fetched from npm instead of linking locally —test-app-vitewas building against the published@docfy/ember-vite@0.11.0. Converted toworkspace:^0.11.0. The alternative,link-workspace-packages=true, is exactly the workaround this migration set out to avoid.Three tsconfigs hardcoded
typeRootsto the repo-rootnode_modules/@types, which only exists under a hoisted layout. Under pnpm this left@types/jestunresolvable and all 11 of@docfy/core's jest suites failed to run. Each now points at its own package'snode_modules.pnpm 11 specifics worth knowing
overrideslive inpnpm-workspace.yaml, notpackage.json. Apnpm.overridesblock in the manifest is silently ignored. With it there,@types/unistresolved to2.0.11, whose genericNodebreaks@docfy/ember-cli's compile; moved to the workspace file, it correctly pins2.0.3— preserving what yarn'sresolutionsdid.esbuild's postinstall installs its platform binary (vite cannot build without it) andnxis lerna's engine, so both are allowed viaallowBuilds.core-js2.x only prints a funding banner and stays off.Task running
Root scripts move to pnpm's native recursive runner.
compiledeliberately omits--parallelso it stays topological —@docfy/corebuilds before@docfy/ember-vitetypechecks against itslib/types. lerna is retained solely forversion/publish, withnpmClient: pnpmand the deadcommand.bootstrapblock removed (lerna 9 has nobootstrap).concurrentlyprefixes wentyarn:→pnpm:; verified safe, since concurrently 9.2.0 parses^(npm|yarn|pnpm|bun|node|deno).Verification
pnpm install --frozen-lockfileclean from a fully wiped treepnpm compilegreen after deleting all build outputs@docfy/core: 11 suites, 57 tests — these were failing to run before the tsconfig fix@docfy/ember-vite: 60 teststest-app-vitebuilds and the base PR's static export still works — 15.mdfiles,llms.txtwith its# DocfyH1, all 15 root-relative links resolving. This was the key regression gate.test-app-classicbuilds successfully under strict pnpm — the migration's primary risk, cleared with no hoisting workaroundscore,plugin-with-prose,ember-viteTwo pre-existing failures, verified not caused by this change
packages/emberlint — ano-nested-splattributestemplate violation and a typescript-eslint config conflict (projectandprojectServiceboth set). Neither is a resolution error, and CI has the lint step commented out, so they were never enforced.@docfy/plugin-with-prosejest — 2 snapshot failures. The snapshots predate jest 29'ssnapshotFormatchange (Array [→[); the oldyarn.lockresolved jest 29.7.0 too, so this failed identically before. CI only ranpackages/core, so it went unnoticed.Both are left alone as out of scope.
Needs your action
Netlify. The install phase should switch to pnpm automatically now that
pnpm-lock.yamlis the only lockfile, but the build command, publish directory, and base directory fordocfy-siteanddocfy-test-app-classiclive only in the Netlify UI. If either build command names yarn, that deploy will still fail. No[build]blocks were added here rather than guess at values I cannot see.Before publishing. The
workspace:protocol must be replaced with real ranges at publish time.pnpm publishdoes this automatically; please confirmlerna publishdoes too for this setup, or published manifests would carry unresolvableworkspace:specifiers.🤖 Generated with Claude Code