• Stars
    star
    882
  • Rank 51,756 (Top 2 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created about 5 years ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

☔️ An umbrella for your monorepo
An umbrella for your monorepo

weekly downloads from npm


Manypkg

Manypkg is a linter for package.json files in Yarn, Bolt or pnpm monorepos.

Install

yarn add @manypkg/cli

Usage

manypkg check

manypkg check runs all of the checks against your repo, logs any errors and exits with a code

manypkg fix

manypkg fix runs all of the checks against your repo and fixes any of problems that can be fixed.

manypkg run <partial package name or directory> <script>

manypkg run executes scripts for packages within a monorepo.

As 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:

yarn manypkg run pkg-a start
yarn manypkg run a start
yarn manypkg run package-a start
yarn manypkg run @project/package-a start
yarn manypkg run packages/pkg-a start
yarn manypkg run package-b start
yarn manypkg run b start

The following wouldn't work though because the package and pkg aren't unique among the package names/directories:

yarn manypkg run package start
yarn manypkg run pkg start

In 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:

yarn manypkg run @project/package-a start
yarn manypkg run packages/pkg-a start

manypkg exec <cli command>

manypkg exec executes a command for all packages within a monorepo.

As an example, let's say there are two packages which both have a dist dir, manypkg exec can be used like this:

yarn manypkg exec rm -rf dist

Dictionary

  • private package - A package that has private: true/is not published. It does not refer to a package published to a private registry here.
  • internal package - A package that is local/in the repo
  • external package - A package that is from a registry like npm
  • range - A node-semver range
  • 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.

Checks

External mismatch

The 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.

Why it's a rule

So 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.

How it's fixed

The 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.

Examples

Incorrect example

NOTE: This example uses Yarn Workspaces but this will work the same with Bolt and pnpm

package.json

{
  "name": "@manypkg-example/repo",
  "version": "1.0.0",
  "workspaces": ["packages/*"]
}

packages/pkg-a/package.json

{
  "name": "@manypkg-example/pkg-a",
  "version": "1.0.0",
  "dependencies": {
    "some-external-package": "1.0.0"
  }
}

packages/pkg-b/package.json

{
  "name": "@manypkg-example/pkg-b",
  "version": "1.0.0",
  "dependencies": {
    "some-external-package": "2.0.0"
  }
}

packages/pkg-c/package.json

{
  "name": "@manypkg-example/pkg-c",
  "version": "1.0.0",
  "dependencies": {
    "some-external-package": "1.0.0"
  }
}

This 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.

Correct example

NOTE: This example uses Yarn Workspaces but this will work the same with Bolt and pnpm

package.json

{
  "name": "@manypkg-example/repo",
  "version": "1.0.0",
  "workspaces": ["packages/*"]
}

packages/pkg-a/package.json

{
  "name": "@manypkg-example/pkg-a",
  "version": "1.0.0",
  "dependencies": {
    "some-external-package": "1.0.0"
  }
}

packages/pkg-b/package.json

{
  "name": "@manypkg-example/pkg-b",
  "version": "1.0.0",
  "dependencies": {
    "some-external-package": "1.0.0"
  }
}

This 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.

Ignoring this rule

There 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.

Note: Do this with care, having different versions of the same package can lead to strange bugs

Internal mismatch

The ranges for all regular dependencies, devDependencies and optionalDependencies(not peerDependencies) on internal packages should include the version of the internal package.

Why it's a rule

So 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.

How it's fixed

If the range is a caret range or a tilde range 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.

Invalid dev and peer dependency relationship

All 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.

Why it's a rule

This is so that peerDependencies are available in the package during development for testing and etc.

How it's fixed

The 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.

Root has devDependencies

The root package should not have any devDependencies, instead all dependencies should be in dependencies

Why it's a rule

The 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.

How it's fixed

All devDependencies in the root package.json are moved to dependencies.

Multiple dependency types

A dependency shouldn't be specified in more than one of dependencies, devDependencies or optionalDependencies.

How it's fixed

The 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.

Invalid package name

There are rules from npm about what a package name can be and a package will fail to publish if those rules are not met.

Why it's a rule

All 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.

How it's fixed

This requires manual fixing as automatically fixing this may lead to valid but incorrect package names.

Unsorted dependencies

Dependencies in the dependency fields(dependencies, devDependencies, peerDependencies, optionalDependencies) should be sorted alphabetically.

Why it's a rule

When 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.

How it's fixed

This is fixed by sorting deps by key alphabetically.

Incorrect repository field

If 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.

Why it's a rule

Having 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.

How it's fixed

This is fixed by setting the correct URL.

License

Copyright (c) 2023 Thinkmill Labs Pty Ltd. Licensed under the MIT License.

More Repositories

1

keystatic

First class CMS experience, TypeScript API, Markdown & YAML/JSON based, no DB
TypeScript
1,110
star
2

react-markings

**Markdown** in <Components/>, <Components/> in **Markdown**
JavaScript
882
star
3

react-conf-app

React Conf 2017 Companion App - built with React Native
JavaScript
798
star
4

monorepo

📦 Thinkmill's Monorepo Style Guide
JavaScript
189
star
5

ts-gql

Write GraphQL queries with a gql tag in TypeScript -> have generated types
TypeScript
163
star
6

reacteu-app

ReactEurope TouchstoneJS Mobile App
Java
161
star
7

monorepo-starter

An example setup of how to do a monorepo, used in our monorepo 'getting started' guide
JavaScript
129
star
8

react-testing

Examples of how to test React.js applications
106
star
9

emery

💎 Polish for the rough parts of TypeScript
TypeScript
82
star
10

hyperterm-spacegray

Spacegray theme for hyperterm
JavaScript
81
star
11

magical-types

🔮 Document React components and other stuff typed with TypeScript magically
TypeScript
77
star
12

jest-expect-contain-deep

Assert deeply nested values in Jest
JavaScript
67
star
13

reacteu-api

ReactEurope KeystoneJS API Server
JavaScript
54
star
14

jest-fixtures

Use file system fixtures in Jest
JavaScript
39
star
15

design-system

🎨 Thinkmill's Design System Style Guide
31
star
16

graphql

🍇 Thinkmill's GraphQL Style Guide
31
star
17

reacteu-assets

24
star
18

changelogs-xyz

A place to see changelogs for any npm package
JavaScript
24
star
19

meetup-alternative

A free, open source, self-hosted alternative to meetup.com
JavaScript
18
star
20

graphql-ts

Type-safety for constructing GraphQL schemas in TypeScript
TypeScript
18
star
21

code-challenge

Monthly code challenges
JavaScript
17
star
22

magical-forms

TypeScript
14
star
23

keystone-forgotten-password

Keystone Password Reset Plugin for Keystone 4.0
JavaScript
13
star
24

markings

📝
TypeScript
12
star
25

keystatic-demo-landing-page

TypeScript
12
star
26

sydjs-keystatic

TypeScript
10
star
27

elemental-ui

TypeScript
10
star
28

pragmatic-forms

A pragmatic approach to forms in React
JavaScript
10
star
29

untitled-docs

📚
TypeScript
9
star
30

manylicense

A node_modules license reporting tool for monorepos that use yarn workspaces.
JavaScript
8
star
31

keystatic-demo-blog

TypeScript
8
star
32

keystatic-examples-knowledge-base

Roff
8
star
33

docsmill

TypeScript
7
star
34

react-conf-app-assets

Assets for the React Conf App
7
star
35

pyn

Rust
5
star
36

parse-toml-stream

Parse TOML files in streams chunked by [sections]
JavaScript
5
star
37

sweet-changelogs

What it says on the tin
JavaScript
4
star
38

keystatic-astro-test

TypeScript
4
star
39

built-with-keystatic

A website showcasing projects built with Keystatic.
Astro
3
star
40

keystone-experiments

JavaScript
3
star
41

node-worker

🎡 A simple, promise-based, worker framework.
JavaScript
3
star
42

auto-approve-action

A GitHub action to automatically approve(and dismiss approval) of PRs based on a condition
TypeScript
3
star
43

badge

"Supported by Thinkmill" badge for READMEs
2
star
44

ui-performance

Testing ui performance
JavaScript
2
star
45

shed

🚲 Find all your bike shedding needs under one roof
JavaScript
2
star
46

barista-slack-integration

Slack integration to make coffee orders easy!
CSS
2
star
47

changesets-publish-slack-action

TypeScript
2
star
48

keystatic-manual-setup-demo

TypeScript
1
star
49

keystatic-starter-portfolio

TypeScript
1
star
50

keystatic-starter-docs-nextjs

TypeScript
1
star
51

keystatic-template-astro

Astro
1
star
52

eslint-config-thinkmill

Thinkmill standard code style config
1
star
53

pingpong-champion

A scoring app
JavaScript
1
star
54

heroku-buildpack-bolt

Shell
1
star
55

sftp-stream-test

Tests whether the response from an SFTP server is the same as the one from downloading a file directly with the request library.
JavaScript
1
star