Skip to content

Commit

Permalink
feat: yarn watch updates (specified) tests when source files change (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
boukeversteegh committed Jan 7, 2022
1 parent 67a3ae0 commit 275d0e7
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 63 deletions.
2 changes: 1 addition & 1 deletion integration/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,5 @@ async function generate(binFile: string, baseDir: string, parameter: string) {
}

main().then(() => {
console.log('done');
console.log(`${process.argv[3]}: Done`);
});
170 changes: 109 additions & 61 deletions integration/watch.ts
Original file line number Diff line number Diff line change
@@ -1,86 +1,134 @@
const chokidar = require('chokidar');
const spawn = require('child_process').spawn;
type FsEvent = 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir';

main()
const colors = {
none: '',
reset: '\x1b[0m',
green: '\x1b[32m',
red: '\x1b[31m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
magenta: '\x1b[35m',
cyan: '\x1b[36m',
};

main();

function main() {
const yarn = process.platform === 'win32' ? 'yarn.cmd' : 'yarn'
const yarn = process.platform === 'win32' ? 'yarn.cmd' : 'yarn';
const usePolling = process.argv.includes('--polling');
const tests = process.argv.slice(2).filter((x) => !x.startsWith('-'));
const watchOptions = { ignoreInitial: true, usePolling };

if (process.argv.includes('-h') || process.argv.includes('--help')) showHelp();
showSettings(tests);

chokidar.watch('integration/*/*.proto', watchOptions).on('all', integrationHandler(yarn, 'proto2bin'));
chokidar.watch('integration/*/*.proto', watchOptions).on('all', integrationHandler(yarn, 'proto2pbjs'));
chokidar.watch('integration/*/*.bin', watchOptions).on('all', integrationHandler(yarn, 'bin2ts'));
chokidar.watch('src/**/*.ts', watchOptions).on('change', srcHandler(yarn, 'bin2ts', tests));

setupKeys({
['']: () => yarnRun(yarn, 'bin2ts', 'enter'),
['q']: () => process.exit(),
['\u0003']: () => process.exit(), // ctrl-c
});
}

function setupKeys(bindings: { [p: string]: () => void }) {
process.stdin.setRawMode(true);
process.stdin.resume();
process.stdin.setEncoding('utf8');

if (process.argv.includes('-h') || process.argv.includes('--help')) {
console.log(`
Object.entries(bindings).forEach(([key, handler]) => {
process.stdin.on('data', (data) => {
if (data.toString().trim() === key) handler();
});
});
}

function showHelp() {
console.log(`
Watches the integration directory for changes and regenerates .bin and .ts files.
Watches the src directory and regenerates given TEST files.
Usage:
$ yarn watch
$ ts-node watch.ts [options]
$ yarn watch [options] [TEST, TEST2, ...]
$ ts-node watch.ts [options] [TEST, TEST2, ...]
Options:
-h, --help Show this help message
--polling Use polling instead of native watchers
`)
process.exit(0)
}
const usePolling = process.argv.includes('--polling')

process.chdir("integration");
-h, --help Show this help message.
--polling Use polling instead of native watchers.
TEST Regenerate the specified TEST(s) when implementation files change.
Equivalent to running 'yarn bin2ts TEST' manually each time.
Examples:
$ yarn watch
$ yarn watch --polling
$ yarn watch simple struct`);
process.exit(0);
}

chokidar
.watch("*/*.proto", { ignoreInitial: true, usePolling })
.on('all', yarnRunHandler(yarn, 'proto2bin'));
function showSettings(tests: string[]) {
console.log(`${colors.yellow}`);
console.log(`Watching for changes:`);
console.log('- integration/\tRegenerating integration/*/*.proto to .bin and .ts');
if (tests.length) {
console.log(`- src/\t\tRegenerating integration/{${tests.join(', ')}}/*.proto to .ts`);
}
console.log('\nPress enter to regenerate all integration tests.');
console.log('Press ctrl+c or q to exit');
console.log(colors.reset);
}

chokidar
.watch("*/*.proto", { ignoreInitial: true, usePolling })
.on('all', yarnRunHandler(yarn, 'proto2pbjs'));
function integrationHandler(yarn: string, task: string) {
return (event: FsEvent, triggerPath: string) => {
if (event !== 'add' && event !== 'change') return;

chokidar
.watch("*/*.bin", { ignoreInitial: true, usePolling })
.on('all', yarnRunHandler(yarn, 'bin2ts'));
}
triggerPath = triggerPath.replace(/\\/g, '/'); // windows
const relativePath = triggerPath.replace(/^integration\//, '');

const colors = {
none: '',
reset: '\x1b[0m',
green: '\x1b[32m',
red: '\x1b[31m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
magenta: '\x1b[35m',
cyan: '\x1b[36m'
yarnRun(yarn, task, triggerPath, relativePath);
};
}

function yarnRunHandler(yarn: string, task: string) {
return (event: "add" | "addDir" | "change" | "unlink" | "unlinkDir", path: string) => {
if (event !== 'add' && event !== 'change') {
return;
}
function yarnRun(yarn: string, task: string, header: string, taskArgument?: string) {
const yarnArgs = taskArgument ? [task, taskArgument] : [task];

path = path.replace(/\\/g, "/"); // windows
console.log(formatLog(colors.green, header, task, `${yarn} ${yarnArgs.join(' ')}`));

yarnRun(yarn, task, path);
const yarnProcess = spawn(yarn, yarnArgs);
yarnProcess.stdout.on('data', (data: Buffer) => console.log(formatLog(colors.none, header, task, data.toString())));
yarnProcess.stderr.on('data', (data: Buffer) => console.error(formatLog(colors.red, header, task, data.toString())));
yarnProcess.on('error', (err: Error) => console.error(formatLog(colors.red, header, task, err.message)));
yarnProcess.on('close', (code: number) => {
if (code !== 0) {
console.error(formatLog(colors.red, header, task, `Exited with code ${code}`));
} else {
console.log(formatLog(colors.green, header, task, 'Done'));
}
});
}

function yarnRun(yarn: string, task: string, path: string) {
const yarnArgs = [task, path];

console.log(formatLog(colors.green, task, path, `${yarn} ${yarnArgs.join(' ')}`));

const yarnProcess = spawn(yarn, yarnArgs);
yarnProcess.stdout.on('data', (data: Buffer) => console.log(formatLog(colors.none, task, path, data.toString())));
yarnProcess.stderr.on('data', (data: Buffer) => console.error(formatLog(colors.red, task, path, data.toString())));
yarnProcess.on('error', (err: Error) => console.error(formatLog(colors.red, task, path, err.message)));
yarnProcess.on('close', (code: number) => {
if (code !== 0) {
console.error(formatLog(colors.red, task, path, `Exited with code ${code}`));
}
});
function srcHandler(yarn: string, task: string, tests: string[]) {
return async (event: FsEvent, triggerPath: string) => {
triggerPath = triggerPath.replace(/\\/g, '/');
if (tests.length === 0) {
const notice = `Plugin modified! Press [enter] to regenerate all integration tests or run 'yarn watch [TEST, ...]'. See 'yarn watch --help'.`;
console.log(formatLog(colors.yellow, triggerPath, 'watch', notice));
} else {
yarnRun(yarn, task, triggerPath, tests.join(' '));
}
};
}

function formatLog(color: string, task: string, path: string, message: string) {
return message
.split('\n')
.filter(line => line.length)
.map(line => `${colors.reset}${path} ${colors.cyan}[${task}]${colors.reset} ${color}` + line)
.join('\n')
+ colors.reset;
function formatLog(color: string, triggerPath: string, category: string, message: string) {
return (
message
.split('\n')
.filter((line) => line.length)
.map((line) => `${colors.reset}${triggerPath} ${colors.cyan}[${category}]${colors.reset} ${color}` + line)
.join('\n') + colors.reset
);
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"proto2pbjs:local": "integration/pbjs.sh",
"bin2ts:local": "integration/codegen.sh",
"test": "yarn jest -c jest.config.js --maxWorkers=2",
"prettier": "prettier --write {src,tests}/**/*.ts",
"prettier": "prettier --write {src,tests}/**/*.ts integration/*.ts",
"prettier:check": "prettier --list-different {src,tests}/**/*.ts",
"setup:docker": "docker-compose build",
"watch": "ts-node integration/watch.ts"
Expand Down

0 comments on commit 275d0e7

Please sign in to comment.