monitor/scripts/list-dependencies.js
2025-04-16 22:30:27 +07:00

90 lines
3.7 KiB
JavaScript

//NOTE: This script is not perfect, it was quickly made to help with the migration to the new package system.
import chalk from 'chalk';
import fs from 'node:fs';
import path from 'node:path';
//Get list of all dependencies in package.json
const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
const dependencies = new Set(Object.keys(packageJson.dependencies).concat(Object.keys(packageJson.devDependencies)));
//Get target folder path
const targetPath = process.argv[2];
if (typeof targetPath !== 'string' || !targetPath.length) {
console.log('Invalid target package name');
process.exit(1);
}
console.clear();
console.log('Scanning for dependencies in:', chalk.blue(targetPath));
//NOTE: To generate this list, use `node -pe "require('repl')._builtinLibs"` in both node16 and 22, then merge them.
const builtInModules = [
'assert', 'assert/strict', 'async_hooks', 'buffer', 'child_process', 'cluster', 'console', 'constants', 'crypto', 'dgram', 'diagnostics_channel', 'dns', 'dns/promises', 'domain', 'events', 'fs', 'fs/promises', 'http', 'http2', 'https', 'inspector', 'inspector/promises', 'module', 'net', 'os', 'path', 'path/posix', 'path/win32', 'perf_hooks', 'process', 'punycode', 'querystring', 'readline', 'readline/promises', 'repl', 'stream', 'stream/consumers', 'stream/promises', 'stream/web', 'string_decoder', 'sys', 'test/reporters', 'timers', 'timers/promises', 'tls', 'trace_events', 'tty', 'url', 'util', 'util/types', 'v8', 'vm', 'wasi', 'worker_threads', 'zlib'
];
//Process file and extract all dependencies
const allDependencies = new Set();
const ignoredPrefixes = [
'node:',
'./',
'../',
'@shared',
'@utils',
'@logic',
'@modules',
'@routes',
'@core',
'@locale/',
'@nui/',
'@shared/',
];
const importRegex = /import\s+.+\s+from\s+['"](.*)['"]/gm;
const processFile = (filePath) => {
const fileContent = fs.readFileSync(filePath, 'utf8');
const matches = [...fileContent.matchAll(importRegex)].map((match) => match[1]);
for (const importedModule of matches) {
if (ignoredPrefixes.some((prefix) => importedModule.startsWith(prefix))) continue;
if (!importedModule) {
console.log(chalk.red(`[ERROR] Invalid import in file: ${filePath}: ${importedModule}`));
continue;
}
if (builtInModules.includes(importedModule)) {
console.log(chalk.red(`[ERROR] builtin module '${importedModule}' without 'node:' from: ${filePath}`));
continue;
}
if (importedModule === '.') {
console.log(chalk.red(`[ERROR] Invalid import in file: ${filePath}: ${importedModule}`));
continue;
}
if (!dependencies.has(importedModule)) {
console.log(chalk.yellow(`[WARN] imported module '${importedModule}' not found in package.json`));
continue;
}
allDependencies.add(importedModule);
}
};
//Recursively read all files in the targetPath and its subfolders
const validExtensions = ['.cjs', '.js', '.ts', '.jsx', '.tsx'];
const processFolder = (dirPath) => {
const files = fs.readdirSync(dirPath, { withFileTypes: true });
for (const file of files) {
const currFilePath = path.join(dirPath, file.name);
if (file.isDirectory()) {
processFolder(currFilePath);
} else if (!file.isFile()) {
console.log(chalk.red(`[ERROR] Invalid file: ${currFilePath}`));
} else if (!validExtensions.includes(path.extname(currFilePath))) {
console.log(chalk.grey(`[INFO] Ignoring file: ${currFilePath}`));
} else {
processFile(currFilePath);
}
}
};
processFolder(targetPath);
console.log(allDependencies);