123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 |
- import * as path from "path";
- import * as fs from "fs";
- // tslint:disable:no-require-imports
- import JSON5 = require("json5");
- import StripBom = require("strip-bom");
- // tslint:enable:no-require-imports
- /**
- * Typing for the parts of tsconfig that we care about
- */
- export interface Tsconfig {
- extends?: string;
- compilerOptions?: {
- baseUrl?: string;
- paths?: { [key: string]: Array<string> };
- strict?: boolean;
- };
- }
- export interface TsConfigLoaderResult {
- tsConfigPath: string | undefined;
- baseUrl: string | undefined;
- paths: { [key: string]: Array<string> } | undefined;
- }
- export interface TsConfigLoaderParams {
- getEnv: (key: string) => string | undefined;
- cwd: string;
- loadSync?(
- cwd: string,
- filename?: string,
- baseUrl?: string
- ): TsConfigLoaderResult;
- }
- export function tsConfigLoader({
- getEnv,
- cwd,
- loadSync = loadSyncDefault,
- }: TsConfigLoaderParams): TsConfigLoaderResult {
- const TS_NODE_PROJECT = getEnv("TS_NODE_PROJECT");
- const TS_NODE_BASEURL = getEnv("TS_NODE_BASEURL");
- // tsconfig.loadSync handles if TS_NODE_PROJECT is a file or directory
- // and also overrides baseURL if TS_NODE_BASEURL is available.
- const loadResult = loadSync(cwd, TS_NODE_PROJECT, TS_NODE_BASEURL);
- return loadResult;
- }
- function loadSyncDefault(
- cwd: string,
- filename?: string,
- baseUrl?: string
- ): TsConfigLoaderResult {
- // Tsconfig.loadSync uses path.resolve. This is why we can use an absolute path as filename
- 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: string, filename?: string): string | undefined {
- 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: string,
- existsSync: (path: string) => boolean = fs.existsSync
- ): string | undefined {
- const configPath = path.join(directory, "./tsconfig.json");
- if (existsSync(configPath)) {
- return configPath;
- }
- const parentDirectory = path.join(directory, "../");
- // If we reached the top
- if (directory === parentDirectory) {
- return undefined;
- }
- return walkForTsConfig(parentDirectory, existsSync);
- }
- export function loadTsconfig(
- configFilePath: string,
- existsSync: (path: string) => boolean = fs.existsSync,
- readFileSync: (filename: string) => string = (filename: string) =>
- fs.readFileSync(filename, "utf8")
- ): Tsconfig | undefined {
- if (!existsSync(configFilePath)) {
- return undefined;
- }
- const configString = readFileSync(configFilePath);
- const cleanedJson = StripBom(configString);
- const config: Tsconfig = 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) || {};
- // baseUrl should be interpreted as relative to the base tsconfig,
- // but we need to update it so it is relative to the original tsconfig being loaded
- 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;
- }
|