{"id":13485007,"url":"https://github.com/Thinkmill/manypkg","last_synced_at":"2025-03-27T17:30:44.731Z","repository":{"id":38418859,"uuid":"201689889","full_name":"Thinkmill/manypkg","owner":"Thinkmill","description":"☔️ An umbrella for your monorepo","archived":false,"fork":false,"pushed_at":"2024-10-17T11:17:31.000Z","size":2058,"stargazers_count":907,"open_issues_count":46,"forks_count":49,"subscribers_count":19,"default_branch":"main","last_synced_at":"2024-10-30T04:54:49.915Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Thinkmill.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-08-10T22:35:22.000Z","updated_at":"2024-10-29T16:56:47.000Z","dependencies_parsed_at":"2023-11-29T11:48:02.965Z","dependency_job_id":"beda9716-d7bc-406b-8769-4b5da89f2ff7","html_url":"https://github.com/Thinkmill/manypkg","commit_stats":{"total_commits":175,"total_committers":33,"mean_commits":5.303030303030303,"dds":0.7257142857142858,"last_synced_commit":"4c32679951b1899b35f1a180934634b167758014"},"previous_names":[],"tags_count":66,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thinkmill%2Fmanypkg","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thinkmill%2Fmanypkg/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thinkmill%2Fmanypkg/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thinkmill%2Fmanypkg/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Thinkmill","download_url":"https://codeload.github.com/Thinkmill/manypkg/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244403940,"owners_count":20447160,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T17:01:42.456Z","updated_at":"2025-03-27T17:30:44.722Z","avatar_url":"https://github.com/Thinkmill.png","language":"TypeScript","readme":"\u003ca href=\"https://www.thinkmill.com.au/\"\u003e\n  \u003cimg src=\".github/assets/manypkg-banner.svg\" alt=\"An umbrella for your monorepo\"\u003e\n  \u003c/br\u003e\n  \u003c/br\u003e\n\u003c/a\u003e\n\u003cp\u003e\n  \u003ca aria-label=\"NPM version\" href=\"https://www.npmjs.com/package/@manypkg/cli.svg\"\u003e\n    \u003cimg alt=\"\" src=\"https://img.shields.io/npm/v/@manypkg/cli.svg?style=for-the-badge\u0026labelColor=0869B8\"\u003e\n  \u003c/a\u003e\n  \u003ca aria-label=\"License\" href=\"#\"\u003e\n    \u003cimg alt=\"\" src=\"https://img.shields.io/npm/l/@manypkg/cli.svg?style=for-the-badge\u0026labelColor=579805\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/@manypkg/cli\"\u003e\n    \u003cimg alt=\"weekly downloads from npm\" src=\"https://img.shields.io/npm/dw/@manypkg/cli.svg?style=for-the-badge\u0026labelColor=30A800\"\u003e\n  \u003c/a\u003e\n  \u003ca aria-label=\"Thinkmill Logo\" href=\"https://www.thinkmill.com.au/open-source?utm_campaign=github-manypkg\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/A%20Thinkmill%20Project-ed0000.svg?style=for-the-badge\u0026logo=data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTg2IiBoZWlnaHQ9IjU4NiIgdmlld0JveD0iMCAwIDU4NiA1ODYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF8xOTk2XzQwNikiPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTU4NiAyOTNDNTg2IDQ1NC44MTkgNDU0LjgxOSA1ODYgMjkzIDU4NkMxMzEuMTgxIDU4NiAwIDQ1NC44MTkgMCAyOTNDMCAxMzEuMTgxIDEzMS4xODEgMCAyOTMgMEM0NTQuODE5IDAgNTg2IDEzMS4xODEgNTg2IDI5M1pNMjA1Ljc3NiAzNTguOTQ0QzE5MS4zNzYgMzU4Ljk0NCAxODUuOTA0IDM1Mi4zMiAxODUuOTA0IDMzNS45MDRWMjYyLjc1MkgyMTQuNDE2VjIzNy42OTZIMTg1LjkwNFYyMDEuMTJIMTUzLjA3MlYyMzcuNjk2SDEyOC41OTJWMjYyLjc1MkgxNTMuMDcyVjM0MC44QzE1My4wNzIgMzcyLjc2OCAxNjYuNjA4IDM4NS43MjggMTk3LjQyNCAzODUuNzI4QzIwMy40NzIgMzg1LjcyOCAyMTAuOTYgMzg0LjU3NiAyMTUuODU2IDM4My4xMzZWMzU3LjUwNEMyMTMuNTUyIDM1OC4zNjggMjA5LjUyIDM1OC45NDQgMjA1Ljc3NiAzNTguOTQ0Wk00MDcuMzc2IDIzNC4yNEMzODUuMiAyMzQuMjQgMzcxLjA4OCAyNDQuMDMyIDM2MC40MzIgMjYwLjczNkMzNTIuOTQ0IDI0My40NTYgMzM3LjM5MiAyMzQuMjQgMzE3LjIzMiAyMzQuMjRDMjk5Ljk1MiAyMzQuMjQgMjg2Ljk5MiAyNDEuMTUyIDI3Ni42MjQgMjU1LjI2NEgyNzYuMDQ4VjIzNy42OTZIMjQ0LjY1NlYzODRIMjc3LjQ4OFYzMDUuNjY0QzI3Ny40ODggMjc3LjQ0IDI4OC43MiAyNjAuNzM2IDMwOC4zMDQgMjYwLjczNkMzMjUuMjk2IDI2MC43MzYgMzM0LjUxMiAyNzIuODMyIDMzNC41MTIgMjkzLjU2OFYzODRIMzY3LjM0NFYzMDUuMDg4QzM2Ny4zNDQgMjc3LjE1MiAzNzguODY0IDI2MC43MzYgMzk4LjE2IDI2MC43MzZDNDE0LjU3NiAyNjAuNzM2IDQyNC42NTYgMjcxLjEwNCA0MjQuNjU2IDI5Ny4wMjRWMzg0SDQ1Ny40ODhWMjkzLjg1NkM0NTcuNDg4IDI1NC40IDQzOC40OCAyMzQuMjQgNDA3LjM3NiAyMzQuMjRaIiBmaWxsPSJ3aGl0ZSIvPgo8L2c+CjxkZWZzPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzE5OTZfNDA2Ij4KPHJlY3Qgd2lkdGg9IjU4NiIgaGVpZ2h0PSI1ODYiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==\u0026labelColor=C60200\u0026locoColor=white\u0026logoWidth=0\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n# Manypkg\n\nManypkg is a linter for `package.json` files in Yarn, Bolt or pnpm monorepos.\n\n## Install\n\n```\nyarn add @manypkg/cli\n```\n\n## Usage\n\n### `manypkg check`\n\n`manypkg check` runs all of the [checks](#checks) against your repo, logs any errors and exits with a code\n\n### `manypkg fix`\n\n`manypkg fix` runs all of the [checks](#checks) against your repo and fixes any of problems that can be fixed.\n\n### `manypkg run \u003cpartial package name or directory\u003e \u003cscript\u003e`\n\n`manypkg run` executes scripts for packages within a monorepo.\n\nAs an example, let's say there are two packages: `@project/package-a` at `packages/pkg-a` and `@project/package-b` at `packages/pkg-b` which both have a `start` script, `manypkg run` can be used like this:\n\n```bash\nyarn manypkg run pkg-a start\nyarn manypkg run a start\nyarn manypkg run package-a start\nyarn manypkg run @project/package-a start\nyarn manypkg run packages/pkg-a start\nyarn manypkg run package-b start\nyarn manypkg run b start\n```\n\nThe following wouldn't work though because the `package` and `pkg` aren't unique among the package names/directories:\n\n```bash\nyarn manypkg run package start\nyarn manypkg run pkg start\n```\n\nIn order to target a package with a name that is a substring of another (for example, `@project/package-a` at `packages/pkg-a` and `@project/package-a-1` at `packages/pkg-a-1`), use the exact package or directory name:\n\n```bash\nyarn manypkg run @project/package-a start\nyarn manypkg run packages/pkg-a start\n```\n\n### `manypkg exec \u003ccli command\u003e`\n\n`manypkg exec` executes a command for all packages within a monorepo.\n\nAs an example, let's say there are two packages which both have a `dist` dir, `manypkg exec` can be used like this:\n\n```bash\nyarn manypkg exec rm -rf dist\n```\n\n## Configuration\n\nManypkg supports a number of options. Options can be provided using the `manypkg` key in your root `package.json` file:\n\n```json\n{\n  \"name\": \"monorepo-root\",\n  \"private\": true,\n  \"manypkg\": {}\n}\n```\n\n### `defaultBranch`\n\nUsed by the [Incorrect `repository` field](#incorrect-repository-field) rule to determine the correct name for repositories. The default value is `master`.\n\n```json\n{\n  \"manypkg\": {\n    \"defaultBranch\": \"master\"\n  }\n}\n```\n\n### `ignoredRules`\n\nUsed to determine which checks/fixes should ignored by the Manypkg cli. The default value is `[]` (all checks/fixes enabled).\n\n```json\n{\n  \"manypkg\": {\n    \"ignoredRules\": []\n  }\n}\n```\n\nTo ignore a rule, find the rule in the [Checks section](#checks) below and add its \"Key\" value to the array. For example, to disable the [External mismatch rule](#external-mismatch):\n\n```json\n{\n  \"manypkg\": {\n    \"ignoredRules\": [\"EXTERNAL_MISMATCH\"]\n  }\n}\n```\n\n### `workspaceProtocol`\n\nUsed to determine whether the `workspace:` protocol for internal packages is required (`require`) or allowed (`allow`). The default value is `allow`.\n\n```json\n{\n  \"manypkg\": {\n    \"workspaceProtocol\": \"allow\"\n  }\n}\n```\n\n## Dictionary\n\n- **private package** - A package that has `private: true`/is not published. It does not refer to a package published to a private registry here.\n- **internal package** - A package that is local/in the repo\n- **external package** - A package that is from a registry like npm\n- **range** - A [node-semver range](https://github.com/npm/node-semver#ranges)\n- **highest range** - The range which has the highest lower bound. If there are multiple ranges with the same highest lower bound, the range with the highest upper bound is the highest range.\n\n## Checks\n\n## External mismatch\n\nKey: `EXTERNAL_MISMATCH`\n\nThe ranges for all dependencies(excluding `peerDependencies`) on external packages should exactly match(`===`). It's important to note that this check does not enforce that only a single version of an external package is installed, only that two versions of an external package will never be installed because they're specified as dependencies of internal packages.\n\n### Why it's a rule\n\nSo that only a single version of an external package will be installed because having multiple versions of the same package can cause confusion and bundle size problems especially with libraries like React that require there to only be a single copy of the library.\n\n### How it's fixed\n\nThe most commonly used range of the dependency is set as the range at every non-peer dependency place it is depended on. If for some reason, every range is used the same amount of times, they'll all be fixed to the highest version.\n\n### Examples\n\n\u003cdetails\u003e\u003csummary\u003eIncorrect example\u003c/summary\u003e\n\n\u003e NOTE: This example uses Yarn Workspaces but this will work the same with Bolt and pnpm\n\n`package.json`\n\n```json\n{\n  \"name\": \"@manypkg-example/repo\",\n  \"version\": \"1.0.0\",\n  \"workspaces\": [\"packages/*\"]\n}\n```\n\n`packages/pkg-a/package.json`\n\n```json\n{\n  \"name\": \"@manypkg-example/pkg-a\",\n  \"version\": \"1.0.0\",\n  \"dependencies\": {\n    \"some-external-package\": \"1.0.0\"\n  }\n}\n```\n\n`packages/pkg-b/package.json`\n\n```json\n{\n  \"name\": \"@manypkg-example/pkg-b\",\n  \"version\": \"1.0.0\",\n  \"dependencies\": {\n    \"some-external-package\": \"2.0.0\"\n  }\n}\n```\n\n`packages/pkg-c/package.json`\n\n```json\n{\n  \"name\": \"@manypkg-example/pkg-c\",\n  \"version\": \"1.0.0\",\n  \"dependencies\": {\n    \"some-external-package\": \"1.0.0\"\n  }\n}\n```\n\nThis example will cause an error because the range `2.0.0` for `some-external-package` specified in `@manypkg-example/pkg-b` is not equal(`===`) to the range `1.0.0` specified in `@manypkg-example/pkg-a` and `@manypkg-example/pkg-c`.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eCorrect example\u003c/summary\u003e\n\n\u003e NOTE: This example uses Yarn Workspaces but this will work the same with Bolt and pnpm\n\n`package.json`\n\n```json\n{\n  \"name\": \"@manypkg-example/repo\",\n  \"version\": \"1.0.0\",\n  \"workspaces\": [\"packages/*\"]\n}\n```\n\n`packages/pkg-a/package.json`\n\n```json\n{\n  \"name\": \"@manypkg-example/pkg-a\",\n  \"version\": \"1.0.0\",\n  \"dependencies\": {\n    \"some-external-package\": \"1.0.0\"\n  }\n}\n```\n\n`packages/pkg-b/package.json`\n\n```json\n{\n  \"name\": \"@manypkg-example/pkg-b\",\n  \"version\": \"1.0.0\",\n  \"dependencies\": {\n    \"some-external-package\": \"1.0.0\"\n  }\n}\n```\n\nThis example will not cause an error because the range `1.0.0` for `some-external-package` specified in `@manypkg-example/pkg-a` is equal(`===`) to the range `1.0.0` specified in `@manypkg-example/pkg-b`.\n\n\u003c/details\u003e\n\n### Ignoring this rule\n\nThere are some cases where you might want to intentionally have conflicts between versions. To do this, you can use something that isn't a valid semver range instead of a range such as a git url or etc. If you'd like a conflicting version of an npm package, you can use `npm:pkg-name@your-range-here` instead of just a range and it will be ignored.\n\n\u003e Note: Do this with care, having different versions of the same package can lead to strange bugs\n\n## Internal mismatch\n\nKey: `INTERNAL_MISMATCH`\n\nThe ranges for all regular dependencies, devDependencies and optionalDependencies(not peerDependencies) on internal packages should include the version of the internal package.\n\n### Why it's a rule\n\nSo that an internal package that depends on another internal package will always get the local version of the internal package rather than a version from the registry because installing internal packages from the registry can be very confusing since you generally expect to get the local version when you depend on an internal package.\n\n### How it's fixed\n\nIf the range is a [caret range](https://github.com/npm/node-semver#caret-ranges-123-025-004) or a [tilde range](https://github.com/npm/node-semver#tilde-ranges-123-12-1) with no other comparators, the range is set as a caret or tilde range respectively with the version of the internal package. If it is any other range, the range is set to the exact version of the internal package.\n\n## Invalid dev and peer dependency relationship\n\nKey: `INVALID_DEV_AND_PEER_DEPENDENCY_RELATIONSHIP`\n\nAll `peerDependencies` should also be specified in `devDependencies` and the range specified in `devDependencies` should be a subset of the range for that dependency in `peerDependencies`.\n\n### Why it's a rule\n\nThis is so that `peerDependencies` are available in the package during development for testing and etc.\n\n### How it's fixed\n\nThe range for the dependency specified in `peerDependencies` is added to `devDependencies` unless the package is already a non-peer dependency elsewhere in the repo in which, that range is used instead.\n\n## Root has prod dependencies\n\nKey: `ROOT_HAS_PROD_DEPENDENCIES`\n\nThe root package should not have any production `dependencies`, instead all dependencies should be in `devDependencies`.\n\n### Why it's a rule\n\nThe root `package.json` of a monorepo is not published so whether a dependency is in `devDependencies` or `dependencies` does not make a difference and having one place to put dependencies in the root means that people do not have to arbitrarily decide where a dependency should go every time they install one. We prefer `devDependencies` because a monorepo root should contain only tooling dependencies.\n\n### How it's fixed\n\nAll `devDependencies` in the root `package.json` are moved to `dependencies`.\n\n## Multiple dependency types\n\nKey: `MULTIPLE_DEPENDENCY_TYPES`\n\nA dependency shouldn't be specified in more than one of `dependencies`, `devDependencies` or `optionalDependencies`.\n\n### How it's fixed\n\nThe dep is removed from `devDependencies` or `optionalDependencies` if it's also in `dependencies`, if it's in `devDependencies` and `optionalDependencies`, it is removed from `dependencies`.\n\n## Invalid package name\n\nKey: `INVALID_PACKAGE_NAME`\n\nThere are rules from npm about what a package name can be and a package will fail to publish if those rules are not met.\n\n### Why it's a rule\n\nAll packages will be published together so some packages may depend on a package which can't be published. Checking for invalid package names prevents this kind of publish failure.\n\n### How it's fixed\n\nThis requires manual fixing as automatically fixing this may lead to valid but incorrect package names.\n\n## Unsorted dependencies\n\nKey: `UNSORTED_DEPENDENCIES`\n\nDependencies in the dependency fields(`dependencies`, `devDependencies`, `peerDependencies`, `optionalDependencies`) should be sorted alphabetically.\n\n### Why it's a rule\n\nWhen you add a package with `yarn add` or etc. dependencies are sorted, and this can cause confusing diffs if the dependencies were not previously sorted.\n\n### How it's fixed\n\nThis is fixed by sorting deps by key alphabetically.\n\n## Incorrect `repository` field\n\nKey: `INCORRECT_REPOSITORY_FIELD`\n\nIf a GitHub repo URL is in the `repository` field in the root `package.json`, all of the packages should have a `repository` field which goes into the directory of the package.\n\n### Why it's a rule\n\nHaving a `repository` field is helpful so there is a link to the source of a package on npm but setting that field on every package and making sure it's correct is error prone and time consuming.\n\n#### How it's fixed\n\nThis is fixed by setting the correct URL.\n\n## `workspace:` protocol required\n\nKey: `WORKSPACE_REQUIRED`\n\nIf `\"workspaceProtocol\": \"require\"` is set in the `manypkg` config in the root `package.json`, all dependencies on internal packages are required to use the `workspace:` protocol.\n\n### Why it's a rule\n\nIf you want to enforce the usage of the `workspace:` protocol.\n\n#### How it's fixed\n\nDependencies are changed to `workspace:^`. Anything else is also allowed after the `workspace:` though.\n\n## License\n\nCopyright (c) 2023 Thinkmill Labs Pty Ltd. Licensed under the MIT License.\n","funding_links":[],"categories":["TypeScript","Repository","others"],"sub_categories":["Monorepo"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FThinkmill%2Fmanypkg","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FThinkmill%2Fmanypkg","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FThinkmill%2Fmanypkg/lists"}