joyent-portal/scripts/format

130 lines
3.3 KiB
JavaScript
Executable File

#!/usr/bin/env node
const { config } = require('../package.json');
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);
const optOut = forceArray(config['fmt-opt-out']).map(pkg =>
path.join(ROOT, `packages/${pkg}`)
);
const statuses = ['Added', 'Modified'];
const filter = (files = []) =>
files
.filter(file => !/node_modules/.test(file))
.map(file => path.resolve(ROOT, file))
.filter(file => !optOut.some(pkg => file.indexOf(pkg) === 0));
const run = async (files = []) => {
const filteredFiles = filter(files);
if (!filteredFiles.length) {
return;
}
return execa(
'prettier',
['--write', '--single-quote'].concat(filteredFiles),
{
stdio: 'inherit'
}
);
};
const add = async filename => execa('git', ['add', filename]);
const all = async () => {
const files = await globby(['packages/**/*.js', 'scripts/*'], {
cwd: path.join(__dirname, '..')
});
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(file =>
Object.assign({}, file, { filename: path.resolve(ROOT, file.filename) })
)
.filter(
file =>
/\.js$/.test(file.filename) || file.filename.indexOf(SCRIPTS) === 0
);
const existing = await asyncFilter(
files,
async ({ filename }) => await exists(filename)
);
if (!existing.length) {
return;
}
const checksums = await map(existing, async file => {
const checksum = await asyncChecksum(file.filename);
return Object.assign({}, file, { checksum });
});
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());