How to use lint-staged in a lerna monorepo to run the same command in all packages?
Asked Answered
M

2

8

I have the following root package.json:

{
  "name": "project",
  "private": true,
  "workspaces": [
    "packages/*"
  ],
  "scripts": {
    "build": "lerna run build",
    "dev": "lerna run start --stream --parallel",
    "format": "yarn prettier --write",
    "prettier": "prettier --ignore-path .gitignore \"**/*.+(ts|tsx|json)\"",
    "test": "lerna run test --"
  },
  "husky": {
    "hooks": {
      "pre-commit": "yarn format && yarn test"
    }
  },
  "devDependencies": {
    "husky": "^4.2.5",
    "lerna": "^3.22.1",
    "prettier": "^2.0.5"
  }
}

The problem is that using this setup, I cannot commit when I am still working on files, to fix this I can make use of the lint-staged module, my question is, how can I set it up so that the commands I currently have still run but only on staged files without installing all the dependencies of the command in every project? The test command might also be a problem since it runs tsc --noEmit in every project, can I force that to only check staged files too?

Menology answered 4/7, 2020 at 13:0 Comment(0)
P
7

There is an alternate solution to this, because as of right now, lint-staged does not play well with lerna commands for testing.

The solution involves using git commands in the package.json to stash files that are untracked and not staged, perform the test, and then after the test reimplement the untracked and files that aren't staged.

The is one exception with this method, is that git see the files changes being brought back as merge conflicts that will need to be manually merged.

Here are the changes to make:

File: ./package.json

{
  "name": "project",
  "private": true,
  "workspaces": [
    "packages/*"
  ],
  "scripts": {
    "build": "lerna run build",
    "dev": "lerna run start --stream --parallel",
    "format": "yarn prettier --write",
    "prettier": "prettier --ignore-path .gitignore \"**/*.+(ts|tsx|json)\"",
    "test": "lerna run test",
    "test:staged": "git stash -k --include-untracked; yarn test; git stash apply;"
  },
  "lint-staged": {
    "packages/**/*.{ts,js,json,md}": [
      "prettier --write"
    ]
  },
  "husky": {
    "hooks": {
      "pre-commit": "yarn format && yarn test:staged"
    }
  },
  "devDependencies": {
    "husky": "^4.2.5",
    "lerna": "^3.22.1",
    "prettier": "^2.0.5"
  }
}

More specifically this line:

{
   "test:staged": "git stash -k --include-untracked; yarn test; git stash apply;"
}
Percussionist answered 6/7, 2020 at 21:26 Comment(2)
I'm just curious, why would the stashed changes being brought back be considered merge conflicts?Repent
I have tried a basic test of this and didn't have any issues. If there were prettier or eslint changes auto-applied in the lint-staged command perhaps you could change the command to add any changes auto-applied to the index before applying the stash i.e. git stash -k --include-untracked; yarn test; git add --all; git stash apply; You probably want to pop the stash as well rather than apply, unless you want to develop a long list of stashes from running this command that is.Repent
B
5

You probably want to install lint-staged in those packages that are currently being developed, not in your root project.

Opinion:

One package may be typescript based, while others may be still flow based, for instance, so you definitely don't want your setting files to be on your root project.

Let's say you have two packages:

packages/
  shared-abc
  app-abc

You will need to install lint-staged on the packages you and your team are working on.

$ npx lerna add --dev lint-staged --scope=@my-abc/app-abc

Then on the packages you installed lint-staged you want to define the configuration file .lintstagedrc, for instance:

{
  "*.{js,jsx,ts,tsx}": ["eslint --fix"]
}

By the way, you probably also want to install eslint, prettier or both on each lerna package as well (not in the root project).

$ npx lerna add --dev eslint --scope=@my-abc/app-abc

The configuration file above tells lint-staged to run eslint --fix against each file that has been staged inside that package.

This approach allows you to have separate .eslintrc.js or .prettierrc files with different linting or prettier settings for your different packages.

If you had installed lint-staged, eslint, prettier, etc in the project root, for each staged file, your git hook (or husky) would probably run eslint --fix with same settings on different packages which may have very specific environment requirements. Your .eslintrc.js would probably be messy and full of overrides.

Conclusion

So after installing lint-staged in your packages, you want your git hook (or husky) to run the following command line with lerna:

npx lerna run lint-staged

Now you still need to add this script to your desired packages' package.json files.

{
...
scripts: {
   ...
   "lint-staged": "lint-staged",      
   ...
}
...
}

So lerna will run the script lint-staged on all packages that has the script lint-staged added to scripts entry on their package.json files.

Beattie answered 4/1, 2022 at 14:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.