Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add traceResolution support #1491

Merged
merged 7 commits into from
Nov 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 {}