diff --git a/package.json b/package.json index aac73e97..47d22894 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "apr-sort-by": "^1.0.5", "babel-eslint": "^7.2.3", "chalk": "^1.1.3", + "checksum": "^0.1.1", "conventional-changelog-angular": "^1.3.3", "conventional-changelog-cli": "^1.3.1", "conventional-changelog-lint": "^1.1.9", diff --git a/scripts/format b/scripts/format index 910ecc36..f084da6d 100755 --- a/scripts/format +++ b/scripts/format @@ -1,18 +1,22 @@ #!/usr/bin/env node const { config } = require('../package.json'); -const { exists } = require('mz/fs'); +const { exists, access } = require('mz/fs'); const sgf = require('staged-git-files'); const forceArray = require('force-array'); const awaitify = require('apr-awaitify'); const asyncFilter = require('apr-filter'); +const map = require('apr-map'); +const reduce = require('apr-reduce'); const execa = require('execa'); const globby = require('globby'); const main = require('apr-main'); const argv = require('yargs').argv; const path = require('path'); +const checksum = require('checksum'); const getStaged = awaitify(sgf); +const asyncChecksum = awaitify(checksum.file); const ROOT = path.join(__dirname, '../'); const SCRIPTS = path.resolve(__dirname); @@ -45,6 +49,8 @@ const run = async (files = []) => { ); }; +const add = async filename => execa('git', ['add', filename]); + const all = async () => { const files = await globby(['packages/**/*.js', 'scripts/*'], { cwd: path.join(__dirname, '..') @@ -53,24 +59,71 @@ const all = async () => { return run(files); }; +const getUnstaged = async () => { + const unstaged = await execa('git', ['ls-files', '-m']); + return unstaged.stdout.split('\n'); +}; + const staged = async () => { + const unstaged = (await getUnstaged()) + .map(file => path.resolve(ROOT, file)) + .filter(file => /\.js$/.test(file) || file.indexOf(SCRIPTS) === 0); + const files = (await getStaged()) .filter(({ status }) => statuses.indexOf(status) >= 0) - .map(({ filename }) => filename) - .map(filename => path.resolve(ROOT, filename)) + .map(file => + Object.assign({}, file, { filename: path.resolve(ROOT, file.filename) }) + ) .filter( - filename => /\.js$/.test(filename) || filename.indexOf(SCRIPTS) === 0 + file => + /\.js$/.test(file.filename) || file.filename.indexOf(SCRIPTS) === 0 ); - const existing = await asyncFilter(files, exists); + const existing = await asyncFilter( + files, + async ({ filename }) => await exists(filename) + ); if (!existing.length) { return; } - console.log('existing = ', existing); + const checksums = await map(existing, async file => { + const checksum = await asyncChecksum(file.filename); + return Object.assign({}, file, { checksum }); + }); - return run(existing); + const filenames = existing.map(file => file.filename); + await run(filenames); + + const changed = await asyncFilter( + checksums, + async ({ filename, checksum }) => { + const newChecksum = await asyncChecksum(filename); + return checksum != newChecksum; + } + ); + + const modifieds = await reduce( + changed, + async (modifieds, file) => { + const isUnstaged = unstaged.filter(f => f === file.filename).length; + if (file.status === 'Modified' && isUnstaged) { + modifieds.push(file); + } else { + await add(file.filename); + } + return modifieds; + }, + [] + ); + + if (modifieds.length) { + modifieds.forEach(modified => + console.log('PARTIALLY STAGED FILE ', modified.filename) + ); + process.exit(1); + } }; main(argv._.length ? run(argv._) : argv.staged ? staged() : all()); diff --git a/yarn.lock b/yarn.lock index 96d2aa53..f8eebbe9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1852,6 +1852,12 @@ character-reference-invalid@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.0.tgz#dec9ad1dfb9f8d06b4fcdaa2adc3c4fd97af1e68" +checksum@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/checksum/-/checksum-0.1.1.tgz#dc6527d4c90be8560dbd1ed4cecf3297d528e9e9" + dependencies: + optimist "~0.3.5" + chokidar@^1.4.2, chokidar@^1.4.3, chokidar@^1.6.1: version "1.7.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" @@ -6540,6 +6546,12 @@ optimist@^0.6.1: minimist "~0.0.1" wordwrap "~0.0.2" +optimist@~0.3.5: + version "0.3.7" + resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.3.7.tgz#c90941ad59e4273328923074d2cf2e7cbc6ec0d9" + dependencies: + wordwrap "~0.0.2" + option-chain@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/option-chain/-/option-chain-0.1.1.tgz#e9b811e006f1c0f54802f28295bfc8970f8dcfbd"