diff --git a/package-lock.json b/package-lock.json index b9a603a99..0eee58dae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4379,9 +4379,9 @@ "dev": true }, "typescript": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.2.tgz", - "integrity": "sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.2.tgz", + "integrity": "sha512-tbb+NVrLfnsJy3M59lsDgrzWIflR4d4TIUjz+heUnHZwdF7YsrMTKoRERiIvI2lvBG95dfpLxB21WZhys1bgaQ==", "dev": true }, "typescript-json-schema": { diff --git a/package.json b/package.json index 8ef5d1902..0d36a3287 100644 --- a/package.json +++ b/package.json @@ -123,7 +123,7 @@ "tslint": "^6.1.0", "tslint-config-standard": "^9.0.0", "typedoc": "^0.20.20", - "typescript": "4.1.2", + "typescript": "4.2.2", "typescript-json-schema": "^0.42.0", "util.promisify": "^1.0.1" }, diff --git a/src/bin.ts b/src/bin.ts index d07a124e8..0924f96b0 100644 --- a/src/bin.ts +++ b/src/bin.ts @@ -10,7 +10,7 @@ import { createRepl, ReplService } from './repl' -import { VERSION, TSError, parse, register, createRequire } from './index' +import { VERSION, TSError, parse, register, createRequire, TSInternal } from './index' /** * Main `bin` functionality. @@ -30,6 +30,7 @@ export function main (argv: string[] = process.argv.slice(2), entrypointArgs: Re '--cwd-mode': Boolean, '--script-mode': Boolean, '--version': arg.COUNT, + '--show-config': Boolean, // Project options. '--cwd': String, @@ -64,7 +65,8 @@ export function main (argv: string[] = process.argv.slice(2), entrypointArgs: Re '-C': '--compiler', '-D': '--ignore-diagnostics', '-O': '--compiler-options', - '--dir': '--cwd' + '--dir': '--cwd', + '--showConfig': '--show-config' }, { argv, stopAtPositional: true @@ -80,6 +82,7 @@ export function main (argv: string[] = process.argv.slice(2), entrypointArgs: Re '--script-mode': scriptMode, '--cwd-mode': cwdMode, '--version': version = 0, + '--show-config': showConfig, '--require': argsRequire = [], '--eval': code = undefined, '--print': print = false, @@ -115,6 +118,7 @@ export function main (argv: string[] = process.argv.slice(2), entrypointArgs: Re -h, --help Print CLI usage -v, --version Print module version information --cwd-mode Use current directory instead of for config resolution + --show-config Print resolved configuration and exit -T, --transpile-only Use TypeScript's faster \`transpileModule\` -H, --compiler-host Use TypeScript's compiler host API @@ -184,6 +188,28 @@ export function main (argv: string[] = process.argv.slice(2), entrypointArgs: Re process.exit(0) } + if (showConfig) { + const ts = service.ts as any as TSInternal + if (typeof ts.convertToTSConfig !== 'function') { // tslint:disable-line:strict-type-predicates + console.error('Error: --show-config requires a typescript versions >=3.2 that support --showConfig') + process.exit(1) + } + const json = ts.convertToTSConfig(service.config, service.configFilePath ?? join(cwd, 'ts-node-implicit-tsconfig.json'), service.ts.sys) + json['ts-node'] = { + ...service.options, + experimentalEsmLoader: undefined, + compilerOptions: undefined, + project: service.configFilePath ?? service.options.project + } + console.log( + // Assumes that all configuration options which can possibly be specified via the CLI are JSON-compatible. + // If, in the future, we must log functions, for example readFile and fileExists, then we can implement a JSON + // replacer function. + JSON.stringify(json, null, 2) + ) + process.exit(0) + } + // Create a local module instance based on `cwd`. const module = new Module(state.path) module.filename = state.path diff --git a/src/index.spec.ts b/src/index.spec.ts index b1fb16d7f..d7db0f5ed 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -1,7 +1,7 @@ import { test, TestInterface } from './testlib' import { expect } from 'chai' import { ChildProcess, exec as childProcessExec, ExecException, ExecOptions } from 'child_process' -import { join } from 'path' +import { join, resolve, sep as pathSep } from 'path' import semver = require('semver') import ts = require('typescript') import proxyquire = require('proxyquire') @@ -35,6 +35,7 @@ function exec (cmd: string, opts: ExecOptions = {}): Promise & { ) } +const ROOT_DIR = resolve(__dirname, '..') const TEST_DIR = join(__dirname, '../tests') const PROJECT = join(TEST_DIR, 'tsconfig.json') const BIN_PATH = join(TEST_DIR, 'node_modules/.bin/ts-node') @@ -583,6 +584,45 @@ test.suite('ts-node', (test) => { ) }) }) + + if (semver.gte(ts.version, '3.2.0')) { + test('--show-config should log resolved configuration', async (t) => { + function native (path: string) { return path.replace(/\/|\\/g, pathSep) } + function posix (path: string) { return path.replace(/\/|\\/g, '/') } + const { err, stdout } = await exec(`${cmd} --showConfig`) + expect(err).to.equal(null) + t.is(stdout, JSON.stringify({ + 'compilerOptions': { + 'target': 'es6', + 'jsx': 'react', + 'noEmit': false, + 'strict': true, + 'typeRoots': [ + posix(`${ ROOT_DIR }/tests/typings`), + posix(`${ ROOT_DIR }/node_modules/@types`) + ], + 'sourceMap': true, + 'inlineSourceMap': false, + 'inlineSources': true, + 'declaration': false, + 'outDir': './.ts-node', + 'module': 'commonjs' + }, + 'ts-node': { + 'cwd': native(`${ ROOT_DIR }/tests`), + 'projectSearchDir': native(`${ ROOT_DIR }/tests`), + 'project': native(`${ ROOT_DIR }/tests/tsconfig.json`), + 'require': [] + } + }, null, 2) + '\n') + }) + } else { + test('--show-config should log error message when used with old typescript versions', async (t) => { + const { err, stderr } = await exec(`${cmd} --showConfig`) + expect(err).to.not.equal(null) + expect(stderr).to.contain('Error: --show-config requires') + }) + } }) test.suite('register', (_test) => { diff --git a/src/index.ts b/src/index.ts index 914e7b205..0866d293d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -135,14 +135,23 @@ export interface TSCommon { /** * Compiler APIs we use that are marked internal and not included in TypeScript's public API declarations + * @internal */ -interface TSInternal { +export interface TSInternal { // https://github.com/microsoft/TypeScript/blob/4a34294908bed6701dcba2456ca7ac5eafe0ddff/src/compiler/core.ts#L1906-L1909 createGetCanonicalFileName (useCaseSensitiveFileNames: boolean): TSInternal.GetCanonicalFileName + // https://github.com/microsoft/TypeScript/blob/c117c266e09c80e8a06b24a6e94b9d018f5fae6b/src/compiler/commandLineParser.ts#L2054 + convertToTSConfig (configParseResult: _ts.ParsedCommandLine, configFileName: string, host: TSInternal.ConvertToTSConfigHost): any } -namespace TSInternal { +/** @internal */ +export namespace TSInternal { // https://github.com/microsoft/TypeScript/blob/4a34294908bed6701dcba2456ca7ac5eafe0ddff/src/compiler/core.ts#L1906 export type GetCanonicalFileName = (fileName: string) => string + // https://github.com/microsoft/TypeScript/blob/c117c266e09c80e8a06b24a6e94b9d018f5fae6b/src/compiler/commandLineParser.ts#L2041 + export interface ConvertToTSConfigHost { + getCurrentDirectory (): string + useCaseSensitiveFileNames: boolean + } } /** @@ -432,6 +441,8 @@ export interface Service { ignored (fileName: string): boolean compile (code: string, fileName: string, lineOffset?: number): string getTypeInfo (code: string, fileName: string, position: number): TypeInfo + /** @internal */ + configFilePath: string | undefined } /** @@ -1053,7 +1064,7 @@ export function create (rawOptions: CreateOptions = {}): Service { return true } - return { ts, config, compile, getTypeInfo, ignored, enabled, options } + return { ts, config, compile, getTypeInfo, ignored, enabled, options, configFilePath } } /**