@visulima/tsconfig
Version:
Find and/or parse the tsconfig.json file from a directory path.
461 lines (457 loc) • 19.2 kB
JavaScript
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 };