configuration.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.getTsConfigDefaults = exports.ComputeAsCommonRootOfFiles = exports.loadCompiler = exports.resolveAndLoadCompiler = exports.readConfig = exports.findAndReadConfig = void 0;
  4. const path_1 = require("path");
  5. const index_1 = require("./index");
  6. const ts_internals_1 = require("./ts-internals");
  7. const tsconfigs_1 = require("./tsconfigs");
  8. const util_1 = require("./util");
  9. /**
  10. * TypeScript compiler option values required by `ts-node` which cannot be overridden.
  11. */
  12. const TS_NODE_COMPILER_OPTIONS = {
  13. sourceMap: true,
  14. inlineSourceMap: false,
  15. inlineSources: true,
  16. declaration: false,
  17. noEmit: false,
  18. outDir: '.ts-node',
  19. };
  20. /*
  21. * Do post-processing on config options to support `ts-node`.
  22. */
  23. function fixConfig(ts, config) {
  24. // Delete options that *should not* be passed through.
  25. delete config.options.out;
  26. delete config.options.outFile;
  27. delete config.options.composite;
  28. delete config.options.declarationDir;
  29. delete config.options.declarationMap;
  30. delete config.options.emitDeclarationOnly;
  31. // Target ES5 output by default (instead of ES3).
  32. if (config.options.target === undefined) {
  33. config.options.target = ts.ScriptTarget.ES5;
  34. }
  35. // Target CommonJS modules by default (instead of magically switching to ES6 when the target is ES6).
  36. if (config.options.module === undefined) {
  37. config.options.module = ts.ModuleKind.CommonJS;
  38. }
  39. return config;
  40. }
  41. /** @internal */
  42. function findAndReadConfig(rawOptions) {
  43. var _a, _b, _c, _d, _e;
  44. const cwd = (0, path_1.resolve)((_c = (_b = (_a = rawOptions.cwd) !== null && _a !== void 0 ? _a : rawOptions.dir) !== null && _b !== void 0 ? _b : index_1.DEFAULTS.cwd) !== null && _c !== void 0 ? _c : process.cwd());
  45. const compilerName = (_d = rawOptions.compiler) !== null && _d !== void 0 ? _d : index_1.DEFAULTS.compiler;
  46. // Compute minimum options to read the config file.
  47. let projectLocalResolveDir = (0, util_1.getBasePathForProjectLocalDependencyResolution)(undefined, rawOptions.projectSearchDir, rawOptions.project, cwd);
  48. let { compiler, ts } = resolveAndLoadCompiler(compilerName, projectLocalResolveDir);
  49. // Read config file and merge new options between env and CLI options.
  50. const { configFilePath, config, tsNodeOptionsFromTsconfig, optionBasePaths } = readConfig(cwd, ts, rawOptions);
  51. const options = (0, util_1.assign)({}, index_1.DEFAULTS, tsNodeOptionsFromTsconfig || {}, { optionBasePaths }, rawOptions);
  52. options.require = [
  53. ...(tsNodeOptionsFromTsconfig.require || []),
  54. ...(rawOptions.require || []),
  55. ];
  56. // Re-resolve the compiler in case it has changed.
  57. // Compiler is loaded relative to tsconfig.json, so tsconfig discovery may cause us to load a
  58. // different compiler than we did above, even if the name has not changed.
  59. if (configFilePath) {
  60. projectLocalResolveDir = (0, util_1.getBasePathForProjectLocalDependencyResolution)(configFilePath, rawOptions.projectSearchDir, rawOptions.project, cwd);
  61. ({ compiler } = resolveCompiler(options.compiler, (_e = optionBasePaths.compiler) !== null && _e !== void 0 ? _e : projectLocalResolveDir));
  62. }
  63. return {
  64. options,
  65. config,
  66. projectLocalResolveDir,
  67. optionBasePaths,
  68. configFilePath,
  69. cwd,
  70. compiler,
  71. };
  72. }
  73. exports.findAndReadConfig = findAndReadConfig;
  74. /**
  75. * Load TypeScript configuration. Returns the parsed TypeScript config and
  76. * any `ts-node` options specified in the config file.
  77. *
  78. * Even when a tsconfig.json is not loaded, this function still handles merging
  79. * compilerOptions from various sources: API, environment variables, etc.
  80. *
  81. * @internal
  82. */
  83. function readConfig(cwd, ts, rawApiOptions) {
  84. var _a, _b, _c;
  85. // Ordered [a, b, c] where config a extends b extends c
  86. const configChain = [];
  87. let config = { compilerOptions: {} };
  88. let basePath = cwd;
  89. let configFilePath = undefined;
  90. const projectSearchDir = (0, path_1.resolve)(cwd, (_a = rawApiOptions.projectSearchDir) !== null && _a !== void 0 ? _a : cwd);
  91. const { fileExists = ts.sys.fileExists, readFile = ts.sys.readFile, skipProject = index_1.DEFAULTS.skipProject, project = index_1.DEFAULTS.project, tsTrace = index_1.DEFAULTS.tsTrace, } = rawApiOptions;
  92. // Read project configuration when available.
  93. if (!skipProject) {
  94. if (project) {
  95. const resolved = (0, path_1.resolve)(cwd, project);
  96. const nested = (0, path_1.join)(resolved, 'tsconfig.json');
  97. configFilePath = fileExists(nested) ? nested : resolved;
  98. }
  99. else {
  100. configFilePath = ts.findConfigFile(projectSearchDir, fileExists);
  101. }
  102. if (configFilePath) {
  103. let pathToNextConfigInChain = configFilePath;
  104. const tsInternals = (0, ts_internals_1.createTsInternals)(ts);
  105. const errors = [];
  106. // Follow chain of "extends"
  107. while (true) {
  108. const result = ts.readConfigFile(pathToNextConfigInChain, readFile);
  109. // Return diagnostics.
  110. if (result.error) {
  111. return {
  112. configFilePath,
  113. config: { errors: [result.error], fileNames: [], options: {} },
  114. tsNodeOptionsFromTsconfig: {},
  115. optionBasePaths: {},
  116. };
  117. }
  118. const c = result.config;
  119. const bp = (0, path_1.dirname)(pathToNextConfigInChain);
  120. configChain.push({
  121. config: c,
  122. basePath: bp,
  123. configPath: pathToNextConfigInChain,
  124. });
  125. if (c.extends == null)
  126. break;
  127. const resolvedExtendedConfigPath = tsInternals.getExtendsConfigPath(c.extends, {
  128. fileExists,
  129. readDirectory: ts.sys.readDirectory,
  130. readFile,
  131. useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
  132. trace: tsTrace,
  133. }, bp, errors, ts.createCompilerDiagnostic);
  134. if (errors.length) {
  135. return {
  136. configFilePath,
  137. config: { errors, fileNames: [], options: {} },
  138. tsNodeOptionsFromTsconfig: {},
  139. optionBasePaths: {},
  140. };
  141. }
  142. if (resolvedExtendedConfigPath == null)
  143. break;
  144. pathToNextConfigInChain = resolvedExtendedConfigPath;
  145. }
  146. ({ config, basePath } = configChain[0]);
  147. }
  148. }
  149. // Merge and fix ts-node options that come from tsconfig.json(s)
  150. const tsNodeOptionsFromTsconfig = {};
  151. const optionBasePaths = {};
  152. for (let i = configChain.length - 1; i >= 0; i--) {
  153. const { config, basePath, configPath } = configChain[i];
  154. const options = filterRecognizedTsConfigTsNodeOptions(config['ts-node']).recognized;
  155. // Some options are relative to the config file, so must be converted to absolute paths here
  156. if (options.require) {
  157. // Modules are found relative to the tsconfig file, not the `dir` option
  158. const tsconfigRelativeResolver = (0, util_1.createProjectLocalResolveHelper)((0, path_1.dirname)(configPath));
  159. options.require = options.require.map((path) => tsconfigRelativeResolver(path, false));
  160. }
  161. if (options.scopeDir) {
  162. options.scopeDir = (0, path_1.resolve)(basePath, options.scopeDir);
  163. }
  164. // Downstream code uses the basePath; we do not do that here.
  165. if (options.moduleTypes) {
  166. optionBasePaths.moduleTypes = basePath;
  167. }
  168. if (options.transpiler != null) {
  169. optionBasePaths.transpiler = basePath;
  170. }
  171. if (options.compiler != null) {
  172. optionBasePaths.compiler = basePath;
  173. }
  174. if (options.swc != null) {
  175. optionBasePaths.swc = basePath;
  176. }
  177. (0, util_1.assign)(tsNodeOptionsFromTsconfig, options);
  178. }
  179. // Remove resolution of "files".
  180. const files = (_c = (_b = rawApiOptions.files) !== null && _b !== void 0 ? _b : tsNodeOptionsFromTsconfig.files) !== null && _c !== void 0 ? _c : index_1.DEFAULTS.files;
  181. // Only if a config file is *not* loaded, load an implicit configuration from @tsconfig/bases
  182. const skipDefaultCompilerOptions = configFilePath != null;
  183. const defaultCompilerOptionsForNodeVersion = skipDefaultCompilerOptions
  184. ? undefined
  185. : {
  186. ...(0, tsconfigs_1.getDefaultTsconfigJsonForNodeVersion)(ts).compilerOptions,
  187. types: ['node'],
  188. };
  189. // Merge compilerOptions from all sources
  190. config.compilerOptions = Object.assign({},
  191. // automatically-applied options from @tsconfig/bases
  192. defaultCompilerOptionsForNodeVersion,
  193. // tsconfig.json "compilerOptions"
  194. config.compilerOptions,
  195. // from env var
  196. index_1.DEFAULTS.compilerOptions,
  197. // tsconfig.json "ts-node": "compilerOptions"
  198. tsNodeOptionsFromTsconfig.compilerOptions,
  199. // passed programmatically
  200. rawApiOptions.compilerOptions,
  201. // overrides required by ts-node, cannot be changed
  202. TS_NODE_COMPILER_OPTIONS);
  203. const fixedConfig = fixConfig(ts, ts.parseJsonConfigFileContent(config, {
  204. fileExists,
  205. readFile,
  206. // Only used for globbing "files", "include", "exclude"
  207. // When `files` option disabled, we want to avoid the fs calls
  208. readDirectory: files ? ts.sys.readDirectory : () => [],
  209. useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
  210. }, basePath, undefined, configFilePath));
  211. return {
  212. configFilePath,
  213. config: fixedConfig,
  214. tsNodeOptionsFromTsconfig,
  215. optionBasePaths,
  216. };
  217. }
  218. exports.readConfig = readConfig;
  219. /**
  220. * Load the typescript compiler. It is required to load the tsconfig but might
  221. * be changed by the tsconfig, so we have to do this twice.
  222. * @internal
  223. */
  224. function resolveAndLoadCompiler(name, relativeToPath) {
  225. const { compiler } = resolveCompiler(name, relativeToPath);
  226. const ts = loadCompiler(compiler);
  227. return { compiler, ts };
  228. }
  229. exports.resolveAndLoadCompiler = resolveAndLoadCompiler;
  230. function resolveCompiler(name, relativeToPath) {
  231. const projectLocalResolveHelper = (0, util_1.createProjectLocalResolveHelper)(relativeToPath);
  232. const compiler = projectLocalResolveHelper(name || 'typescript', true);
  233. return { compiler };
  234. }
  235. /** @internal */
  236. function loadCompiler(compiler) {
  237. return (0, util_1.attemptRequireWithV8CompileCache)(require, compiler);
  238. }
  239. exports.loadCompiler = loadCompiler;
  240. /**
  241. * Given the raw "ts-node" sub-object from a tsconfig, return an object with only the properties
  242. * recognized by "ts-node"
  243. */
  244. function filterRecognizedTsConfigTsNodeOptions(jsonObject) {
  245. if (jsonObject == null)
  246. return { recognized: {}, unrecognized: {} };
  247. const { compiler, compilerHost, compilerOptions, emit, files, ignore, ignoreDiagnostics, logError, preferTsExts, pretty, require, skipIgnore, transpileOnly, typeCheck, transpiler, scope, scopeDir, moduleTypes, experimentalReplAwait, swc, experimentalResolver, esm, experimentalSpecifierResolution, experimentalTsImportSpecifiers, ...unrecognized } = jsonObject;
  248. const filteredTsConfigOptions = {
  249. compiler,
  250. compilerHost,
  251. compilerOptions,
  252. emit,
  253. experimentalReplAwait,
  254. files,
  255. ignore,
  256. ignoreDiagnostics,
  257. logError,
  258. preferTsExts,
  259. pretty,
  260. require,
  261. skipIgnore,
  262. transpileOnly,
  263. typeCheck,
  264. transpiler,
  265. scope,
  266. scopeDir,
  267. moduleTypes,
  268. swc,
  269. experimentalResolver,
  270. esm,
  271. experimentalSpecifierResolution,
  272. experimentalTsImportSpecifiers,
  273. };
  274. // Use the typechecker to make sure this implementation has the correct set of properties
  275. const catchExtraneousProps = null;
  276. const catchMissingProps = null;
  277. return { recognized: filteredTsConfigOptions, unrecognized };
  278. }
  279. /** @internal */
  280. exports.ComputeAsCommonRootOfFiles = Symbol();
  281. /**
  282. * Some TS compiler options have defaults which are not provided by TS's config parsing functions.
  283. * This function centralizes the logic for computing those defaults.
  284. * @internal
  285. */
  286. function getTsConfigDefaults(config, basePath, _files, _include, _exclude) {
  287. const { composite = false } = config.options;
  288. let rootDir = config.options.rootDir;
  289. if (rootDir == null) {
  290. if (composite)
  291. rootDir = basePath;
  292. // Return this symbol to avoid computing from `files`, which would require fs calls
  293. else
  294. rootDir = exports.ComputeAsCommonRootOfFiles;
  295. }
  296. const { outDir = rootDir } = config.options;
  297. // Docs are wrong: https://www.typescriptlang.org/tsconfig#include
  298. // Docs say **, but it's actually **/*; compiler throws error for **
  299. const include = _files ? [] : ['**/*'];
  300. const files = _files !== null && _files !== void 0 ? _files : [];
  301. // Docs are misleading: https://www.typescriptlang.org/tsconfig#exclude
  302. // Docs say it excludes node_modules, bower_components, jspm_packages, but actually those are excluded via behavior of "include"
  303. const exclude = _exclude !== null && _exclude !== void 0 ? _exclude : [outDir]; // TODO technically, outDir is absolute path, but exclude should be relative glob pattern?
  304. // TODO compute baseUrl
  305. return { rootDir, outDir, include, files, exclude, composite };
  306. }
  307. exports.getTsConfigDefaults = getTsConfigDefaults;
  308. //# sourceMappingURL=configuration.js.map