UNPKG

@visulima/tsconfig

Version:

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

461 lines (457 loc) 19.2 kB
import { isAccessibleSync, findUpSync, readFileSync } from '@visulima/fs'; import { NotFoundError } from '@visulima/fs/error'; import { join, resolve, isAbsolute, dirname, relative, normalize, toNamespacedPath } from '@visulima/path'; import { isRelative } from '@visulima/path/utils'; import { parse } from 'jsonc-parser'; import { statSync } from 'node:fs'; import Module from 'node:module'; import { resolveExports } from 'resolve-pkg-maps'; var __defProp$1 = Object.defineProperty; var __name$1 = (target, value) => __defProp$1(target, "name", { value, configurable: true }); const readJsonc$1 = /* @__PURE__ */ __name$1((jsonPath) => parse(readFileSync(jsonPath, { buffer: false })), "readJsonc"); const getPnpApi = /* @__PURE__ */ __name$1(() => { const { findPnpApi } = Module; 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] = resolveExports(packageJson.exports, subpath, ["require", "types"]); resolvedPath = resolvedExport; } catch { return false; } } else if (!subpath && packageJson.tsconfig) { resolvedPath = packageJson.tsconfig; } resolvedPath = 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 = join(filePath, TS_CONFIG_JSON); } if (requestedPath.startsWith(".")) { filePath = resolve(directoryPath, filePath); } if (isAbsolute(filePath)) { if (isAccessibleSync(filePath)) { if (statSync(filePath).isFile()) { return filePath; } } else if (!filePath.endsWith(".json")) { const jsonPath = `${filePath}.json`; if (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(join(packageName, PACKAGE_JSON), directoryPath); if (packageJsonPath2) { const resolvedPath = resolveFromPackageJsonPath(packageJsonPath2, subpath, false, cache); if (resolvedPath && isAccessibleSync(resolvedPath)) { return resolvedPath; } } } else { let resolved; try { resolved = resolveWithPnp(requestedPath, directoryPath, { extensions: [".json"] }); } catch { resolved = resolveWithPnp(join(requestedPath, TS_CONFIG_JSON), directoryPath); } if (resolved) { return resolved; } } } catch { } } const packagePath = findUpSync( (directory) => { const path = join(resolve(directory), "node_modules", packageName); if (isAccessibleSync(path)) { return join("node_modules", packageName); } return void 0; }, { cwd: directoryPath, type: "directory" } ); if (!packagePath || !statSync(packagePath).isDirectory()) { return void 0; } const packageJsonPath = join(packagePath, PACKAGE_JSON); if (isAccessibleSync(packageJsonPath)) { const resolvedPath = resolveFromPackageJsonPath(packageJsonPath, subpath, false, cache); if (resolvedPath === false) { return void 0; } if (resolvedPath && isAccessibleSync(resolvedPath) && statSync(resolvedPath).isFile()) { return resolvedPath; } } const fullPackagePath = join(packagePath, subpath); const jsonExtension = fullPackagePath.endsWith(".json"); if (!jsonExtension) { const fullPackagePathWithJson = fullPackagePath + ".json"; if (isAccessibleSync(fullPackagePathWithJson)) { return fullPackagePathWithJson; } } if (!isAccessibleSync(fullPackagePath)) { return void 0; } if (statSync(fullPackagePath).isDirectory()) { const fullPackageJsonPath = join(fullPackagePath, PACKAGE_JSON); if (isAccessibleSync(fullPackageJsonPath)) { const resolvedPath = resolveFromPackageJsonPath(fullPackageJsonPath, "", true, cache); if (resolvedPath && isAccessibleSync(resolvedPath)) { return resolvedPath; } } const tsconfigPath = join(fullPackagePath, TS_CONFIG_JSON); if (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) => parse(readFileSync(jsonPath, { buffer: false })), "readJsonc"); const normalizePath = /* @__PURE__ */ __name((path) => { const namespacedPath = toNamespacedPath(path); return 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 = 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 = normalize(relative(fromDirectoryPath, join(extendsDirectoryPath, baseUrl))) || "./"; } let { outDir } = compilerOptions; if (outDir) { if (!outDir.startsWith(configDirectoryPlaceholder)) { outDir = relative(fromDirectoryPath, 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 (isAbsolute(file)) { return file; } return relative(fromDirectoryPath, 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 = 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 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 = join(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 = resolve(directoryPath, unresolvedPath); compilerOptions[property] = normalizePath(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 (!isAbsolute(excludePath)) { excludePath = 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) => 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) => 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 normalize(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 = resolve(tsconfigPath); const config = internalParseTsConfig(resolvedTsconfigPath, options); const configDirectory = 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(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(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" && isRelative(filePath)) { return filePath; } if (property === "include" && isRelative(filePath)) { return join(configDirectory, filePath); } return normalize(filePath); }); } } return tsCompatibleWrapper(config, options); }, "readTsConfig"); export { configDirectoryPlaceholder, implicitBaseUrlSymbol, readTsConfig };