import * as path from "path" ; import * as fs from "fs" ; import JSON5 = require ( "json5" ) ; import StripBom = require ( "strip-bom" ) ; /**
 * Typing for the parts of tsconfig that we care about
 */ export interface Tsconfig { extends ? : string ; compilerOptions ? : { baseUrl ? : string ; paths ? : { [ key ] : Array < string > ; } ; strict ? : boolean ; } ; } export interface TsConfigLoaderResult { tsConfigPath : string | undefined ; baseUrl : string | undefined ; paths : { [ key ] : Array < string > ; } | undefined ; } export interface TsConfigLoaderParams { getEnv : ( key : string ) => string | undefined ; cwd : string ; loadSync ? ( cwd , filename ? , baseUrl ? ) : TsConfigLoaderResult ; } export function tsConfigLoader ( { getEnv , cwd , loadSync = loadSyncDefault , } ) { const TS_NODE_PROJECT = getEnv ( "TS_NODE_PROJECT" ) ; const TS_NODE_BASEURL = getEnv ( "TS_NODE_BASEURL" ) ; const loadResult = loadSync ( cwd , TS_NODE_PROJECT , TS_NODE_BASEURL ) ; return loadResult ; } function loadSyncDefault ( cwd , filename ? , baseUrl ? ) { const configPath = resolveConfigPath ( cwd , filename ) ; if ( ! configPath ) { return { tsConfigPath : undefined , baseUrl : undefined , paths : undefined , } ; } const config = loadTsconfig ( configPath ) ; return { tsConfigPath : configPath , baseUrl : baseUrl || ( config && config . compilerOptions && config . compilerOptions . baseUrl ) , paths : config && config . compilerOptions && config . compilerOptions . paths , } ; } function resolveConfigPath ( cwd , filename ? ) { if ( filename ) { const absolutePath = fs . lstatSync ( filename ) . isDirectory ( ) ? path . resolve ( filename , "./tsconfig.json" ) : path . resolve ( cwd , filename ) ; return absolutePath ; } if ( fs . statSync ( cwd ) . isFile ( ) ) { return path . resolve ( cwd ) ; } const configAbsolutePath = walkForTsConfig ( cwd ) ; return configAbsolutePath ? path . resolve ( configAbsolutePath ) : undefined ; } export function walkForTsConfig ( directory , existsSync = fs . existsSync ) { const configPath = path . join ( directory , "./tsconfig.json" ) ; if ( existsSync ( configPath ) ) { return configPath ; } const parentDirectory = path . join ( directory , "../" ) ; if ( directory === parentDirectory ) { return undefined ; } return walkForTsConfig ( parentDirectory , existsSync ) ; } export function loadTsconfig ( configFilePath , existsSync = fs . existsSync , readFileSync = ( filename ) => fs . readFileSync ( filename , "utf8" ) ) { if ( ! existsSync ( configFilePath ) ) { return undefined ; } const configString = readFileSync ( configFilePath ) ; const cleanedJson = StripBom ( configString ) ; const config = JSON5 . parse ( cleanedJson ) ; let extendedConfig = config . extends ; if ( extendedConfig ) { if ( typeof extendedConfig === "string" && extendedConfig . indexOf ( ".json" ) === - 1 ) { extendedConfig += ".json" ; } const currentDir = path . dirname ( configFilePath ) ; let extendedConfigPath = path . join ( currentDir , extendedConfig ) ; if ( extendedConfig . indexOf ( "/" ) !== - 1 && extendedConfig . indexOf ( "." ) !== - 1 && ! existsSync ( extendedConfigPath ) ) { extendedConfigPath = path . join ( currentDir , "node_modules" , extendedConfig ) ; } const base = loadTsconfig ( extendedConfigPath , existsSync , readFileSync ) || { } ; if ( base . compilerOptions && base . compilerOptions . baseUrl ) { const extendsDir = path . dirname ( extendedConfig ) ; base . compilerOptions . baseUrl = path . join ( extendsDir , base . compilerOptions . baseUrl ) ; } return { ... base , ... config , compilerOptions : { ... base . compilerOptions , ... config . compilerOptions , } , } ; } return config ; }