Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configurable Build Delay (Currently Hardcoded at 200ms) #3502

Merged
merged 4 commits into from
May 10, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions src/rollup/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,7 @@ export interface RollupWatcher
event: (event: RollupWatcherEvent) => void;
restart: () => void;
}> {
delay?: number;
close(): void;
}

Expand Down
39 changes: 23 additions & 16 deletions src/watch/watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,16 @@ import {
RollupBuild,
RollupCache,
RollupWatcher,
WatcherOptions,
WatcherOptions
} from '../rollup/types';
import { mergeOptions } from '../utils/mergeOptions';
import { ensureArray, GenericConfigObject } from '../utils/parseOptions';
import { FileWatcher } from './fileWatcher';

const DELAY = 200;

export class Watcher {
emitter: RollupWatcher;

private buildDelay = 0;
private buildTimeout: NodeJS.Timer | null = null;
private invalidatedIds: Set<string> = new Set();
private rerun = false;
Expand All @@ -27,7 +26,15 @@ export class Watcher {
constructor(configs: GenericConfigObject[] | GenericConfigObject, emitter: RollupWatcher) {
this.emitter = emitter;
emitter.close = this.close.bind(this);
this.tasks = ensureArray(configs).map((config) => new Task(this, config));
const configArray = ensureArray(configs);
this.tasks = configArray.map(config => new Task(this, config));
this.buildDelay = configArray.reduce(
(buildDelay, { watch }: any) =>
watch && typeof watch.buildDelay === 'number'
? Math.max(buildDelay, watch.buildDelay)
: buildDelay,
this.buildDelay
);
this.running = true;
process.nextTick(() => this.run());
}
Expand Down Expand Up @@ -63,14 +70,14 @@ export class Watcher {
this.invalidatedIds.clear();
this.emit('restart');
this.run();
}, DELAY);
}, this.buildDelay);
}

private async run() {
this.running = true;

this.emit('event', {
code: 'START',
code: 'START'
});

try {
Expand All @@ -79,13 +86,13 @@ export class Watcher {
}
this.running = false;
this.emit('event', {
code: 'END',
code: 'END'
});
} catch (error) {
this.running = false;
this.emit('event', {
code: 'ERROR',
error,
error
});
}

Expand Down Expand Up @@ -119,7 +126,7 @@ export class Task {
this.skipWrite = config.watch && !!(config.watch as GenericConfigObject).skipWrite;
this.options = mergeOptions(config);
this.outputs = this.options.output;
this.outputFiles = this.outputs.map((output) => {
this.outputFiles = this.outputs.map(output => {
if (output.file || output.dir) return path.resolve(output.file || output.dir!);
return undefined as any;
});
Expand All @@ -129,7 +136,7 @@ export class Task {
this.fileWatcher = new FileWatcher(this, {
...watchOptions.chokidar,
disableGlobbing: true,
ignoreInitial: true,
ignoreInitial: true
});
}

Expand All @@ -156,15 +163,15 @@ export class Task {

const options = {
...this.options,
cache: this.cache,
cache: this.cache
};

const start = Date.now();

this.watcher.emit('event', {
code: 'BUNDLE_START',
input: this.options.input,
output: this.outputFiles,
output: this.outputFiles
});

try {
Expand All @@ -173,13 +180,13 @@ export class Task {
return;
}
this.updateWatchedFiles(result);
this.skipWrite || (await Promise.all(this.outputs.map((output) => result.write(output))));
this.skipWrite || (await Promise.all(this.outputs.map(output => result.write(output))));
this.watcher.emit('event', {
code: 'BUNDLE_END',
duration: Date.now() - start,
input: this.options.input,
output: this.outputFiles,
result,
result
});
} catch (error) {
if (this.closed) {
Expand All @@ -192,7 +199,7 @@ export class Task {
}
}
if (error.id) {
this.cache.modules = this.cache.modules.filter((module) => module.id !== error.id);
this.cache.modules = this.cache.modules.filter(module => module.id !== error.id);
}
throw error;
}
Expand Down Expand Up @@ -222,7 +229,7 @@ export class Task {
if (!this.filter(id)) return;
this.watched.add(id);

if (this.outputFiles.some((file) => file === id)) {
if (this.outputFiles.some(file => file === id)) {
throw new Error('Cannot import the generated bundle');
}

Expand Down
154 changes: 110 additions & 44 deletions test/watch/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ describe('rollup.watch', () => {
});
}

function getTimeDiffInMs(previous) {
const [seconds, nanoseconds] = process.hrtime(previous);
return seconds * 1e3 + nanoseconds / 1e6;
}

it('watches a file and triggers reruns if necessary', () => {
let triggerRestart = false;

Expand Down Expand Up @@ -491,46 +496,6 @@ describe('rollup.watch', () => {
});
});

it('recovers from an error even when erroring dependency was "renamed" (#38)', () => {
return sander
.copydir('test/watch/samples/dependency')
.to('test/_tmp/input')
.then(() => {
watcher = rollup.watch({
input: 'test/_tmp/input/main.js',
output: {
file: 'test/_tmp/output/bundle.js',
format: 'cjs'
}
});

return sequence(watcher, [
'START',
'BUNDLE_START',
'BUNDLE_END',
'END',
() => {
assert.strictEqual(run('../_tmp/output/bundle.js'), 43);
sander.unlinkSync('test/_tmp/input/dep.js');
sander.writeFileSync('test/_tmp/input/dep.js', 'export nope;');
},
'START',
'BUNDLE_START',
'ERROR',
() => {
sander.unlinkSync('test/_tmp/input/dep.js');
sander.writeFileSync('test/_tmp/input/dep.js', 'export const value = 43;');
},
'START',
'BUNDLE_START',
'BUNDLE_END',
'END',
() => {
assert.strictEqual(run('../_tmp/output/bundle.js'), 44);
}
]);
});
});
it('handles closing the watcher during a build', () => {
return sander
.copydir('test/watch/samples/basic')
Expand Down Expand Up @@ -1074,6 +1039,110 @@ describe('rollup.watch', () => {
});
});

it('rebuilds immediately by default', async () => {
await sander.copydir('test/watch/samples/basic').to('test/_tmp/input');
watcher = rollup.watch({
input: 'test/_tmp/input/main.js',
output: {
file: 'test/_tmp/output/bundle.js',
format: 'cjs'
}
});

let startTime;
return sequence(watcher, [
'START',
'BUNDLE_START',
'BUNDLE_END',
'END',
() => {
assert.strictEqual(run('../_tmp/output/bundle.js'), 42);
sander.writeFileSync('test/_tmp/input/main.js', 'export default 43;');
startTime = process.hrtime();
},
'START',
'BUNDLE_START',
'BUNDLE_END',
'END',
() => {
assert.strictEqual(run('../_tmp/output/bundle.js'), 43);
const timeDiff = getTimeDiffInMs(startTime);
assert.ok(timeDiff < 400, `Time difference ${timeDiff} < 400`);
}
]);
});

it('observes configured build delays', async () => {
await sander.copydir('test/watch/samples/basic').to('test/_tmp/input');
watcher = rollup.watch([
{
input: 'test/_tmp/input/main.js',
output: {
file: 'test/_tmp/output/bundle.js',
format: 'cjs'
}
},
{
input: 'test/_tmp/input/main.js',
watch: { clearScreen: true },
output: {
file: 'test/_tmp/output/bundle.js',
format: 'cjs'
}
},
{
input: 'test/_tmp/input/main.js',
watch: { buildDelay: 1000 },
output: {
file: 'test/_tmp/output/bundle.js',
format: 'cjs'
}
},
{
input: 'test/_tmp/input/main.js',
watch: { buildDelay: 50 },
output: {
file: 'test/_tmp/output/bundle.js',
format: 'cjs'
}
}
]);

let startTime;
return sequence(watcher, [
'START',
'BUNDLE_START',
'BUNDLE_END',
'BUNDLE_START',
'BUNDLE_END',
'BUNDLE_START',
'BUNDLE_END',
'BUNDLE_START',
'BUNDLE_END',
'END',
() => {
assert.strictEqual(run('../_tmp/output/bundle.js'), 42);
sander.writeFileSync('test/_tmp/input/main.js', 'export default 43;');
startTime = process.hrtime();
},
'START',
'BUNDLE_START',
'BUNDLE_END',
'BUNDLE_START',
'BUNDLE_END',
'BUNDLE_START',
'BUNDLE_END',
'BUNDLE_START',
'BUNDLE_END',
'END',
() => {
assert.strictEqual(run('../_tmp/output/bundle.js'), 43);
const timeDiff = getTimeDiffInMs(startTime);
assert.ok(timeDiff > 1000, `Time difference ${timeDiff} > 1000`);
}
]);
});

describe('addWatchFile', () => {
it('supports adding additional watch files in plugin hooks', () => {
const watchChangeIds = [];
Expand Down Expand Up @@ -1256,10 +1325,7 @@ describe('rollup.watch', () => {
transform(code, id) {
if (id.endsWith('dep1.js')) {
this.addWatchFile(path.resolve('test/_tmp/input/dep2.js'));
const text = sander
.readFileSync('test/_tmp/input/dep2.js')
.toString()
.trim();
const text = sander.readFileSync('test/_tmp/input/dep2.js').toString().trim();
return `export default ${JSON.stringify(text)}`;
}
}
Expand Down