cli: update watch targets when depedencies of watched package change
This commit is contained in:
@@ -1,10 +1,9 @@
|
||||
import { resolve as resolvePath } from 'path';
|
||||
import { readFileSync } from 'fs';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { createLoggerFactory } from './logger';
|
||||
import { findAllDeps } from './packages';
|
||||
import { startWatchers } from './watcher';
|
||||
import { getPackageDeps } from './packages';
|
||||
import { startWatcher, startPackageWatcher } from './watcher';
|
||||
import { startCompiler } from './compiler';
|
||||
import { startChild } from './child';
|
||||
|
||||
@@ -26,10 +25,6 @@ const WATCH_LOCATIONS = ['package.json', 'src', 'assets'];
|
||||
*/
|
||||
export default async (_command: any, args: string[]) => {
|
||||
const localPackagePath = resolvePath('package.json');
|
||||
const packageJson = JSON.parse(readFileSync(localPackagePath, 'utf8'));
|
||||
|
||||
// Find all direct and transitive local dependencies of the current package.
|
||||
const allDeps = await findAllDeps(packageJson.name, PACKAGE_BLACKLIST);
|
||||
|
||||
// Rotate through different prefix colors to make it easier to differenciate between different deps
|
||||
const logFactory = createLoggerFactory([
|
||||
@@ -40,14 +35,22 @@ export default async (_command: any, args: string[]) => {
|
||||
chalk.cyan,
|
||||
]);
|
||||
|
||||
// Find all direct and transitive local dependencies of the current package.
|
||||
const deps = await getPackageDeps(localPackagePath, PACKAGE_BLACKLIST);
|
||||
|
||||
// We lazily watch all our deps, as in we don't start the actual watch compiler until a change is detected
|
||||
await startWatchers(allDeps, WATCH_LOCATIONS, pkg => {
|
||||
const watcher = await startWatcher(deps, WATCH_LOCATIONS, pkg => {
|
||||
startCompiler(pkg, logFactory(pkg.name)).promise.catch(error => {
|
||||
process.stderr.write(`${error}\n`);
|
||||
});
|
||||
});
|
||||
|
||||
if (args.length) {
|
||||
await startPackageWatcher(localPackagePath, async () => {
|
||||
const newDeps = await getPackageDeps(localPackagePath, PACKAGE_BLACKLIST);
|
||||
await watcher.update(newDeps);
|
||||
});
|
||||
|
||||
if (args?.length) {
|
||||
startChild(args);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { resolve as resolvePath } from 'path';
|
||||
import fs from 'fs';
|
||||
import { promisify } from 'util';
|
||||
|
||||
const readFile = promisify(fs.readFile);
|
||||
|
||||
const LernaProject = require('@lerna/project');
|
||||
const PackageGraph = require('@lerna/package-graph');
|
||||
@@ -44,3 +48,10 @@ export async function findAllDeps(
|
||||
|
||||
return [...deps.values()];
|
||||
}
|
||||
|
||||
export async function getPackageDeps(packagePath: string, blacklist: string[]) {
|
||||
const packageData = await readFile(packagePath, 'utf8');
|
||||
const packageJson = JSON.parse(packageData);
|
||||
|
||||
return await findAllDeps(packageJson.name, blacklist);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { resolve as resolvePath } from 'path';
|
||||
import chalk from 'chalk';
|
||||
import chokidar from 'chokidar';
|
||||
import { Package } from './packages';
|
||||
import { createLogger } from './logger';
|
||||
|
||||
export type Watcher = {
|
||||
update(newPackages: Package[]): Promise<void>;
|
||||
};
|
||||
|
||||
/*
|
||||
* Watch for changes inside a collection of packages. When a change is detected, stop
|
||||
@@ -8,15 +14,17 @@ import { Package } from './packages';
|
||||
*
|
||||
* The returned promise is resolved once all watchers are ready.
|
||||
*/
|
||||
export async function startWatchers(
|
||||
export async function startWatcher(
|
||||
packages: Package[],
|
||||
paths: string[],
|
||||
callback: (pkg: Package) => void,
|
||||
): Promise<void> {
|
||||
const readyPromises = [];
|
||||
): Promise<Watcher> {
|
||||
const watchedPackageLocations = new Set<string>();
|
||||
const logger = createLogger();
|
||||
|
||||
for (const pkg of packages) {
|
||||
const watchPackage = async (pkg: Package) => {
|
||||
let signalled = false;
|
||||
watchedPackageLocations.add(pkg.location);
|
||||
|
||||
const watchLocations = paths.map(path => resolvePath(pkg.location, path));
|
||||
const watcher = chokidar
|
||||
@@ -33,13 +41,69 @@ export async function startWatchers(
|
||||
watcher.close();
|
||||
});
|
||||
|
||||
readyPromises.push(
|
||||
new Promise((resolve, reject) => {
|
||||
watcher.on('ready', resolve);
|
||||
watcher.on('error', reject);
|
||||
}),
|
||||
);
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
watcher.on('ready', resolve);
|
||||
watcher.on('error', reject);
|
||||
});
|
||||
};
|
||||
|
||||
await Promise.all(readyPromises);
|
||||
const update = async (newPackages: Package[]) => {
|
||||
const promises = new Array<Promise<unknown>>();
|
||||
|
||||
for (const pkg of newPackages) {
|
||||
if (watchedPackageLocations.has(pkg.location)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
logger.out(chalk.green(`Starting watch of new dependency ${pkg.name}`));
|
||||
promises.push(watchPackage(pkg));
|
||||
}
|
||||
|
||||
await Promise.all(promises);
|
||||
};
|
||||
|
||||
await Promise.all(packages.map(watchPackage));
|
||||
|
||||
return { update };
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch a package.json for updates
|
||||
*/
|
||||
export function startPackageWatcher(packagePath: string, callback: () => void) {
|
||||
let changed = false;
|
||||
let working = false;
|
||||
|
||||
const notifyDeps = async () => {
|
||||
changed = true;
|
||||
if (working) {
|
||||
return;
|
||||
}
|
||||
working = true;
|
||||
changed = false;
|
||||
|
||||
try {
|
||||
await callback();
|
||||
} finally {
|
||||
working = false;
|
||||
// Keep going if a change was emitted while working
|
||||
if (changed) {
|
||||
notifyDeps();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const watcher = chokidar
|
||||
.watch(packagePath, {
|
||||
ignoreInitial: true,
|
||||
disableGlobbing: true,
|
||||
})
|
||||
.on('all', () => {
|
||||
notifyDeps();
|
||||
});
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
watcher.on('ready', resolve);
|
||||
watcher.on('error', reject);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user