Repository: nuxt/nuxt
File: packages/kit/src/internal/esm.ts (TypeScript)

Code before:
import { fileURLToPath, pathToFileURL } from 'node:url'
import { interopDefault, resolvePath, resolvePathSync } from 'mlly'
import { createJiti } from 'jiti'
import { captureStackTrace } from 'errx'

export interface ResolveModuleOptions {
  /** @deprecated use `url` with URLs pointing at a file - never a directory */
  paths?: string | string[]
  url?: URL | URL[]
}

export function directoryToParentURL (directory: string) {
  return new URL('./index.js', pathToFileURL(directory.replace(/\/?$/, '/')))
}

const importMetaURL = new URL(import.meta.url)

/**
 * Resolve a module from a given root path using an algorithm patterned on
 * the upcoming `import.meta.resolve`. It returns a file URL
 *
 * @internal
 */
export async function tryResolveModule (id: string, url: URL | URL[]): Promise<string | undefined>
/** @deprecated pass URLs pointing at files */
export async function tryResolveModule (id: string, url: string | string[]): Promise<string | undefined>
export async function tryResolveModule (id: string, url: string | string[] | URL | URL[] = importMetaURL) {
  try {
    return await resolvePath(id, { url })
  } catch {
    // intentionally empty as this is a `try-` function
  }
}

export function resolveModule (id: string, options?: ResolveModuleOptions) {
  return resolvePathSync(id, { url: options?.url ?? options?.paths ?? [importMetaURL] })
}


Review comment (bug):
I think this could be simplified. We don't need a `URL` instance but simple string starting with `file://`.

If this util can also trust input is a dir path, we can use `pathToFileURL(join(dir, '_index.js'))` (which returns a string). 

(PS: I would call stub name something distinguished from normal `index.js` mainly for debugging)

Code after:
import { fileURLToPath, pathToFileURL } from 'node:url'
import { interopDefault, resolvePath, resolvePathSync } from 'mlly'
import { createJiti } from 'jiti'
import { captureStackTrace } from 'errx'

export interface ResolveModuleOptions {
  /** @deprecated use `url` with URLs pointing at a file - never a directory */
  paths?: string | string[]
  url?: URL | URL[]
}

export function directoryToURL (dir: string): URL {
  return pathToFileURL(dir + '/')
}

/**
 * Resolve a module from a given root path using an algorithm patterned on
 * the upcoming `import.meta.resolve`. It returns a file URL
 *
 * @internal
 */
export async function tryResolveModule (id: string, url: URL | URL[]): Promise<string | undefined>
/** @deprecated pass URLs pointing at files */
export async function tryResolveModule (id: string, url: string | string[]): Promise<string | undefined>
export async function tryResolveModule (id: string, url: string | string[] | URL | URL[] = import.meta.url) {
  try {
    return await resolvePath(id, { url })
  } catch {
    // intentionally empty as this is a `try-` function
  }
}

export function resolveModule (id: string, options?: ResolveModuleOptions) {
  return resolvePathSync(id, { url: options?.url ?? options?.paths ?? [import.meta.url] })
}

export interface ImportModuleOptions extends ResolveModuleOptions {
  /** Automatically de-default the result of requiring the module. */