-
Notifications
You must be signed in to change notification settings - Fork 22
/
tree.mjs
101 lines (84 loc) · 4.71 KB
/
tree.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import fs from "fs";
import path from "path";
import chalk from "chalk";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const kebabCasePattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
const pascalCasePattern = /^[A-Z][a-zA-Z0-9]*$/;
// List of file extensions to ignore (e.g., package.json, package-lock.json, .md, .json files, etc.)
const ignoreFileExtensions = new Set([".json", ".md"]);
// List of files to explicitly ignore (e.g., package.json, package-lock.json)
const ignoreFiles = new Set(["package.json", "package-lock.json"]);
// List of directories to ignore (e.g., node_modules)
const ignoreDirs = new Set(["node_modules", "dist", "build"]);
function toPascalCase(str) {
return str.replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, (match, index) => (index === 0 ? match.toUpperCase() : match.toUpperCase())).replace(/\s+/g, "");
}
function containsClassDefinition(filePath) {
// Read the file content and check for any class declarations
const fileContent = fs.readFileSync(filePath, "utf-8");
return /\bclass\s+[A-Z][a-zA-Z0-9]*\b/.test(fileContent);
}
function checkNamingConventions(directory, depth = 0) {
const items = fs.readdirSync(directory);
let hasDiscrepancies = false;
items.forEach((item) => {
const fullPath = path.join(directory, item);
const isDirectory = fs.statSync(fullPath).isDirectory();
// Skip specific files and directories that don't need naming checks
if (ignoreFiles.has(item)) {
console.log(`${"│ ".repeat(depth)}└── ${chalk.gray(item)} ${chalk.gray("(Ignored: Configuration file)")}`);
return;
}
// Skip files with extensions that are generally not part of source code
const fileExtension = path.extname(item);
if (ignoreFileExtensions.has(fileExtension)) {
console.log(`${"│ ".repeat(depth)}└── ${chalk.gray(item)} ${chalk.gray("(Ignored: Non-source file extension)")}`);
return;
}
// Skip directories listed in the ignoreDirs set
if (ignoreDirs.has(item)) {
console.log(`${"│ ".repeat(depth)}└── ${chalk.gray(item)} ${chalk.gray("(Ignored: Directory)")}`);
return;
}
const indentation = "│ ".repeat(depth);
const treeBranch = isDirectory ? "├── " : "└── ";
if (isDirectory) {
console.log(`${indentation}${treeBranch}${chalk.blue(item)}`);
const result = checkNamingConventions(fullPath, depth + 1);
hasDiscrepancies = hasDiscrepancies || result;
} else {
const fileNameWithoutExtension = path.basename(item, fileExtension);
const isKebabCase = kebabCasePattern.test(fileNameWithoutExtension);
const isPascalCase = pascalCasePattern.test(toPascalCase(fileNameWithoutExtension.replace(/-([a-z])/g, (g) => g[1].toUpperCase())));
if (fileExtension === ".ts" || fileExtension === ".js") {
if (!isKebabCase) {
console.log(`${indentation}${treeBranch}${chalk.red(item)} ${chalk.red("(Error: Does not follow kebab-case)")}`);
console.log(`${indentation} ${chalk.yellow("Reason:")} The file name "${chalk.red(fileNameWithoutExtension)}" should be in kebab-case (e.g., "${chalk.green("command-handler.ts")}").`);
hasDiscrepancies = true;
} else {
console.log(`${indentation}${treeBranch}${chalk.green(item)}`);
}
if (fileExtension === ".ts" && containsClassDefinition(fullPath)) {
const className = toPascalCase(fileNameWithoutExtension.replace(/-([a-z])/g, (g) => g[1].toUpperCase()));
if (!isPascalCase && fileNameWithoutExtension !== "index") {
console.log(`${indentation}${treeBranch}${chalk.red(className)} ${chalk.red("(Error: Does not follow PascalCase)")}`);
console.log(`${indentation} ${chalk.yellow("Reason:")} Class names should be in PascalCase (e.g., "${chalk.green("CommandHandler")}").`);
hasDiscrepancies = true;
}
}
} else {
console.log(`${indentation}${treeBranch}${chalk.yellow(item)} ${chalk.yellow("(Warning: Unexpected file extension)")}`);
}
}
});
return hasDiscrepancies;
}
// Run the check on the penrose directory
const discrepancies = checkNamingConventions(path.join(__dirname, "penrose"));
if (discrepancies) {
process.exit(1); // Exit with an error code if discrepancies are found
} else {
process.exit(0); // Exit with success code if no discrepancies are found
}