From ac02865e8f7e835e673974b339ee2fbe1928bf9d Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Mon, 22 Feb 2021 18:12:10 -0500 Subject: [PATCH 01/15] Implementation --- node10/tsconfig.json | 3 ++ node12/tsconfig.json | 3 ++ node14/tsconfig.json | 3 ++ package-lock.json | 15 ++++++++++ package.json | 13 +++++++-- src/index.ts | 67 +++++++++++++++++++++++++++++--------------- src/tsconfigs.ts | 12 ++++++++ 7 files changed, 92 insertions(+), 24 deletions(-) create mode 100644 node10/tsconfig.json create mode 100644 node12/tsconfig.json create mode 100644 node14/tsconfig.json create mode 100644 src/tsconfigs.ts diff --git a/node10/tsconfig.json b/node10/tsconfig.json new file mode 100644 index 000000000..dd3a5c3c0 --- /dev/null +++ b/node10/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "@tsconfig/node10" +} diff --git a/node12/tsconfig.json b/node12/tsconfig.json new file mode 100644 index 000000000..76603f1cd --- /dev/null +++ b/node12/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "@tsconfig/node12/tsconfig.json", +} diff --git a/node14/tsconfig.json b/node14/tsconfig.json new file mode 100644 index 000000000..b08285106 --- /dev/null +++ b/node14/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "@tsconfig/node14/tsconfig.json" +} diff --git a/package-lock.json b/package-lock.json index b9a603a99..54ac76a29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -394,6 +394,21 @@ "defer-to-connect": "^1.0.1" } }, + "@tsconfig/node10": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.7.tgz", + "integrity": "sha512-aBvUmXLQbayM4w3A8TrjwrXs4DZ8iduJnuJLLRGdkWlyakCf1q6uHZJBzXoRA/huAEknG5tcUyQxN3A+In5euQ==" + }, + "@tsconfig/node12": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.7.tgz", + "integrity": "sha512-dgasobK/Y0wVMswcipr3k0HpevxFJLijN03A8mYfEPvWvOs14v0ZlYTR4kIgMx8g4+fTyTFv8/jLCIfRqLDJ4A==" + }, + "@tsconfig/node14": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.0.tgz", + "integrity": "sha512-RKkL8eTdPv6t5EHgFKIVQgsDapugbuOptNd9OOunN/HAkzmmTnZELx1kNCK0rSdUYGmiFMM3rRQMAWiyp023LQ==" + }, "@types/chai": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.0.10.tgz", diff --git a/package.json b/package.json index 8ef5d1902..b0186e458 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,10 @@ "./esm": "./esm.mjs", "./esm.mjs": "./esm.mjs", "./esm/transpile-only": "./esm/transpile-only.mjs", - "./esm/transpile-only.mjs": "./esm/transpile-only.mjs" + "./esm/transpile-only.mjs": "./esm/transpile-only.mjs", + "./node10/tsconfig.json": "./node10/tsconfig.json", + "./node12/tsconfig.json": "./node12/tsconfig.json", + "./node14/tsconfig.json": "./node14/tsconfig.json" }, "types": "dist/index.d.ts", "bin": { @@ -40,7 +43,10 @@ "esm.mjs", "LICENSE", "tsconfig.schema.json", - "tsconfig.schemastore-schema.json" + "tsconfig.schemastore-schema.json", + "node10/", + "node12/", + "node14/" ], "scripts": { "lint": "tslint \"src/**/*.ts\" --project tsconfig.json", @@ -131,6 +137,9 @@ "typescript": ">=2.7" }, "dependencies": { + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", "arg": "^4.1.0", "create-require": "^1.1.0", "diff": "^4.0.1", diff --git a/src/index.ts b/src/index.ts index b94798678..76bd45dbd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,6 +8,7 @@ import { fileURLToPath } from 'url' import type * as _ts from 'typescript' import { Module, createRequire as nodeCreateRequire, createRequireFromPath as nodeCreateRequireFromPath } from 'module' import type _createRequire from 'create-require' +import { getDefaultTsconfigJsonForNodeVersion } from './tsconfigs' // tslint:disable-next-line:deprecation export const createRequire = nodeCreateRequire ?? nodeCreateRequireFromPath ?? require('create-require') as typeof _createRequire @@ -253,6 +254,13 @@ export interface CreateOptions { * @default false */ skipProject?: boolean + /** + * Skip loading a default @tsconfig/* that matches the version of nodejs + * TODO needs a better name + * + * @default false + */ + skipDefaultCompilerOptions?: boolean /** * Skip ignore check, so that compilation will be attempted for all files with matching extensions. * @@ -318,6 +326,7 @@ export interface TsConfigOptions extends Omit {} /** @@ -363,7 +372,8 @@ export const DEFAULTS: RegisterOptions = { typeCheck: yn(env.TS_NODE_TYPE_CHECK), compilerHost: yn(env.TS_NODE_COMPILER_HOST), logError: yn(env.TS_NODE_LOG_ERROR), - experimentalEsmLoader: false + experimentalEsmLoader: false, + skipDefaultCompilerOptions: false } /** @@ -509,10 +519,10 @@ export function create (rawOptions: CreateOptions = {}): Service { let { compiler, ts } = loadCompiler(compilerName, rawOptions.projectSearchDir ?? rawOptions.project ?? cwd) // Read config file and merge new options between env and CLI options. - const { configFilePath, config, options: tsconfigOptions } = readConfig(cwd, ts, rawOptions) - const options = assign({}, DEFAULTS, tsconfigOptions || {}, rawOptions) + const { configFilePath, config, tsNodeOptionsFromTsconfig } = readConfig(cwd, ts, rawOptions) + const options = assign({}, DEFAULTS, tsNodeOptionsFromTsconfig || {}, rawOptions) options.require = [ - ...tsconfigOptions.require || [], + ...tsNodeOptionsFromTsconfig.require || [], ...rawOptions.require || [] ] @@ -1155,30 +1165,40 @@ function fixConfig (ts: TSCommon, config: _ts.ParsedCommandLine) { /** * Load TypeScript configuration. Returns the parsed TypeScript config and * any `ts-node` options specified in the config file. + * + * Even when a tsconfig.json is not loaded, this function still handles merging + * compilerOptions from various sources: API, environment variables, etc. */ function readConfig ( cwd: string, ts: TSCommon, - rawOptions: CreateOptions + rawApiOptions: CreateOptions ): { - // Path of tsconfig file + /** + * Path of tsconfig file if one was loaded + */ configFilePath: string | undefined, - // Parsed TypeScript configuration. + /** + * Parsed TypeScript configuration with compilerOptions merged from all other sources (env vars, etc) + */ config: _ts.ParsedCommandLine - // Options pulled from `tsconfig.json`. - options: TsConfigOptions + /** + * ts-node options pulled from `tsconfig.json`, NOT merged with any other sources. Merging must happen outside + * this function. + */ + tsNodeOptionsFromTsconfig: TsConfigOptions } { let config: any = { compilerOptions: {} } let basePath = cwd let configFilePath: string | undefined = undefined - const projectSearchDir = resolve(cwd, rawOptions.projectSearchDir ?? cwd) + const projectSearchDir = resolve(cwd, rawApiOptions.projectSearchDir ?? cwd) const { fileExists = ts.sys.fileExists, readFile = ts.sys.readFile, skipProject = DEFAULTS.skipProject, project = DEFAULTS.project - } = rawOptions + } = rawApiOptions // Read project configuration when available. if (!skipProject) { @@ -1194,7 +1214,7 @@ function readConfig ( return { configFilePath, config: { errors: [result.error], fileNames: [], options: {} }, - options: {} + tsNodeOptionsFromTsconfig: {} } } @@ -1204,23 +1224,26 @@ function readConfig ( } // Fix ts-node options that come from tsconfig.json - const tsconfigOptions: TsConfigOptions = Object.assign({}, filterRecognizedTsConfigTsNodeOptions(config['ts-node'])) + const tsNodeOptionsFromTsconfig: TsConfigOptions = Object.assign({}, filterRecognizedTsConfigTsNodeOptions(config['ts-node'])) // Remove resolution of "files". - const files = rawOptions.files ?? tsconfigOptions.files ?? DEFAULTS.files + const files = rawApiOptions.files ?? tsNodeOptionsFromTsconfig.files ?? DEFAULTS.files if (!files) { config.files = [] config.include = [] } + const skipDefaultCompilerOptions = rawApiOptions.skipDefaultCompilerOptions ?? DEFAULTS.skipDefaultCompilerOptions + const defaultCompilerOptionsForNodeVersion = skipDefaultCompilerOptions ? undefined : getDefaultTsconfigJsonForNodeVersion().compilerOptions // Override default configuration options `ts-node` requires. config.compilerOptions = Object.assign( {}, - config.compilerOptions, - DEFAULTS.compilerOptions, - tsconfigOptions.compilerOptions, - rawOptions.compilerOptions, - TS_NODE_COMPILER_OPTIONS + defaultCompilerOptionsForNodeVersion, // automatically-applied options from @tsconfig/bases + config.compilerOptions, // tsconfig.json "compilerOptions" + DEFAULTS.compilerOptions, // from env var + tsNodeOptionsFromTsconfig.compilerOptions, // tsconfig.json "ts-node": "compilerOptions" + rawApiOptions.compilerOptions, // passed programmatically + TS_NODE_COMPILER_OPTIONS // overrides required by ts-node, cannot be changed ) const fixedConfig = fixConfig(ts, ts.parseJsonConfigFileContent(config, { @@ -1230,15 +1253,15 @@ function readConfig ( useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames }, basePath, undefined, configFilePath)) - if (tsconfigOptions.require) { + if (tsNodeOptionsFromTsconfig.require) { // Modules are found relative to the tsconfig file, not the `dir` option const tsconfigRelativeRequire = createRequire(configFilePath!) - tsconfigOptions.require = tsconfigOptions.require.map((path: string) => { + tsNodeOptionsFromTsconfig.require = tsNodeOptionsFromTsconfig.require.map((path: string) => { return tsconfigRelativeRequire.resolve(path) }) } - return { configFilePath, config: fixedConfig, options: tsconfigOptions } + return { configFilePath, config: fixedConfig, tsNodeOptionsFromTsconfig } } /** diff --git a/src/tsconfigs.ts b/src/tsconfigs.ts new file mode 100644 index 000000000..b46120834 --- /dev/null +++ b/src/tsconfigs.ts @@ -0,0 +1,12 @@ +import type * as _ts from "typescript"; + +const nodeMajor = parseInt(process.versions.node.split('.')[0], 10) +/** + * return parsed JSON of the bundled @tsconfig/bases config appropriate for the + * running version of nodejs + */ +export function getDefaultTsconfigJsonForNodeVersion(): any { + return nodeMajor >= 14 ? require('@tsconfig/node14/tsconfig.json') : + nodeMajor >= 12 ? require('@tsconfig/node12/tsconfig.json') : + require('@tsconfig/node10/tsconfig.json') +} From be233806168527fabe402292c7352f3f1d1b93be Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Mon, 22 Feb 2021 18:28:37 -0500 Subject: [PATCH 02/15] fix --- node10/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node10/tsconfig.json b/node10/tsconfig.json index dd3a5c3c0..7079f3adb 100644 --- a/node10/tsconfig.json +++ b/node10/tsconfig.json @@ -1,3 +1,3 @@ { - "extends": "@tsconfig/node10" + "extends": "@tsconfig/node10/tsconfig.json" } From e70b6a77962eebf2bf71da8f86fde005f7fb11d8 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Tue, 23 Feb 2021 11:05:41 -0500 Subject: [PATCH 03/15] fix lint --- src/tsconfigs.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tsconfigs.ts b/src/tsconfigs.ts index b46120834..c1b7b85da 100644 --- a/src/tsconfigs.ts +++ b/src/tsconfigs.ts @@ -1,11 +1,11 @@ -import type * as _ts from "typescript"; +import type * as _ts from 'typescript' const nodeMajor = parseInt(process.versions.node.split('.')[0], 10) /** * return parsed JSON of the bundled @tsconfig/bases config appropriate for the * running version of nodejs */ -export function getDefaultTsconfigJsonForNodeVersion(): any { +export function getDefaultTsconfigJsonForNodeVersion (): any { return nodeMajor >= 14 ? require('@tsconfig/node14/tsconfig.json') : nodeMajor >= 12 ? require('@tsconfig/node12/tsconfig.json') : require('@tsconfig/node10/tsconfig.json') From 3919b8b4366de05bb6f5ab7a549befd956d95559 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Tue, 23 Feb 2021 13:10:05 -0500 Subject: [PATCH 04/15] fix --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 76bd45dbd..ccbf01547 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1233,7 +1233,7 @@ function readConfig ( config.include = [] } - const skipDefaultCompilerOptions = rawApiOptions.skipDefaultCompilerOptions ?? DEFAULTS.skipDefaultCompilerOptions + const skipDefaultCompilerOptions = rawApiOptions.skipDefaultCompilerOptions ?? DEFAULTS.skipDefaultCompilerOptions ?? configFilePath != null // tslint:disable-line:strict-type-predicates const defaultCompilerOptionsForNodeVersion = skipDefaultCompilerOptions ? undefined : getDefaultTsconfigJsonForNodeVersion().compilerOptions // Override default configuration options `ts-node` requires. config.compilerOptions = Object.assign( From 43099737dd62033ed07d1d45caeae3ff0b6e0572 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Tue, 23 Feb 2021 13:25:08 -0500 Subject: [PATCH 05/15] fix --- src/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index ccbf01547..19f7e558e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1233,8 +1233,9 @@ function readConfig ( config.include = [] } - const skipDefaultCompilerOptions = rawApiOptions.skipDefaultCompilerOptions ?? DEFAULTS.skipDefaultCompilerOptions ?? configFilePath != null // tslint:disable-line:strict-type-predicates + const skipDefaultCompilerOptions = configFilePath != null || (rawApiOptions.skipDefaultCompilerOptions ?? DEFAULTS.skipDefaultCompilerOptions) const defaultCompilerOptionsForNodeVersion = skipDefaultCompilerOptions ? undefined : getDefaultTsconfigJsonForNodeVersion().compilerOptions + console.error(util.inspect({ skipDefaultCompilerOptions, defaultCompilerOptionsForNodeVersion })) // Override default configuration options `ts-node` requires. config.compilerOptions = Object.assign( {}, From 995a0f863a2de2a5d39146d9d2c00fef51e6713c Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Tue, 23 Feb 2021 13:29:13 -0500 Subject: [PATCH 06/15] cleanup --- src/index.ts | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/index.ts b/src/index.ts index 19f7e558e..cca7e1dd3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1233,18 +1233,25 @@ function readConfig ( config.include = [] } - const skipDefaultCompilerOptions = configFilePath != null || (rawApiOptions.skipDefaultCompilerOptions ?? DEFAULTS.skipDefaultCompilerOptions) + // Only if a config file is *not* loaded, load an implicit configuration from @tsconfig/bases + const skipDefaultCompilerOptions = configFilePath != null || (rawApiOptions.skipDefaultCompilerOptions ?? DEFAULTS.skipDefaultCompilerOptions) // tslint:disable-line const defaultCompilerOptionsForNodeVersion = skipDefaultCompilerOptions ? undefined : getDefaultTsconfigJsonForNodeVersion().compilerOptions - console.error(util.inspect({ skipDefaultCompilerOptions, defaultCompilerOptionsForNodeVersion })) - // Override default configuration options `ts-node` requires. + + // Merge compilerOptions from all sources config.compilerOptions = Object.assign( {}, - defaultCompilerOptionsForNodeVersion, // automatically-applied options from @tsconfig/bases - config.compilerOptions, // tsconfig.json "compilerOptions" - DEFAULTS.compilerOptions, // from env var - tsNodeOptionsFromTsconfig.compilerOptions, // tsconfig.json "ts-node": "compilerOptions" - rawApiOptions.compilerOptions, // passed programmatically - TS_NODE_COMPILER_OPTIONS // overrides required by ts-node, cannot be changed + // automatically-applied options from @tsconfig/bases + defaultCompilerOptionsForNodeVersion, + // tsconfig.json "compilerOptions" + config.compilerOptions, + // from env var + DEFAULTS.compilerOptions, + // tsconfig.json "ts-node": "compilerOptions" + tsNodeOptionsFromTsconfig.compilerOptions, + // passed programmatically + rawApiOptions.compilerOptions, + // overrides required by ts-node, cannot be changed + TS_NODE_COMPILER_OPTIONS ) const fixedConfig = fixConfig(ts, ts.parseJsonConfigFileContent(config, { From 182c16b1a8cef14f87deb4f150a1e740e6f6b00f Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Tue, 23 Feb 2021 14:28:30 -0500 Subject: [PATCH 07/15] fallback to older @tsconfig/node* config when we detect an incompatibility with the lib or target options --- src/index.ts | 3 ++- src/tsconfigs.ts | 30 +++++++++++++++++++++++++----- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/index.ts b/src/index.ts index cca7e1dd3..82b03e54c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -132,6 +132,7 @@ export interface TSCommon { parseJsonConfigFileContent: typeof _ts.parseJsonConfigFileContent formatDiagnostics: typeof _ts.formatDiagnostics formatDiagnosticsWithColorAndContext: typeof _ts.formatDiagnosticsWithColorAndContext + libs?: string[] } /** @@ -1235,7 +1236,7 @@ function readConfig ( // Only if a config file is *not* loaded, load an implicit configuration from @tsconfig/bases const skipDefaultCompilerOptions = configFilePath != null || (rawApiOptions.skipDefaultCompilerOptions ?? DEFAULTS.skipDefaultCompilerOptions) // tslint:disable-line - const defaultCompilerOptionsForNodeVersion = skipDefaultCompilerOptions ? undefined : getDefaultTsconfigJsonForNodeVersion().compilerOptions + const defaultCompilerOptionsForNodeVersion = skipDefaultCompilerOptions ? undefined : getDefaultTsconfigJsonForNodeVersion(ts).compilerOptions // Merge compilerOptions from all sources config.compilerOptions = Object.assign( diff --git a/src/tsconfigs.ts b/src/tsconfigs.ts index c1b7b85da..1b6207fe4 100644 --- a/src/tsconfigs.ts +++ b/src/tsconfigs.ts @@ -1,12 +1,32 @@ -import type * as _ts from 'typescript' +import { TSCommon } from '.' const nodeMajor = parseInt(process.versions.node.split('.')[0], 10) /** * return parsed JSON of the bundled @tsconfig/bases config appropriate for the * running version of nodejs */ -export function getDefaultTsconfigJsonForNodeVersion (): any { - return nodeMajor >= 14 ? require('@tsconfig/node14/tsconfig.json') : - nodeMajor >= 12 ? require('@tsconfig/node12/tsconfig.json') : - require('@tsconfig/node10/tsconfig.json') +export function getDefaultTsconfigJsonForNodeVersion (ts: TSCommon): any { + if(nodeMajor >= 14) { + const config = require('@tsconfig/node14/tsconfig.json') + if(configCompatible(config)) return config + } + if(nodeMajor >= 12) { + const config = require('@tsconfig/node12/tsconfig.json') + if(configCompatible(config)) return config + } + return require('@tsconfig/node10/tsconfig.json') + + // Verify that tsconfig target and lib options are compatible with TypeScript compiler + function configCompatible(config: { + compilerOptions: { + lib: string[], + target: string + } + }) { + return ( + typeof (ts.ScriptTarget as any)[config.compilerOptions.target.toUpperCase()] === 'number' && + ts.libs && + config.compilerOptions.lib.every(lib => ts.libs!.includes(lib)) + ) + } } From 42b638438b435f17a6429777f148e19f95663202 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Tue, 23 Feb 2021 14:57:13 -0500 Subject: [PATCH 08/15] lint fix --- src/tsconfigs.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/tsconfigs.ts b/src/tsconfigs.ts index 1b6207fe4..2ce944a4d 100644 --- a/src/tsconfigs.ts +++ b/src/tsconfigs.ts @@ -4,20 +4,21 @@ const nodeMajor = parseInt(process.versions.node.split('.')[0], 10) /** * return parsed JSON of the bundled @tsconfig/bases config appropriate for the * running version of nodejs + * @internal */ export function getDefaultTsconfigJsonForNodeVersion (ts: TSCommon): any { - if(nodeMajor >= 14) { + if (nodeMajor >= 14) { const config = require('@tsconfig/node14/tsconfig.json') - if(configCompatible(config)) return config + if (configCompatible(config)) return config } - if(nodeMajor >= 12) { + if (nodeMajor >= 12) { const config = require('@tsconfig/node12/tsconfig.json') - if(configCompatible(config)) return config + if (configCompatible(config)) return config } return require('@tsconfig/node10/tsconfig.json') // Verify that tsconfig target and lib options are compatible with TypeScript compiler - function configCompatible(config: { + function configCompatible (config: { compilerOptions: { lib: string[], target: string From e988cfcd452100674e6d6b89c8ad90f2df1d62fa Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Tue, 23 Feb 2021 21:23:21 -0500 Subject: [PATCH 09/15] WIP --- src/index.spec.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/index.spec.ts b/src/index.spec.ts index b1fb16d7f..250a26932 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -6,7 +6,7 @@ import semver = require('semver') import ts = require('typescript') import proxyquire = require('proxyquire') import type * as tsNodeTypes from './index' -import { unlinkSync, existsSync, lstatSync } from 'fs' +import { unlinkSync, existsSync, lstatSync, mkdtempSync, fstat, copyFileSync } from 'fs' import * as promisify from 'util.promisify' import { sync as rimrafSync } from 'rimraf' import type _createRequire from 'create-require' @@ -537,6 +537,15 @@ test.suite('ts-node', (test) => { }) }) + test('should use implicit @tsconfig/bases config when one is not loaded from disk', async (t) => { + const tempDir = mkdtempSync('ts-node-spec') + // const fixtureDir = join(TEST_DIR, 'implicit-tsconfig') + // copyFileSync(join(fixtureDir, 'script.ts'), join(tempDir, 'script.ts')) + const { err, stdout, stderr } = await exec(`${BIN_PATH} -pe 10n`, { cwd: tempDir }) + expect(err).to.equal(null) + expect(stdout).to.equal('10n\n') + }) + test.suite('compiler host', (test) => { test('should execute cli', async () => { const { err, stdout } = await exec(`${cmd} --compiler-host hello-world`) From 994044d5272ef0f4cba85056c16c79bd18307cba Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 26 Feb 2021 18:14:45 -0500 Subject: [PATCH 10/15] Add CLI and programmatic option to disable implicit compiler options --- src/bin.ts | 4 ++++ src/index.ts | 14 ++++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/bin.ts b/src/bin.ts index 0924f96b0..bc07f7db0 100644 --- a/src/bin.ts +++ b/src/bin.ts @@ -45,6 +45,7 @@ export function main (argv: string[] = process.argv.slice(2), entrypointArgs: Re '--compiler-host': Boolean, '--pretty': Boolean, '--skip-project': Boolean, + '--no-implicit-compiler-options': Boolean, '--skip-ignore': Boolean, '--prefer-ts-exts': Boolean, '--log-error': Boolean, @@ -90,6 +91,7 @@ export function main (argv: string[] = process.argv.slice(2), entrypointArgs: Re '--files': files, '--compiler': compiler, '--compiler-options': compilerOptions, + '--no-implicit-compiler-options': noImplicitCompilerOptions, '--project': project, '--ignore-diagnostics': ignoreDiagnostics, '--ignore': ignore, @@ -132,6 +134,7 @@ export function main (argv: string[] = process.argv.slice(2), entrypointArgs: Re --files Load \`files\`, \`include\` and \`exclude\` from \`tsconfig.json\` on startup --pretty Use pretty diagnostic formatter (usually enabled by default) --skip-project Skip reading \`tsconfig.json\` + --no-implicit-compiler-options Do not use a default \`tsconfig.json\` from @tsconfig/bases matching your node version. --skip-ignore Skip \`--ignore\` checks --prefer-ts-exts Prefer importing TypeScript files over JavaScript files --log-error Logs TypeScript errors to stderr instead of throwing exceptions @@ -172,6 +175,7 @@ export function main (argv: string[] = process.argv.slice(2), entrypointArgs: Re compiler, ignoreDiagnostics, compilerOptions, + noImplicitCompilerOptions, require: argsRequire, readFile: code !== undefined ? evalAwarePartialHost.readFile : undefined, fileExists: code !== undefined ? evalAwarePartialHost.fileExists : undefined diff --git a/src/index.ts b/src/index.ts index 32077922e..2412c74fa 100644 --- a/src/index.ts +++ b/src/index.ts @@ -265,12 +265,14 @@ export interface CreateOptions { */ skipProject?: boolean /** - * Skip loading a default @tsconfig/* that matches the version of nodejs - * TODO needs a better name + * Do not apply compiler options from the @tsconfig/bases configuration matching your node version + * + * This option has no effect if a tsconfig.json is loaded, because loading a tsconfig.json disables + * implicit compiler options. * * @default false */ - skipDefaultCompilerOptions?: boolean + noImplicitCompilerOptions?: boolean /** * Skip ignore check, so that compilation will be attempted for all files with matching extensions. * @@ -329,6 +331,7 @@ export interface TsConfigOptions extends Omit {} /** @@ -384,7 +386,7 @@ export const DEFAULTS: RegisterOptions = { compilerHost: yn(env.TS_NODE_COMPILER_HOST), logError: yn(env.TS_NODE_LOG_ERROR), experimentalEsmLoader: false, - skipDefaultCompilerOptions: false + noImplicitCompilerOptions: false } /** @@ -1247,7 +1249,7 @@ function readConfig ( } // Only if a config file is *not* loaded, load an implicit configuration from @tsconfig/bases - const skipDefaultCompilerOptions = configFilePath != null || (rawApiOptions.skipDefaultCompilerOptions ?? DEFAULTS.skipDefaultCompilerOptions) // tslint:disable-line + const skipDefaultCompilerOptions = configFilePath != null || (rawApiOptions.noImplicitCompilerOptions ?? DEFAULTS.noImplicitCompilerOptions) // tslint:disable-line const defaultCompilerOptionsForNodeVersion = skipDefaultCompilerOptions ? undefined : getDefaultTsconfigJsonForNodeVersion(ts).compilerOptions // Merge compilerOptions from all sources From d01e52735ca2031ffedf81e8499f2e1c796072d3 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 26 Feb 2021 18:19:16 -0500 Subject: [PATCH 11/15] Remove --no-implicit-compiler-options flag and programmatic option; it is implemented in another PR --- src/bin.ts | 4 ---- src/index.ts | 15 ++------------- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/src/bin.ts b/src/bin.ts index bc07f7db0..0924f96b0 100644 --- a/src/bin.ts +++ b/src/bin.ts @@ -45,7 +45,6 @@ export function main (argv: string[] = process.argv.slice(2), entrypointArgs: Re '--compiler-host': Boolean, '--pretty': Boolean, '--skip-project': Boolean, - '--no-implicit-compiler-options': Boolean, '--skip-ignore': Boolean, '--prefer-ts-exts': Boolean, '--log-error': Boolean, @@ -91,7 +90,6 @@ export function main (argv: string[] = process.argv.slice(2), entrypointArgs: Re '--files': files, '--compiler': compiler, '--compiler-options': compilerOptions, - '--no-implicit-compiler-options': noImplicitCompilerOptions, '--project': project, '--ignore-diagnostics': ignoreDiagnostics, '--ignore': ignore, @@ -134,7 +132,6 @@ export function main (argv: string[] = process.argv.slice(2), entrypointArgs: Re --files Load \`files\`, \`include\` and \`exclude\` from \`tsconfig.json\` on startup --pretty Use pretty diagnostic formatter (usually enabled by default) --skip-project Skip reading \`tsconfig.json\` - --no-implicit-compiler-options Do not use a default \`tsconfig.json\` from @tsconfig/bases matching your node version. --skip-ignore Skip \`--ignore\` checks --prefer-ts-exts Prefer importing TypeScript files over JavaScript files --log-error Logs TypeScript errors to stderr instead of throwing exceptions @@ -175,7 +172,6 @@ export function main (argv: string[] = process.argv.slice(2), entrypointArgs: Re compiler, ignoreDiagnostics, compilerOptions, - noImplicitCompilerOptions, require: argsRequire, readFile: code !== undefined ? evalAwarePartialHost.readFile : undefined, fileExists: code !== undefined ? evalAwarePartialHost.fileExists : undefined diff --git a/src/index.ts b/src/index.ts index 2412c74fa..f9fb36593 100644 --- a/src/index.ts +++ b/src/index.ts @@ -264,15 +264,6 @@ export interface CreateOptions { * @default false */ skipProject?: boolean - /** - * Do not apply compiler options from the @tsconfig/bases configuration matching your node version - * - * This option has no effect if a tsconfig.json is loaded, because loading a tsconfig.json disables - * implicit compiler options. - * - * @default false - */ - noImplicitCompilerOptions?: boolean /** * Skip ignore check, so that compilation will be attempted for all files with matching extensions. * @@ -331,7 +322,6 @@ export interface TsConfigOptions extends Omit Date: Fri, 26 Feb 2021 19:39:33 -0500 Subject: [PATCH 12/15] add tests --- src/index.spec.ts | 53 +++++++++++++++++++---- tests/tsconfig-bases/node10/tsconfig.json | 3 ++ tests/tsconfig-bases/node12/tsconfig.json | 3 ++ tests/tsconfig-bases/node14/tsconfig.json | 3 ++ 4 files changed, 54 insertions(+), 8 deletions(-) create mode 100644 tests/tsconfig-bases/node10/tsconfig.json create mode 100644 tests/tsconfig-bases/node12/tsconfig.json create mode 100644 tests/tsconfig-bases/node14/tsconfig.json diff --git a/src/index.spec.ts b/src/index.spec.ts index 3b35e4b2f..b4e621ad9 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -2,11 +2,12 @@ import { test, TestInterface } from './testlib' import { expect } from 'chai' import { ChildProcess, exec as childProcessExec, ExecException, ExecOptions } from 'child_process' import { join, resolve, sep as pathSep } from 'path' +import { tmpdir } from 'os' import semver = require('semver') import ts = require('typescript') import proxyquire = require('proxyquire') import type * as tsNodeTypes from './index' -import { unlinkSync, existsSync, lstatSync, mkdtempSync, fstat, copyFileSync } from 'fs' +import { unlinkSync, existsSync, lstatSync, mkdtempSync, fstat, copyFileSync, writeFileSync } from 'fs' import * as promisify from 'util.promisify' import { sync as rimrafSync } from 'rimraf' import type _createRequire from 'create-require' @@ -538,13 +539,49 @@ test.suite('ts-node', (test) => { }) }) - test('should use implicit @tsconfig/bases config when one is not loaded from disk', async (t) => { - const tempDir = mkdtempSync('ts-node-spec') - // const fixtureDir = join(TEST_DIR, 'implicit-tsconfig') - // copyFileSync(join(fixtureDir, 'script.ts'), join(tempDir, 'script.ts')) - const { err, stdout, stderr } = await exec(`${BIN_PATH} -pe 10n`, { cwd: tempDir }) - expect(err).to.equal(null) - expect(stdout).to.equal('10n\n') + test.suite('should use implicit @tsconfig/bases config when one is not loaded from disk', _test => { + const test = _test.context(async t => ({ + tempDir: mkdtempSync(join(tmpdir(), 'ts-node-spec')) + })) + if (semver.gte(ts.version, '3.5.0') && semver.gte(process.versions.node, '14.0.0')) { + test('implicitly uses @tsconfig/node14 compilerOptions when both TS and node versions support it', async t => { + const { context: { tempDir } } = t + const { err: err1, stdout: stdout1, stderr: stderr1 } = await exec(`${BIN_PATH} --showConfig -e 10n`, { cwd: tempDir }) + expect(err1).to.equal(null) + t.like(JSON.parse(stdout1), { + compilerOptions: { + target: 'es2020', + lib: ['es2020'] + } + }) + const { err: err2, stdout: stdout2, stderr: stderr2 } = await exec(`${BIN_PATH} -pe 10n`, { cwd: tempDir }) + expect(err2).to.equal(null) + expect(stdout2).to.equal('10n\n') + }) + } else { + test('implicitly uses @tsconfig/* lower than node14 (node10 or node12) when either TS or node versions do not support @tsconfig/node14', async ({ context: { tempDir } }) => { + const { err, stdout, stderr } = await exec(`${BIN_PATH} -pe 10n`, { cwd: tempDir }) + expect(err).to.not.equal(null) + expect(stderr).to.contain('BigInt literals are not available when targeting lower than') + }) + } + }) + + test.suite('should bundle @tsconfig/bases to be used in your own tsconfigs', test => { + const macro = test.macro((nodeVersion: string) => async t => { + const config = require(`@tsconfig/${ nodeVersion }/tsconfig.json`) + const { err, stdout, stderr } = await exec(`${BIN_PATH} --showConfig -e 10n`, { cwd: join(TEST_DIR, 'tsconfig-bases', nodeVersion) }) + expect(err).to.equal(null) + t.like(JSON.parse(stdout), { + compilerOptions: { + target: config.compilerOptions.target, + lib: config.compilerOptions.lib + } + }) + }) + test(`ts-node/node10/tsconfig.json`, macro, 'node10') + test(`ts-node/node12/tsconfig.json`, macro, 'node12') + test(`ts-node/node14/tsconfig.json`, macro, 'node14') }) test.suite('compiler host', (test) => { diff --git a/tests/tsconfig-bases/node10/tsconfig.json b/tests/tsconfig-bases/node10/tsconfig.json new file mode 100644 index 000000000..f8b881e4c --- /dev/null +++ b/tests/tsconfig-bases/node10/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "ts-node/node10/tsconfig.json" +} diff --git a/tests/tsconfig-bases/node12/tsconfig.json b/tests/tsconfig-bases/node12/tsconfig.json new file mode 100644 index 000000000..eda168e10 --- /dev/null +++ b/tests/tsconfig-bases/node12/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "ts-node/node12/tsconfig.json" +} diff --git a/tests/tsconfig-bases/node14/tsconfig.json b/tests/tsconfig-bases/node14/tsconfig.json new file mode 100644 index 000000000..8a496b8a8 --- /dev/null +++ b/tests/tsconfig-bases/node14/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "ts-node/node14/tsconfig.json" +} From 321ff32755e2be8bd7ac0d4e9efd2a4b7d09784c Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 26 Feb 2021 19:45:53 -0500 Subject: [PATCH 13/15] fix tests --- src/index.spec.ts | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/index.spec.ts b/src/index.spec.ts index b4e621ad9..e1abf594b 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -567,22 +567,24 @@ test.suite('ts-node', (test) => { } }) - test.suite('should bundle @tsconfig/bases to be used in your own tsconfigs', test => { - const macro = test.macro((nodeVersion: string) => async t => { - const config = require(`@tsconfig/${ nodeVersion }/tsconfig.json`) - const { err, stdout, stderr } = await exec(`${BIN_PATH} --showConfig -e 10n`, { cwd: join(TEST_DIR, 'tsconfig-bases', nodeVersion) }) - expect(err).to.equal(null) - t.like(JSON.parse(stdout), { - compilerOptions: { - target: config.compilerOptions.target, - lib: config.compilerOptions.lib - } + if (semver.gte(ts.version, '3.2.0')) { + test.suite('should bundle @tsconfig/bases to be used in your own tsconfigs', test => { + const macro = test.macro((nodeVersion: string) => async t => { + const config = require(`@tsconfig/${ nodeVersion }/tsconfig.json`) + const { err, stdout, stderr } = await exec(`${BIN_PATH} --showConfig -e 10n`, { cwd: join(TEST_DIR, 'tsconfig-bases', nodeVersion) }) + expect(err).to.equal(null) + t.like(JSON.parse(stdout), { + compilerOptions: { + target: config.compilerOptions.target, + lib: config.compilerOptions.lib + } + }) }) + test(`ts-node/node10/tsconfig.json`, macro, 'node10') + test(`ts-node/node12/tsconfig.json`, macro, 'node12') + test(`ts-node/node14/tsconfig.json`, macro, 'node14') }) - test(`ts-node/node10/tsconfig.json`, macro, 'node10') - test(`ts-node/node12/tsconfig.json`, macro, 'node12') - test(`ts-node/node14/tsconfig.json`, macro, 'node14') - }) + } test.suite('compiler host', (test) => { test('should execute cli', async () => { From 135faa288a89745bd608bd456a9985f883dcbee4 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 26 Feb 2021 19:59:49 -0500 Subject: [PATCH 14/15] fix tests --- src/index.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.spec.ts b/src/index.spec.ts index e1abf594b..39b8693ad 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -546,7 +546,7 @@ test.suite('ts-node', (test) => { if (semver.gte(ts.version, '3.5.0') && semver.gte(process.versions.node, '14.0.0')) { test('implicitly uses @tsconfig/node14 compilerOptions when both TS and node versions support it', async t => { const { context: { tempDir } } = t - const { err: err1, stdout: stdout1, stderr: stderr1 } = await exec(`${BIN_PATH} --showConfig -e 10n`, { cwd: tempDir }) + const { err: err1, stdout: stdout1, stderr: stderr1 } = await exec(`${BIN_PATH} --showConfig`, { cwd: tempDir }) expect(err1).to.equal(null) t.like(JSON.parse(stdout1), { compilerOptions: { @@ -562,7 +562,7 @@ test.suite('ts-node', (test) => { test('implicitly uses @tsconfig/* lower than node14 (node10 or node12) when either TS or node versions do not support @tsconfig/node14', async ({ context: { tempDir } }) => { const { err, stdout, stderr } = await exec(`${BIN_PATH} -pe 10n`, { cwd: tempDir }) expect(err).to.not.equal(null) - expect(stderr).to.contain('BigInt literals are not available when targeting lower than') + expect(stderr).to.match(/BigInt literals are not available when targeting lower than|error TS2304: Cannot find name 'n'/) }) } }) From 294eeb66f4891e8a3915b07405f1beca13a733d7 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 26 Feb 2021 20:17:46 -0500 Subject: [PATCH 15/15] fix tests --- src/index.spec.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/index.spec.ts b/src/index.spec.ts index 39b8693ad..e6ea53eff 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -98,6 +98,10 @@ test.suite('ts-node', (test) => { testsDirRequire.resolve('ts-node/esm.mjs') testsDirRequire.resolve('ts-node/esm/transpile-only') testsDirRequire.resolve('ts-node/esm/transpile-only.mjs') + + testsDirRequire.resolve('ts-node/node10/tsconfig.json') + testsDirRequire.resolve('ts-node/node12/tsconfig.json') + testsDirRequire.resolve('ts-node/node14/tsconfig.json') }) test.suite('cli', (test) => {