packages/cli: added a more reusable and less verbose logging util and use in watch-deps
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
import { spawn } from 'child_process';
|
||||
import { waitForExit } from 'lib/run';
|
||||
import { watchDeps } from 'commands/watch-deps';
|
||||
import { createLogger } from 'commands/watch-deps/logger';
|
||||
import { createLogFunc } from 'lib/logging';
|
||||
|
||||
export default async () => {
|
||||
// Start dynamic watch and build of dependencies, then serve the app
|
||||
@@ -35,8 +35,6 @@ export default async () => {
|
||||
});
|
||||
|
||||
// We need to avoid clearing the terminal, or the build feedback of dependencies will be lost
|
||||
const log = createLogger();
|
||||
child.stdout.setEncoding('utf8');
|
||||
child.stdout!.on('data', log.out);
|
||||
child.stdout.on('data', createLogFunc(process.stdout));
|
||||
await waitForExit(child);
|
||||
};
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
*/
|
||||
|
||||
import { spawn } from 'child_process';
|
||||
|
||||
import { createLogger } from './logger';
|
||||
import { createLogPipe } from 'lib/logging';
|
||||
|
||||
export function startChild(args: string[]) {
|
||||
const [command, ...commandArgs] = args;
|
||||
@@ -27,12 +26,8 @@ export function startChild(args: string[]) {
|
||||
});
|
||||
|
||||
// We need to avoid clearing the terminal, or the build feedback of dependencies will be lost
|
||||
const log = createLogger();
|
||||
child.stdout!.on('data', (data: Buffer) => {
|
||||
log.out(data.toString('utf8'));
|
||||
});
|
||||
child.stderr!.on('data', data => {
|
||||
log.err(data.toString('utf8'));
|
||||
});
|
||||
const logPipe = createLogPipe();
|
||||
child.stdout.on('data', logPipe(process.stdout));
|
||||
child.stderr.on('data', logPipe(process.stderr));
|
||||
return child;
|
||||
}
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
*/
|
||||
|
||||
import { spawn } from 'child_process';
|
||||
import { Logger } from './logger';
|
||||
import { LogPipe } from 'lib/logging';
|
||||
import chalk from 'chalk';
|
||||
import { Package } from './packages';
|
||||
|
||||
export function startCompiler(pkg: Package, log: Logger) {
|
||||
export function startCompiler(pkg: Package, logPipe: LogPipe) {
|
||||
// First we figure out which yarn script is a available, falling back to "build --watch"
|
||||
const scriptName = ['build:watch', 'watch'].find(
|
||||
script => script in pkg.scripts,
|
||||
@@ -35,12 +35,9 @@ export function startCompiler(pkg: Package, log: Logger) {
|
||||
});
|
||||
|
||||
watch.stdin.end();
|
||||
watch.stdout!.on('data', (data: Buffer) => {
|
||||
log.out(data.toString('utf8'));
|
||||
});
|
||||
watch.stderr!.on('data', data => {
|
||||
log.err(data.toString('utf8'));
|
||||
});
|
||||
watch.stdout.on('data', logPipe(process.stdout));
|
||||
const logErr = logPipe(process.stderr);
|
||||
watch.stderr.on('data', logErr);
|
||||
|
||||
const promise = new Promise<void>((resolve, reject) => {
|
||||
watch.on('error', error => {
|
||||
@@ -50,7 +47,7 @@ export function startCompiler(pkg: Package, log: Logger) {
|
||||
watch.on('close', (code: number) => {
|
||||
if (code !== 0) {
|
||||
const msg = `Compiler exited with code ${code}`;
|
||||
log.err(chalk.red(msg));
|
||||
logErr(chalk.red(msg));
|
||||
reject(new Error(msg));
|
||||
} else {
|
||||
resolve();
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import chalk from 'chalk';
|
||||
import fs from 'fs-extra';
|
||||
import { createLoggerFactory } from './logger';
|
||||
import { createLogPipeFactory } from './logger';
|
||||
import { findAllDeps } from './packages';
|
||||
import { startWatcher, startPackageWatcher } from './watcher';
|
||||
import { startCompiler } from './compiler';
|
||||
@@ -42,7 +42,7 @@ export async function watchDeps(options: Options = {}) {
|
||||
const localPackagePath = paths.resolveTarget('package.json');
|
||||
|
||||
// Rotate through different prefix colors to make it easier to differenciate between different deps
|
||||
const logFactory = createLoggerFactory([
|
||||
const createLogPipe = createLogPipeFactory([
|
||||
chalk.yellow,
|
||||
chalk.blue,
|
||||
chalk.magenta,
|
||||
@@ -69,7 +69,7 @@ export async function watchDeps(options: Options = {}) {
|
||||
|
||||
// We lazily watch all our deps, as in we don't start the actual watch compiler until a change is detected
|
||||
const watcher = await startWatcher(deps, WATCH_LOCATIONS, pkg => {
|
||||
startCompiler(pkg, logFactory(pkg.name)).promise.catch(error => {
|
||||
startCompiler(pkg, createLogPipe(pkg.name)).promise.catch(error => {
|
||||
process.stderr.write(`${error}\n`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,33 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export type Logger = {
|
||||
out(msg: string): void;
|
||||
err(msg: string): void;
|
||||
};
|
||||
import { createLogPipe } from 'lib/logging';
|
||||
|
||||
export type ColorFunc = (msg: string) => string;
|
||||
|
||||
// Logger utility that prefixes logs and removes terminal clear commands
|
||||
export function createLogger(prefix: string = ''): Logger {
|
||||
const write = (stream: NodeJS.WriteStream, msg: string) => {
|
||||
const noClearMsg = msg.startsWith('\x1b\x63') ? msg.slice(2) : msg;
|
||||
const prefixedMsg = noClearMsg.trimRight().replace(/^/gm, prefix);
|
||||
stream.write(`${prefixedMsg}\n`, 'utf8');
|
||||
};
|
||||
|
||||
return {
|
||||
out(msg: string) {
|
||||
write(process.stdout, msg);
|
||||
},
|
||||
err(msg: string) {
|
||||
write(process.stderr, msg);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// A factory for creating loggers that rotate between different coloring functions
|
||||
export function createLoggerFactory(colorFuncs: ColorFunc[]) {
|
||||
// A factory for creating log pipes that rotate between different coloring functions
|
||||
export function createLogPipeFactory(colorFuncs: ColorFunc[]) {
|
||||
let colorIndex = 0;
|
||||
|
||||
return (name: string) => {
|
||||
@@ -49,6 +28,6 @@ export function createLoggerFactory(colorFuncs: ColorFunc[]) {
|
||||
colorIndex = (colorIndex + 1) % colorFuncs.length;
|
||||
|
||||
const prefix = `${colorFunc(name)}: `;
|
||||
return createLogger(prefix);
|
||||
return createLogPipe({ prefix });
|
||||
};
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import { resolve as resolvePath } from 'path';
|
||||
import chalk from 'chalk';
|
||||
import chokidar from 'chokidar';
|
||||
import { Package } from './packages';
|
||||
import { createLogger } from './logger';
|
||||
import { createLogFunc } from 'lib/logging';
|
||||
|
||||
export type Watcher = {
|
||||
update(newPackages: Package[]): Promise<void>;
|
||||
@@ -36,7 +36,7 @@ export async function startWatcher(
|
||||
callback: (pkg: Package) => void,
|
||||
): Promise<Watcher> {
|
||||
const watchedPackageLocations = new Set<string>();
|
||||
const logger = createLogger();
|
||||
const log = createLogFunc(process.stdout);
|
||||
|
||||
const watchPackage = async (pkg: Package) => {
|
||||
let signalled = false;
|
||||
@@ -71,7 +71,7 @@ export async function startWatcher(
|
||||
continue;
|
||||
}
|
||||
|
||||
logger.out(chalk.green(`Starting watch of new dependency ${pkg.name}`));
|
||||
log(chalk.green(`Starting watch of new dependency ${pkg.name}`));
|
||||
promises.push(watchPackage(pkg));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export type LogFunc = (data: Buffer | string) => void;
|
||||
export type LogPipe = (dst: NodeJS.WriteStream) => LogFunc;
|
||||
|
||||
export type LogOptions = {
|
||||
// If set, prefix each log message with this string
|
||||
prefix?: string;
|
||||
|
||||
// If true, clear terminal commands will be forwarded, otherwise they are removed
|
||||
forwardClearTerm?: boolean;
|
||||
};
|
||||
|
||||
// Creates a log pipe that binds to a destination stream and forwards logs with optional transforms.
|
||||
// Use returned logPipe e.g. as follows: child.stdout.on('data', logPipe(process.stdout))
|
||||
export function createLogPipe(options: LogOptions = {}): LogPipe {
|
||||
const { prefix = '', forwardClearTerm = false } = options;
|
||||
|
||||
return (dst: NodeJS.WriteStream) => (data: Buffer | string) => {
|
||||
let str = typeof data === 'string' ? data : data.toString('utf8');
|
||||
|
||||
if (!forwardClearTerm) {
|
||||
str = trimClearTerm(str);
|
||||
}
|
||||
if (prefix) {
|
||||
str = `${prefix}${str}`;
|
||||
}
|
||||
|
||||
dst.write(Buffer.from(str, 'utf8'));
|
||||
};
|
||||
}
|
||||
|
||||
// Wrapper around createLogPipe to avoid awkward immediate call of returned function
|
||||
export function createLogFunc(
|
||||
dst: NodeJS.WriteStream,
|
||||
options: LogOptions = {},
|
||||
): LogFunc {
|
||||
return createLogPipe(options)(dst);
|
||||
}
|
||||
|
||||
// Returns the string without terminal clear command if it was prefixed with one.
|
||||
export function trimClearTerm(msg: string): string {
|
||||
return msg.startsWith('\x1b\x63') ? msg.slice(2) : msg;
|
||||
}
|
||||
Reference in New Issue
Block a user