merge-tsconfigs
Version:
Merge-tsconfigs is a CLI and node tool for merging tsconfig files into the exact tsconfig file you want 🛣️
255 lines (250 loc) • 8.71 kB
JavaScript
// src/scripts.ts
import { mkdirSync, readFileSync, writeFileSync } from "fs";
import { dirname, join } from "path";
import JSON5 from "json5";
var logger = ({ isDebugging = false, emoji = `\u{1F6E3}\uFE0F`, gap = ` => `, name = "merge-tsconfigs" }) => (type) => (section) => (message) => (err) => {
const debugMsg = isDebugging ? "debugging:" : "";
const sectionMsg = section.length ? `${section}:` : "";
const firstLine = `${name}:${debugMsg}${sectionMsg}`;
const secondLine = message ? `${emoji}${gap}${message}` : "";
if (type === "error") {
console.error(firstLine);
if (secondLine)
console.error(secondLine);
if (err)
console.error(err);
} else if (type === "debug") {
console.debug(firstLine);
if (secondLine)
console.debug(secondLine);
} else if (type === "info") {
console.info(firstLine);
if (secondLine)
console.info(secondLine);
} else {
console.log(firstLine);
if (secondLine)
console.log(secondLine);
}
};
function resolveJSON(path, debug = false) {
try {
const json = JSON5.parse(readFileSync(path, "utf8"));
return json;
} catch (err) {
if (debug)
logger({ isDebugging: debug })("error")("resolveJSON")("There was an error:")(err);
return {};
}
}
var mergeConfigObjects = (tsconfig1, tsconfig2) => ({
...tsconfig1,
...tsconfig2,
compilerOptions: {
...tsconfig1?.compilerOptions,
...tsconfig2?.compilerOptions
},
...tsconfig1?.exclude || tsconfig2?.exclude ? {
exclude: [
...tsconfig1?.exclude || [],
...tsconfig2?.exclude || []
]
} : {},
...tsconfig1?.include || tsconfig2?.include ? {
include: [
...tsconfig1?.include || [],
...tsconfig2?.include || []
]
} : {}
});
var mergeConfigContent = (tsconfigs, cwd, debug = false) => tsconfigs.reduce((acc = {}, tsconfig) => {
const path = `${cwd}/${tsconfig}`;
let tsconfigJSON = resolveJSON(path, debug);
const parentPath = tsconfigJSON?.extends;
if (parentPath) {
const relativeParentPath = join(dirname(path), parentPath);
const parentTsconfig = resolveJSON(relativeParentPath, debug);
if (parentTsconfig?.extends) {
logger({ isDebugging: debug })("error")("mergeConfigContent")("Parent tsconfig:merge-tsconfigs only handles extending from a parent, consider extending tsconfigs less.")(parentTsconfig);
}
const { extends: _, ...tsconfigWithoutExtends } = tsconfigJSON;
tsconfigJSON = mergeConfigObjects(parentTsconfig, tsconfigWithoutExtends);
}
if (!tsconfigJSON) {
if (debug)
logger({ isDebugging: debug })("error")("mergeConfigContent")("There was an error:")(tsconfigJSON);
return acc;
}
return mergeConfigObjects(acc, tsconfigJSON);
}, {});
var writeTsconfig = (tsconfig, cwd, out, isTesting) => {
if (isTesting)
return tsconfig;
const path = `${cwd}/${out}`;
mkdirSync(dirname(path), { recursive: true });
writeFileSync(path, JSON.stringify(tsconfig, null, 2));
return tsconfig;
};
var updateCompilerOptions = (compilerOptions2 = {}, currentCompilerOptions, updatedPath) => {
const compilerOptionKeys = compilerOptions2 ? Object.keys(compilerOptions2) : [];
const hasCompilerOptions = compilerOptionKeys.length > 0;
if (!hasCompilerOptions)
return {};
return compilerOptionKeys.reduce((acc = {}, key) => {
const updatedOptions = { ...acc, ...currentCompilerOptions };
const value = compilerOptions2?.[key];
if (updatedOptions?.[key] === "delete") {
delete updatedOptions[key];
return updatedOptions || {};
}
const paths = {
...updatedOptions.paths || {},
...key === "paths" ? { value } : {},
...updatedPath ? { ...updatedPath } : {}
};
return { ...updatedOptions, [key]: value, ...Object.keys(paths).length > 0 ? { paths } : {} };
}, {});
};
var parsePath = (path = "", debug = false) => {
if (!path)
return {};
try {
const json = JSON5.parse(path);
return json;
} catch (err) {
if (debug)
logger({ isDebugging: debug })("error")("parsePath")("There was an error:")(err);
return {};
}
};
var mergeTsConfigs = ({
tsconfigs = [],
exclude,
include,
compilerOptions: compilerOptions2,
debug = false,
out = "tsconfig.merged.json",
path,
isTesting = false
}) => {
if (tsconfigs.length === 0) {
if (debug)
logger({ isDebugging: debug })("error")("mergeTsConfig")("No tsconfig files were provided.")(null);
return;
}
const cwd = process.cwd();
const updatedTsconfig = mergeConfigContent(tsconfigs, cwd, debug);
if (debug)
logger({ isDebugging: debug })("debug")("mergeTsConfig")("Updated tsconfig:")(updatedTsconfig);
const updatedPath = parsePath(path, debug);
const updatedCompilerOptions = updateCompilerOptions(
compilerOptions2,
updatedTsconfig?.compilerOptions || {},
updatedPath
);
const updatedExclude = exclude ? { exclude: [...updatedTsconfig?.exclude || [], ...exclude] } : {};
const updatedInclude = include ? { include: [...updatedTsconfig.include || [], ...include] } : {};
const tsconfig = {
...updatedTsconfig,
...updatedExclude,
...updatedInclude,
...Object.keys(updatedCompilerOptions).length > 0 ? { compilerOptions: updatedCompilerOptions } : {}
};
return writeTsconfig(tsconfig, cwd, out, isTesting);
};
var script = mergeTsConfigs;
var scripts_default = mergeTsConfigs;
// src/program.ts
import { program } from "commander";
// src/config.ts
var compilerOptions = {
target: "string",
module: "string",
lib: "array",
allowJs: "boolean",
checkJs: "boolean",
jsx: "string",
declaration: "boolean",
sourceMap: "boolean",
outFile: "string",
outDir: "string",
rootDir: "string",
removeComments: "boolean",
noEmit: "boolean",
importHelpers: "boolean",
downlevelIteration: "boolean",
isolatedModules: "boolean",
strict: "boolean",
noImplicitAny: "boolean",
strictNullChecks: "boolean",
noImplicitThis: "boolean",
alwaysStrict: "boolean",
noUnusedLocals: "boolean",
noUnusedParameters: "boolean",
noImplicitReturns: "boolean",
noFallthroughCasesInSwitch: "boolean",
moduleResolution: "string",
baseUrl: "string",
paths: "object",
rootDirs: "array",
typeRoots: "array",
types: "array",
allowSyntheticDefaultImports: "boolean",
sourceRoot: "string",
mapRoot: "string",
inlineSourceMap: "boolean",
inlineSources: "boolean",
experimentalDecorators: "boolean",
emitDecoratorMetadata: "boolean"
};
// src/program.ts
function action(files, options = {}) {
try {
const {
debug = false,
exclude,
include,
isTesting = false,
isTestingCLI = false,
out,
path,
...compilerOptions2
} = options;
if (isTestingCLI) {
console.info({ files, options });
return;
}
script({ debug, exclude, include, isTesting, path, out, tsconfigs: files, compilerOptions: compilerOptions2 });
} catch (err) {
logger({ isDebugging: options.debug })("error")("action")("There was an error:")(err);
}
}
program.name("merge-tsconfigs").description(
"Merge-tsconfigs is a CLI and node tool for merging tsconfig files into the exact tsconfig file you want \u{1F6E3}\uFE0F"
).argument("[files...]", "files to check, matches an array pattern").option("-d, --debug", "enable debugging").option("-e, --exclude [exclude...]", "files to exclude, matches a glob or array pattern").option("-i, --include [include...]", "files to include, matches a glob or array pattern").option("--isTesting", "enable testing").option("-o, --out <file>", "output file, otherwise, the file will be written to tsconfig.merged.json").option("--isTesting", "enable testing").option("-t, --isTestingCLI", "enable CLI only testing").option("-p, --path <path>", 'a json parseable string wrapped object, e.g. {"item/*": ["foo": "bar"]}');
Object.keys(compilerOptions).map((name) => ({ name, value: compilerOptions[name] })).forEach(({ name, value }) => {
if (value === "boolean") {
program.option(`--${name}`, `tsconfig.compilerOptions.${name}`);
} else if (value === "string") {
program.option(`--${name} <${value}>`, `tsconfig.compilerOptions.${name}`);
} else if (value === "array") {
program.option(`--${name} [${value}...]`, `tsconfig.compilerOptions.${name}`);
} else if (value === "object") {
program.option(`--${name} <${value}>`, `tsconfig.compilerOptions.${name}`);
}
});
program.action(action).parse(process.argv);
// src/index.ts
var src_default = scripts_default;
export {
src_default as default,
logger,
mergeConfigContent,
mergeConfigObjects,
mergeTsConfigs,
parsePath,
resolveJSON,
script,
updateCompilerOptions,
writeTsconfig
};