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

Code before:
import type { CustomPluginOptions, Plugin, PluginContext, ResolveIdResult } from '../rollup/types';
import type { PluginDriver, ReplaceContext } from './PluginDriver';
import { BLANK, EMPTY_OBJECT } from './blank';

export function resolveIdViaPlugins(
	source: string,
	importer: string | undefined,
	pluginDriver: PluginDriver,
	moduleLoaderResolveId: ModuleLoaderResolveId,
	skip: readonly { importer: string | undefined; plugin: Plugin; source: string }[] | null,
	customOptions: CustomPluginOptions | undefined,
	isEntry: boolean,
	attributes: Record<string, string>
): Promise<[NonNullable<ResolveIdResult>, Plugin] | null> {
	let skipped: Set<Plugin> | null = null;
	let replaceContext: ReplaceContext | null = null;
	if (skip) {
		skipped = new Set();
		for (const skippedCall of skip) {
			if (source === skippedCall.source && importer === skippedCall.importer) {
				skipped.add(skippedCall.plugin);
			}
		}
		replaceContext = (pluginContext, plugin): PluginContext => ({
			...pluginContext,
			// @ts-expect-error returns null instead of Promise.resolve(null) by design
			resolve: (source, importer, { attributes, custom, isEntry, skipSelf } = BLANK) => {
				skipSelf ??= true;
				if (
					skipSelf &&
					skip.findIndex(skippedCall => {
						return (
							skippedCall.plugin === plugin &&
							skippedCall.source === source &&
							skippedCall.importer === importer
						);
					}) !== -1
				) {
					// This means that the plugin recursively called itself
					// Thus returning null in purpose of fallback to default behavior of `resolveId` plugin hook.
					// FYI: https://rollupjs.org/plugin-development/#resolveid:~:text=Returning%20null%20defers%20to%20other%20resolveId%20functions%20and%20eventually%20the%20default%20resolution%20behavior.
					return null;
				}
				return moduleLoaderResolveId(
					source,
					importer,
					custom,
					isEntry,
					attributes || EMPTY_OBJECT,
					skipSelf ? [...skip, { importer, plugin, source }] : skip
				);

Review comment (suggestion):
If you return null, wouldn't this defeat the assumption that this.resolve returns a Promise? This would breaks plugins that append a .then(...) to this.resolve. The link you added below is about the `resolveId` plugin hook, and not the `this.resolve` context function. Also, I definitely do not like `@ts-expect-error` as it completely turns off type checking and easily hides legitimate errors.

Code after:
import type { CustomPluginOptions, Plugin, PluginContext, ResolveIdResult } from '../rollup/types';
import type { PluginDriver, ReplaceContext } from './PluginDriver';
import { BLANK, EMPTY_OBJECT } from './blank';

export function resolveIdViaPlugins(
	source: string,
	importer: string | undefined,
	pluginDriver: PluginDriver,
	moduleLoaderResolveId: ModuleLoaderResolveId,
	skip: readonly { importer: string | undefined; plugin: Plugin; source: string }[] | null,
	customOptions: CustomPluginOptions | undefined,
	isEntry: boolean,
	attributes: Record<string, string>
): Promise<[NonNullable<ResolveIdResult>, Plugin] | null> {
	let skipped: Set<Plugin> | null = null;
	let replaceContext: ReplaceContext | null = null;
	if (skip) {
		skipped = new Set();
		for (const skippedCall of skip) {
			if (source === skippedCall.source && importer === skippedCall.importer) {
				skipped.add(skippedCall.plugin);
			}
		}
		replaceContext = (pluginContext, plugin): PluginContext => ({
			...pluginContext,
			resolve: (source, importer, { attributes, custom, isEntry, skipSelf } = BLANK) => {
				skipSelf ??= true;
				if (
					skipSelf &&
					skip.findIndex(skippedCall => {
						return (
							skippedCall.plugin === plugin &&
							skippedCall.source === source &&
							skippedCall.importer === importer
						);
					}) !== -1
				) {
					// This means that the plugin recursively called itself
					// Thus returning Promise.resolve(null) in purpose of fallback to default behavior of `resolveId` plugin hook.
					return Promise.resolve(null);
				}
				return moduleLoaderResolveId(
					source,
					importer,
					custom,
					isEntry,
					attributes || EMPTY_OBJECT,
					skipSelf ? [...skip, { importer, plugin, source }] : skip
				);
			}
		});