nestch
Version:
A CLI tool for renaming NestJS resource names easily.
336 lines • 13 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RenameResCommand = void 0;
const fs = __importStar(require("fs/promises"));
const path = __importStar(require("path"));
const prompts_1 = __importDefault(require("prompts"));
function capitalizeFirstLetter(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
function pluralize(word) {
if (/[^aeiou]y$/i.test(word)) {
return word.replace(/y$/i, "ies");
}
else if (/(?:f|fe)$/i.test(word)) {
return word.replace(/(?:f|fe)$/i, "ves");
}
else if (/(s|x|z|ch|sh)$/i.test(word)) {
return word + "es";
}
else {
return word + "s";
}
}
function singularize(word) {
if (/ies$/i.test(word)) {
return word.replace(/ies$/i, "y");
}
else if (/ves$/i.test(word)) {
return word.replace(/ves$/i, (m) => {
if (/[^aeiou]ves$/i.test(word))
return "f";
return "fe";
});
}
else if (/(s|x|z|ch|sh)es$/i.test(word)) {
return word.replace(/es$/i, "");
}
else if (/s$/i.test(word) && !/ss$/i.test(word)) {
return word.replace(/s$/i, "");
}
else {
return word;
}
}
class RenameResCommand {
constructor() {
this.oldName = "";
this.newName = "";
this.found = false;
this.renamedFiles = [];
this.updatedFiles = [];
this.srcPath = path.join(process.cwd(), "src");
}
async execute(oldName, newName) {
this.oldName = oldName;
this.newName = newName;
const oldNameSingular = singularize(oldName);
const oldNamePlural = pluralize(oldNameSingular);
const newNameSingular = singularize(newName);
const newNamePlural = pluralize(newNameSingular);
const packageJsonPath = path.join(process.cwd(), "package.json");
try {
await fs.access(packageJsonPath);
}
catch {
console.error("package.json not found.");
process.exit(1);
}
try {
const pkg = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
if (!deps["@nestjs/core"] && !deps["@nestjs/common"]) {
console.error("This is not a NestJS project.");
process.exit(1);
}
}
catch (error) {
console.error("Error reading package.json:", error);
process.exit(1);
}
try {
await fs.access(this.srcPath);
}
catch {
console.error("src directory not found.");
process.exit(1);
}
const oldNameLower = oldNamePlural.toLowerCase();
const oldNameCapital = capitalizeFirstLetter(oldNamePlural);
const oldNameSingularLower = oldNameSingular.toLowerCase();
const oldNameSingularCapital = capitalizeFirstLetter(oldNameSingular);
const newNameLower = newNamePlural.toLowerCase();
const newNameCapital = capitalizeFirstLetter(newNamePlural);
const newNameSingularLower = newNameSingular.toLowerCase();
const newNameSingularCapital = capitalizeFirstLetter(newNameSingular);
const oldNames = {
lower: oldNameLower,
capital: oldNameCapital,
singularLower: oldNameSingularLower,
singularCapital: oldNameSingularCapital,
};
const newNames = {
lower: newNameLower,
capital: newNameCapital,
singularLower: newNameSingularLower,
singularCapital: newNameSingularCapital,
};
await this.checkOldNameExists(this.srcPath, oldNames.lower, oldNames.capital);
if (!this.found) {
console.error(`'${this.oldName}' not found.`);
process.exit(1);
}
const confirm = await (0, prompts_1.default)({
type: "confirm",
name: "confirm",
message: `Do you want to rename '${this.oldName}' to '${this.newName}'?`,
initial: false,
});
if (!confirm.confirm) {
console.log("Operation cancelled.");
return;
}
await this.renameInProject(oldNames, newNames);
if (this.renamedFiles.length > 0) {
console.log(`\n[Renamed files/directories: ${this.renamedFiles.length}]`);
this.renamedFiles.forEach((f) => console.log(` - ${f}`));
}
else {
console.log("No files or directories were renamed.");
}
if (this.updatedFiles.length > 0) {
console.log(`\n[Updated file contents: ${this.updatedFiles.length}]`);
this.updatedFiles.forEach((f) => console.log(` - ${f}`));
}
else {
console.log("No file contents were updated.");
}
console.log("\nRenaming completed successfully.");
}
async checkFileContentForOldName(filePath, oldNameLower, oldNameCapital) {
try {
const content = await fs.readFile(filePath, "utf8");
return content.includes(oldNameLower) || content.includes(oldNameCapital);
}
catch (error) {
const msg = typeof error === "object" && error && "message" in error
? error.message
: String(error);
console.error(`[Error] Failed to read file: ${filePath}\n Reason:`, msg);
return false;
}
}
async checkOldNameExists(dir, oldNameLower, oldNameCapital) {
if (this.found)
return;
let entries = [];
try {
entries = await fs.readdir(dir, { withFileTypes: true });
}
catch (e) {
return;
}
for (const entry of entries) {
if (this.found)
break;
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
await this.checkOldNameExists(fullPath, oldNameLower, oldNameCapital);
}
else if (entry.isFile() && entry.name.endsWith(".ts")) {
if (entry.name.toLowerCase().includes(oldNameLower)) {
this.found = true;
break;
}
if (await this.checkFileContentForOldName(fullPath, oldNameLower, oldNameCapital)) {
this.found = true;
break;
}
}
}
}
async renameInProject(oldNames, newNames) {
await this.renameFilesAndDirs(this.srcPath, oldNames.lower, newNames.lower, oldNames.singularLower, newNames.singularLower);
await this.updateFileContents(this.srcPath, oldNames, newNames);
}
async renameEntry(oldPath, newPath) {
try {
await fs.rename(oldPath, newPath);
this.renamedFiles.push(path.relative(process.cwd(), oldPath) +
" -> " +
path.relative(process.cwd(), newPath));
return true;
}
catch (error) {
const msg = typeof error === "object" && error && "message" in error
? error.message
: String(error);
console.error(`[Error] Failed to rename: ${oldPath} -> ${newPath}\n Reason:`, msg);
return false;
}
}
async renameFilesAndDirs(dir, oldName, newName, oldNameSingular, newNameSingular) {
let entries = [];
try {
entries = await fs.readdir(dir, { withFileTypes: true });
}
catch (e) {
return;
}
entries.sort((a, b) => {
if (a.isDirectory() && !b.isDirectory())
return -1;
if (!a.isDirectory() && b.isDirectory())
return 1;
return 0;
});
for (const entry of entries) {
const oldPath = path.join(dir, entry.name);
let newFileName = entry.name;
if (entry.name.toLowerCase().includes(oldName)) {
const index = entry.name.toLowerCase().indexOf(oldName);
const prefix = entry.name.substring(0, index);
const suffix = entry.name.substring(index + oldName.length);
newFileName = prefix + newName + suffix;
}
if (newFileName.toLowerCase().includes(oldNameSingular)) {
const index = newFileName.toLowerCase().indexOf(oldNameSingular);
const prefix = newFileName.substring(0, index);
const suffix = newFileName.substring(index + oldNameSingular.length);
newFileName = prefix + newNameSingular + suffix;
}
const newPath = path.join(dir, newFileName);
let nextDir = oldPath;
if (oldPath !== newPath) {
const renamedSuccessfully = await this.renameEntry(oldPath, newPath);
if (renamedSuccessfully) {
nextDir = newPath;
}
else {
nextDir = oldPath; // Renaming failed, continue with old path
}
}
if (entry.isDirectory()) {
await this.renameFilesAndDirs(nextDir, oldName, newName, oldNameSingular, newNameSingular);
}
else if (oldPath !== newPath) {
await this.renameEntry(oldPath, newPath);
}
}
}
async replaceContentInFile(filePath, oldNames, newNames) {
try {
let content = await fs.readFile(filePath, "utf8");
// 복수/단수, 대소문자 모두 치환
const regexes = [
[new RegExp(oldNames.lower, "g"), newNames.lower],
[new RegExp(oldNames.capital, "g"), newNames.capital],
[new RegExp(oldNames.singularLower, "g"), newNames.singularLower],
[new RegExp(oldNames.singularCapital, "g"), newNames.singularCapital],
];
let updated = false;
for (const [regex, replacement] of regexes) {
if (regex.test(content)) {
content = content.replace(regex, replacement);
updated = true;
}
}
if (updated) {
await fs.writeFile(filePath, content, "utf8");
this.updatedFiles.push(path.relative(process.cwd(), filePath));
}
}
catch (error) {
const msg = typeof error === "object" && error && "message" in error
? error.message
: String(error);
console.error(`[Error] Failed to update file: ${filePath}\n Reason:`, msg);
}
}
async updateFileContents(dir, oldNames, newNames) {
let entries = [];
try {
entries = await fs.readdir(dir, { withFileTypes: true });
}
catch (e) {
return;
}
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
await this.updateFileContents(fullPath, oldNames, newNames);
}
else if (entry.isFile() && entry.name.endsWith(".ts")) {
await this.replaceContentInFile(fullPath, oldNames, newNames);
}
}
}
}
exports.RenameResCommand = RenameResCommand;
//# sourceMappingURL=rename-resource.js.map