Skip to content

Commit 3a2848c

Browse files
Add traceResolution support (#1491)
* Add traceResolution support * Run prettier * Add disabled tests for traceResolution * Remove pre-compile code from REPL * re-enable test * Rename trace to tsTrace to avoid hypothetical future collision with ts-node tracing flags (tracing ts-node behavior as opposed to tsc behavior); also pass trace option in compilerHost codepath Co-authored-by: Andrew Bradley <cspotcode@gmail.com>
1 parent 2832a59 commit 3a2848c

File tree

7 files changed

+85
-16
lines changed

7 files changed

+85
-16
lines changed

src/configuration.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
import type { TSInternal } from './ts-compiler-types';
1111
import { createTsInternals } from './ts-internals';
1212
import { getDefaultTsconfigJsonForNodeVersion } from './tsconfigs';
13-
import { assign, createRequire, trace } from './util';
13+
import { assign, createRequire } from './util';
1414

1515
/**
1616
* TypeScript compiler option values required by `ts-node` which cannot be overridden.
@@ -94,6 +94,7 @@ export function readConfig(
9494
readFile = ts.sys.readFile,
9595
skipProject = DEFAULTS.skipProject,
9696
project = DEFAULTS.project,
97+
tsTrace = DEFAULTS.tsTrace,
9798
} = rawApiOptions;
9899

99100
// Read project configuration when available.
@@ -137,7 +138,7 @@ export function readConfig(
137138
readDirectory: ts.sys.readDirectory,
138139
readFile,
139140
useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
140-
trace,
141+
trace: tsTrace,
141142
},
142143
bp,
143144
errors,

src/index.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,12 @@ export interface CreateOptions {
353353
* the configuration loader, so it is *not* necessary for their source to be set here.
354354
*/
355355
optionBasePaths?: OptionBasePaths;
356+
/**
357+
* A function to collect trace messages from the TypeScript compiler, for example when `traceResolution` is enabled.
358+
*
359+
* @default console.log
360+
*/
361+
tsTrace?: (str: string) => void;
356362
}
357363

358364
/** @internal */
@@ -389,6 +395,7 @@ export interface TsConfigOptions
389395
| 'cwd'
390396
| 'projectSearchDir'
391397
| 'optionBasePaths'
398+
| 'tsTrace'
392399
> {}
393400

394401
/**
@@ -424,6 +431,7 @@ export const DEFAULTS: RegisterOptions = {
424431
compilerHost: yn(env.TS_NODE_COMPILER_HOST),
425432
logError: yn(env.TS_NODE_LOG_ERROR),
426433
experimentalReplAwait: yn(env.TS_NODE_EXPERIMENTAL_REPL_AWAIT) ?? undefined,
434+
tsTrace: console.log.bind(console),
427435
};
428436

429437
/**
@@ -883,6 +891,7 @@ export function create(rawOptions: CreateOptions = {}): Service {
883891
getCompilationSettings: () => config.options,
884892
getDefaultLibFileName: () => ts.getDefaultLibFilePath(config.options),
885893
getCustomTransformers: getCustomTransformers,
894+
trace: options.tsTrace,
886895
};
887896
const {
888897
resolveModuleNames,
@@ -891,7 +900,7 @@ export function create(rawOptions: CreateOptions = {}): Service {
891900
isFileKnownToBeInternal,
892901
markBucketOfFilenameInternal,
893902
} = createResolverFunctions({
894-
serviceHost,
903+
host: serviceHost,
895904
getCanonicalFileName,
896905
ts,
897906
cwd,
@@ -1036,13 +1045,14 @@ export function create(rawOptions: CreateOptions = {}): Service {
10361045
),
10371046
useCaseSensitiveFileNames: () => sys.useCaseSensitiveFileNames,
10381047
};
1048+
host.trace = options.tsTrace;
10391049
const {
10401050
resolveModuleNames,
10411051
resolveTypeReferenceDirectives,
10421052
isFileKnownToBeInternal,
10431053
markBucketOfFilenameInternal,
10441054
} = createResolverFunctions({
1045-
serviceHost: host,
1055+
host,
10461056
cwd,
10471057
configFilePath,
10481058
config,
@@ -1057,7 +1067,7 @@ export function create(rawOptions: CreateOptions = {}): Service {
10571067
? ts.createIncrementalProgram({
10581068
rootNames: Array.from(rootFileNames),
10591069
options: config.options,
1060-
host: host,
1070+
host,
10611071
configFileParsingDiagnostics: config.errors,
10621072
projectReferences: config.projectReferences,
10631073
})

src/resolver-functions.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ import type * as _ts from 'typescript';
77
*/
88
export function createResolverFunctions(kwargs: {
99
ts: typeof _ts;
10-
serviceHost: _ts.ModuleResolutionHost;
10+
host: _ts.ModuleResolutionHost;
1111
cwd: string;
1212
getCanonicalFileName: (filename: string) => string;
1313
config: _ts.ParsedCommandLine;
1414
configFilePath: string | undefined;
1515
}) {
1616
const {
17-
serviceHost,
17+
host,
1818
ts,
1919
config,
2020
cwd,
@@ -93,7 +93,7 @@ export function createResolverFunctions(kwargs: {
9393
moduleName,
9494
containingFile,
9595
config.options,
96-
serviceHost,
96+
host,
9797
moduleResolutionCache,
9898
redirectedReference
9999
);
@@ -132,7 +132,7 @@ export function createResolverFunctions(kwargs: {
132132
typeDirectiveName,
133133
containingFile,
134134
config.options,
135-
serviceHost,
135+
host,
136136
redirectedReference
137137
);
138138
if (typeDirectiveName === 'node' && !resolvedTypeReferenceDirective) {
@@ -157,7 +157,7 @@ export function createResolverFunctions(kwargs: {
157157
...config.options,
158158
typeRoots,
159159
},
160-
serviceHost,
160+
host,
161161
redirectedReference
162162
));
163163
}

src/test/index.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,26 @@ test.suite('ts-node', (test) => {
331331
});
332332
}
333333

334+
test.suite('should support `traceResolution` compiler option', (test) => {
335+
test('prints traces before running code when enabled', async () => {
336+
const { err, stdout } = await exec(
337+
`${BIN_PATH} --compiler-options="{ \\"traceResolution\\": true }" -e "console.log('ok')"`
338+
);
339+
expect(err).toBeNull();
340+
expect(stdout).toContain('======== Resolving module');
341+
expect(stdout.endsWith('ok\n')).toBe(true);
342+
});
343+
344+
test('does NOT print traces when not enabled', async () => {
345+
const { err, stdout } = await exec(
346+
`${BIN_PATH} -e "console.log('ok')"`
347+
);
348+
expect(err).toBeNull();
349+
expect(stdout).not.toContain('======== Resolving module');
350+
expect(stdout.endsWith('ok\n')).toBe(true);
351+
});
352+
});
353+
334354
if (semver.gte(process.version, '12.16.0')) {
335355
test('swc transpiler supports native ESM emit', async () => {
336356
const { err, stdout } = await exec(

src/test/repl/helpers.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export async function contextReplHelpers(
4545
...replService.evalAwarePartialHost,
4646
project: `${TEST_DIR}/tsconfig.json`,
4747
...createServiceOpts,
48+
tsTrace: replService.console.log.bind(replService.console),
4849
});
4950
replService.setService(service);
5051
t.teardown(async () => {

src/test/repl/repl.spec.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ import * as expect from 'expect';
44
import {
55
CMD_TS_NODE_WITH_PROJECT_FLAG,
66
contextTsNodeUnderTest,
7+
getStream,
78
TEST_DIR,
89
} from '../helpers';
910
import { createExec, createExecTester } from '../exec-helpers';
1011
import { upstreamTopLevelAwaitTests } from './node-repl-tla';
1112
import { _test } from '../testlib';
1213
import { contextReplHelpers } from './helpers';
14+
import { promisify } from 'util';
1315

1416
const test = _test.context(contextTsNodeUnderTest).context(contextReplHelpers);
1517

@@ -412,6 +414,47 @@ test.suite(
412414
}
413415
);
414416

417+
test.suite('REPL works with traceResolution', (test) => {
418+
test.serial(
419+
'startup traces should print before the prompt appears when traceResolution is enabled',
420+
async (t) => {
421+
const repl = t.context.createReplViaApi({
422+
registerHooks: false as true,
423+
createServiceOpts: {
424+
compilerOptions: {
425+
traceResolution: true,
426+
},
427+
},
428+
});
429+
430+
repl.replService.start();
431+
432+
repl.stdin.end();
433+
434+
await promisify(setTimeout)(3e3);
435+
436+
repl.stdout.end();
437+
const stdout = await getStream(repl.stdout);
438+
439+
expect(stdout).toContain('======== Resolving module');
440+
expect(stdout.endsWith('> ')).toBe(true);
441+
}
442+
);
443+
444+
test.serial(
445+
'traces should NOT appear when traceResolution is not enabled',
446+
async (t) => {
447+
const { stdout, stderr } = await t.context.executeInRepl('1', {
448+
registerHooks: true,
449+
startInternalOptions: { useGlobal: false },
450+
waitPattern: '1\n>',
451+
});
452+
expect(stderr).toBe('');
453+
expect(stdout).not.toContain('======== Resolving module');
454+
}
455+
);
456+
});
457+
415458
test.serial('REPL declares types for node built-ins within REPL', async (t) => {
416459
const { stdout, stderr } = await t.context.executeInRepl(
417460
`util.promisify(setTimeout)("should not be a string" as string)

src/util.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,3 @@ export function cachedLookup<T, R>(fn: (arg: T) => R): (arg: T) => R {
9090
return cache.get(arg)!;
9191
};
9292
}
93-
94-
/**
95-
* We do not support ts's `trace` option yet. In the meantime, rather than omit
96-
* `trace` options in hosts, I am using this placeholder.
97-
*/
98-
export function trace(s: string): void {}

0 commit comments

Comments
 (0)