Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dprint.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"/website/readme-sources",
"/website/static",
"tests/main-realpath/symlink/tsconfig.json",
"tests/esm-invalid-tsconfig/tsconfig.json",
"tests/throw error.ts",
"tests/throw error react tsx.tsx",
"tests/esm/throw error.ts",
Expand Down
62 changes: 56 additions & 6 deletions src/esm.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { register, RegisterOptions, Service } from './index';
import { register, RegisterOptions, Service, TSError } from './index';
import { parse as parseUrl, format as formatUrl, UrlWithStringQuery, fileURLToPath, pathToFileURL } from 'url';
import { extname, resolve as pathResolve } from 'path';
import * as assert from 'assert';
Expand Down Expand Up @@ -101,7 +101,12 @@ export function filterHooksByAPIVersion(
/** @internal */
export function registerAndCreateEsmHooks(opts?: RegisterOptions) {
// Automatically performs registration just like `-r ts-node/register`
const tsNodeInstance = register(opts);
let tsNodeInstance: Service;
try {
tsNodeInstance = register(opts);
} catch (error) {
throw makeSerializableLoaderError(error);
}

return createEsmHooks(tsNodeInstance);
}
Expand All @@ -113,10 +118,10 @@ export function createEsmHooks(tsNodeService: Service) {
const extensions = tsNodeService.extensions;

const hooksAPI = filterHooksByAPIVersion({
resolve,
load,
getFormat,
transformSource,
resolve: wrapHook(resolve),
load: wrapHook(load),
getFormat: wrapHook(getFormat),
transformSource: wrapHook(transformSource),
});

function isFileUrlOrNodeStyleSpecifier(parsed: UrlWithStringQuery) {
Expand Down Expand Up @@ -356,3 +361,48 @@ async function addShortCircuitFlag<T>(fn: () => Promise<T>) {
shortCircuit: true,
};
}

function wrapHook<T extends (...args: any[]) => Promise<any>>(hook: T): T {
return (async (...args: Parameters<T>) => {
try {
return await hook(...args);
} catch (error) {
throw makeSerializableLoaderError(error);
}
}) as T;
}

function makeSerializableLoaderError(error: unknown) {
if (error instanceof TSError || isTSError(error)) {
const serializable = new Error(error.message);
serializable.name = error.name;
if (typeof error.stack === 'string') {
serializable.stack = error.stack;
}
Object.defineProperty(serializable, 'diagnosticText', {
configurable: true,
enumerable: true,
writable: true,
value: error.diagnosticText,
});
Object.defineProperty(serializable, 'diagnosticCodes', {
configurable: true,
enumerable: true,
writable: true,
value: error.diagnosticCodes,
});
return serializable;
}
return error;
}

function isTSError(error: unknown): error is TSError {
return (
typeof error === 'object' &&
error !== null &&
(error as TSError).name === 'TSError' &&
typeof (error as TSError).message === 'string' &&
typeof (error as TSError).diagnosticText === 'string' &&
Array.isArray((error as TSError).diagnosticCodes)
);
}
10 changes: 10 additions & 0 deletions src/test/esm-loader.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,16 @@ test.suite('esm', (test) => {
expect(r.err).toBe(null);
expect(r.stdout).toBe('');
});
test('reports invalid tsconfig diagnostics from the ESM loader', async () => {
const r = await exec(`${CMD_ESM_LOADER_WITHOUT_PROJECT} -e "console.log(1)"`, {
cwd: join(TEST_DIR, './esm-invalid-tsconfig'),
});

expect(r.err).not.toBe(null);
expect(r.stderr).toMatch('Unable to compile TypeScript');
expect(r.stderr).toMatch("tsconfig.json(1,5): error TS1005: ':' expected.");
expect(r.stderr).not.toMatch('[Object: null prototype]');
});
test('should throw type errors without transpile-only enabled', async () => {
const r = await exec(`${CMD_ESM_LOADER_WITHOUT_PROJECT} index.ts`, {
cwd: join(TEST_DIR, './esm-transpile-only'),
Expand Down
3 changes: 3 additions & 0 deletions tests/esm-invalid-tsconfig/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"type": "module"
}
1 change: 1 addition & 0 deletions tests/esm-invalid-tsconfig/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ 1 }