jazelle/utils/yarn-commands.js (112 lines of code) (raw):
// @flow
const {dirname} = require('path');
const {checksumCache} = require('./checksum-cache.js');
const {getDownstreams} = require('./get-downstreams.js');
const {spawn} = require('./node-helpers.js');
const {bazel, node, yarn} = require('./binary-paths.js');
const errorsOnly = ['ignore', 'ignore', 'inherit'];
const batchBuild = async ({root, deps, self, stdio}) => {
const others = deps.slice(0, -1);
const main = deps.slice(-1).pop();
const depths = {};
for (const dep of others) {
if (!depths[dep.depth]) depths[dep.depth] = [];
depths[dep.depth].push(dep);
}
// loops in order of insertion (i.e. higher depths first)
for (const depth in depths) {
await Promise.all(
depths[depth].map(async dep => {
if (dep.meta.scripts && dep.meta.scripts.build) {
await buildCacheable({root, dep, deps, stdio});
}
})
);
}
if (self) await buildCacheable({root, dep: main, deps, stdio});
};
const buildCacheable = async ({root, dep, deps, stdio}) => {
const {dir, meta} = dep;
const cache = await checksumCache(root);
if (!(await cache.isCached(dir))) {
console.log(`Building ${meta.name}`);
await spawn(node, [yarn, 'build'], {stdio, env: process.env, cwd: dir});
getDownstreams(deps, dep).forEach(d => {
// check depth to ensure we only invalidate downstreams, not cyclical deps
if (dep.depth < d.depth) cache.invalidate(d.dir);
});
await cache.update(dir);
await cache.save();
}
};
/*::
import type {Metadata} from './get-local-dependencies.js';
import type {Stdio} from './node-helpers.js';
export type BuildArgs = {
root: string,
deps: Array<Metadata>,
stdio?: Stdio,
};
export type Build = (BuildArgs) => Promise<void>;
*/
const build /*: Build */ = async ({root, deps, stdio = errorsOnly}) => {
await batchBuild({root, deps, self: true, stdio}); // allow non-ignore stdio here so we can test
};
/*::
export type DevArgs = {
root: string,
deps: Array<Metadata>,
args: Array<string>,
stdio?: Stdio,
};
export type Dev = (DevArgs) => Promise<void>;
*/
const dev /*: Dev */ = async ({root, deps, args, stdio = 'inherit'}) => {
const main = deps.slice(-1).pop();
await batchBuild({root, deps, self: false, stdio: errorsOnly});
await spawn(node, [yarn, 'dev', ...args], {
stdio,
env: process.env,
cwd: main.dir,
});
};
/*::
export type TestArgs = {
root: string,
deps: Array<Metadata>,
args: Array<string>,
stdio?: Stdio,
};
export type Test = (TestArgs) => Promise<void>;
*/
const test /*: Test */ = async ({root, deps, args, stdio = 'inherit'}) => {
const main = deps.slice(-1).pop();
await batchBuild({root, deps, self: false, stdio: errorsOnly});
await spawn(node, [yarn, 'test', ...args], {
stdio,
env: process.env,
cwd: main.dir,
});
};
/*::
export type LintArgs = {
root: string,
deps: Array<Metadata>,
args: Array<string>,
stdio?: Stdio,
};
export type Lint = (LintArgs) => Promise<void>;
*/
const lint /*: Lint */ = async ({root, deps, args, stdio = 'inherit'}) => {
const main = deps.slice(-1).pop();
await batchBuild({root, deps, self: false, stdio: errorsOnly});
await spawn(node, [yarn, 'lint', ...args], {
stdio,
env: process.env,
cwd: main.dir,
});
};
/*::
export type FlowArgs = {
root: string,
deps: Array<Metadata>,
args: Array<string>,
stdio?: Stdio,
};
export type Flow = (FlowArgs) => Promise<void>;
*/
const flow /*: Flow */ = async ({root, deps, args, stdio = 'inherit'}) => {
const main = deps.slice(-1).pop();
await batchBuild({root, deps, self: false, stdio: errorsOnly});
await spawn(node, [yarn, 'flow', ...args], {
stdio,
env: process.env,
cwd: main.dir,
});
};
/*::
export type StartArgs = {
root: string,
deps: Array<Metadata>,
args: Array<string>,
stdio?: Stdio,
};
export type Start = (StartArgs) => Promise<void>;
*/
const start /*: Start */ = async ({root, deps, args, stdio = 'inherit'}) => {
const main = deps.slice(-1).pop();
await batchBuild({root, deps, self: true, stdio: errorsOnly});
await spawn(node, [yarn, 'start', ...args], {
stdio,
env: process.env,
cwd: main.dir,
});
};
/*::
export type ExecArgs = {
root: string,
deps: Array<Metadata>,
args: Array<string>,
stdio?: Stdio,
};
export type Exec = (ExecArgs) => Promise<void>;
*/
const exec /*: Exec */ = async ({root, deps, args, stdio = 'inherit'}) => {
const [command, ...params] = args;
const main = deps.slice(-1).pop();
const cwd = main.dir;
const path = process.env.PATH || '';
const bazelDir = dirname(bazel);
const nodeDir = dirname(node);
const env = {PATH: `${bazelDir}:${nodeDir}:${path}:${cwd}/node_modules/.bin`};
await spawn(command, params, {stdio, env, cwd});
};
/*::
export type ScriptArgs = {
root: string,
deps: Array<Metadata>,
command: string,
args: Array<string>,
stdio?: Stdio,
};
export type Script = (ScriptArgs) => Promise<void>;
*/
const script /*: Script */ = async ({
root,
deps,
command,
args,
stdio = 'inherit',
}) => {
const main = deps.slice(-1).pop();
await batchBuild({root, deps, self: false, stdio: errorsOnly});
await spawn(node, [yarn, command, ...args], {
stdio,
env: process.env,
cwd: main.dir,
});
};
module.exports = {build, test, lint, flow, dev, start, exec, script};