dipend
Version:
This library implements a dependency injection (DI) system in JavaScript/TypeScript, making it easier to manage dependencies in modular applications.
218 lines (216 loc) • 9.01 kB
JavaScript
/*
* Copyright 2025 Saulo V. Alvarenga. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.InitCommand = void 0;
const tslib_1 = require("tslib");
const fs_1 = tslib_1.__importDefault(require("fs"));
const path_1 = tslib_1.__importDefault(require("path"));
const jsonc_parser_1 = require("jsonc-parser");
class InitCommand {
command = "init [tsConfigPath]";
description = "Adds Dipend configurations to tsconfig.json";
recommendInstall = false;
builder = (yargs) => {
return yargs
.option("ts-config", {
alias: "ts",
describe: "Path to the tsconfig.json file",
type: "string",
default: "tsconfig.json",
})
.option("package-json", {
alias: "p",
describe: "Path to the package.json file",
type: "string",
default: "package.json",
});
};
readTsConfig(tsConfigPath) {
if (!fs_1.default.existsSync(tsConfigPath)) {
throw new Error("Missing tsconfig.json");
}
const content = fs_1.default.readFileSync(tsConfigPath, "utf-8");
const parsed = (0, jsonc_parser_1.parse)(content);
if (!parsed || typeof parsed !== "object") {
throw new Error("Invalid tsconfig.json format");
}
return parsed;
}
readPackageJson(packageJsonPath) {
let packageJson = {};
if (fs_1.default.existsSync(packageJsonPath)) {
packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, "utf-8"));
}
if (Object.keys(packageJson).length === 0) {
throw new Error("Missing package.json");
}
return packageJson;
}
addTsNodeConfigs(tsConfig) {
if (tsConfig?.["ts-node"]?.compiler === "ts-patch/compiler") {
console.log(`tsconfig.json: ts-patch already setted as ts-node compiler`);
return tsConfig;
}
return {
...tsConfig,
"ts-node": {
...(tsConfig?.["ts-node"] ?? {}),
compiler: "ts-patch/compiler",
},
};
}
addLibs(tsConfig) {
if (tsConfig?.compilerOptions?.lib?.includes("esnext.decorators")) {
console.log(`tsconfig.json: esnext.decorators already exists in libs`);
return tsConfig;
}
return {
...tsConfig,
compilerOptions: {
...(tsConfig.compilerOptions ?? {}),
lib: [...(tsConfig?.compilerOptions?.lib ?? []), "esnext.decorators"],
},
};
}
addPlugins(tsConfig) {
if (tsConfig.compilerOptions?.plugins?.find((plugin) => plugin.transform === "dipend/tsc-plugin" && plugin.transformProgram === true) !== undefined) {
console.log(`tsconfig.json: Dipend already exists in plugins`);
return tsConfig;
}
const misconfiguredPluginIndex = tsConfig.compilerOptions?.plugins?.findIndex((plugin) => plugin.transform === "dipend/tsc-plugin");
if (misconfiguredPluginIndex !== undefined && misconfiguredPluginIndex !== -1) {
tsConfig.compilerOptions?.plugins?.splice(misconfiguredPluginIndex, 1);
}
return {
...tsConfig,
compilerOptions: {
...tsConfig.compilerOptions,
plugins: [
...(tsConfig.compilerOptions?.plugins ?? []),
{
transform: "dipend/tsc-plugin",
transformProgram: true,
},
],
},
};
}
mergeTsConfig(tsConfig) {
const newTsConfig = [this.addTsNodeConfigs.bind(this), this.addLibs.bind(this), this.addPlugins.bind(this)].reduce((mergedTsConfig, method) => method(mergedTsConfig), tsConfig);
return newTsConfig;
}
addDipendCliTool(packageJson) {
if (packageJson.scripts) {
for (const [scriptName, scriptCmd] of Object.entries(packageJson.scripts)) {
if (typeof scriptCmd === "string") {
if (/\bdipend\s+(ts-node|tsc)\b/.test(scriptCmd)) {
continue;
}
const updatedCmd = scriptCmd.replace(/(^|\s)(ts-node|tsc)(\s|$)/g, "$1dipend $2$3");
packageJson.scripts[scriptName] = updatedCmd;
}
}
}
return packageJson;
}
addTsPatch(packageJson) {
if (packageJson.devDependencies?.["ts-patch"]) {
console.log(`package.json: ts-patch already exists in devDependencies`);
return packageJson;
}
this.recommendInstall = true;
return {
...packageJson,
devDependencies: {
...(packageJson.devDependencies ?? {}),
"ts-patch": "3.3.0",
},
};
}
getDipendVersion() {
const packageJsonPath = path_1.default.join(__dirname, "..", "..", "package.json");
const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, "utf8"));
return packageJson.version;
}
addDipend(packageJson) {
if (packageJson.dependencies?.["dipend"]) {
console.log(`package.json: dipend already exists in dependencies`);
return packageJson;
}
this.recommendInstall = true;
return {
...packageJson,
dependencies: {
...(packageJson.dependencies ?? {}),
dipend: this.getDipendVersion(),
},
};
}
mergePackageJson(packageJson) {
const newPackageJson = [
this.addDipendCliTool.bind(this),
this.addTsPatch.bind(this),
this.addDipend.bind(this),
].reduce((mergedPackageJson, method) => method(mergedPackageJson), packageJson);
return newPackageJson;
}
applyChangesAndKeepComments(originalTsConfigAsString, mergedTsConfig) {
const formattingOptions = { insertSpaces: true, tabSize: 2 };
let result = originalTsConfigAsString;
if (mergedTsConfig["ts-node"]?.compiler) {
const edit = (0, jsonc_parser_1.modify)(result, ["ts-node", "compiler"], mergedTsConfig["ts-node"].compiler, {
formattingOptions,
});
result = (0, jsonc_parser_1.applyEdits)(result, edit);
}
if (mergedTsConfig.compilerOptions?.lib) {
const edit = (0, jsonc_parser_1.modify)(result, ["compilerOptions", "lib"], mergedTsConfig.compilerOptions.lib, {
formattingOptions,
});
result = (0, jsonc_parser_1.applyEdits)(result, edit);
}
if (mergedTsConfig.compilerOptions?.plugins) {
const edit = (0, jsonc_parser_1.modify)(result, ["compilerOptions", "plugins"], mergedTsConfig.compilerOptions.plugins, {
formattingOptions,
});
result = (0, jsonc_parser_1.applyEdits)(result, edit);
}
return result;
}
handler = async (args) => {
try {
const tsConfigPath = path_1.default.resolve(args.tsConfig);
const packageJsonPath = path_1.default.resolve(args.packageJson);
const tsConfig = this.readTsConfig(tsConfigPath);
const originalTsConfigAsString = fs_1.default.readFileSync(tsConfigPath, "utf-8");
const packageJson = this.readPackageJson(packageJsonPath);
const mergedTsConfig = this.mergeTsConfig(tsConfig);
console.log(`Dipend configurations successfully added to tsconfig.json`);
const mergedPackageJson = this.mergePackageJson(packageJson);
console.log(`Dipend configurations successfully added to package.json`);
if (this.recommendInstall)
console.log("Dipend: Please, run npm install again");
const newTsConfig = this.applyChangesAndKeepComments(originalTsConfigAsString, mergedTsConfig);
fs_1.default.writeFileSync(tsConfigPath, newTsConfig, "utf-8");
fs_1.default.writeFileSync(packageJsonPath, JSON.stringify(mergedPackageJson, null, 2));
}
catch (error) {
console.error("Error updating tsconfig.json or package.json:", error);
}
};
}
exports.InitCommand = InitCommand;