Files
n3wt-school/Front-End/scripts/check-hardcoded-strings.js
2025-04-15 19:41:42 +02:00

198 lines
5.4 KiB
JavaScript

// scripts/check-hardcoded-strings.js
const fs = require('fs');
const path = require('path');
const babel = require('@babel/parser');
const traverse = require('@babel/traverse').default;
// Patterns pour les classes Tailwind
const TAILWIND_PATTERNS = [
// Layout
/^(container|flex|grid|block|inline|hidden)/,
// Spacing
/^(m|p|mt|mb|ml|mr|mx|my|pt|pb|pl|pr|px|py)-[0-9]+/,
// Sizing
/^(w|h|min-w|min-h|max-w|max-h)-[0-9]+/,
// Typography
/^(text|font|leading)-/,
// Backgrounds
/^bg-/,
// Borders
/^(border|rounded|divide)-/,
// Effects
/^(shadow|opacity|transition)-/,
// Interactivity
/^(cursor|pointer-events|select|resize)-/,
// Layout
/^(z|top|right|bottom|left|float|clear)-/,
// Flexbox & Grid
/^(justify|items|content|self|place|gap)-/,
// États
/^(hover|focus|active|disabled|group|dark):/,
// Couleurs
/-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-[0-9]+$/,
];
// Nouveaux patterns pour ignorer les logs et imports
const CODE_PATTERNS = [
// Console et debug avec contenu
/console\.(log|error|warn|info|debug)\(['"].*['"]/,
/^console\.(log|error|warn|info|debug)\(/,
/^debugger/,
// Imports et requires avec leurs chaînes
/^import\s+.*\s+from\s+['"].*['"]/,
/^import\s+['"].*['"]/,
/^require\(['"].*['"]\)/,
/^from\s+['"].*['"]/,
/^@/,
// Autres cas courants
/^process\.env\./,
/^module\.exports/,
/^export\s+/,
];
function isHardcodedString(str) {
// Ignorer les chaînes vides ou trop courtes
if (!str || str.length <= 1) return false;
// Vérifier si la chaîne fait partie d'un console.log ou d'un import
const context = str.trim();
if (CODE_PATTERNS.some((pattern) => pattern.test(context))) {
return false;
}
// Vérifier si c'est une chaîne dans un console.log
if (context.includes('console.log(')) {
return false;
}
// Vérifier si c'est une chaîne dans un import
if (context.includes("from '") || context.includes('from "')) {
return false;
}
// Vérifier si c'est une classe Tailwind
const classes = str.split(' ');
if (
classes.some((cls) =>
TAILWIND_PATTERNS.some((pattern) => pattern.test(cls))
)
) {
return false;
}
// Autres patterns à ignorer
const IGNORE_PATTERNS = [
/^[A-Z][A-Za-z]+$/, // Noms de composants
/^@/, // Imports
/^[./]/, // Chemins de fichiers
/^[0-9]+$/, // Nombres
/^style=/, // Props style
/^id=/, // Props id
/^data-/, // Data attributes
/^aria-/, // Aria attributes
/^role=/, // Role attributes
/^className=/, // className attributes
];
return !IGNORE_PATTERNS.some((pattern) => pattern.test(str));
}
async function scanFile(filePath) {
const content = fs.readFileSync(filePath, 'utf-8');
const hardcodedStrings = new Map(); // Utiliser Map pour stocker string -> ligne
try {
const ast = babel.parse(content, {
sourceType: 'module',
plugins: ['jsx', 'typescript'],
locations: true, // Active le tracking des positions
});
traverse(ast, {
StringLiteral(path) {
const value = path.node.value;
const line = path.node.loc.start.line;
if (isHardcodedString(value)) {
hardcodedStrings.set(value, line);
}
},
JSXText(path) {
const value = path.node.value.trim();
const line = path.node.loc.start.line;
if (isHardcodedString(value)) {
hardcodedStrings.set(value, line);
}
},
});
} catch (error) {
console.error(`Erreur dans le fichier ${filePath}:`, error);
}
return Array.from(hardcodedStrings.entries());
}
async function scanDirectory(dir) {
const results = {};
const files = fs.readdirSync(dir);
for (const file of files) {
const filePath = path.join(dir, file);
const stat = fs.statSync(filePath);
if (
stat.isDirectory() &&
!file.startsWith('.') &&
file !== 'node_modules'
) {
Object.assign(results, await scanDirectory(filePath));
} else if (
stat.isFile() &&
(file.endsWith('.js') || file.endsWith('.jsx') || file.endsWith('.tsx'))
) {
const strings = await scanFile(filePath);
if (strings.length > 0) {
results[filePath] = strings;
}
}
}
return results;
}
async function logStringsToFile(results) {
const outputPath = path.join(process.cwd(), 'hardcoded-strings-report.md');
let content = '# Rapport des chaînes en dur\n\n';
let totalStrings = 0;
for (const [file, strings] of Object.entries(results)) {
if (strings.length > 0) {
const relativePath = path.relative(process.cwd(), file);
content += `\n## ${relativePath}\n\n`;
strings.forEach(([str, line]) => {
totalStrings++;
content += `- Ligne ${line}: \`${str}\`\n`;
content += ` → [Voir dans le code](${relativePath}:${line}:1)\n\n`;
});
}
}
content += `\n## Résumé\n`;
content += `- Total des chaînes trouvées: ${totalStrings}\n`;
content += `- Date du scan: ${new Date().toLocaleString('fr-FR')}\n`;
fs.writeFileSync(outputPath, content, 'utf8');
console.log(`\nRapport généré: ${outputPath}`);
}
// Modifier la fonction main existante
async function main() {
const rootDir = process.cwd();
const results = await scanDirectory(path.join(rootDir, 'src'));
await logStringsToFile(results);
}
main().catch(console.error);