Last active
May 9, 2020 15:13
-
-
Save btoo/e7b5bee4a73efc6c0e10e4297738c0df to your computer and use it in GitHub Desktop.
husky pre-commit hook to `eslint --fix` and `prettier --write` files staged in git
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
*dist* | |
*.json | |
yarn.lock | |
*.proto | |
*.md | |
Makefile | |
.eslintignore |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** @see {@tutorial https://blog.totominc.io/linting-vue-typescript-eslint-prettier/} */ | |
module.exports = { | |
root: true, | |
env: { | |
node: true, | |
}, | |
extends: [ | |
// 'plugin:prettier/recommended', // add prettier-eslint plugin which will uses the `.prettierrc.js` config | |
'plugin:import/typescript', | |
// prettier configs need to be last - https://github.com/prettier/eslint-config-prettier#installation | |
'prettier', | |
'prettier/@typescript-eslint', | |
'prettier/babel', | |
'prettier/standard', | |
], | |
rules: { | |
// you can put some custom rules here | |
'no-nested-ternary': 'off', | |
'no-unused-expressions': [ | |
'error', | |
{ allowShortCircuit: true, allowTernary: true, allowTaggedTemplates: true }, | |
], | |
'object-curly-spacing': ['error', 'always'], | |
'import/no-cycle': 'off', | |
radix: 'off', | |
'no-console': 'off', | |
'no-unsafe-finally': 'off', | |
'consistent-return': 'off', | |
semi: 'error', | |
'space-infix-ops': 'error', | |
'no-multi-spaces': 'error', | |
'no-return-assign': 'off', | |
'no-unused-expressions': 'off', | |
/** @see {@link https://github.com/typescript-eslint/typescript-eslint/issues/600#issuecomment-499979248} */ | |
'spaced-comment': ['error', 'always', { markers: ['/'] }], | |
'import/prefer-default-export': 'off', | |
'no-async-promise-executor': 'off', | |
camelcase: 'off', | |
'no-underscore-dangle': 'off', | |
}, | |
parserOptions: { | |
parser: '@typescript-eslint/parser', // the typescript-parser for eslint, instead of tslint | |
sourceType: 'module', // allow the use of imports statements | |
ecmaVersion: 2018, // allow the parsing of modern ecmascript | |
}, | |
settings: { | |
'import/resolver': { | |
/** @see {@tutorial https://www.npmjs.com/package/eslint-import-resolver-typescript#configuration} */ | |
typescript: { | |
/** always try to resolve types under `<root>@types` directory even it doesn't contain any source code, like `@types/unist` */ | |
alwaysTryTypes: true, | |
}, | |
/** | |
* @see {@tutorial https://www.npmjs.com/package/eslint-import-resolver-alias} | |
* @see {@link import('./build/webpack.config.js').resolve.alias} | |
*/ | |
alias: { | |
map: [ | |
['@', './src'], | |
['@test', require('path').resolve(__dirname, '../test')], | |
], | |
extensions: ['.js', '.jsx', '.ts', '.tsx'], | |
}, | |
}, | |
}, | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @see {@tutorial https://github.com/typicode/husky} | |
* _Requires Node >= 10 and Git >= 2.13.0._ | |
*/ | |
module.exports = { | |
hooks: { | |
/** | |
* @see {@tutorial https://stackoverflow.com/a/15656652/3942699} | |
* | |
* ___only works with files staged in git___ | |
* because doing on the entire repo is overkill atm | |
* | |
* if linting succeeds, proceed to committing the staged files. | |
* if linting fails, prevent the commit from happening and fix the auto-fixable errors | |
*/ | |
'pre-commit': "npx ts-node -O '{\"module\":\"commonjs\"}' lint", | |
}, | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module.exports = { | |
singleQuote: true, | |
printWidth: 100, | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as execa from 'execa'; | |
const parse = (c: string | string[]) => { | |
let command = '', args = []; | |
if (Array.isArray(c)) ([command, ...args] = c); | |
/** @see {@link https://stackoverflow.com/a/14912552/3942699} */ | |
else ([command, ...args] = c.match(/\S+/g) || [command]); | |
return { command, args }; | |
}; | |
type Command = Parameters<typeof parse>[0]; | |
const read = async (c: Command) => { | |
const { command, args } = parse(c); | |
return (await execa(command, args)).stdout; | |
}; | |
const run = (c: Command, options?: import('child_process').SpawnOptions) => { | |
const { command, args } = parse(c); | |
return execa(command, args, { ...options, stdio: 'inherit' }); | |
}; | |
/** | |
* performs linting (eslint) and formatting (prettier) on __staged files only__ | |
* | |
* (probably as a pre-commit hook @see .huskyrc.js) | |
*/ | |
(async () => { | |
const { default: chalk } = await import('chalk'); | |
const stagedFiles = (await read('git diff --diff-filter=d --cached --name-only')) | |
.split('\n') | |
.filter(Boolean); | |
if (stagedFiles.length) { | |
console.log(chalk.blueBright('\nLinting staged files with ESLint before committing...')); | |
/** eslint only works with js and ts for now */ | |
const stagedFilesForEslint = stagedFiles.filter((f) => f.match(/\.(js|jsx|ts|tsx|json)$/)); | |
const stagedFilesForEslintStr = stagedFilesForEslint.join(' '); | |
/** | |
* a reusable function for formatting with Prettier. | |
* this function should be called after either: | |
* - linting succeeds or | |
* - linting fails and then lint --fix is attempted | |
*/ | |
const formatStagedFilesWithPrettier = async () => { | |
try { | |
console.log( | |
chalk.blueBright('\nFormatting staged files with Prettier before committing...\n') | |
); | |
const stagedFilesStr = stagedFiles.join(' '); | |
await read(`npx prettier --list-different ${stagedFilesStr}`); | |
// nothing to format | |
} catch (resultOfCheckingToSeeIfAnythingNeedsToBeFormatted) { | |
const filesThatNeedToBeFormatted = (resultOfCheckingToSeeIfAnythingNeedsToBeFormatted.stdout as string) | |
.split('\n') | |
.filter(Boolean); | |
if (filesThatNeedToBeFormatted.length) { | |
try { | |
await read(`npx prettier --write ${filesThatNeedToBeFormatted.join(' ')}`); | |
} finally { | |
const { default: path } = await import('path'); | |
console.log(chalk.redBright.bold('The following files needed formatting:')); | |
const cwd = process.cwd(); | |
filesThatNeedToBeFormatted.forEach((f) => | |
console.log(chalk.underline(path.join(cwd, f))) | |
); | |
console.log(); | |
process.exit(1); | |
} | |
} | |
} | |
}; | |
try { | |
stagedFilesForEslint.length && (await run(`npx eslint ${stagedFilesForEslintStr}`)); | |
await formatStagedFilesWithPrettier(); | |
} catch (lintingErr) { | |
// there are linting problems, so: | |
// 1. try to fix the linting problems with eslint --fix | |
// 2. try to format the code with prettier --write | |
// 3. prevent the commit from happening | |
console.log(chalk.blueBright('Attempting to `--fix` problems...')); | |
try { | |
stagedFilesForEslint.length && (await run(`npx eslint --fix ${stagedFilesForEslintStr}`)); | |
await formatStagedFilesWithPrettier(); | |
} finally { | |
process.exit(1); | |
} | |
} | |
} else { | |
console.log(chalk.blueBright('\nThere are no files staged in git to lint/format\n')); | |
} | |
})(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"scripts": { | |
"postinstall": "node ./node_modules/husky/lib/installer/bin install" | |
}, | |
"dependencies": { | |
"chalk": "^3.0.0", | |
}, | |
"devDependencies": { | |
"@types/node": "^12.7.2", | |
"@typescript-eslint/eslint-plugin": "^2.25.0", | |
"@typescript-eslint/parser": "^2.25.0", | |
"eslint": "^6.8.0", | |
"eslint-config-prettier": "^6.10.1", | |
"eslint-import-resolver-alias": "^1.1.2", | |
"eslint-import-resolver-typescript": "^2.0.0", | |
"eslint-plugin-html": "^4.0.2", | |
"eslint-plugin-prettier": "^3.1.2", | |
"execa": "^2.0.3", | |
"husky": "^4.2.3", | |
"prettier": "^2.0.2", | |
"ts-node": "^8.8.1" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment