mirror of
https://github.com/hanzoai/husky.git
synced 2026-09-24 16:59:45 +00:00
@@ -0,0 +1 @@
|
||||
src/* linguist-documentation
|
||||
+4
-2
@@ -1,3 +1,5 @@
|
||||
*.log
|
||||
.DS_Store
|
||||
node_modules
|
||||
tmp
|
||||
ci-post-checkout
|
||||
lib
|
||||
node_modules/
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"skipCI": false,
|
||||
"hooks": {
|
||||
"pre-commit": "npm test",
|
||||
"post-checkout": "rm -f ci-post-checkout && echo \"running CI post-checkout test\" && node scripts/ci-post-checkout-script"
|
||||
}
|
||||
}
|
||||
+11
-2
@@ -1,3 +1,12 @@
|
||||
test/
|
||||
.gitattributes
|
||||
.huskyrc
|
||||
.travis.yml
|
||||
|
||||
appveyor.yml
|
||||
ci-post-checkout
|
||||
preprocessor.js
|
||||
scripts
|
||||
src
|
||||
tsconfig.json
|
||||
tslint.json
|
||||
typings
|
||||
docs-util.js
|
||||
|
||||
+16
-4
@@ -3,9 +3,21 @@ os:
|
||||
- osx
|
||||
language: node_js
|
||||
node_js:
|
||||
- "stable"
|
||||
- "4"
|
||||
- "node"
|
||||
- "6"
|
||||
cache:
|
||||
directories:
|
||||
- "node_modules"
|
||||
script:
|
||||
- npm test
|
||||
- HUSKY_IGNORE_CI=true node bin/install
|
||||
- bash -n .git/hooks/pre-commit
|
||||
# Test post-checkout
|
||||
- npm run devinstall
|
||||
- cat .git/hooks/post-checkout
|
||||
- time git checkout
|
||||
- node scripts/ci-post-checkout-check
|
||||
# Should not fail due to missing script
|
||||
- node scripts/ci-break-path.js
|
||||
- cat .git/hooks/post-checkout
|
||||
- time git checkout
|
||||
# Test uninstall
|
||||
- npm run devuninstall
|
||||
|
||||
+114
-4
@@ -1,10 +1,120 @@
|
||||
# CHANGELOG
|
||||
|
||||
## Unreleased
|
||||
## 1.0.0-rc.14
|
||||
|
||||
* Support `sendemail-validate` hook [#173](https://github.com/typicode/husky/pull/173)
|
||||
* Support `HUSKY_SKIP_INSTALL` environment variable for skipping git hooks installation
|
||||
* Drop `nvm` installed with `brew` support as it's not supported by `nvm` itself (see [creationix/nvm#important-notes](https://github.com/creationix/nvm#important-notes)), `nvm` standard installation is still supported though
|
||||
* Make `--no-verify` message more accurate and only show it for hooks that can be skipped
|
||||
* Improve error messages
|
||||
|
||||
## 1.0.0-rc.13
|
||||
|
||||
* Show a message when Node can't be found in PATH
|
||||
|
||||
## 1.0.0-rc.12
|
||||
|
||||
* Fix exit code when used with unsupported Node version
|
||||
* Update dependencies
|
||||
|
||||
## 1.0.0-rc.11
|
||||
|
||||
* Show a message instead of crashing if Node version is unsupported by Husky
|
||||
|
||||
## 1.0.0-rc.10
|
||||
|
||||
* Fix `HUSKY_GIT_PARAMS` containing only the first Git param
|
||||
|
||||
## 1.0.0-rc.9
|
||||
|
||||
* If `node_modules/husky` is missing, show message but do not crash
|
||||
* Remove and upgrade some `dependencies`
|
||||
|
||||
## 1.0.0-rc.8
|
||||
|
||||
* Add `HUSKY_GIT_STDIN` environment variable for `pre-push`, `pre-receive`, `post-receive` and `post-rewrite` to access arguments passed by Git via stdin.
|
||||
|
||||
## 1.0.0-rc.7
|
||||
|
||||
* Update `cosmiconfig` dependency
|
||||
* Fix `package.json` normalization error
|
||||
|
||||
## 1.0.0-rc.6
|
||||
|
||||
* Fix install error with `pnpm`
|
||||
|
||||
## 1.0.0-rc.5
|
||||
|
||||
* Auto migrate `yorkie` hooks
|
||||
|
||||
## 1.0.0-rc.4
|
||||
|
||||
* Re-enable Git submodule and worktree support
|
||||
|
||||
## 1.0.0-rc.3
|
||||
|
||||
* Re-enable subdirectory support
|
||||
|
||||
## 1.0.0-rc.2
|
||||
|
||||
* Upgrade `run-node` dependency
|
||||
|
||||
## 1.0.0-rc.1
|
||||
|
||||
* Namespace environment variable created by husky
|
||||
* `GIT_PARAMS` becomes `HUSKY_GIT_PARAMS`
|
||||
* Starting also with `1.0.0` versioning
|
||||
|
||||
## 0.15.0-rc.13
|
||||
|
||||
* Revert `GIT_STDIN` for the moment. Needs more testing.
|
||||
|
||||
`rc.10`, `rc.11` and `rc.12` are broken for some GUI clients due to `read` command
|
||||
|
||||
## 0.15.0-rc.9
|
||||
|
||||
* Handle case where `.git/hooks` directory doesn't exit
|
||||
|
||||
## 0.15.0-rc.8
|
||||
|
||||
* Handle case where `v0.14` git hooks wouldn't have been uninstalled
|
||||
|
||||
## 0.15.0-rc.7
|
||||
|
||||
* Move `postinstall` script to `install`
|
||||
* Fix line ending error when running `upgrader` from OS X/Linux
|
||||
|
||||
## 0.15.0-rc.6
|
||||
|
||||
* Fix `[[` error
|
||||
|
||||
## 0.15.0-rc.5
|
||||
|
||||
* Fix error with GitHub Desktop on Windows
|
||||
|
||||
## 0.15.0-rc.4
|
||||
|
||||
* Catch error if `git` command fails
|
||||
|
||||
## 0.15.0-rc.3
|
||||
|
||||
* Fix `husky-upgrade`
|
||||
* Drop `Node 4` support
|
||||
|
||||
## 0.15.0-rc.2
|
||||
|
||||
* Fix install error
|
||||
|
||||
## 0.15.0-rc.1
|
||||
|
||||
* `sendemail-validate` hook [#173](https://github.com/typicode/husky/pull/173)
|
||||
* `HUSKY_SKIP_INSTALL` environment variable for skipping git hooks installation [#178](https://github.com/typicode/husky/pull/178)
|
||||
* `.huskyrc` config [#209](https://github.com/typicode/husky/pull/209)
|
||||
* `pnpm` support
|
||||
* Support environments where `yarn` is the only package manager installed
|
||||
* Move config from `scripts` field to `husky` field
|
||||
* Prefer raw names for Git hooks (`pre-commit` rather than `precommit`)
|
||||
* Drop integrated `nvm` support
|
||||
* To ease upgrade:
|
||||
* Provide `husky-upgrade` command
|
||||
* Add deprecation warning for hooks that are defined in `scripts` (but still run them)
|
||||
|
||||
## 0.14.3
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
# Documentation
|
||||
|
||||
## Supported hooks
|
||||
|
||||
`husky` supports all Git hooks defined [here](https://git-scm.com/docs/githooks).
|
||||
|
||||
Server-side hooks (`pre-receive`, `update` and `post-receive`) aren't supported.
|
||||
|
||||
## Access Git params and stdin
|
||||
|
||||
Git hooks can get parameters via command-line arguments and stdin. Husky makes them accessible via `HUSKY_GIT_PARAMS` and `HUSKY_GIT_STDIN` environment variables.
|
||||
|
||||
```
|
||||
{
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"commit-msg": "echo $HUSKY_GIT_PARAMS"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Disable auto-install
|
||||
|
||||
If you don't want `husky` to automatically install Git hooks, simply set `HUSKY_SKIP_INSTALL` environment variable to `true`.
|
||||
|
||||
```
|
||||
HUSKY_SKIP_INSTALL=true npm install
|
||||
```
|
||||
|
||||
## Multi-package repository (monorepo)
|
||||
|
||||
If you have a multi-package repository, it's __recommended__ to use tools like [lerna](https://github.com/lerna/lerna) and have `husky` installed ONLY in the root `package.json` to act as the source of truth.
|
||||
|
||||
Generally speaking, you should AVOID defining `husky` in multiple `package.json`, as each package would overwrite previous `husky` installations.
|
||||
|
||||
```sh
|
||||
.
|
||||
└── root
|
||||
├── .git
|
||||
├── package.json 🐶 # Add husky here
|
||||
└── packages
|
||||
├── A
|
||||
│ └── package.json
|
||||
├── B
|
||||
│ └── package.json
|
||||
└── C
|
||||
└── package.json
|
||||
```
|
||||
|
||||
```js
|
||||
// root/package.json
|
||||
{
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"husky": "..."
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lerna run test"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Node version management
|
||||
|
||||
If you're on Windows, husky will simply use the version installed globally on your system.
|
||||
|
||||
For macOS and Linux users:
|
||||
- if you're running `git` commands in the terminal, husky will use the version defined in your shell `PATH`. So if you're a `nvm` user, husky will use the version that you've set with `nvm`.
|
||||
- if you're using a GUI client and `nvm`, it may have a different `PATH` and not load `nvm`, in this case the highest `node` version installed by `nvm` will usually be picked. You can also check `~/.node_path` to see which version is used by GUIs and edit if you want to use something else.
|
||||
@@ -1,25 +0,0 @@
|
||||
# Hooks
|
||||
|
||||
Husky supports all git hooks (https://git-scm.com/docs/githooks). Simply add the corresponding `npm script` to your `package.json`.
|
||||
|
||||
| Git hook | npm script |
|
||||
| -------- | ---------- |
|
||||
| applypatch-msg | applypatchmsg |
|
||||
| commit-msg | commitmsg |
|
||||
| post-applypatch | postapplypatch |
|
||||
| post-checkout | postcheckout |
|
||||
| post-commit | postcommit |
|
||||
| post-merge | postmerge |
|
||||
| post-receive | postreceive |
|
||||
| post-rewrite | postrewrite |
|
||||
| post-update | postupdate |
|
||||
| pre-applypatch | preapplypatch |
|
||||
| pre-auto-gc | preautogc |
|
||||
| pre-commit | precommit |
|
||||
| pre-push | prepush |
|
||||
| pre-rebase | prerebase |
|
||||
| pre-receive | prereceive |
|
||||
| prepare-commit-msg | preparecommitmsg |
|
||||
| push-to-checkout | pushtocheckout |
|
||||
| update | update |
|
||||
| sendemail-validate | sendemailvalidate |
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2014
|
||||
Copyright (c) 2017
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
SOFTWARE.
|
||||
|
||||
@@ -1,50 +1,41 @@
|
||||
# husky [](https://www.npmjs.org/package/husky) [](https://www.npmjs.com/package/husky) [](https://travis-ci.org/typicode/husky) [](https://ci.appveyor.com/project/typicode/husky/branch/master)
|
||||
# husky [](https://www.npmjs.org/package/husky) [](https://travis-ci.org/typicode/husky) [](https://ci.appveyor.com/project/typicode/husky/dev)
|
||||
|
||||
> Git hooks made easy
|
||||
|
||||
Husky can prevent bad commit, push and more :dog: _woof!_
|
||||
Husky can prevent bad `git commit`, `git push` and more :dog: _woof!_
|
||||
|
||||
__New beta version available__ [here](https://github.com/typicode/husky/tree/dev). Feedback and bug reports welcome :wink:
|
||||
_You're viewing the documentation for the next version of husky, click [here](https://github.com/typicode/husky/tree/v0.14.3) if you prefer to view docs for the stable version (`v0.14.3`)_
|
||||
|
||||
_9/14: to all the amazing people who have answered the Husky survey I posted a few months ago, thanks so much <3 !_
|
||||
|
||||
<a href="https://www.patreon.com/typicode">
|
||||
<img src="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png" width="160">
|
||||
</a>
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
npm install husky --save-dev
|
||||
npm install husky@next --save-dev
|
||||
```
|
||||
|
||||
```
|
||||
yarn add husky --dev
|
||||
```
|
||||
|
||||
```javascript
|
||||
// Edit package.json
|
||||
```js
|
||||
// package.json
|
||||
{
|
||||
"scripts": {
|
||||
"precommit": "npm test",
|
||||
"prepush": "npm test",
|
||||
"...": "..."
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "npm test",
|
||||
"pre-push": "npm test",
|
||||
"...": "..."
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
git commit -m "Keep calm and commit"
|
||||
```sh
|
||||
git commit -m 'Keep calm and commit'
|
||||
```
|
||||
|
||||
_Existing hooks aren't replaced and you can use [any Git hook](HOOKS.md)._
|
||||
|
||||
_If you're migrating from `ghooks`, simply run `npm uninstall ghooks --save-dev && npm install husky --save-dev` and edit `package.json`. Husky will automatically migrate `ghooks` hooks._
|
||||
|
||||
## Used by
|
||||
|
||||
* [jQuery](https://github.com/jquery/jquery)
|
||||
* [Next.js](https://github.com/zeit/next.js)
|
||||
* [Hyper](https://github.com/zeit/hyper)
|
||||
* [Paper.js](https://github.com/paperjs/paper.js)
|
||||
* [Kibana](https://github.com/elastic/kibana)
|
||||
* [JSON Server](https://github.com/typicode/json-server)
|
||||
* [Hotel](https://github.com/typicode/hotel)
|
||||
* ... and 12000+ [other awesome projects](https://libraries.io/npm/husky/dependent-repositories).
|
||||
Check [documentation](https://github.com/typicode/husky/blob/dev/DOCS.md) for more.
|
||||
|
||||
## Uninstall
|
||||
|
||||
@@ -52,66 +43,55 @@ _If you're migrating from `ghooks`, simply run `npm uninstall ghooks --save-dev
|
||||
npm uninstall husky
|
||||
```
|
||||
|
||||
```sh
|
||||
yarn remove husky
|
||||
## Upgrading from 0.14
|
||||
|
||||
If you're upgrading from `0.14`, simply move your hooks to `husky.hooks`:
|
||||
|
||||
```diff
|
||||
{
|
||||
"scripts": {
|
||||
- "precommit": "npm test"
|
||||
},
|
||||
+ "husky": {
|
||||
+ "hooks": {
|
||||
+ "pre-commit": "npm test"
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
```
|
||||
|
||||
## Tricks
|
||||
Or run the following command which will do the same automatically for you ;)
|
||||
|
||||
<details>
|
||||
|
||||
### Debug hooks easily
|
||||
|
||||
If you need to debug hooks, simply use `npm run <script-name>`. For example:
|
||||
|
||||
```bash
|
||||
npm run precommit
|
||||
```
|
||||
./node_modules/.bin/husky-upgrade
|
||||
```
|
||||
|
||||
### Git GUI clients support
|
||||
Alternatively, you can also use any of the files/formats that are supported by [cosmiconfig](https://github.com/davidtheclark/cosmiconfig). This means that you can place your husky hooks config in a `.huskyrc` file or export them from a `husky.config.js` file as well. Cosmiconfig supports `js`, `json`, and `yaml` file formats.
|
||||
|
||||
If you've installed Node using the [standard installer](https://nodejs.org/en/), [nvm](https://github.com/creationix/nvm) or [homebrew](http://brew.sh/), Git hooks will be executed in GUI applications.
|
||||
## Features
|
||||
|
||||
### Working with multiple version of Node
|
||||
* Keeps existing user hooks
|
||||
* Supports GUI Git clients
|
||||
* Supports all Git hooks (`pre-commit`, `pre-push`, ...)
|
||||
|
||||
If [`nvm`](https://github.com/creationix/nvm) is installed, husky will try to use the `default`/`current` installed Node version or use the project `.nvmrc`.
|
||||
## Used by
|
||||
|
||||
__Tip__ to use the system-installed version of node, `nvm` provides a [`system`](https://github.com/creationix/nvm#system-version-of-node) alias
|
||||
|
||||
### Accessing Git params
|
||||
|
||||
Git params can be found in `GIT_PARAMS` environment variable.
|
||||
|
||||
### Setting a different log level
|
||||
|
||||
By default, husky will run scripts using `--silent` to make the output more readable. If you want to override this, simply pass a different log level to your scripts:
|
||||
|
||||
```json
|
||||
"precommit": "npm run some-script -q"
|
||||
```
|
||||
|
||||
_`-q/--quiet` is equivalent to `--loglevel warn` which is npm default log level._
|
||||
|
||||
### Git submodule and subtree support
|
||||
|
||||
Yes
|
||||
|
||||
### Cygwin support
|
||||
|
||||
Yes
|
||||
|
||||
### Yarn support
|
||||
|
||||
Please use `yarn` `v0.24+`
|
||||
|
||||
</details>
|
||||
* [jQuery](https://github.com/jquery/jquery)
|
||||
* [babel](https://github.com/babel/babel)
|
||||
* [create-react-app](https://github.com/facebookincubator/create-react-app)
|
||||
* [Next.js](https://github.com/zeit/next.js)
|
||||
* [Hyper](https://github.com/zeit/hyper)
|
||||
* [Kibana](https://github.com/elastic/kibana)
|
||||
* [JSON Server](https://github.com/typicode/json-server)
|
||||
* [Hotel](https://github.com/typicode/hotel)
|
||||
* ... and 23k+ [other awesome repos](https://libraries.io/npm/husky/dependent-repositories) :tada:
|
||||
|
||||
## See also
|
||||
|
||||
* [pkg-ok](https://github.com/typicode/pkg-ok) - Prevents publishing modules with bad paths
|
||||
* [pkg-ok](https://github.com/typicode/pkg-ok) - Prevents publishing a module with bad paths or incorrect line endings
|
||||
* [please-upgrade-node](https://github.com/typicode/please-upgrade-node) - Show a message to upgrade Node instead of a stacktrace in your CLIs
|
||||
* [react-fake-props](https://github.com/typicode/react-fake-props) - Fake props for your React tests
|
||||
|
||||
## License
|
||||
|
||||
MIT - [Typicode :cactus:](https://github.com/typicode) - [Patreon](https://www.patreon.com/typicode)
|
||||
MIT - [Typicode :cactus:](https://github.com/typicode) - [Patreon](https://www.patreon.com/typicode) - [Supporters](https://thanks.typicode.com)
|
||||
|
||||
@@ -1,171 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const mkdirp = require('mkdirp')
|
||||
const rimraf = require('rimraf')
|
||||
const tempy = require('tempy')
|
||||
const installFrom = require('../src/install')
|
||||
const uninstallFrom = require('../src/uninstall')
|
||||
|
||||
function install(rootDir, dir) {
|
||||
installFrom(path.join(rootDir, dir))
|
||||
}
|
||||
|
||||
function uninstall(rootDir, dir) {
|
||||
uninstallFrom(path.join(rootDir, dir))
|
||||
}
|
||||
|
||||
function mkdir(rootDir, dir) {
|
||||
mkdirp.sync(path.join(rootDir, dir))
|
||||
}
|
||||
|
||||
function writeFile(dir, filePath, data) {
|
||||
fs.writeFileSync(path.join(dir, filePath), data)
|
||||
}
|
||||
|
||||
function readFile(dir, filePath) {
|
||||
return fs.readFileSync(path.join(dir, filePath), 'utf-8')
|
||||
}
|
||||
|
||||
function exists(dir, filePath) {
|
||||
return fs.existsSync(path.join(dir, filePath))
|
||||
}
|
||||
|
||||
describe('husky', () => {
|
||||
let dir
|
||||
beforeEach(() => (dir = tempy.directory()))
|
||||
afterEach(() => rimraf.sync(dir))
|
||||
|
||||
it('should support basic layout', () => {
|
||||
mkdir(dir, '.git/hooks')
|
||||
mkdir(dir, 'node_modules/husky')
|
||||
|
||||
install(dir, '/node_modules/husky')
|
||||
const hook = readFile(dir, '.git/hooks/pre-commit')
|
||||
|
||||
expect(hook).toMatch('#husky')
|
||||
expect(hook).toMatch('cd "."')
|
||||
expect(hook).toMatch('npm run -s precommit')
|
||||
expect(hook).toMatch('--no-verify')
|
||||
|
||||
const prepareCommitMsg = readFile(dir, '.git/hooks/prepare-commit-msg')
|
||||
expect(prepareCommitMsg).toMatch('cannot be bypassed')
|
||||
|
||||
uninstall(dir, 'node_modules/husky')
|
||||
expect(exists(dir, '.git/hooks/pre-push')).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should support project installed in sub directory', () => {
|
||||
mkdir(dir, '.git/hooks')
|
||||
mkdir(dir, 'A/B/node_modules/husky')
|
||||
|
||||
install(dir, 'A/B/node_modules/husky')
|
||||
const hook = readFile(dir, '.git/hooks/pre-commit')
|
||||
|
||||
expect(hook).toMatch('cd "A/B"')
|
||||
|
||||
uninstall(dir, 'A/B/node_modules/husky')
|
||||
expect(exists(dir, '.git/hooks/pre-push')).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should support git submodule', () => {
|
||||
mkdir(dir, '.git/modules/A/B')
|
||||
mkdir(dir, 'A/B/node_modules/husky')
|
||||
writeFile(dir, 'A/B/.git', 'git: ../../.git/modules/A/B')
|
||||
|
||||
install(dir, 'A/B/node_modules/husky')
|
||||
const hook = readFile(dir, '.git/modules/A/B/hooks/pre-commit')
|
||||
|
||||
expect(hook).toMatch('cd "."')
|
||||
|
||||
uninstall(dir, 'A/B/node_modules/husky')
|
||||
expect(exists(dir, '.git/hooks/pre-push')).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should support git submodule and sub directory', () => {
|
||||
mkdir(dir, '.git/modules/A/B')
|
||||
mkdir(dir, 'A/B/C/node_modules/husky')
|
||||
writeFile(dir, 'A/B/.git', 'git: ../../.git/modules/A/B')
|
||||
|
||||
install(dir, 'A/B/C/node_modules/husky')
|
||||
const hook = readFile(dir, '.git/modules/A/B/hooks/pre-commit')
|
||||
|
||||
expect(hook).toMatch('cd "C"')
|
||||
|
||||
uninstall(dir, 'A/B/app/node_modules/husky')
|
||||
expect(exists(dir, '.git/hooks/pre-push')).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should support git worktrees', () => {
|
||||
mkdir(dir, '.git/worktrees/B')
|
||||
mkdir(dir, 'A/B/node_modules/husky')
|
||||
|
||||
// Git path for worktrees is absolute
|
||||
const absolutePath = path.join(dir, '.git/worktrees/B')
|
||||
writeFile(dir, 'A/B/.git', `git: ${absolutePath}`)
|
||||
|
||||
install(dir, 'A/B/node_modules/husky')
|
||||
const hook = readFile(dir, '.git/worktrees/B/hooks/pre-commit')
|
||||
|
||||
expect(hook).toMatch('cd "."')
|
||||
|
||||
uninstall(dir, 'A/B/node_modules/husky')
|
||||
expect(exists(dir, '.git/hooks/pre-commit')).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should not modify user hooks', () => {
|
||||
mkdir(dir, '.git/hooks')
|
||||
mkdir(dir, 'node_modules/husky')
|
||||
writeFile(dir, '.git/hooks/pre-push', 'foo')
|
||||
|
||||
// Verify that it's not overwritten
|
||||
install(dir, 'node_modules/husky')
|
||||
const hook = readFile(dir, '.git/hooks/pre-push')
|
||||
expect(hook).toBe('foo')
|
||||
|
||||
uninstall(dir, 'node_modules/husky')
|
||||
expect(exists(dir, '.git/hooks/pre-push')).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should not install from /node_modules/A/node_modules', () => {
|
||||
mkdir(dir, '.git/hooks')
|
||||
mkdir(dir, 'node_modules/A/node_modules/husky')
|
||||
|
||||
install(dir, 'node_modules/A/node_modules/husky')
|
||||
expect(exists(dir, '.git/hooks/pre-push')).toBeFalsy()
|
||||
})
|
||||
|
||||
it("should not crash if there's no .git directory", () => {
|
||||
mkdir(dir, 'node_modules/husky')
|
||||
|
||||
expect(() => install(dir, 'node_modules/husky')).not.toThrow()
|
||||
expect(() => uninstall(dir, 'node_modules/husky')).not.toThrow()
|
||||
})
|
||||
|
||||
it('should migrate existing scripts (ghooks)', () => {
|
||||
mkdir(dir, '.git/hooks')
|
||||
mkdir(dir, '/node_modules/husky')
|
||||
writeFile(
|
||||
dir,
|
||||
'.git/hooks/pre-commit',
|
||||
'// Generated by ghooks. Do not edit this file.'
|
||||
)
|
||||
|
||||
install(dir, 'node_modules/husky')
|
||||
const hook = readFile(dir, '.git/hooks/pre-commit')
|
||||
expect(hook).toMatch('husky')
|
||||
expect(hook).not.toMatch('ghooks')
|
||||
})
|
||||
|
||||
it('should migrate existing scripts (pre-commit)', () => {
|
||||
mkdir(dir, '.git/hooks')
|
||||
mkdir(dir, '/node_modules/husky')
|
||||
writeFile(dir, '.git/hooks/pre-commit', './node_modules/pre-commit/hook')
|
||||
|
||||
install(dir, 'node_modules/husky')
|
||||
const hook = readFile(dir, '.git/hooks/pre-commit')
|
||||
expect(hook).toMatch('husky')
|
||||
expect(hook).not.toMatch('./node_modules/pre-commit/hook')
|
||||
})
|
||||
})
|
||||
+19
-4
@@ -1,21 +1,36 @@
|
||||
# Test against the latest version of this Node.js version
|
||||
environment:
|
||||
nodejs_version: "8"
|
||||
nodejs_version: "10"
|
||||
|
||||
# Install scripts. (runs after repo cloning)
|
||||
install:
|
||||
# Get the latest stable version of Node.js or io.js
|
||||
- ps: Install-Product node $env:nodejs_version
|
||||
# install modules
|
||||
- npm install
|
||||
# Install modules
|
||||
- npm ci
|
||||
|
||||
cache:
|
||||
- '%APPDATA%\npm-cache'
|
||||
- node_modules
|
||||
|
||||
# Post-install test scripts.
|
||||
test_script:
|
||||
# Output useful info for debugging.
|
||||
- node --version
|
||||
- npm --version
|
||||
# run tests
|
||||
# Run tests
|
||||
- npm test
|
||||
# Run post-checkout hook test
|
||||
- npm run devinstall
|
||||
- cat .git/hooks/post-checkout
|
||||
- git checkout
|
||||
- node scripts/ci-post-checkout-check
|
||||
# Should not fail due to missing script
|
||||
- node scripts/ci-break-path.js
|
||||
- cat .git/hooks/post-checkout
|
||||
- git checkout
|
||||
# Test uninstall
|
||||
- npm run devuninstall
|
||||
|
||||
# Don't actually build.
|
||||
build: off
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
// Run when package is installed
|
||||
const path = require('path')
|
||||
const isCI = require('is-ci')
|
||||
const installFrom = require('../src/install')
|
||||
|
||||
console.log('husky')
|
||||
|
||||
if (isCI && !process.env.HUSKY_IGNORE_CI) {
|
||||
console.log('CI detected, skipping Git hooks installation')
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
if (process.env.HUSKY_SKIP_INSTALL) {
|
||||
console.log(
|
||||
`env variable HUSKY_SKIP_INSTALL is set to ${process.env
|
||||
.HUSKY_SKIP_INSTALL}, skipping Git hooks installation`
|
||||
)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
console.log('setting up Git hooks')
|
||||
|
||||
const huskyDir = path.join(__dirname, '..')
|
||||
installFrom(huskyDir)
|
||||
@@ -1,11 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
// Run when package is uninstalled
|
||||
const path = require('path')
|
||||
const uninstallFrom = require('../src/uninstall')
|
||||
|
||||
console.log('husky')
|
||||
console.log('uninstalling Git hooks')
|
||||
|
||||
const huskyDir = path.join(__dirname, '..')
|
||||
uninstallFrom(huskyDir)
|
||||
@@ -0,0 +1,24 @@
|
||||
// Used to generate trees for docs.md
|
||||
const formatree = require('formatree')
|
||||
|
||||
console.log(formatree({
|
||||
root: {
|
||||
'.git': 0,
|
||||
'package.json 🐶 # add husky here': 0,
|
||||
packages: {
|
||||
A: { 'package.json': 0 },
|
||||
B: { 'package.json': 0 },
|
||||
C: { 'package.json': 0 },
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
console.log(formatree({
|
||||
root: {
|
||||
'.git': 0,
|
||||
'package.json 🐶 # add husky here': 0,
|
||||
'sub-directory': {
|
||||
'package.json': 0
|
||||
}
|
||||
}
|
||||
}))
|
||||
@@ -0,0 +1,13 @@
|
||||
const pleaseUpgradeNode = require('please-upgrade-node')
|
||||
const pkg = require('./package.json')
|
||||
|
||||
// Node version isn't supported, skip install
|
||||
pleaseUpgradeNode(pkg, {
|
||||
exitCode: 0,
|
||||
message: function(requiredVersion) {
|
||||
return 'Husky requires Node ' + requiredVersion + ', skipping Git hooks installation.'
|
||||
}
|
||||
})
|
||||
|
||||
// Node version is supported, continue
|
||||
require('./lib/installer/bin')
|
||||
Generated
+5177
-1368
File diff suppressed because it is too large
Load Diff
+65
-23
@@ -1,19 +1,30 @@
|
||||
{
|
||||
"name": "husky",
|
||||
"version": "0.14.3",
|
||||
"version": "1.0.0-rc.14",
|
||||
"description": "Prevents bad commit or push (git hooks, pre-commit/precommit, pre-push/prepush, post-merge/postmerge and all that stuff...)",
|
||||
"bin": {
|
||||
"husky-upgrade": "./lib/upgrader/bin.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
"node": ">=6"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "jest",
|
||||
"format": "prettier --single-quote --no-semi --write **/*.js",
|
||||
"install": "node ./bin/install.js",
|
||||
"uninstall": "node ./bin/uninstall.js"
|
||||
"test": "npm run lint && jest",
|
||||
"_install": "node husky install",
|
||||
"preuninstall": "node husky uninstall",
|
||||
"devinstall": "npm run build && npm run _install -- node_modules/husky && node scripts/dev-fix-path",
|
||||
"devuninstall": "npm run build && npm run preuninstall -- node_modules/husky",
|
||||
"build": "del-cli lib && tsc",
|
||||
"version": "jest -u && git add -A src/installer/__tests__/__snapshots__",
|
||||
"postversion": "git push && git push --tags",
|
||||
"prepublishOnly": "npm run test && npm run build && pinst --enable && pkg-ok",
|
||||
"postpublish": "pinst --disable",
|
||||
"lint": "tslint 'src/**/*.ts'",
|
||||
"fix": "npm run lint -- --fix"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/typicode/husky.git"
|
||||
"url": "git+https://github.com/typicode/husky.git"
|
||||
},
|
||||
"keywords": [
|
||||
"git",
|
||||
@@ -27,29 +38,60 @@
|
||||
"prepush",
|
||||
"post-merge",
|
||||
"postmerge",
|
||||
"test"
|
||||
"test",
|
||||
"lint"
|
||||
],
|
||||
"author": "Typicode <[email protected]>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/typicode/husky/issues"
|
||||
},
|
||||
"homepage": "https://github.com/typicode/husky",
|
||||
"devDependencies": {
|
||||
"jest": "^20.0.4",
|
||||
"mkdirp": "^0.5.1",
|
||||
"prettier": "^1.4.4",
|
||||
"rimraf": "^2.6.1",
|
||||
"tempy": "^0.1.0"
|
||||
},
|
||||
"homepage": "https://github.com/typicode/husky#readme",
|
||||
"dependencies": {
|
||||
"is-ci": "^1.0.10",
|
||||
"normalize-path": "^1.0.0",
|
||||
"strip-indent": "^2.0.0"
|
||||
"cosmiconfig": "^5.0.6",
|
||||
"execa": "^0.9.0",
|
||||
"find-up": "^3.0.0",
|
||||
"get-stdin": "^6.0.0",
|
||||
"is-ci": "^1.2.1",
|
||||
"pkg-dir": "^3.0.0",
|
||||
"please-upgrade-node": "^3.1.1",
|
||||
"read-pkg": "^4.0.1",
|
||||
"run-node": "^1.0.0",
|
||||
"slash": "^2.0.0"
|
||||
},
|
||||
"standard": {
|
||||
"env": {
|
||||
"jest": true
|
||||
}
|
||||
"devDependencies": {
|
||||
"@types/execa": "^0.9.0",
|
||||
"@types/find-up": "^2.1.1",
|
||||
"@types/get-stdin": "^5.0.1",
|
||||
"@types/jest": "^23.3.2",
|
||||
"@types/node": "^10.9.4",
|
||||
"del": "^3.0.0",
|
||||
"del-cli": "^1.1.0",
|
||||
"formatree": "^1.0.2",
|
||||
"jest": "^23.6.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
"pinst": "^1.1.1",
|
||||
"pkg-ok": "^2.2.0",
|
||||
"prettier": "^1.14.2",
|
||||
"tempy": "^0.2.1",
|
||||
"ts-jest": "^23.1.4",
|
||||
"tslint": "^5.11.0",
|
||||
"tslint-config-prettier": "^1.15.0",
|
||||
"tslint-plugin-prettier": "^1.3.0",
|
||||
"typescript": "^3.0.3"
|
||||
},
|
||||
"jest": {
|
||||
"transform": {
|
||||
"^.+\\.tsx?$": "ts-jest"
|
||||
},
|
||||
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
|
||||
"moduleFileExtensions": [
|
||||
"ts",
|
||||
"tsx",
|
||||
"js",
|
||||
"jsx",
|
||||
"json",
|
||||
"node"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
const replaceScriptPath = require('./replace-script-path')
|
||||
|
||||
// Replace scriptPath with missing script, which is the same as node_modules/husky being deleted
|
||||
replaceScriptPath('missing/script.js')
|
||||
@@ -0,0 +1,20 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const filename = path.join(__dirname, '../ci-post-checkout')
|
||||
|
||||
if (fs.existsSync(filename)) {
|
||||
const data = fs.readFileSync(filename, 'utf-8')
|
||||
|
||||
if (data.split(' ').length !== 3) {
|
||||
console.log('Not all params were set in HUSKY_GIT_PARAMS')
|
||||
console.log('Got', data)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
fs.unlinkSync(filename)
|
||||
console.log('.git/hooks/post-checkout script has run successfully on CI')
|
||||
} else {
|
||||
console.log('.git/hooks/post-checkout script has failed running on CI')
|
||||
process.exit(1)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const filename = path.join(__dirname, '../ci-post-checkout')
|
||||
|
||||
fs.writeFileSync(filename, process.env.HUSKY_GIT_PARAMS)
|
||||
@@ -0,0 +1,4 @@
|
||||
const replaceScriptPath = require('./replace-script-path')
|
||||
|
||||
// Replace scriptPath with local dev-runner to test locally
|
||||
replaceScriptPath('scripts/dev-runner.js')
|
||||
@@ -0,0 +1,5 @@
|
||||
const path = require('path')
|
||||
|
||||
// Fake script path
|
||||
process.argv[1] = path.join(__dirname, '../node_modules/husky/lib/runner/bin')
|
||||
require('../lib/runner/bin')
|
||||
@@ -0,0 +1,22 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
module.exports = function replaceScriptPath(newPath) {
|
||||
const regExp = /scriptPath="(.*)"/
|
||||
|
||||
fs.readdirSync('.git/hooks')
|
||||
.filter(filename => path.extname(filename) !== '.sample')
|
||||
.map(filename => {
|
||||
console.log('devinstall: update', filename)
|
||||
const absoluteFilename = path.join('.git/hooks', filename)
|
||||
const data = fs.readFileSync(absoluteFilename, 'utf-8')
|
||||
|
||||
if (!regExp.test(data)) {
|
||||
throw new Error("Can't find scriptPath")
|
||||
}
|
||||
|
||||
const updatedData = data.replace(regExp, `scriptPath="${newPath}"`)
|
||||
|
||||
fs.writeFileSync(absoluteFilename, updatedData)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
import * as tempy from 'tempy'
|
||||
import getConf from '../getConf'
|
||||
|
||||
const testConf = { husky: { foo: 'bar' } }
|
||||
|
||||
describe('getConf', () => {
|
||||
it('should return default conf', () => {
|
||||
const tempDir = tempy.directory()
|
||||
fs.writeFileSync(
|
||||
path.join(tempDir, 'package.json'),
|
||||
JSON.stringify(testConf)
|
||||
)
|
||||
|
||||
expect(getConf(tempDir)).toEqual({ skipCI: true, foo: 'bar' })
|
||||
})
|
||||
|
||||
it('should allow overriding default conf', () => {
|
||||
const tempDir = tempy.directory()
|
||||
fs.writeFileSync(
|
||||
path.join(tempDir, 'package.json'),
|
||||
JSON.stringify(testConf)
|
||||
)
|
||||
|
||||
expect(getConf(tempDir)).toEqual({ skipCI: true, foo: 'bar' })
|
||||
})
|
||||
|
||||
it('should support .huskyrc', () => {
|
||||
const tempDir = tempy.directory()
|
||||
fs.writeFileSync(
|
||||
path.join(tempDir, '.huskyrc'),
|
||||
JSON.stringify(testConf.husky)
|
||||
)
|
||||
|
||||
expect(getConf(tempDir)).toEqual({ skipCI: true, foo: 'bar' })
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,17 @@
|
||||
import * as cosmiconfig from 'cosmiconfig'
|
||||
|
||||
interface IConf {
|
||||
skipCI: boolean
|
||||
hooks?: any
|
||||
}
|
||||
|
||||
export default function getConf(dir: string): IConf {
|
||||
const explorer = cosmiconfig('husky')
|
||||
const { config = {} } = explorer.searchSync(dir) || {}
|
||||
|
||||
const defaults: IConf = {
|
||||
skipCI: true
|
||||
}
|
||||
|
||||
return { ...defaults, ...config }
|
||||
}
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
declare module 'is-ci'
|
||||
declare module 'pupa'
|
||||
declare module 'pkg-dir'
|
||||
declare module 'slash'
|
||||
declare module 'read-pkg'
|
||||
@@ -1,21 +0,0 @@
|
||||
[
|
||||
"applypatch-msg",
|
||||
"pre-applypatch",
|
||||
"post-applypatch",
|
||||
"pre-commit",
|
||||
"prepare-commit-msg",
|
||||
"commit-msg",
|
||||
"post-commit",
|
||||
"pre-rebase",
|
||||
"post-checkout",
|
||||
"post-merge",
|
||||
"pre-push",
|
||||
"pre-receive",
|
||||
"update",
|
||||
"post-receive",
|
||||
"post-update",
|
||||
"push-to-checkout",
|
||||
"pre-auto-gc",
|
||||
"post-rewrite",
|
||||
"sendemail-validate"
|
||||
]
|
||||
-115
@@ -1,115 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const findParent = require('./utils/find-parent')
|
||||
const findHooksDir = require('./utils/find-hooks-dir')
|
||||
const getHookScript = require('./utils/get-hook-script')
|
||||
const is = require('./utils/is')
|
||||
const hooks = require('./hooks.json')
|
||||
|
||||
const SKIP = 'SKIP'
|
||||
const UPDATE = 'UPDATE'
|
||||
const MIGRATE_GHOOKS = 'MIGRATE_GHOOKS'
|
||||
const MIGRATE_PRE_COMMIT = 'MIGRATE_PRE_COMMIT'
|
||||
const CREATE = 'CREATE'
|
||||
|
||||
function write(filename, data) {
|
||||
fs.writeFileSync(filename, data)
|
||||
fs.chmodSync(filename, parseInt('0755', 8))
|
||||
}
|
||||
|
||||
function createHook(huskyDir, hooksDir, hookName, cmd) {
|
||||
const filename = path.join(hooksDir, hookName)
|
||||
|
||||
// Assuming that this file is in node_modules/husky
|
||||
const packageDir = path.join(huskyDir, '..', '..')
|
||||
|
||||
// Get project directory
|
||||
// When used in submodule, the project dir is the first .git that is found
|
||||
const projectDir = findParent(huskyDir, '.git')
|
||||
|
||||
// In order to support projects with package.json in a different directory
|
||||
// than .git, find relative path from project directory to package.json
|
||||
const relativePath = path.join('.', path.relative(projectDir, packageDir))
|
||||
|
||||
const hookScript = getHookScript(hookName, relativePath, cmd)
|
||||
|
||||
// Create hooks directory if needed
|
||||
if (!fs.existsSync(hooksDir)) fs.mkdirSync(hooksDir)
|
||||
|
||||
if (!fs.existsSync(filename)) {
|
||||
write(filename, hookScript)
|
||||
return CREATE
|
||||
}
|
||||
|
||||
if (is.ghooks(filename)) {
|
||||
write(filename, hookScript)
|
||||
return MIGRATE_GHOOKS
|
||||
}
|
||||
|
||||
if (is.preCommit(filename)) {
|
||||
write(filename, hookScript)
|
||||
return MIGRATE_PRE_COMMIT
|
||||
}
|
||||
|
||||
if (is.husky(filename)) {
|
||||
write(filename, hookScript)
|
||||
return UPDATE
|
||||
}
|
||||
|
||||
return SKIP
|
||||
}
|
||||
|
||||
function installFrom(huskyDir) {
|
||||
try {
|
||||
const isInSubNodeModule = (huskyDir.match(/node_modules/g) || []).length > 1
|
||||
if (isInSubNodeModule) {
|
||||
return console.log(
|
||||
"trying to install from sub 'node_module' directory,",
|
||||
'skipping Git hooks installation'
|
||||
)
|
||||
}
|
||||
|
||||
const hooksDir = findHooksDir(huskyDir)
|
||||
|
||||
if (hooksDir) {
|
||||
hooks
|
||||
.map(function(hookName) {
|
||||
const npmScriptName = hookName.replace(/-/g, '')
|
||||
return {
|
||||
hookName: hookName,
|
||||
action: createHook(huskyDir, hooksDir, hookName, npmScriptName)
|
||||
}
|
||||
})
|
||||
.forEach(function(item) {
|
||||
switch (item.action) {
|
||||
case MIGRATE_GHOOKS:
|
||||
console.log(`migrating existing ghooks ${item.hookName} script`)
|
||||
break
|
||||
case MIGRATE_PRE_COMMIT:
|
||||
console.log(
|
||||
`migrating existing pre-commit ${item.hookName} script`
|
||||
)
|
||||
break
|
||||
case UPDATE:
|
||||
break
|
||||
case SKIP:
|
||||
console.log(`skipping ${item.hookName} hook (existing user hook)`)
|
||||
break
|
||||
case CREATE:
|
||||
break
|
||||
default:
|
||||
console.error('Unknown action')
|
||||
}
|
||||
})
|
||||
console.log('done\n')
|
||||
} else {
|
||||
console.log("can't find .git directory, skipping Git hooks installation")
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = installFrom
|
||||
@@ -0,0 +1,41 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`hookScript should match snapshot (OS X/Linux) 1`] = `
|
||||
"#!/bin/sh
|
||||
# husky
|
||||
# v1.0.0-rc.14 darwin
|
||||
|
||||
scriptPath=\\"node_modules/husky/lib/runner/bin.js\\"
|
||||
hookName=\`basename \\"$0\\"\`
|
||||
gitParams=\\"$*\\"
|
||||
|
||||
if ! command -v node >/dev/null 2>&1; then
|
||||
echo \\"Can't find node in PATH, trying to find a node binary on your system\\"
|
||||
fi
|
||||
|
||||
if [ -f $scriptPath ]; then
|
||||
node_modules/run-node/run-node $scriptPath $hookName \\"$gitParams\\"
|
||||
else
|
||||
echo \\"Can't find Husky, skipping $hookName hook\\"
|
||||
echo \\"You can reinstall it using 'npm install husky --save-dev' or delete this hook\\"
|
||||
fi
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`hookScript should match snapshot (Windows) 1`] = `
|
||||
"#!/bin/sh
|
||||
# husky
|
||||
# v1.0.0-rc.14 win32
|
||||
|
||||
scriptPath=\\"node_modules/husky/lib/runner/bin.js\\"
|
||||
hookName=\`basename \\"$0\\"\`
|
||||
gitParams=\\"$*\\"
|
||||
|
||||
if [ -f $scriptPath ]; then
|
||||
node $scriptPath $hookName \\"$gitParams\\"
|
||||
else
|
||||
echo \\"Can't find Husky, skipping $hookName hook\\"
|
||||
echo \\"You can reinstall it using 'npm install husky --save-dev' or delete this hook\\"
|
||||
fi
|
||||
"
|
||||
`;
|
||||
@@ -0,0 +1,23 @@
|
||||
import getScript from '../getScript'
|
||||
|
||||
const rootDir = '/home/typicode/project'
|
||||
const huskyDir = '/home/typicode/project/node_modules/husky'
|
||||
|
||||
// On OS X/Linux runNodePath gets resolved to the following value
|
||||
// In order to make the test deterministic on AppVeyor, the value is hardcoded
|
||||
const runNodePath = '/home/typicode/project/node_modules/run-node/run-node'
|
||||
|
||||
describe('hookScript', () => {
|
||||
it('should match snapshot (OS X/Linux)', () => {
|
||||
const script = getScript(rootDir, huskyDir, runNodePath, 'darwin')
|
||||
expect(script).toMatchSnapshot()
|
||||
expect(script).toMatch('run-node')
|
||||
})
|
||||
|
||||
it('should match snapshot (Windows)', () => {
|
||||
const script = getScript(rootDir, huskyDir, runNodePath, 'win32')
|
||||
|
||||
expect(script).toMatchSnapshot()
|
||||
expect(script).not.toMatch('run-node')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,310 @@
|
||||
import * as del from 'del'
|
||||
import * as fs from 'fs'
|
||||
import * as mkdirp from 'mkdirp'
|
||||
import * as os from 'os'
|
||||
import * as path from 'path'
|
||||
import * as tempy from 'tempy'
|
||||
import { install, uninstall } from '../'
|
||||
import { huskyIdentifier } from '../getScript'
|
||||
|
||||
let tempDir
|
||||
|
||||
const pkg = JSON.stringify({})
|
||||
|
||||
function installFrom(
|
||||
huskyDir: string,
|
||||
requireRunNodePath?: string,
|
||||
isCI = false
|
||||
) {
|
||||
install(
|
||||
path.join(tempDir, huskyDir),
|
||||
requireRunNodePath && path.join(tempDir, requireRunNodePath),
|
||||
isCI
|
||||
)
|
||||
}
|
||||
|
||||
function uninstallFrom(dir: string) {
|
||||
uninstall(path.join(tempDir, dir))
|
||||
}
|
||||
|
||||
function mkdir(dir: string) {
|
||||
mkdirp.sync(path.join(tempDir, dir))
|
||||
}
|
||||
|
||||
function writeFile(filename: string, data: string) {
|
||||
fs.writeFileSync(path.join(tempDir, filename), data)
|
||||
}
|
||||
|
||||
function readFile(filename: string) {
|
||||
return fs.readFileSync(path.join(tempDir, filename), 'utf-8')
|
||||
}
|
||||
|
||||
function exists(filename: string) {
|
||||
return fs.existsSync(path.join(tempDir, filename))
|
||||
}
|
||||
|
||||
function expectHookToExist(filename: string) {
|
||||
const hook = readFile(filename)
|
||||
expect(hook).toMatch(huskyIdentifier)
|
||||
}
|
||||
|
||||
describe('install', () => {
|
||||
beforeEach(() => {
|
||||
delete process.env.INIT_CWD
|
||||
tempDir = tempy.directory()
|
||||
})
|
||||
afterEach(() => del(tempDir, { force: true }))
|
||||
|
||||
it('should install and uninstall', () => {
|
||||
const huskyDir = 'node_modules/husky'
|
||||
const hookFilename = '.git/hooks/pre-commit'
|
||||
|
||||
mkdir('.git/hooks')
|
||||
mkdir(huskyDir)
|
||||
writeFile('package.json', pkg)
|
||||
|
||||
installFrom(huskyDir)
|
||||
expectHookToExist(hookFilename)
|
||||
|
||||
uninstallFrom(huskyDir)
|
||||
expect(exists(hookFilename)).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should install and uninstall with pnpm', () => {
|
||||
// pnpm installs husky in node_modules/.../node_modules/husky
|
||||
// this tests ensures that husky will install from this path
|
||||
process.env.INIT_CWD = tempDir
|
||||
const huskyDir =
|
||||
'node_modules/.registry.npmjs.org/husky/1.0.0/node_modules/husky'
|
||||
const hookFilename = '.git/hooks/pre-commit'
|
||||
|
||||
mkdir('.git/hooks')
|
||||
mkdir(huskyDir)
|
||||
writeFile('package.json', pkg)
|
||||
|
||||
installFrom(huskyDir)
|
||||
expectHookToExist(hookFilename)
|
||||
|
||||
uninstallFrom(huskyDir)
|
||||
expect(exists(hookFilename)).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should update existing husky hooks', () => {
|
||||
const huskyDir = 'node_modules/husky'
|
||||
const hookFilename = '.git/hooks/pre-commit'
|
||||
|
||||
mkdir('.git/hooks')
|
||||
mkdir(huskyDir)
|
||||
writeFile('package.json', pkg)
|
||||
|
||||
// Create an existing husky hook
|
||||
writeFile(hookFilename, '# husky\nfoo')
|
||||
|
||||
// Verify that it has been updated
|
||||
installFrom(huskyDir)
|
||||
const hook = readFile(hookFilename)
|
||||
expect(hook).toContain('# husky')
|
||||
expect(hook).not.toContain('foo')
|
||||
})
|
||||
|
||||
it('should update existing husky hooks (v0.14 and earlier)', () => {
|
||||
const huskyDir = 'node_modules/husky'
|
||||
const hookFilename = '.git/hooks/pre-commit'
|
||||
|
||||
mkdir('.git/hooks')
|
||||
mkdir(huskyDir)
|
||||
writeFile('package.json', pkg)
|
||||
|
||||
// Create an existing husky hook
|
||||
writeFile(hookFilename, '#!/bin/sh\n#husky 0.14.3\nfoo')
|
||||
|
||||
// Verify that it has been updated
|
||||
installFrom(huskyDir)
|
||||
const hook = readFile(hookFilename)
|
||||
expect(hook).toContain('# husky')
|
||||
expect(hook).not.toContain('foo')
|
||||
})
|
||||
|
||||
it('should not modify user hooks', () => {
|
||||
const huskyDir = 'node_modules/husky'
|
||||
const hookFilename = '.git/hooks/pre-push'
|
||||
|
||||
mkdir('.git/hooks')
|
||||
mkdir(huskyDir)
|
||||
writeFile('package.json', pkg)
|
||||
writeFile(hookFilename, 'foo')
|
||||
|
||||
// Verify that it's not overwritten
|
||||
installFrom(huskyDir)
|
||||
const hook = readFile(hookFilename)
|
||||
expect(hook).toBe('foo')
|
||||
|
||||
// Verify that it's not deleted
|
||||
uninstallFrom(huskyDir)
|
||||
expect(exists(hookFilename)).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should support package.json installed in sub directory', () => {
|
||||
mkdir('.git/hooks')
|
||||
mkdir('A/B/node_modules/husky')
|
||||
writeFile('A/B/package.json', pkg)
|
||||
|
||||
installFrom('A/B/node_modules/husky', 'A/B/node_modules/.bin/run-node')
|
||||
const hook = readFile('.git/hooks/pre-commit')
|
||||
|
||||
const node =
|
||||
os.platform() !== 'win32' ? 'A/B/node_modules/.bin/run-node' : 'node'
|
||||
expect(hook).toMatch(node)
|
||||
expect(hook).toMatch('A/B/node_modules/husky/lib/runner/bin.js')
|
||||
|
||||
uninstallFrom('A/B/node_modules/husky')
|
||||
expect(exists('.git/hooks/pre-commit')).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should support git submodule', () => {
|
||||
mkdir('.git/modules/A/B/hooks')
|
||||
mkdir('A/B/node_modules/husky')
|
||||
writeFile('A/B/package.json', pkg)
|
||||
writeFile('A/B/.git', 'git: ../../.git/modules/A/B')
|
||||
|
||||
installFrom('A/B/node_modules/husky', 'A/B/node_modules/.bin/run-node')
|
||||
const hook = readFile('.git/modules/A/B/hooks/pre-commit')
|
||||
|
||||
const node =
|
||||
os.platform() !== 'win32' ? 'node_modules/.bin/run-node' : 'node'
|
||||
expect(hook).toMatch(node)
|
||||
expect(hook).toMatch('node_modules/husky/lib/runner/bin.js')
|
||||
|
||||
uninstallFrom('A/B/node_modules/husky')
|
||||
expect(exists('.git/modules/A/B/hooks/pre-commit')).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should support git submodule and sub directory', () => {
|
||||
mkdir('.git/modules/A/B/hooks')
|
||||
mkdir('A/B/C/node_modules/husky')
|
||||
writeFile('A/B/C/package.json', pkg)
|
||||
writeFile('A/B/.git', 'git: ../../.git/modules/A/B')
|
||||
|
||||
installFrom('A/B/C/node_modules/husky', 'A/B/C/node_modules/.bin/run-node')
|
||||
const hook = readFile('.git/modules/A/B/hooks/pre-commit')
|
||||
|
||||
const node =
|
||||
os.platform() !== 'win32' ? 'C/node_modules/.bin/run-node' : 'node'
|
||||
expect(hook).toMatch(node)
|
||||
expect(hook).toMatch('C/node_modules/husky/lib/runner/bin.js')
|
||||
|
||||
uninstallFrom('A/B/C/node_modules/husky')
|
||||
expect(exists('.git/hooks/pre-push')).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should support git worktrees', () => {
|
||||
mkdir('.git/worktrees/B/hooks')
|
||||
mkdir('A/B/node_modules/husky')
|
||||
writeFile('A/B/package.json', pkg)
|
||||
// Git path for worktrees is absolute
|
||||
const absolutePath = path.join(tempDir, '.git/worktrees/B')
|
||||
writeFile('A/B/.git', `git: ${absolutePath}`)
|
||||
|
||||
installFrom('A/B/node_modules/husky', 'A/B/node_modules/.bin/run-node')
|
||||
|
||||
const hook = readFile('.git/worktrees/B/hooks/pre-commit')
|
||||
|
||||
const node =
|
||||
os.platform() !== 'win32' ? 'node_modules/.bin/run-node' : 'node'
|
||||
expect(hook).toMatch(node)
|
||||
expect(hook).toMatch('node_modules/husky/lib/runner/bin.js')
|
||||
|
||||
uninstallFrom('A/B/node_modules/husky')
|
||||
expect(exists('.git/worktrees/B/hooks/pre-commit')).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should not install from /node_modules/A/node_modules', () => {
|
||||
mkdir('.git/hooks')
|
||||
mkdir('node_modules/A/node_modules/husky')
|
||||
writeFile('node_modules/A/package.json', '{}')
|
||||
writeFile('package.json', pkg)
|
||||
|
||||
installFrom('node_modules/A/node_modules/husky')
|
||||
expect(exists('.git/hooks/pre-commit')).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should not install from node_modules using INIT_CWD', () => {
|
||||
process.env.INIT_CWD = 'node_modules'
|
||||
const huskyDir = 'node_modules/husky'
|
||||
const hookFilename = '.git/hooks/pre-commit'
|
||||
mkdir('.git/hooks')
|
||||
writeFile('package.json', pkg)
|
||||
|
||||
installFrom(huskyDir)
|
||||
expect(exists('.git/hooks/pre-commit')).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should migrate existing scripts (ghooks)', () => {
|
||||
const huskyDir = 'node_modules/husky'
|
||||
const hookFilename = '.git/hooks/pre-commit'
|
||||
|
||||
mkdir('.git/hooks')
|
||||
mkdir(huskyDir)
|
||||
writeFile('package.json', pkg)
|
||||
writeFile(hookFilename, '// Generated by ghooks. Do not edit this file.')
|
||||
|
||||
installFrom(huskyDir)
|
||||
const hook = readFile(hookFilename)
|
||||
expect(hook).toMatch(huskyIdentifier)
|
||||
})
|
||||
|
||||
it('should migrate existing scripts (pre-commit)', () => {
|
||||
const huskyDir = 'node_modules/husky'
|
||||
const hookFilename = '.git/hooks/pre-commit'
|
||||
|
||||
mkdir('.git/hooks')
|
||||
mkdir(huskyDir)
|
||||
writeFile('package.json', pkg)
|
||||
writeFile(hookFilename, './node_modules/pre-commit/hook')
|
||||
|
||||
installFrom(huskyDir)
|
||||
const hook = readFile(hookFilename)
|
||||
expect(hook).toMatch(huskyIdentifier)
|
||||
})
|
||||
|
||||
it('should not install hooks if HUSKY_SKIP_INSTALL=true', () => {
|
||||
mkdir('.git/hooks')
|
||||
mkdir('node_modules/husky')
|
||||
writeFile('package.json', pkg)
|
||||
|
||||
process.env.HUSKY_SKIP_INSTALL = 'true'
|
||||
installFrom('node_modules/husky')
|
||||
delete process.env.HUSKY_SKIP_INSTALL
|
||||
expect(exists('.git/hooks/pre-commit')).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should not install hooks in CI server by default', () => {
|
||||
mkdir('.git/hooks')
|
||||
mkdir('node_modules/husky')
|
||||
writeFile('package.json', pkg)
|
||||
|
||||
const isCI = true
|
||||
installFrom('node_modules/husky', undefined, isCI)
|
||||
expect(exists('.git/hooks/pre-commit')).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should install in CI server if skipCI is set to false', () => {
|
||||
mkdir('.git/hooks')
|
||||
mkdir('node_modules/husky')
|
||||
writeFile('package.json', JSON.stringify({ husky: { skipCI: false } }))
|
||||
|
||||
const isCI = true
|
||||
installFrom('node_modules/husky', undefined, isCI)
|
||||
expect(exists('.git/hooks/pre-commit')).toBeTruthy()
|
||||
})
|
||||
|
||||
it("should not crash if .git/hooks doesn't exist", () => {
|
||||
const huskyDir = 'node_modules/husky'
|
||||
|
||||
mkdir('.git')
|
||||
mkdir(huskyDir)
|
||||
writeFile('package.json', pkg)
|
||||
|
||||
expect(() => installFrom(huskyDir)).not.toThrow()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,25 @@
|
||||
import * as isCI from 'is-ci'
|
||||
import * as path from 'path'
|
||||
import { install, uninstall } from './'
|
||||
|
||||
// Just for testing
|
||||
if (process.env.HUSKY_DEBUG) {
|
||||
console.log(process.env.INIT_CWD)
|
||||
}
|
||||
|
||||
// Action can be "install" or "uninstall"
|
||||
// huskyDir is ONLY used in dev, don't use this arguments
|
||||
const [, , action, huskyDir = path.join(__dirname, '../..')] = process.argv
|
||||
|
||||
// Find Git dir
|
||||
try {
|
||||
// Run installer
|
||||
if (action === 'install') {
|
||||
install(huskyDir, undefined, isCI)
|
||||
} else {
|
||||
uninstall(huskyDir)
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`husky > failed to ${action}`)
|
||||
console.log(error.message)
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import * as fs from 'fs'
|
||||
import * as os from 'os'
|
||||
import * as path from 'path'
|
||||
import * as slash from 'slash'
|
||||
|
||||
interface IContext {
|
||||
node: string
|
||||
platform: string
|
||||
script: string
|
||||
version: string
|
||||
}
|
||||
|
||||
// Used to identify scripts created by Husky
|
||||
export const huskyIdentifier = '# husky'
|
||||
|
||||
// Render script
|
||||
const render = ({ node, platform, script, version }: IContext) => `#!/bin/sh
|
||||
${huskyIdentifier}
|
||||
# v${version} ${platform}
|
||||
|
||||
scriptPath="${script}.js"
|
||||
hookName=\`basename "$0"\`
|
||||
gitParams="$*"
|
||||
${
|
||||
platform !== 'win32'
|
||||
? `
|
||||
if ! command -v node >/dev/null 2>&1; then
|
||||
echo "Can't find node in PATH, trying to find a node binary on your system"
|
||||
fi
|
||||
`
|
||||
: ''
|
||||
}
|
||||
if [ -f $scriptPath ]; then
|
||||
${node} $scriptPath $hookName "$gitParams"
|
||||
else
|
||||
echo "Can't find Husky, skipping $hookName hook"
|
||||
echo "You can reinstall it using 'npm install husky --save-dev' or delete this hook"
|
||||
fi
|
||||
`
|
||||
|
||||
/**
|
||||
* @param rootDir - e.g. /home/typicode/project/
|
||||
* @param huskyDir - e.g. /home/typicode/project/node_modules/husky/
|
||||
* @param requireRunNodePath - path to run-node resolved by require e.g. /home/typicode/project/node_modules/.bin/run-node
|
||||
* @param platform - platform husky installer is running on (used to produce win32 specific script)
|
||||
*/
|
||||
export default function(
|
||||
rootDir: string,
|
||||
huskyDir: string,
|
||||
requireRunNodePath: string,
|
||||
// Additional param used for testing only
|
||||
platform: string = os.platform()
|
||||
) {
|
||||
const runNodePath = slash(path.relative(rootDir, requireRunNodePath))
|
||||
|
||||
// On Windows do not rely on run-node
|
||||
const node = platform === 'win32' ? 'node' : runNodePath
|
||||
|
||||
const { version } = JSON.parse(
|
||||
fs.readFileSync(path.join(__dirname, '../../package.json'), 'utf-8')
|
||||
)
|
||||
|
||||
const script = slash(
|
||||
path.join(path.relative(rootDir, huskyDir), 'lib/runner/bin')
|
||||
)
|
||||
|
||||
return render({ node, platform, script, version })
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
import * as findUp from 'find-up'
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
import * as pkgDir from 'pkg-dir'
|
||||
import getConf from '../getConf'
|
||||
import getScript from './getScript'
|
||||
import { isGhooks, isHusky, isPreCommit, isYorkie } from './is'
|
||||
import resolveGitDir from './resolveGitDir'
|
||||
|
||||
const hookList = [
|
||||
'applypatch-msg',
|
||||
'pre-applypatch',
|
||||
'post-applypatch',
|
||||
'pre-commit',
|
||||
'prepare-commit-msg',
|
||||
'commit-msg',
|
||||
'post-commit',
|
||||
'pre-rebase',
|
||||
'post-checkout',
|
||||
'post-merge',
|
||||
'pre-push',
|
||||
'pre-receive',
|
||||
'update',
|
||||
'post-receive',
|
||||
'post-update',
|
||||
'push-to-checkout',
|
||||
'pre-auto-gc',
|
||||
'post-rewrite',
|
||||
'sendemail-validate'
|
||||
]
|
||||
|
||||
function writeHook(filename: string, script: string) {
|
||||
fs.writeFileSync(filename, script, 'utf-8')
|
||||
fs.chmodSync(filename, parseInt('0755', 8))
|
||||
}
|
||||
|
||||
function createHook(filename: string, script: string) {
|
||||
// Get name, used for logging
|
||||
const name = path.basename(filename)
|
||||
|
||||
// Check if hook exist
|
||||
if (fs.existsSync(filename)) {
|
||||
const hook = fs.readFileSync(filename, 'utf-8')
|
||||
|
||||
// Migrate
|
||||
if (isGhooks(hook)) {
|
||||
console.log(`migrating existing ghooks script: ${name}`)
|
||||
return writeHook(filename, script)
|
||||
}
|
||||
|
||||
// Migrate
|
||||
if (isPreCommit(hook)) {
|
||||
console.log(`migrating existing pre-commit script: ${name}`)
|
||||
return writeHook(filename, script)
|
||||
}
|
||||
|
||||
// Update
|
||||
if (isHusky(hook) || isYorkie(hook)) {
|
||||
return writeHook(filename, script)
|
||||
}
|
||||
|
||||
// Skip
|
||||
console.log(`skipping existing user hook: ${name}`)
|
||||
return
|
||||
}
|
||||
|
||||
// Create hook if it doesn't exist
|
||||
writeHook(filename, script)
|
||||
}
|
||||
|
||||
function createHooks(filenames: string[], script: string) {
|
||||
filenames.forEach(filename => createHook(filename, script))
|
||||
}
|
||||
|
||||
function canRemove(filename: string): boolean {
|
||||
if (fs.existsSync(filename)) {
|
||||
const data = fs.readFileSync(filename, 'utf-8')
|
||||
return isHusky(data)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
function removeHook(filename: string) {
|
||||
fs.unlinkSync(filename)
|
||||
}
|
||||
|
||||
function removeHooks(filenames: string[]) {
|
||||
filenames.filter(canRemove).forEach(removeHook)
|
||||
}
|
||||
|
||||
// This prevents the case where someone would want to debug a node_module that has
|
||||
// husky as devDependency and run npm install from node_modules directory
|
||||
function isInNodeModules(dir: string) {
|
||||
// INIT_CWD holds the full path you were in when you ran npm install (supported also by yarn and pnpm)
|
||||
// See https://docs.npmjs.com/cli/run-script
|
||||
if (process.env.INIT_CWD) {
|
||||
return process.env.INIT_CWD.indexOf('node_modules') !== -1
|
||||
}
|
||||
|
||||
// Old technique
|
||||
return (dir.match(/node_modules/g) || []).length > 1
|
||||
}
|
||||
|
||||
function getHooks(gitDir: string): string[] {
|
||||
const gitHooksDir = path.join(gitDir, 'hooks')
|
||||
return hookList.map(hookName => path.join(gitHooksDir, hookName))
|
||||
}
|
||||
|
||||
/**
|
||||
* @param huskyDir - e.g. /home/typicode/project/node_modules/husky/
|
||||
* @param requireRunNodePath - path to run-node resolved by require e.g. /home/typicode/project/node_modules/.bin/run-node
|
||||
* @param isCI - true if running in CI
|
||||
*/
|
||||
export function install(
|
||||
huskyDir: string,
|
||||
requireRunNodePath: string = require.resolve('.bin/run-node'),
|
||||
isCI: boolean
|
||||
) {
|
||||
console.log('husky > setting up git hooks')
|
||||
|
||||
// First directory containing user's package.json
|
||||
const userPkgDir = pkgDir.sync(path.join(huskyDir, '..'))
|
||||
// Get conf from package.json or .huskyrc
|
||||
const conf = getConf(userPkgDir)
|
||||
// Get directory containing .git directory or in the case of Git submodules, the .git file
|
||||
const gitDirOrFile = findUp.sync('.git', { cwd: userPkgDir })
|
||||
// Resolve git directory (e.g. .git/ or .git/modules/path/to/submodule)
|
||||
const resolvedGitDir = resolveGitDir(userPkgDir)
|
||||
|
||||
// Checks
|
||||
if (gitDirOrFile === null) {
|
||||
console.log("Can't find .git, skipping Git hooks installation.")
|
||||
console.log(
|
||||
"Please check that you're in a cloned repository",
|
||||
"or run 'git init' to create an empty Git repository and reinstall husky."
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if (resolvedGitDir === null) {
|
||||
console.log(
|
||||
"Can't find resolved .git directory, skipping Git hooks installation."
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if (process.env.HUSKY_SKIP_INSTALL === 'true') {
|
||||
console.log(
|
||||
"HUSKY_SKIP_INSTALL environment variable is set to 'true',",
|
||||
'skipping Git hooks installation.'
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if (isCI && conf.skipCI) {
|
||||
console.log('CI detected, skipping Git hooks installation.')
|
||||
return
|
||||
}
|
||||
|
||||
if (userPkgDir === null) {
|
||||
console.log("Can't find package.json, skipping Git hooks installation.")
|
||||
console.log(
|
||||
'Please check that your project has a package.json or create it and reinstall husky.'
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if (isInNodeModules(huskyDir)) {
|
||||
console.log(
|
||||
'Trying to install from node_modules directory, skipping Git hooks installation.'
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if (!fs.existsSync(path.join(resolvedGitDir, 'hooks'))) {
|
||||
console.log(
|
||||
`Can't find hooks directory in ${resolvedGitDir}, skipping Git hooks installation.`
|
||||
)
|
||||
console.log(
|
||||
`Please create ${path.join(
|
||||
resolvedGitDir,
|
||||
'hooks'
|
||||
)} directory and reinstall husky.`
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Create hooks
|
||||
// Get root dir based on the first .git directory of file found
|
||||
const rootDir = path.dirname(gitDirOrFile)
|
||||
|
||||
const hooks = getHooks(resolvedGitDir)
|
||||
const script = getScript(rootDir, huskyDir, requireRunNodePath)
|
||||
createHooks(hooks, script)
|
||||
|
||||
console.log(`husky > done`)
|
||||
}
|
||||
|
||||
export function uninstall(huskyDir: string) {
|
||||
console.log('husky > uninstalling git hooks')
|
||||
const userPkgDir = pkgDir.sync(path.join(huskyDir, '..'))
|
||||
const resolvedGitDir = resolveGitDir(userPkgDir)
|
||||
|
||||
if (resolvedGitDir === null) {
|
||||
console.log(
|
||||
"Can't find resolved .git directory, skipping Git hooks uninstallation."
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if (isInNodeModules(huskyDir)) {
|
||||
console.log(
|
||||
'Trying to uninstall from node_modules directory, skipping Git hooks uninstallation.'
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Remove hooks
|
||||
const hooks = getHooks(resolvedGitDir)
|
||||
removeHooks(hooks)
|
||||
|
||||
console.log('husky > done')
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { huskyIdentifier } from './getScript'
|
||||
|
||||
export function isHusky(data: string): boolean {
|
||||
// v0.14 and prior used #husky as an identifier.
|
||||
// Just in case some previous hooks weren't correctly uninstalled,
|
||||
// and for a better transition this will allow v0.15+ to uninstall them as well.
|
||||
const previousHuskyIdentifier = '#husky'
|
||||
return (
|
||||
data.indexOf(huskyIdentifier) !== -1 ||
|
||||
data.indexOf(previousHuskyIdentifier) !== -1
|
||||
)
|
||||
}
|
||||
|
||||
export function isYorkie(data: string): boolean {
|
||||
return data.indexOf('#yorkie') !== -1
|
||||
}
|
||||
|
||||
export function isGhooks(data: string): boolean {
|
||||
return data.indexOf('// Generated by ghooks. Do not edit this file.') !== -1
|
||||
}
|
||||
|
||||
export function isPreCommit(data: string): boolean {
|
||||
return data.indexOf('./node_modules/pre-commit/hook') !== -1
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import * as findUp from 'find-up'
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
|
||||
export default function(cwd: string): string | null {
|
||||
const foundPath = findUp.sync('.git', { cwd })
|
||||
|
||||
if (foundPath) {
|
||||
const stats = fs.lstatSync(foundPath)
|
||||
|
||||
// If it's a .git file resolve path
|
||||
if (stats.isFile()) {
|
||||
// Expect following format
|
||||
// git: pathToGit
|
||||
// On Windows pathToGit can contain ':' (example "gitdir: C:/Some/Path")
|
||||
const gitFileData = fs.readFileSync(foundPath, 'utf-8')
|
||||
const resolvedGitDir = gitFileData
|
||||
.split(':')
|
||||
.slice(1)
|
||||
.join(':')
|
||||
.trim()
|
||||
return path.resolve(path.dirname(foundPath), resolvedGitDir)
|
||||
}
|
||||
|
||||
// Else return path to .git directory
|
||||
return foundPath
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
import * as execa from 'execa'
|
||||
import * as fs from 'fs'
|
||||
import * as mkdirp from 'mkdirp'
|
||||
import * as path from 'path'
|
||||
import * as tempy from 'tempy'
|
||||
import index from '../'
|
||||
|
||||
let spy
|
||||
|
||||
function getScriptPath(dir) {
|
||||
return path.join(dir, 'node_modules/husky/runner/index.js')
|
||||
}
|
||||
|
||||
describe('run', () => {
|
||||
beforeEach(() => {
|
||||
spy = jest.spyOn(execa, 'shellSync')
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
spy.mockReset()
|
||||
spy.mockRestore()
|
||||
})
|
||||
|
||||
it('should run working command and return 0 status', async () => {
|
||||
const dir = tempy.directory()
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(dir, 'package.json'),
|
||||
JSON.stringify({
|
||||
husky: {
|
||||
hooks: {
|
||||
'pre-commit': 'echo success'
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
const status = await index([, getScriptPath(dir), 'pre-commit'])
|
||||
expect(execa.shellSync).toHaveBeenCalledWith('echo success', {
|
||||
cwd: dir,
|
||||
env: {},
|
||||
stdio: 'inherit'
|
||||
})
|
||||
expect(status).toBe(0)
|
||||
})
|
||||
|
||||
it('should run working command and return 0 status when husky is installed in a sub directory', async () => {
|
||||
const dir = tempy.directory()
|
||||
const subDir = path.join(dir, 'A/B')
|
||||
mkdirp.sync(subDir)
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(subDir, 'package.json'),
|
||||
JSON.stringify({
|
||||
husky: {
|
||||
hooks: {
|
||||
'pre-commit': 'echo success'
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
const status = await index([, getScriptPath(subDir), 'pre-commit'])
|
||||
expect(execa.shellSync).toHaveBeenCalledWith('echo success', {
|
||||
cwd: subDir,
|
||||
env: {},
|
||||
stdio: 'inherit'
|
||||
})
|
||||
expect(status).toBe(0)
|
||||
})
|
||||
|
||||
// This shouldn't happen since the shell script checks for command existence
|
||||
// but in case there's a false positive, we're testing this also
|
||||
it('should return 0 status if the command is undefined', async () => {
|
||||
const dir = tempy.directory()
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(dir, 'package.json'),
|
||||
JSON.stringify({
|
||||
husky: {}
|
||||
})
|
||||
)
|
||||
|
||||
const status = await index([, getScriptPath(dir), 'pre-commit'])
|
||||
expect(execa.shellSync).not.toBeCalled()
|
||||
expect(status).toBe(0)
|
||||
})
|
||||
|
||||
it('should run failing command and return 1 status', async () => {
|
||||
const dir = tempy.directory()
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(dir, 'package.json'),
|
||||
JSON.stringify({
|
||||
husky: {
|
||||
hooks: {
|
||||
'pre-commit': 'echo fail && exit 2'
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
const status = await index([, getScriptPath(dir), 'pre-commit'])
|
||||
expect(execa.shellSync).toHaveBeenCalledWith('echo fail && exit 2', {
|
||||
cwd: dir,
|
||||
env: {},
|
||||
stdio: 'inherit'
|
||||
})
|
||||
expect(status).toBe(2)
|
||||
})
|
||||
|
||||
it('should support old scripts but show a deprecated message', async () => {
|
||||
const dir = tempy.directory()
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(dir, 'package.json'),
|
||||
JSON.stringify({
|
||||
scripts: {
|
||||
precommit: 'echo success'
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
const status = await index([, getScriptPath(dir), 'pre-commit'])
|
||||
expect(execa.shellSync).toHaveBeenCalledWith('echo success', {
|
||||
cwd: dir,
|
||||
env: {},
|
||||
stdio: 'inherit'
|
||||
})
|
||||
expect(status).toBe(0)
|
||||
})
|
||||
|
||||
it('should set HUSKY_GIT_STDIN env for some hooks', async () => {
|
||||
const dir = tempy.directory()
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(dir, 'package.json'),
|
||||
JSON.stringify({
|
||||
husky: {
|
||||
hooks: {
|
||||
'pre-push': 'echo success'
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
const status = await index([, getScriptPath(dir), 'pre-push'], () =>
|
||||
Promise.resolve('foo')
|
||||
)
|
||||
expect(execa.shellSync).toHaveBeenCalledWith('echo success', {
|
||||
cwd: dir,
|
||||
env: {
|
||||
HUSKY_GIT_STDIN: 'foo'
|
||||
},
|
||||
stdio: 'inherit'
|
||||
})
|
||||
expect(status).toBe(0)
|
||||
})
|
||||
|
||||
it('should set HUSKY_GIT_PARAMS', async () => {
|
||||
const dir = tempy.directory()
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(dir, 'package.json'),
|
||||
JSON.stringify({
|
||||
husky: {
|
||||
hooks: {
|
||||
'commit-msg': 'echo success'
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
// commit-msg takes one parameter from git
|
||||
const status = await index([
|
||||
,
|
||||
getScriptPath(dir),
|
||||
'commit-msg',
|
||||
'git fake param'
|
||||
])
|
||||
expect(execa.shellSync).toHaveBeenCalledWith('echo success', {
|
||||
cwd: dir,
|
||||
env: {
|
||||
HUSKY_GIT_PARAMS: 'git fake param'
|
||||
},
|
||||
stdio: 'inherit'
|
||||
})
|
||||
expect(status).toBe(0)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
import index from './'
|
||||
|
||||
index(process.argv)
|
||||
.then(status => process.exit(status))
|
||||
.catch(err => {
|
||||
console.log('Husky > unexpected error', err)
|
||||
process.exit(1)
|
||||
})
|
||||
@@ -0,0 +1,83 @@
|
||||
import * as execa from 'execa'
|
||||
import * as getStdin from 'get-stdin'
|
||||
import * as path from 'path'
|
||||
import * as readPkg from 'read-pkg'
|
||||
import getConf from '../getConf'
|
||||
|
||||
export interface IEnv extends NodeJS.ProcessEnv {
|
||||
HUSKY_GIT_STDIN?: string
|
||||
HUSKY_GIT_PARAMS?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* @param argv - process.argv
|
||||
*/
|
||||
export default async function run(
|
||||
[, scriptPath, hookName = '', HUSKY_GIT_PARAMS]: string[],
|
||||
getStdinFn = getStdin // Used for mocking
|
||||
): Promise<number> {
|
||||
const cwd = path.resolve(scriptPath.split('node_modules')[0])
|
||||
const pkg = readPkg.sync({ cwd, normalize: false })
|
||||
const config = getConf(cwd)
|
||||
|
||||
const command: string | undefined =
|
||||
config && config.hooks && config.hooks[hookName]
|
||||
|
||||
const oldCommand: string | undefined =
|
||||
pkg && pkg.scripts && pkg.scripts[hookName.replace('-', '')]
|
||||
|
||||
try {
|
||||
const env: IEnv = {}
|
||||
|
||||
if (HUSKY_GIT_PARAMS) {
|
||||
env.HUSKY_GIT_PARAMS = HUSKY_GIT_PARAMS
|
||||
}
|
||||
|
||||
if (
|
||||
['pre-push', 'pre-receive', 'post-receive', 'post-rewrite'].includes(
|
||||
hookName
|
||||
)
|
||||
) {
|
||||
env.HUSKY_GIT_STDIN = await getStdinFn()
|
||||
}
|
||||
|
||||
if (command) {
|
||||
console.log(`husky > ${hookName} (node ${process.version})`)
|
||||
execa.shellSync(command, { cwd, env, stdio: 'inherit' })
|
||||
return 0
|
||||
}
|
||||
|
||||
if (oldCommand) {
|
||||
console.log()
|
||||
console.log(
|
||||
`Warning: Setting ${hookName} script in package.json > scripts will be deprecated`
|
||||
)
|
||||
console.log(
|
||||
`Please move it to husky.hooks in package.json, a .huskyrc file, or a husky.config.js file`
|
||||
)
|
||||
console.log(
|
||||
`Or run ./node_modules/.bin/husky-upgrade for automatic update`
|
||||
)
|
||||
console.log()
|
||||
console.log(`See https://github.com/typicode/husky for usage`)
|
||||
console.log()
|
||||
console.log(`husky > ${hookName} (node ${process.version})`)
|
||||
execa.shellSync(oldCommand, { cwd, env, stdio: 'inherit' })
|
||||
return 0
|
||||
}
|
||||
|
||||
return 0
|
||||
} catch (err) {
|
||||
const noVerifyMessage = [
|
||||
'commit-msg',
|
||||
'pre-commit',
|
||||
'pre-rebase',
|
||||
'pre-push'
|
||||
].includes(hookName)
|
||||
? '(add --no-verify to bypass)'
|
||||
: '(cannot be bypassed with --no-verify due to Git specs)'
|
||||
|
||||
console.log(`husky > ${hookName} hook failed ${noVerifyMessage}`)
|
||||
return err.code
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const hooks = require('./hooks.json')
|
||||
const findHooksDir = require('./utils/find-hooks-dir')
|
||||
const is = require('./utils/is')
|
||||
|
||||
function removeHook(dir, name) {
|
||||
const filename = `${dir}/${name}`
|
||||
|
||||
if (fs.existsSync(filename) && is.husky(filename)) {
|
||||
fs.unlinkSync(`${dir}/${name}`)
|
||||
}
|
||||
}
|
||||
|
||||
function uninstallFrom(huskyDir) {
|
||||
try {
|
||||
const hooksDir = findHooksDir(huskyDir)
|
||||
|
||||
hooks.forEach(function(hookName) {
|
||||
removeHook(hooksDir, hookName)
|
||||
})
|
||||
console.log('done\n')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = uninstallFrom
|
||||
@@ -0,0 +1,32 @@
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
import * as tempy from 'tempy'
|
||||
import index from '../'
|
||||
|
||||
describe('upgrade', () => {
|
||||
it('should run working command and return 0 status', () => {
|
||||
const dir = tempy.directory()
|
||||
const filename = path.join(dir, 'package.json')
|
||||
|
||||
fs.writeFileSync(
|
||||
filename,
|
||||
JSON.stringify({
|
||||
scripts: {
|
||||
precommit: 'npm test'
|
||||
}
|
||||
}),
|
||||
'utf-8'
|
||||
)
|
||||
|
||||
index(dir)
|
||||
|
||||
expect(JSON.parse(fs.readFileSync(filename, 'utf-8'))).toEqual({
|
||||
husky: {
|
||||
hooks: {
|
||||
'pre-commit': 'npm test'
|
||||
}
|
||||
},
|
||||
scripts: {}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env node
|
||||
import index from './index'
|
||||
|
||||
index(process.cwd())
|
||||
@@ -0,0 +1,63 @@
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
import * as readPkg from 'read-pkg'
|
||||
|
||||
interface IHookMap {
|
||||
[key: string]: string
|
||||
}
|
||||
|
||||
const hookList: IHookMap = {
|
||||
applypatchmsg: 'applypatch-msg',
|
||||
commitmsg: 'commit-msg',
|
||||
postapplypatch: 'post-applypatch',
|
||||
postcheckout: 'post-checkout',
|
||||
postcommit: 'post-commit',
|
||||
postmerge: 'post-merge',
|
||||
postreceive: 'post-receive',
|
||||
postrewrite: 'post-rewrite',
|
||||
postupdate: 'post-update',
|
||||
preapplypatch: 'pre-applypatch',
|
||||
preautogc: 'pre-auto-gc',
|
||||
precommit: 'pre-commit',
|
||||
preparecommitmsg: 'prepare-commit-msg',
|
||||
prepush: 'pre-push',
|
||||
prerebase: 'pre-rebase',
|
||||
prereceive: 'pre-receive',
|
||||
pushtocheckout: 'push-to-checkout',
|
||||
sendemailvalidate: 'sendemail-validate',
|
||||
update: 'update'
|
||||
}
|
||||
|
||||
export default function upgrade(cwd: string) {
|
||||
const pkgFile = path.join(cwd, 'package.json')
|
||||
if (fs.existsSync(pkgFile)) {
|
||||
const pkg = readPkg.sync({ cwd, normalize: false })
|
||||
|
||||
console.log(`husky > upgrading ${pkgFile}`)
|
||||
|
||||
// Don't overwrite pkg.husky if it exists
|
||||
if (pkg.husky) {
|
||||
return console.log(
|
||||
`husky field in package.json isn't empty, skipping automatic upgrade`
|
||||
)
|
||||
}
|
||||
|
||||
// Create empty husky.hooks field
|
||||
pkg.husky = { hooks: {} }
|
||||
|
||||
// Loop trhough hooks and move them to husky.hooks
|
||||
Object.keys(hookList).forEach(name => {
|
||||
const script = pkg.scripts[name]
|
||||
if (script) {
|
||||
delete pkg.scripts[name]
|
||||
const newName = hookList[name]
|
||||
pkg.husky.hooks[newName] = script
|
||||
console.log(`moved scripts.${name} to husky.hooks.${newName}`)
|
||||
}
|
||||
})
|
||||
|
||||
// Update package.json
|
||||
fs.writeFileSync(pkgFile, JSON.stringify(pkg, null, 2), 'utf-8')
|
||||
console.log(`husky > done`)
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const findParent = require('./find-parent')
|
||||
|
||||
function findHooksDir(dirname) {
|
||||
const dir = findParent(dirname, '.git')
|
||||
|
||||
if (dir) {
|
||||
let gitDir = path.join(dir, '.git')
|
||||
const stats = fs.lstatSync(gitDir)
|
||||
|
||||
if (stats.isFile()) {
|
||||
// Expect following format
|
||||
// git: pathToGit
|
||||
// On Windows pathToGit can contain ':' (example "gitdir: C:/Some/Path")
|
||||
const gitFileData = fs.readFileSync(gitDir, 'utf-8')
|
||||
gitDir = gitFileData
|
||||
.split(':')
|
||||
.slice(1)
|
||||
.join(':')
|
||||
.trim()
|
||||
}
|
||||
|
||||
return path.resolve(dir, gitDir, 'hooks')
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = findHooksDir
|
||||
@@ -1,16 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
module.exports = function findParent(currentDir, name) {
|
||||
const dirs = currentDir.split(path.sep)
|
||||
|
||||
while (dirs.pop()) {
|
||||
const dir = dirs.join(path.sep)
|
||||
|
||||
if (fs.existsSync(path.join(dir, name))) {
|
||||
return path.resolve(dir)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const normalize = require('normalize-path')
|
||||
const stripIndent = require('strip-indent')
|
||||
const pkg = require('../../package.json')
|
||||
|
||||
function platformSpecific() {
|
||||
// On OS X and Linux, try to use nvm if it's installed
|
||||
if (process.platform === 'win32') {
|
||||
// Add
|
||||
// Node standard installation path /c/Program Files/nodejs
|
||||
// for GUI apps
|
||||
// https://github.com/typicode/husky/issues/49
|
||||
return stripIndent(
|
||||
`
|
||||
# Node standard installation
|
||||
export PATH="$PATH:/c/Program Files/nodejs"`
|
||||
)
|
||||
} else {
|
||||
// Using normalize to support ' in path
|
||||
// https://github.com/typicode/husky/issues/117
|
||||
const home = normalize(process.env.HOME)
|
||||
|
||||
return stripIndent(
|
||||
`
|
||||
# Add common path where Node can be found
|
||||
# Brew standard installation path /usr/local/bin
|
||||
# Node standard installation path /usr/local
|
||||
export PATH="$PATH:/usr/local/bin:/usr/local"
|
||||
|
||||
# Try to load nvm using path of standard installation
|
||||
load_nvm ${home}/.nvm
|
||||
run_nvm`
|
||||
)
|
||||
|
||||
return arr.join('\n')
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function getHookScript(hookName, relativePath, npmScriptName) {
|
||||
// On Windows normalize path (i.e. convert \ to /)
|
||||
const normalizedPath = normalize(relativePath)
|
||||
|
||||
const noVerifyMessage =
|
||||
hookName === 'prepare-commit-msg'
|
||||
? '(cannot be bypassed with --no-verify due to Git specs)'
|
||||
: '(add --no-verify to bypass)'
|
||||
|
||||
return [
|
||||
stripIndent(
|
||||
`
|
||||
#!/bin/sh
|
||||
#husky ${pkg.version}
|
||||
|
||||
command_exists () {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
has_hook_script () {
|
||||
[ -f package.json ] && cat package.json | grep -q "\\"$1\\"[[:space:]]*:"
|
||||
}
|
||||
|
||||
# OS X and Linux only
|
||||
load_nvm () {
|
||||
# If nvm is not loaded, load it
|
||||
command_exists nvm || {
|
||||
export NVM_DIR="$1"
|
||||
[ -s "$1/nvm.sh" ] && . "$1/nvm.sh"
|
||||
}
|
||||
}
|
||||
|
||||
# OS X and Linux only
|
||||
run_nvm () {
|
||||
# If nvm has been loaded correctly, use project .nvmrc
|
||||
command_exists nvm && [ -f .nvmrc ] && nvm use
|
||||
}
|
||||
|
||||
cd "${normalizedPath}"
|
||||
|
||||
# Check if ${npmScriptName} script is defined, skip if not
|
||||
has_hook_script ${npmScriptName} || exit 0`
|
||||
).trim(),
|
||||
|
||||
platformSpecific(),
|
||||
|
||||
stripIndent(
|
||||
`
|
||||
# Check that npm exists
|
||||
command_exists npm || {
|
||||
echo >&2 "husky > can't find npm in PATH, skipping ${npmScriptName} script in package.json"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Export Git hook params
|
||||
export GIT_PARAMS="$*"
|
||||
|
||||
# Run npm script
|
||||
echo "husky > npm run -s ${npmScriptName} (node \`node -v\`)"
|
||||
echo
|
||||
|
||||
npm run -s ${npmScriptName} || {
|
||||
echo
|
||||
echo "husky > ${hookName} hook failed ${noVerifyMessage}"
|
||||
exit 1
|
||||
}
|
||||
`
|
||||
)
|
||||
].join('\n')
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
|
||||
function readFile(filename) {
|
||||
return fs.readFileSync(filename, 'utf-8')
|
||||
}
|
||||
|
||||
function husky(filename) {
|
||||
const data = readFile(filename)
|
||||
return data.indexOf('#husky') !== -1
|
||||
}
|
||||
|
||||
function ghooks(filename) {
|
||||
const data = readFile(filename)
|
||||
return data.indexOf('// Generated by ghooks. Do not edit this file.') !== -1
|
||||
}
|
||||
|
||||
function preCommit(filename) {
|
||||
const data = readFile(filename)
|
||||
return data.indexOf('./node_modules/pre-commit/hook') !== -1
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
husky,
|
||||
ghooks,
|
||||
preCommit
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2016",
|
||||
"module": "commonjs",
|
||||
"outDir": "./lib",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"exclude": [
|
||||
"**/__tests__/*"
|
||||
]
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"defaultSeverity": "error",
|
||||
"extends": [
|
||||
"tslint:recommended",
|
||||
"tslint-config-prettier"
|
||||
],
|
||||
"jsRules": {},
|
||||
"rulesDirectory": [
|
||||
"tslint-plugin-prettier"
|
||||
],
|
||||
"rules": {
|
||||
"prettier": [
|
||||
true,
|
||||
{
|
||||
"semi": false,
|
||||
"singleQuote": true
|
||||
}
|
||||
],
|
||||
"no-console": false
|
||||
}
|
||||
}
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
declare module 'cosmiconfig' {
|
||||
export = Cosmiconfig;
|
||||
}
|
||||
|
||||
declare function Cosmiconfig(
|
||||
moduleName: string
|
||||
):Cosmiconfig.Explorer;
|
||||
|
||||
declare namespace Cosmiconfig {
|
||||
export interface Explorer {
|
||||
searchSync(searchFrom?: string): Cosmiconfig.Result | null;
|
||||
}
|
||||
|
||||
export interface Result {
|
||||
config: any;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user