function fullResolve(modulePath, sourceFile, settings) {
  // check if this is a bonus core module
  const coreSet = new Set(settings['import/core-modules']);
  if (coreSet.has(modulePath)) { return { found: true, path: null }; }

  const sourceDir = path.dirname(sourceFile);

  if (prevSettings !== settings) {
    memoizedHash = hashObject(settings).digest('hex');
    prevSettings = settings;
  }

  const cacheKey = sourceDir + memoizedHash + modulePath;

  const cacheSettings = ModuleCache.getSettings(settings);

  const cachedPath = fileExistsCache.get(cacheKey, cacheSettings);
  if (cachedPath !== undefined) { return { found: true, path: cachedPath }; }

  /** @type {(resolvedPath: string | null) => void} */
  function cache(resolvedPath) {
    fileExistsCache.set(cacheKey, resolvedPath);
  }

  /** @type {(resolver: import('./resolve').Resolver, config: unknown) => import('./resolve').ResolvedResult} */
  function withResolver(resolver, config) {
    if (resolver.interfaceVersion === 2) {
      return resolver.resolve(modulePath, sourceFile, config);
    }

    try {
      const resolved = resolver.resolveImport(modulePath, sourceFile, config);
      if (resolved === undefined) { return { found: false }; }
      return { found: true, path: resolved };
    } catch (err) {
      return { found: false };
    }
  }

  const configResolvers = settings['import/resolver']
    || { node: settings['import/resolve'] }; // backward compatibility

  const resolvers = resolverReducer(configResolvers, new Map());

  for (const pair of resolvers) {
    const name = pair[0];
    const config = pair[1];
    const resolver = requireResolver(name, sourceFile);
    const resolved = withResolver(resolver, config);

    if (!resolved.found) { continue; }

    // else, counts
    cache(resolved.path);
    return resolved;
  }

  // failed
  // cache(undefined)
  return { found: false };
}
@type {(modulePath: string, sourceFile: string, settings: import('./types').ESLintSettings) => import('./resolve').ResolvedResult}