diff --git a/src/config.ts b/src/config.ts index b3ffccc82..06419cfde 100644 --- a/src/config.ts +++ b/src/config.ts @@ -24,7 +24,7 @@ export function getConfigFile( log: logger.Logger, compilerDetailsLogMessage: string ) { - const configFilePath = findConfigFile(compiler, path.dirname(loader.resourcePath), loaderOptions.configFileName); + const configFilePath = findConfigFile(compiler, path.dirname(loader.resourcePath), loaderOptions.configFile); let configFileError: WebpackError | undefined; let configFile: ConfigFile; @@ -69,26 +69,51 @@ export function getConfigFile( }; } + /** - * The tsconfig.json is found using the same method as `tsc`, starting in the current directory - * and continuing up the parent directory chain. + * Find a tsconfig file by name or by path. + * By name, the tsconfig.json is found using the same method as `tsc`, starting in the current + * directory and continuing up the parent directory chain. + * By path, the file will be found by resolving the given path relative to the requesting entry file. + * + * @param compiler The TypeScript compiler instance + * @param requestDirPath The directory in which the entry point requesting the tsconfig.json lies + * @param configFile The tsconfig file name to look for or a path to that file + * @return The absolute path to the tsconfig file, undefined if none was found. */ -function findConfigFile(compiler: typeof typescript, searchPath: string, configFileName: string): string | undefined { - // Remove path.root from configFileName to fix windows specific issue. #604 - configFileName = configFileName.substr(path.parse(configFileName).root.length) - - while (true) { - const fileName = path.join(searchPath, configFileName); - if (compiler.sys.fileExists(fileName)) { - return fileName; - } - const parentPath = path.dirname(searchPath); - if (parentPath === searchPath) { - break; +function findConfigFile(compiler: typeof typescript, requestDirPath: string, configFile: string): string | undefined { + // If `configFile` is an absolute path, return it right away + if (path.isAbsolute(configFile)) { + return compiler.sys.fileExists(configFile) + ? configFile + : undefined; + } + + // If `configFile` is a relative path, resolve it. + // We define a relative path as: starts with + // one or two dots + a common directory delimiter + if (configFile.match(/^\.\.?(\/|\\)/)) { + const resolvedPath = path.resolve(configFile); + return compiler.sys.fileExists(resolvedPath) + ? resolvedPath + : undefined; + + // If `configFile` is a file name, find it in the directory tree + } else { + while (true) { + const fileName = path.join(requestDirPath, configFile); + if (compiler.sys.fileExists(fileName)) { + return fileName; + } + const parentPath = path.dirname(requestDirPath); + if (parentPath === requestDirPath) { + break; + } + requestDirPath = parentPath; } - searchPath = parentPath; + + return undefined; } - return undefined; } export function getConfigParseResult( diff --git a/src/index.ts b/src/index.ts index c507107aa..a42c86073 100644 --- a/src/index.ts +++ b/src/index.ts @@ -104,7 +104,7 @@ function getLoaderOptions(loader: Webpack) { logLevel: 'INFO', logInfoToStdOut: false, compiler: 'typescript', - configFileName: 'tsconfig.json', + configFile: 'tsconfig.json', transpileOnly: false, visualStudioErrorFormat: false, compilerOptions: {}, @@ -115,6 +115,15 @@ function getLoaderOptions(loader: Webpack) { happyPackMode: false, }, configFileOptions, queryOptions); + // Use deprecated `configFileName` as fallback for `configFile` + if (queryOptions.configFileName) { + if (queryOptions.configFile) { + throw new Error('ts-loader options `configFile` and `configFileName` are mutually exclusive'); + } else { + options.configFile = queryOptions.configFileName; + } + } + options.ignoreDiagnostics = arrify(options.ignoreDiagnostics).map(Number); options.logLevel = options.logLevel.toUpperCase(); options.instance = instanceName; diff --git a/src/instances.ts b/src/instances.ts index 02f017709..a6abfbd07 100644 --- a/src/instances.ts +++ b/src/instances.ts @@ -1,6 +1,7 @@ import * as typescript from 'typescript'; import * as path from 'path'; import * as fs from 'fs'; +import { yellow } from 'chalk'; import { makeAfterCompile } from './after-compile'; import { getConfigFile, getConfigParseResult } from './config'; @@ -38,6 +39,10 @@ export function getTypeScriptInstance( const log = logger.makeLogger(loaderOptions); const compiler = getCompiler(loaderOptions, log); + if (loaderOptions.configFileName) { + log.logWarning(yellow('Usage of ts-loader option `configFileName` is deprecated. Use `configFile` instead.')); + } + if (compiler.errorMessage !== undefined) { return { error: makeError({ rawMessage: compiler.errorMessage }) }; } diff --git a/src/interfaces.ts b/src/interfaces.ts index 9ee5a24d8..b32a72d3a 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -253,7 +253,8 @@ export interface LoaderOptions { logInfoToStdOut: boolean; instance: string; compiler: string; - configFileName: string; + configFile: string; + configFileName: string; // deprecated, remove in next major release transpileOnly: boolean; ignoreDiagnostics: number[]; visualStudioErrorFormat: boolean;