Skip to content

Commit

Permalink
Add traceResolution support (#1491)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
TheUnlocked and cspotcode authored Nov 7, 2021
1 parent 2832a59 commit 3a2848c
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 16 deletions.
5 changes: 3 additions & 2 deletions src/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
import type { TSInternal } from './ts-compiler-types';
import { createTsInternals } from './ts-internals';
import { getDefaultTsconfigJsonForNodeVersion } from './tsconfigs';
import { assign, createRequire, trace } from './util';
import { assign, createRequire } from './util';

/**
* TypeScript compiler option values required by `ts-node` which cannot be overridden.
Expand Down Expand Up @@ -94,6 +94,7 @@ export function readConfig(
readFile = ts.sys.readFile,
skipProject = DEFAULTS.skipProject,
project = DEFAULTS.project,
tsTrace = DEFAULTS.tsTrace,
} = rawApiOptions;

// Read project configuration when available.
Expand Down Expand Up @@ -137,7 +138,7 @@ export function readConfig(
readDirectory: ts.sys.readDirectory,
readFile,
useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
trace,
trace: tsTrace,
},
bp,
errors,
Expand Down
16 changes: 13 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,12 @@ export interface CreateOptions {
* the configuration loader, so it is *not* necessary for their source to be set here.
*/
optionBasePaths?: OptionBasePaths;
/**
* A function to collect trace messages from the TypeScript compiler, for example when `traceResolution` is enabled.
*
* @default console.log
*/
tsTrace?: (str: string) => void;
}

/** @internal */
Expand Down Expand Up @@ -389,6 +395,7 @@ export interface TsConfigOptions
| 'cwd'
| 'projectSearchDir'
| 'optionBasePaths'
| 'tsTrace'
> {}

/**
Expand Down Expand Up @@ -424,6 +431,7 @@ export const DEFAULTS: RegisterOptions = {
compilerHost: yn(env.TS_NODE_COMPILER_HOST),
logError: yn(env.TS_NODE_LOG_ERROR),
experimentalReplAwait: yn(env.TS_NODE_EXPERIMENTAL_REPL_AWAIT) ?? undefined,
tsTrace: console.log.bind(console),
};

/**
Expand Down Expand Up @@ -883,6 +891,7 @@ export function create(rawOptions: CreateOptions = {}): Service {
getCompilationSettings: () => config.options,
getDefaultLibFileName: () => ts.getDefaultLibFilePath(config.options),
getCustomTransformers: getCustomTransformers,
trace: options.tsTrace,
};
const {
resolveModuleNames,
Expand All @@ -891,7 +900,7 @@ export function create(rawOptions: CreateOptions = {}): Service {
isFileKnownToBeInternal,
markBucketOfFilenameInternal,
} = createResolverFunctions({
serviceHost,
host: serviceHost,
getCanonicalFileName,
ts,
cwd,
Expand Down Expand Up @@ -1036,13 +1045,14 @@ export function create(rawOptions: CreateOptions = {}): Service {
),
useCaseSensitiveFileNames: () => sys.useCaseSensitiveFileNames,
};
host.trace = options.tsTrace;
const {
resolveModuleNames,
resolveTypeReferenceDirectives,
isFileKnownToBeInternal,
markBucketOfFilenameInternal,
} = createResolverFunctions({
serviceHost: host,
host,
cwd,
configFilePath,
config,
Expand All @@ -1057,7 +1067,7 @@ export function create(rawOptions: CreateOptions = {}): Service {
? ts.createIncrementalProgram({
rootNames: Array.from(rootFileNames),
options: config.options,
host: host,
host,
configFileParsingDiagnostics: config.errors,
projectReferences: config.projectReferences,
})
Expand Down
10 changes: 5 additions & 5 deletions src/resolver-functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import type * as _ts from 'typescript';
*/
export function createResolverFunctions(kwargs: {
ts: typeof _ts;
serviceHost: _ts.ModuleResolutionHost;
host: _ts.ModuleResolutionHost;
cwd: string;
getCanonicalFileName: (filename: string) => string;
config: _ts.ParsedCommandLine;
configFilePath: string | undefined;
}) {
const {
serviceHost,
host,
ts,
config,
cwd,
Expand Down Expand Up @@ -93,7 +93,7 @@ export function createResolverFunctions(kwargs: {
moduleName,
containingFile,
config.options,
serviceHost,
host,
moduleResolutionCache,
redirectedReference
);
Expand Down Expand Up @@ -132,7 +132,7 @@ export function createResolverFunctions(kwargs: {
typeDirectiveName,
containingFile,
config.options,
serviceHost,
host,
redirectedReference
);
if (typeDirectiveName === 'node' && !resolvedTypeReferenceDirective) {
Expand All @@ -157,7 +157,7 @@ export function createResolverFunctions(kwargs: {
...config.options,
typeRoots,
},
serviceHost,
host,
redirectedReference
));
}
Expand Down
20 changes: 20 additions & 0 deletions src/test/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,26 @@ test.suite('ts-node', (test) => {
});
}

test.suite('should support `traceResolution` compiler option', (test) => {
test('prints traces before running code when enabled', async () => {
const { err, stdout } = await exec(
`${BIN_PATH} --compiler-options="{ \\"traceResolution\\": true }" -e "console.log('ok')"`
);
expect(err).toBeNull();
expect(stdout).toContain('======== Resolving module');
expect(stdout.endsWith('ok\n')).toBe(true);
});

test('does NOT print traces when not enabled', async () => {
const { err, stdout } = await exec(
`${BIN_PATH} -e "console.log('ok')"`
);
expect(err).toBeNull();
expect(stdout).not.toContain('======== Resolving module');
expect(stdout.endsWith('ok\n')).toBe(true);
});
});

if (semver.gte(process.version, '12.16.0')) {
test('swc transpiler supports native ESM emit', async () => {
const { err, stdout } = await exec(
Expand Down
1 change: 1 addition & 0 deletions src/test/repl/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export async function contextReplHelpers(
...replService.evalAwarePartialHost,
project: `${TEST_DIR}/tsconfig.json`,
...createServiceOpts,
tsTrace: replService.console.log.bind(replService.console),
});
replService.setService(service);
t.teardown(async () => {
Expand Down
43 changes: 43 additions & 0 deletions src/test/repl/repl.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import * as expect from 'expect';
import {
CMD_TS_NODE_WITH_PROJECT_FLAG,
contextTsNodeUnderTest,
getStream,
TEST_DIR,
} from '../helpers';
import { createExec, createExecTester } from '../exec-helpers';
import { upstreamTopLevelAwaitTests } from './node-repl-tla';
import { _test } from '../testlib';
import { contextReplHelpers } from './helpers';
import { promisify } from 'util';

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

Expand Down Expand Up @@ -412,6 +414,47 @@ test.suite(
}
);

test.suite('REPL works with traceResolution', (test) => {
test.serial(
'startup traces should print before the prompt appears when traceResolution is enabled',
async (t) => {
const repl = t.context.createReplViaApi({
registerHooks: false as true,
createServiceOpts: {
compilerOptions: {
traceResolution: true,
},
},
});

repl.replService.start();

repl.stdin.end();

await promisify(setTimeout)(3e3);

repl.stdout.end();
const stdout = await getStream(repl.stdout);

expect(stdout).toContain('======== Resolving module');
expect(stdout.endsWith('> ')).toBe(true);
}
);

test.serial(
'traces should NOT appear when traceResolution is not enabled',
async (t) => {
const { stdout, stderr } = await t.context.executeInRepl('1', {
registerHooks: true,
startInternalOptions: { useGlobal: false },
waitPattern: '1\n>',
});
expect(stderr).toBe('');
expect(stdout).not.toContain('======== Resolving module');
}
);
});

test.serial('REPL declares types for node built-ins within REPL', async (t) => {
const { stdout, stderr } = await t.context.executeInRepl(
`util.promisify(setTimeout)("should not be a string" as string)
Expand Down
6 changes: 0 additions & 6 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,3 @@ export function cachedLookup<T, R>(fn: (arg: T) => R): (arg: T) => R {
return cache.get(arg)!;
};
}

/**
* We do not support ts's `trace` option yet. In the meantime, rather than omit
* `trace` options in hosts, I am using this placeholder.
*/
export function trace(s: string): void {}

0 comments on commit 3a2848c

Please sign in to comment.