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

Code before:
			}
			await graph.pluginDriver.hookParallel('buildEnd', [error_]);
			await graph.pluginDriver.hookParallel('closeBundle', []);
			throw error_;
		}
		await graph.pluginDriver.hookParallel('buildEnd', []);
	});

	timeEnd('BUILD', 1);

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

			result.closed = true;

			await graph.pluginDriver.hookParallel('closeBundle', []);
		},
		closed: false,
		async [Symbol.asyncDispose]() {
			await this.close();
		},
		async generate(rawOutputOptions: OutputOptions) {
			if (result.closed) return error(logAlreadyClosed());

			return handleGenerateWrite(false, inputOptions, unsetInputOptions, rawOutputOptions, graph);
		},
		get watchFiles() {
			return Object.keys(graph.watchFiles);
		},
		async write(rawOutputOptions: OutputOptions) {
			if (result.closed) return error(logAlreadyClosed());

			return handleGenerateWrite(true, inputOptions, unsetInputOptions, rawOutputOptions, graph);
		}
	};
	if (inputOptions.perf) result.getTimings = getTimings;
	return result;
}

async function getInputOptions(
	initialInputOptions: InputOptions,
	watchMode: boolean
): Promise<{ options: NormalizedInputOptions; unsetOptions: Set<string> }> {
	if (!initialInputOptions) {
		throw new Error('You must supply an options object to rollup');
	}
	const processedInputOptions = await getProcessedInputOptions(initialInputOptions, watchMode);
	const { options, unsetOptions } = await normalizeInputOptions(processedInputOptions, watchMode);
	normalizePlugins(options.plugins, ANONYMOUS_PLUGIN_PREFIX);

Review comment (suggestion):
As this feature at the moment is not widely supported, we should probably do what the [TypeScript docs](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management) state and polyfill the Symbol before using it, otherwise this will be a nice "undefined" property:

```js
Symbol.asyncDispose ??= Symbol("Symbol.asyncDispose");
```

I think this should be safe to do. Apparently, Node [supports at least the Symbols](https://github.com/nodejs/node/pull/48518) since [v20.4.0](https://nodejs.org/en/blog/release/v20.4.0#support-to-the-explicit-resource-management-proposal), but browsers do not and the polyfill is cheap.

Code after:
			const watchFiles = Object.keys(graph.watchFiles);
			if (watchFiles.length > 0) {
				error_.watchFiles = watchFiles;
			}
			await graph.pluginDriver.hookParallel('buildEnd', [error_]);
			await graph.pluginDriver.hookParallel('closeBundle', []);
			throw error_;
		}
		await graph.pluginDriver.hookParallel('buildEnd', []);
	});

	timeEnd('BUILD', 1);

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

			result.closed = true;

			await graph.pluginDriver.hookParallel('closeBundle', []);
		},
		closed: false,
		async [Symbol.asyncDispose]() {
			await this.close();
		},
		async generate(rawOutputOptions: OutputOptions) {
			if (result.closed) return error(logAlreadyClosed());

			return handleGenerateWrite(false, inputOptions, unsetInputOptions, rawOutputOptions, graph);
		},
		get watchFiles() {
			return Object.keys(graph.watchFiles);
		},
		async write(rawOutputOptions: OutputOptions) {
			if (result.closed) return error(logAlreadyClosed());

			return handleGenerateWrite(true, inputOptions, unsetInputOptions, rawOutputOptions, graph);
		}
	};
	if (inputOptions.perf) result.getTimings = getTimings;
	return result;
}

async function getInputOptions(
	initialInputOptions: InputOptions,
	watchMode: boolean
): Promise<{ options: NormalizedInputOptions; unsetOptions: Set<string> }> {
	if (!initialInputOptions) {
		throw new Error('You must supply an options object to rollup');
	}