#!/usr/bin/env node const { readFile, exists } = require('mz/fs'); const sgf = require('staged-git-files'); const execa = require('execa'); const awaitify = require('apr-awaitify'); const asyncFilter = require('apr-filter'); const main = require('apr-main'); const map = require('apr-map'); const reduce = require('apr-reduce'); const globby = require('globby'); const path = require('path'); const uniq = require('lodash.uniq'); const argv = require('yargs').argv; const checksum = require('checksum'); const { lstatSync, readdirSync } = require('fs'); const { join } = require('path'); const ROOT = path.join(__dirname, '..'); const getStaged = awaitify(sgf); const statuses = [ 'Added', 'Copied', 'Deleted', 'Modified', 'Renamed', 'Unmerged' ]; const exec = (args = []) => execa('lerna', args, { stdio: 'inherit' }); const lint = scope => exec(['run', 'lint', '--scope', scope]); const test = scope => exec(['run', 'test', '--scope', scope]); const run = async scope => { if (argv.lint) { await lint(scope); } if (argv.test) { await test(scope); } }; const runLint = async scope => { await lint(scope); }; const runTest = async scope => { await test(scope); }; const getUnstaged = async () => { const unstaged = await execa('git', ['ls-files', '-m']); return unstaged.stdout.split('\n'); }; const asyncChecksum = awaitify(checksum.file); const add = async filename => execa('git', ['add', filename]); const runLintAndGitAdd = async (staged, pkgs) => { const unstaged = (await getUnstaged()).map(file => path.resolve(ROOT, file)); const existing = await asyncFilter(staged, ({ filename }) => exists(filename) ); const checksums = await map(existing, async file => { const checksum = await asyncChecksum(file.filename); return Object.assign({}, file, { checksum }); }); await map(pkgs.map(({ name }) => name), runLint); 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' || file.status === 'Added') && 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(0); } }; const gather = async () => { const isDirectory = source => lstatSync(source).isDirectory(); const getDirectories = source => readdirSync(source) .map(name => join(source, name)) .filter(isDirectory); const locations = await globby( getDirectories('./packages').concat(getDirectories('./prototypes')), { cwd: ROOT } ); const staged = (await getStaged()) .filter(({ status }) => statuses.indexOf(status) >= 0) // .map(({ filename }) => path.resolve(ROOT, filename)); .map(file => Object.assign({}, file, { filename: path.resolve(ROOT, file.filename) }) ); const folders = uniq( locations .map(folder => path.resolve(ROOT, folder)) .filter(folder => staged.some(i => i.filename.indexOf(folder) >= 0)) ); const pkgs = await map(folders, async folder => JSON.parse(await readFile(path.join(folder, 'package.json'), 'utf-8')) ); if (argv.lint) { await runLintAndGitAdd(staged, pkgs); } if (argv.test) { await await map(pkgs.map(({ name }) => name), runTest); } }; main(gather());