Last active
September 15, 2025 08:52
-
-
Save yellowled/43d03663fef238909fc1b8da7bd84984 to your computer and use it in GitHub Desktop.
Checks a list of npm packages (optionally with versions) across a codebase and its subfolders, reporting whether each is found and listing installed versions when no exact version is specified.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Usage examples: | |
| * | |
| * 1. Check packages listed as CLI arguments: | |
| * node check-deps.js chalk debug [email protected] | |
| * | |
| * 2. Check packages from a file (one per line): | |
| * node check-deps.js -f packages.txt | |
| * | |
| * 3. Mix file and CLI arguments together: | |
| * node check-deps.js -f packages.txt chalk debug | |
| * | |
| * 4. Override default directories to scan: | |
| * node check-deps.js -d src,lib -f packages.txt | |
| * | |
| * Notes: | |
| * - Supports both bare package names (any version) and package@version (exact match). | |
| * - Default directories: ".", "ui-scanner", "ui-sspa". | |
| * - If no version is specified, outputs all installed versions found. | |
| */ | |
| const { execSync } = require("child_process"); | |
| const fs = require("fs"); | |
| const path = require("path"); | |
| // Default folders to scan | |
| let dirs = [".", "ui-scanner", "ui-sspa"]; | |
| // Parse CLI args | |
| let packages = []; | |
| const args = process.argv.slice(2); | |
| for (let i = 0; i < args.length; i++) { | |
| if (args[i] === "-f") { | |
| const filePath = args[i + 1]; | |
| if (!filePath) { | |
| console.error("❌ Please provide a file path after -f"); | |
| process.exit(1); | |
| } | |
| try { | |
| const content = fs.readFileSync(filePath, "utf-8"); | |
| const filePackages = content | |
| .split("\n") | |
| .map(line => line.trim()) | |
| .filter(line => line && !line.startsWith("#")); // ignore empty lines and comments | |
| packages.push(...filePackages); | |
| } catch (err) { | |
| console.error(`❌ Failed to read file: ${err.message}`); | |
| process.exit(1); | |
| } | |
| i++; // skip file name in the next iteration | |
| } else if (args[i] === "-d") { | |
| const dirList = args[i + 1]; | |
| if (!dirList) { | |
| console.error("❌ Please provide a comma-separated list of directories after -d"); | |
| process.exit(1); | |
| } | |
| dirs = dirList.split(",").map(d => d.trim()).filter(Boolean); | |
| i++; // skip dir list in the next iteration | |
| } else { | |
| packages.push(args[i]); | |
| } | |
| } | |
| if (packages.length === 0) { | |
| console.error("❌ No packages found to check."); | |
| process.exit(1); | |
| } | |
| // Check packages across specified dirs | |
| packages.forEach(pkg => { | |
| let found = false; | |
| let versions = []; | |
| const hasVersion = pkg.includes("@") && !pkg.startsWith("@"); // skip scoped packages like @babel/core | |
| for (const dir of dirs) { | |
| try { | |
| const output = execSync(`npm ls ${pkg} --all`, { | |
| cwd: path.resolve(dir), | |
| stdio: "pipe" | |
| }).toString(); | |
| found = true; | |
| if (!hasVersion) { | |
| const regex = new RegExp(`${pkg}@([0-9][^ ]*)`, "g"); | |
| let match; | |
| while ((match = regex.exec(output)) !== null) { | |
| versions.push(match[1]); | |
| } | |
| } | |
| } catch { | |
| // not found in this dir, continue | |
| } | |
| } | |
| if (found) { | |
| if (hasVersion) { | |
| console.log(`FOUND ${pkg}`); | |
| } else { | |
| const uniqVersions = [...new Set(versions)].sort(); | |
| console.log(`FOUND ${pkg} → versions: ${uniqVersions.join(", ")}`); | |
| } | |
| } else { | |
| console.log(`NOT FOUND ${pkg}`); | |
| } | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment