Repository: rollup/rollup
File: src/rollup/rollup.ts (TypeScript)

Code before:
	initialiseTimers(inputOptions);

	await initWasm();

	const graph = new Graph(inputOptions, watcher);

	// remove the cache object from the memory after graph creation (cache is not used anymore)
	const useCache = rawInputOptions.cache !== false;
	if (rawInputOptions.cache) {
		inputOptions.cache = undefined;
		rawInputOptions.cache = undefined;
	}

	timeStart('BUILD', 1);

	await catchUnfinishedHookActions(graph.pluginDriver, async () => {
		try {
			timeStart('initialize', 2);
			await graph.pluginDriver.hookParallel('buildStart', [inputOptions]);
			timeEnd('initialize', 2);
			await graph.build();
		} catch (error_: any) {
			const watchFiles = Object.keys(graph.watchFiles);
			if (watchFiles.length > 0) {
				error_.watchFiles = watchFiles;
			}
			try {
				await graph.pluginDriver.hookParallel('buildEnd', [error_]);
			} catch (buildEndError: any) {
				await graph.pluginDriver.hookParallel('closeBundle', [buildEndError]);
				throw buildEndError;
			}
			await graph.pluginDriver.hookParallel('closeBundle', [error_]);
			throw error_;
		}
		try {
			await graph.pluginDriver.hookParallel('buildEnd', []);
		} catch (buildEndError: any) {
			await graph.pluginDriver.hookParallel('closeBundle', [buildEndError]);
			throw buildEndError;
		}
	});

	timeEnd('BUILD', 1);

	const result: RollupBuild = {
		cache: useCache ? graph.getCache() : undefined,
		async close() {
			if (result.closed) return;

			result.closed = true;

Review comment (suggestion):
If we throw another error, we should also assign watchFiles if they are present. This is needed in watchMode to keep tracking changed files in case of errors.

Code after:
	);
	initialiseTimers(inputOptions);

	await initWasm();

	const graph = new Graph(inputOptions, watcher);

	// remove the cache object from the memory after graph creation (cache is not used anymore)
	const useCache = rawInputOptions.cache !== false;
	if (rawInputOptions.cache) {
		inputOptions.cache = undefined;
		rawInputOptions.cache = undefined;
	}

	timeStart('BUILD', 1);

	await catchUnfinishedHookActions(graph.pluginDriver, async () => {
		try {
			timeStart('initialize', 2);
			await graph.pluginDriver.hookParallel('buildStart', [inputOptions]);
			timeEnd('initialize', 2);
			await graph.build();
		} catch (error_: any) {
			const watchFiles = Object.keys(graph.watchFiles);
			if (watchFiles.length > 0) {
				error_.watchFiles = watchFiles;
			}
			try {
				await graph.pluginDriver.hookParallel('buildEnd', [error_]);
			} catch (buildEndError: any) {
				// Create a compound error object to include both errors, based on the original error
				const compoundError = getRollupError({
					...error_,
					message: `There was an error during the build:\n  ${error_.message}\nAdditionally, handling the error in the 'buildEnd' hook caused the following error:\n  ${buildEndError.message}`
				});
				await graph.pluginDriver.hookParallel('closeBundle', [compoundError]);
				throw compoundError;
			}
			await graph.pluginDriver.hookParallel('closeBundle', [error_]);
			throw error_;
		}
		try {
			await graph.pluginDriver.hookParallel('buildEnd', []);
		} catch (buildEndError: any) {
			await graph.pluginDriver.hookParallel('closeBundle', [buildEndError]);
			throw buildEndError;
		}
	});

	timeEnd('BUILD', 1);