Skip to content

Commit

Permalink
fix: lazy-load repl to avoid domain side effects
Browse files Browse the repository at this point in the history
Actually starting the repl will still put the process into domain-mode,
but this at least allows programs to use `ts-node` or
`--loader=ts-node/esm` without losing the ability to use
process.setUncaughtExceptionCaptureCallback().

The problem should ideally be fixed (or mitigated) in node core, but
this is still worthwhile for the benefit of supporting current node
versions.

Re: nodejs/node#48131
Fix: TypeStrong#2024
  • Loading branch information
isaacs committed May 24, 2023
1 parent 7af5c48 commit 82789ed
Showing 1 changed file with 16 additions and 3 deletions.
19 changes: 16 additions & 3 deletions src/repl.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type * as _diff from 'diff';
import { homedir } from 'os';
import { join } from 'path';
import { Recoverable, ReplOptions, REPLServer, start as nodeReplStart } from 'repl';
import type * as _nodeRepl from 'repl';
import type { REPLServer, ReplOptions } from 'repl';
import { Context, createContext, Script } from 'vm';
import { Service, CreateOptions, TSError, env } from './index';
import { readFileSync, statSync } from 'fs';
Expand All @@ -28,6 +29,17 @@ function getDiffLines() {
return diff.diffLines;
}

// Lazy-loaded to prevent repl's require('domain') from causing problems
// https://github.com/TypeStrong/ts-node/issues/2024
// https://github.com/nodejs/node/issues/48131
let nodeRepl: typeof _nodeRepl;
function getNodeRepl() {
if (nodeRepl === undefined) {
nodeRepl = require('repl');
}
return nodeRepl;
}

/** @internal */
export const EVAL_FILENAME = `[eval].ts`;
/** @internal */
Expand Down Expand Up @@ -140,7 +152,7 @@ interface StartReplInternalOptions extends ReplOptions {
*/
export function createRepl(options: CreateReplOptions = {}) {
const { ignoreDiagnosticsThatAreAnnoyingInInteractiveRepl = true } = options;
let nodeReplServer: REPLServer;
let nodeReplServer: _nodeRepl.REPLServer;
// If `useGlobal` is not true, then REPL creates a context when started.
// This stores a reference to it or to `global`, whichever is used, after REPL has started.
let context: Context | undefined;
Expand Down Expand Up @@ -271,6 +283,7 @@ export function createRepl(options: CreateReplOptions = {}) {
const canLogTopLevelAwaitHint = service!.options.experimentalReplAwait !== false && !service!.shouldReplAwait;
if (error instanceof TSError) {
// Support recoverable compilations using >= node 6.
const { Recoverable } = getNodeRepl();
if (Recoverable && isRecoverable(error)) {
callback(new Recoverable(error));
return;
Expand Down Expand Up @@ -335,7 +348,7 @@ export function createRepl(options: CreateReplOptions = {}) {
// the REPL starts for a snappier user experience on startup.
service?.compile('', state.path);

const repl = nodeReplStart({
const repl = getNodeRepl().start({
prompt: '> ',
input: replService.stdin,
output: replService.stdout,
Expand Down

0 comments on commit 82789ed

Please sign in to comment.