UNPKG

@visulima/tsconfig

Version:

Find and/or parse the tsconfig.json file from a directory path.

471 lines (463 loc) 19.9 kB
'use strict'; Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } }); const fs = require('@visulima/fs'); const error = require('@visulima/fs/error'); const path = require('@visulima/path'); const utils = require('@visulima/path/utils'); const jsoncParser = require('jsonc-parser'); const node_fs = require('node:fs'); const Module = require('node:module'); const resolvePkgMaps = require('resolve-pkg-maps'); const _interopDefaultCompat = e => e && typeof e === 'object' && 'default' in e ? e.default : e; const Module__default = /*#__PURE__*/_interopDefaultCompat(Module); var __defProp$1 = Object.defineProperty; var __name$1 = (target, value) => __defProp$1(target, "name", { value, configurable: true }); const readJsonc$1 = /* @__PURE__ */ __name$1((jsonPath) => jsoncParser.parse(fs.readFileSync(jsonPath, { buffer: false })), "readJsonc"); const getPnpApi = /* @__PURE__ */ __name$1(() => { const { findPnpApi } = Module__default; return findPnpApi?.(process.cwd()); }, "getPnpApi"); const resolveFromPackageJsonPath = /* @__PURE__ */ __name$1((packageJsonPath, subpath, ignoreExports, cache) => { const cacheKey = "resolveFromPackageJsonPath:" + packageJsonPath + ":" + subpath + ":" + (ignoreExports ? "yes" : "no"); if (cache?.has(cacheKey)) { return cache.get(cacheKey); } const packageJson = readJsonc$1(packageJsonPath); if (!packageJson) { return void 0; } let resolvedPath = subpath || "tsconfig.json"; if (!ignoreExports && packageJson.exports) { try { const [resolvedExport] = resolvePkgMaps.resolveExports(packageJson.exports, subpath, ["require", "types"]); resolvedPath = resolvedExport; } catch { return false; } } else if (!subpath && packageJson.tsconfig) { resolvedPath = packageJson.tsconfig; } resolvedPath = path.join(packageJsonPath, "..", resolvedPath); cache?.set(cacheKey, resolvedPath); return resolvedPath; }, "resolveFromPackageJsonPath"); const PACKAGE_JSON = "package.json"; const TS_CONFIG_JSON = "tsconfig.json"; const resolveExtendsPath = /* @__PURE__ */ __name$1((requestedPath, directoryPath, cache) => { let filePath = requestedPath; if (requestedPath === "..") { filePath = path.join(filePath, TS_CONFIG_JSON); } if (requestedPath.startsWith(".")) { filePath = path.resolve(directoryPath, filePath); } if (path.isAbsolute(filePath)) { if (fs.isAccessibleSync(filePath)) { if (node_fs.statSync(filePath).isFile()) { return filePath; } } else if (!filePath.endsWith(".json")) { const jsonPath = `${filePath}.json`; if (fs.isAccessibleSync(jsonPath)) { return jsonPath; } } return void 0; } const [orgOrName, ...remaining] = requestedPath.split("/"); const packageName = orgOrName.startsWith("@") ? orgOrName + "/" + remaining.shift() : orgOrName; const subpath = remaining.join("/"); const pnpApi = getPnpApi(); if (pnpApi) { const { resolveRequest: resolveWithPnp } = pnpApi; try { if (packageName === requestedPath) { const packageJsonPath2 = resolveWithPnp(path.join(packageName, PACKAGE_JSON), directoryPath); if (packageJsonPath2) { const resolvedPath = resolveFromPackageJsonPath(packageJsonPath2, subpath, false, cache); if (resolvedPath && fs.isAccessibleSync(resolvedPath)) { return resolvedPath; } } } else { let resolved; try { resolved = resolveWithPnp(requestedPath, directoryPath, { extensions: [".json"] }); } catch { resolved = resolveWithPnp(path.join(requestedPath, TS_CONFIG_JSON), directoryPath); } if (resolved) { return resolved; } } } catch { } } const packagePath = fs.findUpSync( (directory) => { const path$1 = path.join(path.resolve(directory), "node_modules", packageName); if (fs.isAccessibleSync(path$1)) { return path.join("node_modules", packageName); } return void 0; }, { cwd: directoryPath, type: "directory" } ); if (!packagePath || !node_fs.statSync(packagePath).isDirectory()) { return void 0; } const packageJsonPath = path.join(packagePath, PACKAGE_JSON); if (fs.isAccessibleSync(packageJsonPath)) { const resolvedPath = resolveFromPackageJsonPath(packageJsonPath, subpath, false, cache); if (resolvedPath === false) { return void 0; } if (resolvedPath && fs.isAccessibleSync(resolvedPath) && node_fs.statSync(resolvedPath).isFile()) { return resolvedPath; } } const fullPackagePath = path.join(packagePath, subpath); const jsonExtension = fullPackagePath.endsWith(".json"); if (!jsonExtension) { const fullPackagePathWithJson = fullPackagePath + ".json"; if (fs.isAccessibleSync(fullPackagePathWithJson)) { return fullPackagePathWithJson; } } if (!fs.isAccessibleSync(fullPackagePath)) { return void 0; } if (node_fs.statSync(fullPackagePath).isDirectory()) { const fullPackageJsonPath = path.join(fullPackagePath, PACKAGE_JSON); if (fs.isAccessibleSync(fullPackageJsonPath)) { const resolvedPath = resolveFromPackageJsonPath(fullPackageJsonPath, "", true, cache); if (resolvedPath && fs.isAccessibleSync(resolvedPath)) { return resolvedPath; } } const tsconfigPath = path.join(fullPackagePath, TS_CONFIG_JSON); if (fs.isAccessibleSync(tsconfigPath)) { return tsconfigPath; } } else if (jsonExtension) { return fullPackagePath; } return void 0; }, "resolveExtendsPath"); var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); const readJsonc = /* @__PURE__ */ __name((jsonPath) => jsoncParser.parse(fs.readFileSync(jsonPath, { buffer: false })), "readJsonc"); const normalizePath = /* @__PURE__ */ __name((path$1) => { const namespacedPath = path.toNamespacedPath(path$1); return utils.isRelative(namespacedPath) ? namespacedPath : "./" + namespacedPath; }, "normalizePath"); const filesProperties = ["files", "include", "exclude"]; const resolveExtends = /* @__PURE__ */ __name((resolvedExtendsPath, fromDirectoryPath, circularExtendsTracker, options) => { if (circularExtendsTracker.has(resolvedExtendsPath)) { throw new Error(`Circularity detected while resolving configuration: ${resolvedExtendsPath}`); } circularExtendsTracker.add(resolvedExtendsPath); const extendsDirectoryPath = path.dirname(resolvedExtendsPath); const extendsConfig = internalParseTsConfig(resolvedExtendsPath, options, circularExtendsTracker); delete extendsConfig.references; const { compilerOptions } = extendsConfig; if (compilerOptions) { const { baseUrl } = compilerOptions; if (baseUrl && !baseUrl.startsWith(configDirectoryPlaceholder)) { compilerOptions.baseUrl = path.normalize(path.relative(fromDirectoryPath, path.join(extendsDirectoryPath, baseUrl))) || "./"; } let { outDir } = compilerOptions; if (outDir) { if (!outDir.startsWith(configDirectoryPlaceholder)) { outDir = path.relative(fromDirectoryPath, path.join(extendsDirectoryPath, outDir)); } compilerOptions.outDir = normalizePath(outDir.replace(configDirectoryPlaceholder + "/", "")) || "./"; } } for (const property of filesProperties) { const filesList = extendsConfig[property]; if (filesList) { extendsConfig[property] = filesList.map((file) => { if (file.startsWith(configDirectoryPlaceholder)) { return file; } if (path.isAbsolute(file)) { return file; } return path.relative(fromDirectoryPath, path.join(extendsDirectoryPath, file)); }); } } return extendsConfig; }, "resolveExtends"); const internalParseTsConfig = /* @__PURE__ */ __name((tsconfigPath, options, circularExtendsTracker = /* @__PURE__ */ new Set()) => { let config; try { config = readJsonc(tsconfigPath) || {}; } catch { throw new Error(`Cannot resolve tsconfig at path: ${tsconfigPath}`); } if (typeof config !== "object") { throw new SyntaxError(`Failed to parse tsconfig at: ${tsconfigPath}`); } const directoryPath = path.dirname(tsconfigPath); if (config.compilerOptions) { const { compilerOptions } = config; if (compilerOptions.paths && !compilerOptions.baseUrl) { compilerOptions[implicitBaseUrlSymbol] = directoryPath; } } if (config.extends) { const extendsPathList = Array.isArray(config.extends) ? config.extends : [config.extends]; delete config.extends; for (const extendsPath of extendsPathList.reverse()) { const resolvedExtendsPath = resolveExtendsPath(extendsPath, directoryPath); if (!resolvedExtendsPath) { throw new error.NotFoundError("No such file or directory, for '" + extendsPath + "' found."); } const extendsConfig = resolveExtends(resolvedExtendsPath, directoryPath, new Set(circularExtendsTracker), options); if (extendsConfig.compilerOptions?.rootDir !== void 0 && !extendsConfig.compilerOptions.rootDir.startsWith(configDirectoryPlaceholder)) { extendsConfig.compilerOptions.rootDir = path.join(path.dirname(resolvedExtendsPath), extendsConfig.compilerOptions.rootDir); } const merged = { ...extendsConfig, ...config, compilerOptions: { ...extendsConfig.compilerOptions, ...config.compilerOptions } }; if (extendsConfig.watchOptions) { merged.watchOptions = { ...extendsConfig.watchOptions, ...config.watchOptions }; } config = merged; } } if (config.compilerOptions) { const { compilerOptions } = config; for (const property of ["baseUrl", "rootDir"]) { const unresolvedPath = compilerOptions[property]; if (unresolvedPath && !unresolvedPath.startsWith(configDirectoryPlaceholder)) { const resolvedBaseUrl = path.resolve(directoryPath, unresolvedPath); compilerOptions[property] = normalizePath(path.relative(directoryPath, resolvedBaseUrl)); } } for (const outputField of ["outDir", "declarationDir"]) { let outputPath = compilerOptions[outputField]; if (outputPath) { if (!Array.isArray(config.exclude)) { config.exclude = []; } let excludePath = outputPath; if (!path.isAbsolute(excludePath)) { excludePath = path.join(directoryPath, excludePath); } excludePath = excludePath.replace(configDirectoryPlaceholder, ""); if (!config.exclude.includes(excludePath)) { config.exclude.push(excludePath); } if (!outputPath.startsWith(configDirectoryPlaceholder)) { outputPath = normalizePath(outputPath); } compilerOptions[outputField] = outputPath; } } } else { config.compilerOptions = {}; } if (config.include) { config.include = config.include.map((element) => path.normalize(element)); if (config.files) { delete config.files; } } else if (config.files) { config.files = config.files.map((file) => file.startsWith(configDirectoryPlaceholder) ? file : normalizePath(file)); } if (config.watchOptions) { const { watchOptions } = config; if (watchOptions.excludeDirectories) { watchOptions.excludeDirectories = watchOptions.excludeDirectories.map((excludePath) => path.resolve(directoryPath, excludePath)); } } if (config.compilerOptions?.lib) { config.compilerOptions.lib = config.compilerOptions.lib.map((library) => library.toLowerCase()); } if (config.compilerOptions.module) { config.compilerOptions.module = config.compilerOptions.module.toLowerCase(); } if (config.compilerOptions.target) { config.compilerOptions.target = config.compilerOptions.target.toLowerCase(); } return config; }, "internalParseTsConfig"); const interpolateConfigDirectory = /* @__PURE__ */ __name((filePath, configDirectory) => { if (filePath.startsWith(configDirectoryPlaceholder)) { return path.normalize(path.join(configDirectory, filePath.slice(configDirectoryPlaceholder.length))); } return void 0; }, "interpolateConfigDirectory"); const compilerFieldsWithConfigDirectory = ["outDir", "declarationDir", "outFile", "rootDir", "baseUrl", "tsBuildInfoFile"]; const tsCompatibleWrapper = /* @__PURE__ */ __name((config, options) => { if (config.compilerOptions === void 0) { return config; } if (["5.4", "5.5", "5.6", "5.7", "5.8", "true"].includes(String(options?.tscCompatible))) { if (config.compilerOptions.esModuleInterop === void 0 && (config.compilerOptions.module === "node16" || config.compilerOptions.module === "nodenext" || config.compilerOptions.module === "preserve")) { config.compilerOptions.esModuleInterop = true; } if ((config.compilerOptions.esModuleInterop || config.compilerOptions.module === "system" || config.compilerOptions.moduleResolution === "bundler") && config.compilerOptions.allowSyntheticDefaultImports === void 0) { config.compilerOptions.allowSyntheticDefaultImports = true; } if (config?.compilerOptions.moduleDetection === void 0 && config.compilerOptions.module && ["node16", "nodenext"].includes(config.compilerOptions.module)) { config.compilerOptions.moduleDetection = "force"; } if (config.compilerOptions.moduleResolution === void 0) { let moduleResolution = "classic"; if (config.compilerOptions.module !== void 0) { switch ((config.compilerOptions?.module).toLocaleLowerCase()) { case "commonjs": { moduleResolution = "node10"; break; } case "node16": { moduleResolution = "node16"; break; } case "nodenext": { moduleResolution = "nodenext"; break; } case "preserve": { moduleResolution = "bundler"; break; } } } if (moduleResolution !== "classic") { config.compilerOptions.moduleResolution = moduleResolution; } } if (["5.7", "5.8", "true"].includes(String(options?.tscCompatible)) && config.compilerOptions.moduleResolution) { let resolvePackageJson = false; if (["bundler", "node16", "nodenext"].includes(config.compilerOptions.moduleResolution.toLocaleLowerCase())) { resolvePackageJson = true; } if (config.compilerOptions.resolvePackageJsonExports === void 0 && resolvePackageJson) { config.compilerOptions.resolvePackageJsonExports = true; } if (config.compilerOptions.resolvePackageJsonImports === void 0 && resolvePackageJson) { config.compilerOptions.resolvePackageJsonImports = true; } } if (config.compilerOptions.target === void 0) { let target = "es5"; if (config.compilerOptions.module === "node16") { target = "es2022"; } else if (config.compilerOptions.module === "nodenext") { target = "esnext"; } if (target !== "es5") { config.compilerOptions.target = target; } } if (config.compilerOptions.useDefineForClassFields === void 0 && config.compilerOptions.target && (config.compilerOptions.target.includes("es202") || config.compilerOptions.target === "esnext")) { config.compilerOptions.useDefineForClassFields = true; } } if (["5.6", "5.7", "5.8", "true"].includes(String(options?.tscCompatible)) && config.compilerOptions.strict && config.compilerOptions.strictBuiltinIteratorReturn === void 0) { config.compilerOptions.strictBuiltinIteratorReturn = true; } if (["5.4", "5.5", "5.6", "5.7", "5.8", "true"].includes(String(options?.tscCompatible))) { if (config.compilerOptions.strict) { config.compilerOptions.noImplicitAny = config.compilerOptions.noImplicitAny ?? true; config.compilerOptions.noImplicitThis = config.compilerOptions.noImplicitThis ?? true; config.compilerOptions.strictNullChecks = config.compilerOptions.strictNullChecks ?? true; config.compilerOptions.strictFunctionTypes = config.compilerOptions.strictFunctionTypes ?? true; config.compilerOptions.strictBindCallApply = config.compilerOptions.strictBindCallApply ?? true; config.compilerOptions.strictPropertyInitialization = config.compilerOptions.strictPropertyInitialization ?? true; config.compilerOptions.alwaysStrict = config.compilerOptions.alwaysStrict ?? true; } if (config.compilerOptions.useDefineForClassFields === void 0 && config.compilerOptions.target) { let useDefineForClassFields = false; if (config.compilerOptions.target.includes("es202") || config.compilerOptions.target === "esnext") { useDefineForClassFields = true; } if (useDefineForClassFields) { config.compilerOptions.useDefineForClassFields = true; } } if (config.compilerOptions.strict && config.compilerOptions.useUnknownInCatchVariables === void 0) { config.compilerOptions.useUnknownInCatchVariables = true; } if (config.compilerOptions.isolatedModules) { config.compilerOptions.preserveConstEnums = config.compilerOptions.preserveConstEnums ?? true; } } if (config.compileOnSave === false) { delete config.compileOnSave; } return config; }, "tsCompatibleWrapper"); const configDirectoryPlaceholder = "${configDir}"; const implicitBaseUrlSymbol = Symbol("implicitBaseUrl"); const readTsConfig = /* @__PURE__ */ __name((tsconfigPath, options) => { const resolvedTsconfigPath = path.resolve(tsconfigPath); const config = internalParseTsConfig(resolvedTsconfigPath, options); const configDirectory = path.dirname(resolvedTsconfigPath); const { compilerOptions } = config; if (compilerOptions) { for (const property of compilerFieldsWithConfigDirectory) { const value = compilerOptions[property]; if (value) { const resolvedPath = interpolateConfigDirectory(value, configDirectory); compilerOptions[property] = resolvedPath ? normalizePath(path.relative(configDirectory, resolvedPath)) : value; } } for (const property of ["rootDirs", "typeRoots"]) { const value = compilerOptions[property]; if (value) { compilerOptions[property] = value.map((v) => { const resolvedPath = interpolateConfigDirectory(v, configDirectory); return resolvedPath ? normalizePath(path.relative(configDirectory, resolvedPath)) : v; }); } } const { paths } = compilerOptions; if (paths) { for (const name of Object.keys(paths)) { paths[name] = paths[name].map((filePath) => interpolateConfigDirectory(filePath, configDirectory) ?? filePath); } } if (compilerOptions.outDir) { compilerOptions.outDir = compilerOptions.outDir.replace(configDirectoryPlaceholder, ""); } } for (const property of filesProperties) { const value = config[property]; if (value) { config[property] = value.map((filePath) => { const interpolate = interpolateConfigDirectory(filePath, configDirectory); if (interpolate) { return interpolate; } if (property === "files" && utils.isRelative(filePath)) { return filePath; } if (property === "include" && utils.isRelative(filePath)) { return path.join(configDirectory, filePath); } return path.normalize(filePath); }); } } return tsCompatibleWrapper(config, options); }, "readTsConfig"); exports.configDirectoryPlaceholder = configDirectoryPlaceholder; exports.implicitBaseUrlSymbol = implicitBaseUrlSymbol; exports.readTsConfig = readTsConfig;