(tsNodeService: Service)
| 119 | } |
| 120 | |
| 121 | export function createEsmHooks(tsNodeService: Service) { |
| 122 | tsNodeService.enableExperimentalEsmLoaderInterop(); |
| 123 | |
| 124 | // Custom implementation that considers additional file extensions and automatically adds file extensions |
| 125 | const nodeResolveImplementation = tsNodeService.getNodeEsmResolver(); |
| 126 | const nodeGetFormatImplementation = tsNodeService.getNodeEsmGetFormat(); |
| 127 | const extensions = tsNodeService.extensions; |
| 128 | |
| 129 | const hooksAPI = filterHooksByAPIVersion({ |
| 130 | resolve, |
| 131 | load, |
| 132 | getFormat, |
| 133 | transformSource, |
| 134 | }); |
| 135 | |
| 136 | function isFileUrlOrNodeStyleSpecifier(parsed: UrlWithStringQuery) { |
| 137 | // We only understand file:// URLs, but in node, the specifier can be a node-style `./foo` or `foo` |
| 138 | const { protocol } = parsed; |
| 139 | return protocol === null || protocol === 'file:'; |
| 140 | } |
| 141 | |
| 142 | /** |
| 143 | * Named "probably" as a reminder that this is a guess. |
| 144 | * node does not explicitly tell us if we're resolving the entrypoint or not. |
| 145 | */ |
| 146 | function isProbablyEntrypoint(specifier: string, parentURL: string) { |
| 147 | return parentURL === undefined && specifier.startsWith('file://'); |
| 148 | } |
| 149 | // Side-channel between `resolve()` and `load()` hooks |
| 150 | const rememberIsProbablyEntrypoint = new Set(); |
| 151 | const rememberResolvedViaCommonjsFallback = new Set(); |
| 152 | |
| 153 | async function resolve( |
| 154 | specifier: string, |
| 155 | context: { parentURL: string }, |
| 156 | defaultResolve: typeof resolve |
| 157 | ): Promise<{ url: string; format?: NodeLoaderHooksFormat }> { |
| 158 | const defer = async () => { |
| 159 | const r = await defaultResolve(specifier, context, defaultResolve); |
| 160 | return r; |
| 161 | }; |
| 162 | // See: https://github.com/nodejs/node/discussions/41711 |
| 163 | // nodejs will likely implement a similar fallback. Till then, we can do our users a favor and fallback today. |
| 164 | async function entrypointFallback( |
| 165 | cb: () => ReturnType<typeof resolve> | Awaited<ReturnType<typeof resolve>> |
| 166 | ): ReturnType<typeof resolve> { |
| 167 | try { |
| 168 | const resolution = await cb(); |
| 169 | if ( |
| 170 | resolution?.url && |
| 171 | isProbablyEntrypoint(specifier, context.parentURL) |
| 172 | ) |
| 173 | rememberIsProbablyEntrypoint.add(resolution.url); |
| 174 | return resolution; |
| 175 | } catch (esmResolverError) { |
| 176 | if (!isProbablyEntrypoint(specifier, context.parentURL)) |
| 177 | throw esmResolverError; |
| 178 | try { |
no test coverage detected
searching dependent graphs…