Skip to content

Commit

Permalink
Merge 994044d into 4e04302
Browse files Browse the repository at this point in the history
  • Loading branch information
cspotcode committed Feb 26, 2021
2 parents 4e04302 + 994044d commit 7816ac7
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 23 deletions.
3 changes: 3 additions & 0 deletions node10/tsconfig.json
@@ -0,0 +1,3 @@
{
"extends": "@tsconfig/node10/tsconfig.json"
}
3 changes: 3 additions & 0 deletions node12/tsconfig.json
@@ -0,0 +1,3 @@
{
"extends": "@tsconfig/node12/tsconfig.json",
}
3 changes: 3 additions & 0 deletions node14/tsconfig.json
@@ -0,0 +1,3 @@
{
"extends": "@tsconfig/node14/tsconfig.json"
}
15 changes: 15 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 11 additions & 2 deletions package.json
Expand Up @@ -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": {
Expand All @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
4 changes: 4 additions & 0 deletions src/bin.ts
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
11 changes: 10 additions & 1 deletion src/index.spec.ts
Expand Up @@ -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'
Expand Down Expand Up @@ -538,6 +538,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`)
Expand Down
74 changes: 54 additions & 20 deletions src/index.ts
Expand Up @@ -7,6 +7,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'

/** @internal */
export const createRequire = nodeCreateRequire ?? nodeCreateRequireFromPath ?? require('create-require') as typeof _createRequire // tslint:disable-line:deprecation
Expand Down Expand Up @@ -131,6 +132,7 @@ export interface TSCommon {
parseJsonConfigFileContent: typeof _ts.parseJsonConfigFileContent
formatDiagnostics: typeof _ts.formatDiagnostics
formatDiagnosticsWithColorAndContext: typeof _ts.formatDiagnosticsWithColorAndContext
libs?: string[]
}

/**
Expand Down Expand Up @@ -262,6 +264,15 @@ 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.
*
Expand Down Expand Up @@ -320,6 +331,7 @@ export interface TsConfigOptions extends Omit<RegisterOptions,
| 'readFile'
| 'fileExists'
| 'skipProject'
| 'noImplicitCompilerOptions'
| 'project'
| 'dir'
| 'cwd'
Expand Down Expand Up @@ -373,7 +385,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,
noImplicitCompilerOptions: false
}

/**
Expand Down Expand Up @@ -521,10 +534,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<RegisterOptions>({}, DEFAULTS, tsconfigOptions || {}, rawOptions)
const { configFilePath, config, tsNodeOptionsFromTsconfig } = readConfig(cwd, ts, rawOptions)
const options = assign<RegisterOptions>({}, DEFAULTS, tsNodeOptionsFromTsconfig || {}, rawOptions)
options.require = [
...tsconfigOptions.require || [],
...tsNodeOptionsFromTsconfig.require || [],
...rawOptions.require || []
]

Expand Down Expand Up @@ -1167,30 +1180,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) {
Expand All @@ -1206,7 +1229,7 @@ function readConfig (
return {
configFilePath,
config: { errors: [result.error], fileNames: [], options: {} },
options: {}
tsNodeOptionsFromTsconfig: {}
}
}

Expand All @@ -1216,22 +1239,33 @@ 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 = []
}

// Override default configuration options `ts-node` requires.
// Only if a config file is *not* loaded, load an implicit configuration from @tsconfig/bases
const skipDefaultCompilerOptions = configFilePath != null || (rawApiOptions.noImplicitCompilerOptions ?? DEFAULTS.noImplicitCompilerOptions) // tslint:disable-line
const defaultCompilerOptionsForNodeVersion = skipDefaultCompilerOptions ? undefined : getDefaultTsconfigJsonForNodeVersion(ts).compilerOptions

// Merge compilerOptions from all sources
config.compilerOptions = Object.assign(
{},
// automatically-applied options from @tsconfig/bases
defaultCompilerOptionsForNodeVersion,
// tsconfig.json "compilerOptions"
config.compilerOptions,
// from env var
DEFAULTS.compilerOptions,
tsconfigOptions.compilerOptions,
rawOptions.compilerOptions,
// tsconfig.json "ts-node": "compilerOptions"
tsNodeOptionsFromTsconfig.compilerOptions,
// passed programmatically
rawApiOptions.compilerOptions,
// overrides required by ts-node, cannot be changed
TS_NODE_COMPILER_OPTIONS
)

Expand All @@ -1242,15 +1276,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 }
}

/**
Expand Down
33 changes: 33 additions & 0 deletions src/tsconfigs.ts
@@ -0,0 +1,33 @@
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
* @internal
*/
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))
)
}
}

0 comments on commit 7816ac7

Please sign in to comment.