Skip to content

Commit

Permalink
add short-circuit flag to all ESM hook return values
Browse files Browse the repository at this point in the history
  • Loading branch information
cspotcode committed Apr 14, 2022
1 parent 511e0e7 commit d9960eb
Showing 1 changed file with 78 additions and 59 deletions.
137 changes: 78 additions & 59 deletions src/esm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,11 @@ export namespace NodeLoaderHooksAPI2 {
parentURL: string;
},
defaultResolve: ResolveHook
) => Promise<{ url: string; format?: NodeLoaderHooksFormat }>;
) => Promise<{
url: string;
format?: NodeLoaderHooksFormat;
shortCircuit?: boolean;
}>;
export type LoadHook = (
url: string,
context: {
Expand All @@ -80,6 +84,7 @@ export namespace NodeLoaderHooksAPI2 {
) => Promise<{
format: NodeLoaderHooksFormat;
source: string | Buffer | undefined;
shortCircuit?: boolean;
}>;
export type NodeImportConditions = unknown;
export interface NodeImportAssertions {
Expand Down Expand Up @@ -205,32 +210,34 @@ export function createEsmHooks(tsNodeService: Service) {
}
}

const parsed = parseUrl(specifier);
const { pathname, protocol, hostname } = parsed;
return addShortCircuitFlag(async () => {
const parsed = parseUrl(specifier);
const { pathname, protocol, hostname } = parsed;

if (!isFileUrlOrNodeStyleSpecifier(parsed)) {
return entrypointFallback(defer);
}
if (!isFileUrlOrNodeStyleSpecifier(parsed)) {
return entrypointFallback(defer);
}

if (protocol !== null && protocol !== 'file:') {
return entrypointFallback(defer);
}
if (protocol !== null && protocol !== 'file:') {
return entrypointFallback(defer);
}

// Malformed file:// URL? We should always see `null` or `''`
if (hostname) {
// TODO file://./foo sets `hostname` to `'.'`. Perhaps we should special-case this.
return entrypointFallback(defer);
}
// Malformed file:// URL? We should always see `null` or `''`
if (hostname) {
// TODO file://./foo sets `hostname` to `'.'`. Perhaps we should special-case this.
return entrypointFallback(defer);
}

// pathname is the path to be resolved
// pathname is the path to be resolved

return entrypointFallback(() =>
nodeResolveImplementation.defaultResolve(
specifier,
context,
defaultResolve
)
);
return entrypointFallback(() =>
nodeResolveImplementation.defaultResolve(
specifier,
context,
defaultResolve
)
);
});
}

// `load` from new loader hook API (See description at the top of this file)
Expand All @@ -245,47 +252,49 @@ export function createEsmHooks(tsNodeService: Service) {
format: NodeLoaderHooksFormat;
source: string | Buffer | undefined;
}> {
// If we get a format hint from resolve() on the context then use it
// otherwise call the old getFormat() hook using node's old built-in defaultGetFormat() that ships with ts-node
const format =
context.format ??
(await getFormat(url, context, defaultGetFormat)).format;

let source = undefined;
if (format !== 'builtin' && format !== 'commonjs') {
// Call the new defaultLoad() to get the source
const { source: rawSource } = await defaultLoad(
url,
{
...context,
format,
},
defaultLoad
);
return addShortCircuitFlag(async () => {
// If we get a format hint from resolve() on the context then use it
// otherwise call the old getFormat() hook using node's old built-in defaultGetFormat() that ships with ts-node
const format =
context.format ??
(await getFormat(url, context, defaultGetFormat)).format;

let source = undefined;
if (format !== 'builtin' && format !== 'commonjs') {
// Call the new defaultLoad() to get the source
const { source: rawSource } = await defaultLoad(
url,
{
...context,
format,
},
defaultLoad
);

if (rawSource === undefined || rawSource === null) {
throw new Error(
`Failed to load raw source: Format was '${format}' and url was '${url}''.`
);
}

if (rawSource === undefined || rawSource === null) {
throw new Error(
`Failed to load raw source: Format was '${format}' and url was '${url}''.`
// Emulate node's built-in old defaultTransformSource() so we can re-use the old transformSource() hook
const defaultTransformSource: typeof transformSource = async (
source,
_context,
_defaultTransformSource
) => ({ source });

// Call the old hook
const { source: transformedSource } = await transformSource(
rawSource,
{ url, format },
defaultTransformSource
);
source = transformedSource;
}

// Emulate node's built-in old defaultTransformSource() so we can re-use the old transformSource() hook
const defaultTransformSource: typeof transformSource = async (
source,
_context,
_defaultTransformSource
) => ({ source });

// Call the old hook
const { source: transformedSource } = await transformSource(
rawSource,
{ url, format },
defaultTransformSource
);
source = transformedSource;
}

return { format, source };
return { format, source };
});
}

async function getFormat(
Expand Down Expand Up @@ -384,3 +393,13 @@ export function createEsmHooks(tsNodeService: Service) {

return hooksAPI;
}

async function addShortCircuitFlag<T>(fn: () => Promise<T>) {
const ret = await fn();
// Not sure if this is necessary; being lazy. Can revisit in the future.
if (ret == null) return ret;
return {
...ret,
shortCircuit: true,
};
}

0 comments on commit d9960eb

Please sign in to comment.