summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Goetz <dev@leogtz.de>2026-05-24 06:50:57 +0200
committerLeo Goetz <dev@leogtz.de>2026-05-24 06:50:57 +0200
commitca8537af0ab596fddd8d72e6630f278326ef5360 (patch)
tree170d9f82b28f292f40785e33b3981121b6793c3c
parentd05bfaf95fb666fea02c8aceae3ce02c9e315d3d (diff)
feat: added fix functionality
-rw-r--r--src/actions/checkDependencies.ts80
-rw-r--r--src/actions/fixDependencies.ts21
-rw-r--r--src/actions/sendEmail.ts8
-rw-r--r--src/commands/fix.ts14
-rw-r--r--src/index.ts2
-rw-r--r--src/lib/project.ts85
-rw-r--r--src/utils/helper.ts15
7 files changed, 154 insertions, 71 deletions
diff --git a/src/actions/checkDependencies.ts b/src/actions/checkDependencies.ts
index 4234bf9..999733e 100644
--- a/src/actions/checkDependencies.ts
+++ b/src/actions/checkDependencies.ts
@@ -1,77 +1,21 @@
-import fs from "fs/promises";
-import ora, { type Ora } from "ora";
-import type { Config, Project } from "../types.js";
+import ora from "ora";
+import type { Config } from "../types.js";
import { getConfig } from "../utils/config.js";
-import { exec, type ExecException } from "child_process";
+import { getProjectAudits, goThroughProjects } from "../lib/project.js";
export async function checkDependencies() {
- const spinner = ora("Getting all Project Data").start();
-
const config: Config = await getConfig();
- let projectAudits: PromiseSettledResult<Project>[];
- let projects: Promise<Project>[] = [];
-
- try {
- const entries = await fs.readdir(config.path, { withFileTypes: true });
-
- for (let entry of entries) {
- if (!entry.isDirectory()) {
- continue;
- }
-
- let dirFullPath = `${config.path}${entry.name}`;
- const projectDir = await fs.readdir(dirFullPath);
-
- if (projectDir.includes("package.json")) {
- let auditPromise = getAuditPromise(dirFullPath, entry.name, spinner);
- projects.push(auditPromise);
- }
- }
-
- projectAudits = await Promise.allSettled(projects);
-
- spinner.succeed("Got the Data successfully");
- return projectAudits;
- } catch (error) {
- spinner.fail("Ups and Error :(");
- throw error;
- }
-}
-
-async function getAuditPromise(
- path: string,
- dirname: string,
- spinner: Ora,
-): Promise<Project> {
- await pullLatest(path, spinner);
-
- spinner.text = "getting audit";
-
- let { stdout } = await promiseExec(`cd "${path}" && npm audit --json`);
+ let spinnerProjects = ora("Getting all projects").start();
+ let projects = await goThroughProjects(config);
+ spinnerProjects.succeed("Got all projects");
- let output = JSON.parse(stdout);
- let project: Project = { projectName: dirname, ...output };
- if (project.error) {
- throw new Error(
- `${dirname} could not be audited, maybe package lock is corrupted`,
- );
- }
+ let spinnerAudit = ora("Getting all audits").start();
+ let projectAudits = await getProjectAudits(projects);
- return project;
-}
-
-async function pullLatest(path: string, spinner: Ora) {
- spinner.text = "pulling latest";
- await promiseExec(`cd "${path}" && git pull`);
-}
+ spinnerAudit.text = "Resolving audits";
+ let audit = await Promise.allSettled(projectAudits);
+ spinnerAudit.succeed("Successfully resolved audits");
-function promiseExec(
- cmd: string,
-): Promise<{ error: ExecException | null; stdout: string; stderr: string }> {
- return new Promise((resolve, _) => {
- exec(cmd, (error, stdout, stderr) => {
- resolve({ error, stdout, stderr });
- });
- });
+ return audit;
}
diff --git a/src/actions/fixDependencies.ts b/src/actions/fixDependencies.ts
new file mode 100644
index 0000000..9dfb6c6
--- /dev/null
+++ b/src/actions/fixDependencies.ts
@@ -0,0 +1,21 @@
+import ora from "ora";
+import type { Config } from "../types.js";
+import { getConfig } from "../utils/config.js";
+import { getProjectFixes, goThroughProjects } from "../lib/project.js";
+
+export async function fixDependencies(force: boolean) {
+ const config: Config = await getConfig();
+
+ let spinnerProjects = ora("Getting all projects").start();
+ let projects = await goThroughProjects(config);
+ spinnerProjects.succeed("Got all projects");
+
+ let spinnerFixes = ora("Getting all fixes").start();
+ let projectFixes = await getProjectFixes(projects, force);
+
+ spinnerFixes.text = "Resolving fixes";
+ let fixes = await Promise.allSettled(projectFixes);
+ spinnerFixes.succeed("Successfully resolved fixes");
+
+ return fixes;
+}
diff --git a/src/actions/sendEmail.ts b/src/actions/sendEmail.ts
index 4509a8e..62c527f 100644
--- a/src/actions/sendEmail.ts
+++ b/src/actions/sendEmail.ts
@@ -1,11 +1,11 @@
import nodemailer from "nodemailer";
import { emailConfig } from "../utils/email.js";
-import type { Project } from "../types.js";
+import type { ProjectAudit } from "../types.js";
const transporter = nodemailer.createTransport(emailConfig);
export const sendAuditEmail = async (
- projects: PromiseSettledResult<Project>[],
+ projects: PromiseSettledResult<ProjectAudit>[],
) => {
const text = emailContent(projects);
const email = await transporter.sendMail({
@@ -18,7 +18,9 @@ export const sendAuditEmail = async (
return email;
};
-const emailContent = (projects: PromiseSettledResult<Project>[]): string => {
+const emailContent = (
+ projects: PromiseSettledResult<ProjectAudit>[],
+): string => {
return `Here is your Report:
${projects.map((project) => {
if (project.status === "fulfilled") {
diff --git a/src/commands/fix.ts b/src/commands/fix.ts
new file mode 100644
index 0000000..94ab2e4
--- /dev/null
+++ b/src/commands/fix.ts
@@ -0,0 +1,14 @@
+import type { Command } from "commander";
+import { fixDependencies } from "../actions/fixDependencies.js";
+import { outputFixes } from "../actions/output.js";
+
+export const fix = (program: Command) => {
+ program
+ .command("fix")
+ .description("Runs 'npm audit fix' on each project")
+ .option("-f, --force", "Uses 'npm audit fix --force' on each project")
+ .action(async (options) => {
+ let tests = await fixDependencies(options.force);
+ outputFixes(tests);
+ });
+};
diff --git a/src/index.ts b/src/index.ts
index 3a6b348..0f81fdb 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -2,10 +2,12 @@
import { Command } from "commander";
import { check } from "./commands/check.js";
import { config } from "./commands/config.js";
+import { fix } from "./commands/fix.js";
const program = new Command();
check(program);
+fix(program);
config(program);
program.parse(process.argv);
diff --git a/src/lib/project.ts b/src/lib/project.ts
new file mode 100644
index 0000000..fc1f6b8
--- /dev/null
+++ b/src/lib/project.ts
@@ -0,0 +1,85 @@
+import type {
+ AuditOutput,
+ Config,
+ FixOutput,
+ ProjectAudit,
+ ProjectFix,
+} from "../types.js";
+import fs from "fs/promises";
+import { promiseExec } from "../utils/helper.js";
+
+export async function getProjectAudits(projects: any[]) {
+ let audits = [];
+ for (let project of projects) {
+ let auditPromise = getAuditPromise(project.path, project.name);
+ audits.push(auditPromise);
+ }
+ return audits;
+}
+
+export async function getProjectFixes(projects: any[], force: boolean) {
+ let fixes = [];
+ for (let project of projects) {
+ let fixPromise = getFixPromise(project.path, project.name, force);
+ fixes.push(fixPromise);
+ }
+ return fixes;
+}
+
+export async function goThroughProjects(config: Config) {
+ let projects = [];
+ try {
+ const entries = await fs.readdir(config.path, { withFileTypes: true });
+
+ for (let entry of entries) {
+ if (!entry.isDirectory()) {
+ continue;
+ }
+
+ let dirFullPath = `${config.path}${entry.name}`;
+ const projectDir = await fs.readdir(dirFullPath);
+
+ if (projectDir.includes("package.json")) {
+ projects.push({ path: dirFullPath, name: entry.name });
+ }
+ }
+
+ return projects;
+ } catch (error) {
+ throw error;
+ }
+}
+
+async function getAuditPromise(
+ path: string,
+ dirname: string,
+): Promise<ProjectAudit> {
+ let { stdout } = await promiseExec(`cd "${path}" && npm audit --json`);
+
+ let output: AuditOutput = JSON.parse(stdout);
+ let project: ProjectAudit = { projectName: dirname, ...output };
+ if (project.error) {
+ throw new Error(
+ `${dirname} could not be audited, maybe package lock is corrupted`,
+ );
+ }
+
+ return project;
+}
+
+async function getFixPromise(
+ path: string,
+ dirname: string,
+ force: boolean,
+): Promise<ProjectFix> {
+ let { stdout } = await promiseExec(
+ `cd "${path}" && npm audit fix --json ${force && "--force"}`,
+ );
+
+ let output: FixOutput = JSON.parse(stdout);
+ let project: ProjectFix = { projectName: dirname, ...output };
+ if (project.error) {
+ throw new Error(`${dirname} could not be fixed`);
+ }
+ return project;
+}
diff --git a/src/utils/helper.ts b/src/utils/helper.ts
new file mode 100644
index 0000000..379aeef
--- /dev/null
+++ b/src/utils/helper.ts
@@ -0,0 +1,15 @@
+import { exec, type ExecException } from "child_process";
+
+export async function pullLatest(path: string) {
+ await promiseExec(`cd "${path}" && git pull`);
+}
+
+export function promiseExec(
+ cmd: string,
+): Promise<{ error: ExecException | null; stdout: string; stderr: string }> {
+ return new Promise((resolve, _) => {
+ exec(cmd, (error, stdout, stderr) => {
+ resolve({ error, stdout, stderr });
+ });
+ });
+}