Skip to content

Commit

Permalink
Immediately restart Rollup upon config file changes
Browse files Browse the repository at this point in the history
and change test to look for a message from the second config
  • Loading branch information
lukastaegert committed Jan 14, 2022
1 parent 88aa452 commit 1f084ff
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 61 deletions.
2 changes: 1 addition & 1 deletion cli/logging.ts
Expand Up @@ -3,7 +3,7 @@ import { bold, cyan, dim, red } from '../src/utils/colors';
import relativeId from '../src/utils/relativeId';

// log to stderr to keep `rollup main.js > bundle.js` from breaking
export const stderr = console.error.bind(console);
export const stderr = (...args: unknown[]) => process.stderr.write(`${args.join('')}\n`);

export function handleError(err: RollupError, recover = false): void {
let description = err.message || err;
Expand Down
37 changes: 16 additions & 21 deletions cli/run/watch-cli.ts
Expand Up @@ -23,6 +23,7 @@ export async function watch(command: Record<string, any>): Promise<void> {
let warnings: BatchWarnings;
let watcher: RollupWatcher;
let configWatcher: FSWatcher;
let resetScreen: (heading: string) => void;
const configFile = command.config ? getConfigPath(command.config) : null;

onExit(close);
Expand All @@ -33,11 +34,10 @@ export async function watch(command: Record<string, any>): Promise<void> {
}

async function loadConfigFromFileAndTrack(configFile: string): Promise<void> {
let reloadingConfig = false;
let aborted = false;
let configFileData: string | null = null;
let configFileRevision = 0;

configWatcher = chokidar.watch(configFile).on('change', () => reloadConfigFile());
configWatcher = chokidar.watch(configFile).on('change', reloadConfigFile);
await reloadConfigFile();

async function reloadConfigFile() {
Expand All @@ -46,29 +46,23 @@ export async function watch(command: Record<string, any>): Promise<void> {
if (newConfigFileData === configFileData) {
return;
}
if (reloadingConfig) {
aborted = true;
return;
}
configFileRevision++;
const currentConfigFileRevision = configFileRevision;
if (configFileData) {
stderr(`\nReloading updated config...`);
}
configFileData = newConfigFileData;
reloadingConfig = true;
({ options: configs, warnings } = await loadAndParseConfigFile(configFile, command));
reloadingConfig = false;
if (aborted) {
aborted = false;
reloadConfigFile();
} else {
if (watcher) {
watcher.close();
}
start(configs);
const parsedConfig = await loadAndParseConfigFile(configFile, command);
if (currentConfigFileRevision !== configFileRevision) {
return;
}
if (watcher) {
watcher.close();
}
({ options: configs, warnings } = parsedConfig);
start(configs);
} catch (err: any) {
configs = [];
reloadingConfig = false;
handleError(err, true);
}
}
Expand All @@ -81,8 +75,6 @@ export async function watch(command: Record<string, any>): Promise<void> {
start(configs);
}

const resetScreen = getResetScreen(configs!, isTTY);

function start(configs: MergedRollupOptions[]): void {
try {
watcher = rollup.watch(configs as any);
Expand All @@ -99,6 +91,9 @@ export async function watch(command: Record<string, any>): Promise<void> {

case 'START':
if (!silent) {
if (!resetScreen) {
resetScreen = getResetScreen(configs!, isTTY);
}
resetScreen(underline(`rollup v${rollup.VERSION}`));
}
break;
Expand Down
70 changes: 31 additions & 39 deletions test/cli/samples/watch/watch-config-early-update/_config.js
Expand Up @@ -5,57 +5,50 @@ const { writeAndSync } = require('../../../../utils');
let configFile;

module.exports = {
solo: true,
repeat: 100,
description: 'immediately reloads the config file if a change happens while it is parsed',
command: 'rollup -cw',
before() {
// This test writes a config file that prints a message to stderr but delays resolving to a
// config. The stderr message is observed by the parent process and triggers overwriting the
// config file. That way, we simulate a complicated config file being changed while it is parsed.
configFile = path.resolve(__dirname, 'rollup.config.js');
console.time('testTime');
// This test writes a config file that prints a message to stderr which signals to the test that
// the config files has been parsed, at which point the test replaces the config file. The
// initial file returns a Promise that only resolves once the second config file has been
// parsed. To do that, the first config hooks into process.stderr and looks for a log from the
// second config.
// That way, we simulate a complicated config file being changed while it is parsed.
configFile = path.join(__dirname, 'rollup.config.js');
fs.mkdirSync(path.join(__dirname, '_actual'));
writeAndSync(
configFile,
`
import { watch } from 'fs';
let watcher;
// Sometimes, fs.watch does not seem to trigger on MacOS. Thus, we wait at most 5 seconds.
export default Promise.race([
new Promise(resolve => {
watcher = watch(${JSON.stringify(configFile)}, () => {
console.error('config update detected');
watcher.close();
watcher = null;
// wait a moment to make sure we do not trigger before Rollup's watcher
setTimeout(resolve, 600);
})
}),
new Promise(resolve => setTimeout(() => {
if (watcher) {
watcher.close();
};
resolve();
}, 5000))
]).then(() => ({
input: 'main.js',
output: {
file: '_actual/output1.js',
format: 'es'
}
}));
console.error('initial');
`
import { Writable } from 'stream';
process.stderr.write('initial\\n');
const processStderr = process.stderr;
export default new Promise(resolve => {
delete process.stderr;
process.stderr = new Writable({
write(chunk, encoding, next) {
processStderr.write(chunk, encoding, next);
if (chunk.toString() === 'updated\\n') {
process.stderr.end();
process.stderr = processStderr;
resolve({
input: 'main.js',
output: {
file: '_actual/output1.js',
format: 'es'
}
})
}
},
});
});`
);
return new Promise(resolve => setTimeout(resolve, 600));
},
after() {
console.timeEnd('testTime');
fs.unlinkSync(configFile);
},
abortOnStderr(data) {
console.log('data:', data);
if (data === 'initial\n') {
writeAndSync(
configFile,
Expand All @@ -67,8 +60,7 @@ module.exports = {
file: '_actual/output2.js',
format: "es"
}
};
`
};`
);
return false;
}
Expand Down

0 comments on commit 1f084ff

Please sign in to comment.