Repository navigation
DLS-997 chore(deps): add syncpack to enforce one version per package #940
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 18 commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
2d12d3f
chore(deps): add syncpack to enforce one version per package
gamegee bbe10a1
fix(repo-tools): check peers against the lockfile-installed version
gamegee 29844d6
chore(lint): enable @nx/dependency-checks on ui-react, ui-rnative and…
gamegee 6383b5b
chore(deps): bound ui-rnative peers, drop dead Storybook deps, pin pr…
gamegee af2e7a6
chore(deps): keep published lib manifests unchanged
gamegee 47d6b77
refactor(repo-tools): one folder per use case
gamegee 673e029
test(repo-tools): cover the peer dependency check
gamegee 0e64f52
chore(syncpack): lock the on-device Storybook family, ban dead packages
gamegee 0484075
chore(deps): refresh the lockfile in fix:deps
gamegee 4694fbc
fix(repo-tools): accept literal file paths in skill paths globs
gamegee a20e751
docs(skills): add dependency-management
gamegee d71321c
chore(repo-tools): give each unbounded-peer exception a reason
gamegee 06b607e
chore(sandbox-rnative): drop unused @react-native/eslint-config
gamegee bf05e44
chore(deps): drop unused @storybook/test-runner
gamegee 9803088
ci: read the Node version from .mise.toml
gamegee 2c54add
chore(lint): share the @nx/dependency-checks config across published …
gamegee 930d0a8
refactor(repo-tools): group the CI scripts under src/ci/
gamegee 82a6bdb
chore: trim redundant comments
gamegee 7055dbf
ci(setup): drop the redundant empty node-version default
gamegee File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| --- | ||
| name: dependency-management | ||
| description: >- | ||
| Use when adding, removing, upgrading or moving an npm dependency in any | ||
| package.json of the workspace, or when `check:deps`, `check:peer-deps` or | ||
| `@nx/dependency-checks` fails — where a package belongs (root | ||
| devDependencies, a lib's dependencies, or a peer), which range to write, how | ||
| to allow a second version, and when a version plan is needed. | ||
| paths: package.json, libs/*/package.json, apps/*/package.json, internals/*/package.json, .syncpackrc.json | ||
| --- | ||
|
|
||
| # Dependency management | ||
|
|
||
| One version per package across every manifest. The rules are enforced by | ||
| `.syncpackrc.json` (`npm run check:deps`), the peer validator | ||
| (`npm run check:peer-deps`) and `@nx/dependency-checks` in each lib's | ||
| `eslint.config.mjs`. This skill says where a package goes and what to run. | ||
|
|
||
| ## Where a package goes | ||
|
|
||
| | The package is… | Declare it in | Range | | ||
| | --- | --- | --- | | ||
| | Build, test, lint or type tooling | root `devDependencies` | match the neighbors; the lockfile pins the version | | ||
| | Imported by a published lib's shipped code | that lib's `dependencies` only | `^` (enforced), so consumers can dedupe | | ||
| | Provided once by the consumer app (React, React Native, Radix, `@ledgerhq/lumen-design-core`) | the lib's `peerDependencies` **and** root `devDependencies` | peer: a range with an upper bound; root: the version we test with | | ||
| | Used only by a sandbox app | that app's `package.json` | the same specifier as everywhere else | | ||
| | Used only by an `internals/*` project | root `devDependencies` (see `## Internals` in `AGENTS.md`) | as tooling | | ||
|
|
||
| - `@ledgerhq/lumen-*` versions between libs are written by `nx release`. Never | ||
| edit them by hand. Sandbox apps use `*`. | ||
| - A types-only package (`@types/*`) goes in a lib's `dependencies` only when the | ||
| emitted `.d.ts` imports it; list it in that lib's `ignoredDependencies` for | ||
| `@nx/dependency-checks`, with a comment saying why. | ||
|
|
||
| ## Adding or upgrading | ||
|
|
||
| 1. Install where it belongs: `npm install -D <pkg>@<version>` at the root, or | ||
| `npm install <pkg>@<version> -w libs/<lib>`. `.npmrc` already sets | ||
| `legacy-peer-deps`. | ||
| 2. Upgrade a family in one step. The families are the `dependencyGroups` in | ||
| `.syncpackrc.json` (nx, Storybook web, Storybook on-device, react, | ||
| react-native, vitest, tailwind, svgr). Nx goes through `nx migrate` (see the | ||
| `nx` skill). | ||
| 3. `npm run fix:deps` aligns the other manifests and refreshes the lockfile. | ||
| 4. `npm run check:deps && npm run check:peer-deps`, then lint the touched libs. | ||
| 5. Any edit to a `libs/*/package.json` needs a version plan (see the | ||
| `release-plan` skill). | ||
|
|
||
| Changing a lib's `dependencies` or `peerDependencies` reaches consumers. Widening | ||
| a peer range is safe; raising its floor or adding an upper bound can break a | ||
| consumer, so it ships in a planned release, not a dependency chore. | ||
|
|
||
| ## Allowing an exception | ||
|
|
||
| Never loosen a rule for everyone. Each exception names its package and says why: | ||
|
|
||
| - **A second version**: a `versionGroups` entry at the top of `.syncpackrc.json` | ||
| (the first matching group wins) with a `label` giving the reason. | ||
| - **A package that must not come back**: a `versionGroups` entry with | ||
| `isBanned: true` and a `label`. | ||
| - **A peer with no upper bound**: a `{ name, reason }` entry in | ||
| `unboundedPeerExceptions` in | ||
| `internals/repo-tools/src/ci/check-peer-deps/peerDeps.mjs`. The check fails once | ||
| the range is bounded, so remove the entry then. | ||
|
|
||
| ## When a check fails | ||
|
|
||
| | Message | Fix | | ||
| | --- | --- | | ||
| | `check:deps`: `DiffersToHighestOrLowestSemver`, `SemverRangeMismatch` | `npm run fix:deps` | | ||
| | `check:deps`: `IsBanned` | remove the package; its `label` names the replacement | | ||
| | `check:peer-deps`: not in the root devDependencies | add it to the root at the version you test with | | ||
| | `check:peer-deps`: installed … is outside peer … | move the root version into the range; widening the peer is a lib change | | ||
| | `check:peer-deps`: has no upper bound | bound the range, or add an explained exception | | ||
| | `@nx/dependency-checks`: missing / not used | declare what the shipped code imports; remove what it doesn't | | ||
|
|
||
| ## Review checks | ||
|
|
||
| Rules verifiable from a diff. | ||
|
|
||
| | Check | Applies to | Detect | Skip | | ||
| | --- | --- | --- | --- | | ||
| | Tooling in a published lib's `dependencies` | libs | test, build or lint package under `dependencies` | — | | ||
| | Peer without its root devDependency | libs | new `peerDependencies` key absent from root `devDependencies` | `@ledgerhq/lumen-*` | | ||
| | Peer narrowed in a dependency chore | libs | peer floor raised or upper bound added, PR not framed as a release change | — | | ||
| | Unexplained exception | `.syncpackrc.json`, `peerDeps.mjs` | new `versionGroups` entry without a `label`, or exception without a comment | — | | ||
| | Lib manifest change without a version plan | libs | `libs/*/package.json` changed, no matching `.nx/version-plans/` file | — | | ||
| | Lockfile not refreshed | all | `package.json` dependency changed, `package-lock.json` untouched | — | |
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
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
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| { | ||
| "$schema": "./node_modules/syncpack/schema.json", | ||
| "source": [ | ||
| "package.json", | ||
| "apps/*/package.json", | ||
| "internals/*/package.json", | ||
| "libs/*/package.json" | ||
| ], | ||
| "dependencyGroups": [ | ||
| { | ||
| "aliasName": "nx (family)", | ||
| "dependencies": ["nx", "@nx/**"], | ||
| "dependencyTypes": ["dev", "prod"] | ||
| }, | ||
| { | ||
| "aliasName": "storybook (web family)", | ||
| "dependencies": [ | ||
| "storybook", | ||
| "@storybook/addon-a11y", | ||
| "@storybook/addon-docs", | ||
| "@storybook/addon-themes", | ||
| "@storybook/cli", | ||
| "@storybook/react-native-web-vite", | ||
| "@storybook/react-vite", | ||
| "eslint-plugin-storybook" | ||
| ], | ||
| "dependencyTypes": ["dev", "prod"] | ||
| }, | ||
| { | ||
| "aliasName": "storybook (on-device family)", | ||
| "dependencies": [ | ||
| "@storybook/react-native", | ||
| "@storybook/addon-ondevice-*" | ||
| ], | ||
| "dependencyTypes": ["dev", "prod"] | ||
| }, | ||
| { | ||
| "aliasName": "react (family)", | ||
| "dependencies": ["react", "react-dom", "react-test-renderer"], | ||
| "dependencyTypes": ["dev", "prod"] | ||
| }, | ||
| { | ||
| "aliasName": "react-native (family)", | ||
| "dependencies": ["react-native", "@react-native/**"], | ||
| "dependencyTypes": ["dev", "prod"] | ||
| }, | ||
| { | ||
| "aliasName": "vitest (family)", | ||
| "dependencies": ["vitest", "@vitest/**"], | ||
| "dependencyTypes": ["dev", "prod"] | ||
| }, | ||
| { | ||
| "aliasName": "tailwindcss (family)", | ||
| "dependencies": ["tailwindcss", "@tailwindcss/**"], | ||
| "dependencyTypes": ["dev", "prod"] | ||
| }, | ||
| { | ||
| "aliasName": "svgr (family)", | ||
| "dependencies": ["@svgr/**"], | ||
| "dependencyTypes": ["dev", "prod"] | ||
| } | ||
| ], | ||
| "versionGroups": [ | ||
| { | ||
| "label": "Deprecated since Storybook 8: import from storybook/test instead", | ||
| "dependencies": ["@storybook/jest", "@storybook/testing-library"], | ||
| "isBanned": true | ||
| }, | ||
| { | ||
| "label": "Superseded by the @storybook/react-native-web-vite framework", | ||
| "dependencies": ["@storybook/addon-react-native-web"], | ||
| "isBanned": true | ||
| }, | ||
| { | ||
| "label": "Sandbox apps consume the local libs straight from the workspace", | ||
| "packages": ["app-sandbox-*"], | ||
| "dependencies": ["$LOCAL"], | ||
| "dependencyTypes": ["prod", "dev"], | ||
| "pinVersion": "*" | ||
| }, | ||
| { | ||
| "label": "ui-rnative peers on React 19 only: React Native 0.81 requires it, the web libs still support 18", | ||
| "packages": ["@ledgerhq/lumen-ui-rnative"], | ||
| "dependencies": ["react"], | ||
| "dependencyTypes": ["peer"], | ||
| "pinVersion": "^19.0.0" | ||
| }, | ||
| { | ||
| "label": "Peer ranges are identical across the published libs", | ||
| "dependencyTypes": ["peer"] | ||
| }, | ||
| { | ||
| "label": "One version per package (or package family) across dependencies and devDependencies", | ||
| "dependencyTypes": ["!peer"] | ||
| } | ||
| ], | ||
| "semverGroups": [ | ||
| { | ||
| "label": "Local packages keep the exact version nx release writes", | ||
| "dependencies": ["$LOCAL"], | ||
| "isIgnored": true | ||
| }, | ||
| { | ||
| "label": "Published libs use caret ranges so consumers can dedupe", | ||
| "packages": ["@ledgerhq/lumen-*"], | ||
| "dependencyTypes": ["prod"], | ||
| "range": "^" | ||
| } | ||
| ] | ||
| } |
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
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
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.