From 9a5f0a6836f09709a919217815f18fbf061bb35e Mon Sep 17 00:00:00 2001 From: anas-codx Date: Sun, 7 Dec 2025 18:47:22 +0530 Subject: [PATCH 1/8] add: lockfiles scanning util --- workspaces/mama/src/utils/scan-lockfiles.ts | 22 +++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 workspaces/mama/src/utils/scan-lockfiles.ts diff --git a/workspaces/mama/src/utils/scan-lockfiles.ts b/workspaces/mama/src/utils/scan-lockfiles.ts new file mode 100644 index 00000000..aaba194d --- /dev/null +++ b/workspaces/mama/src/utils/scan-lockfiles.ts @@ -0,0 +1,22 @@ +// Import Node.js Dependencies +import fs from "node:fs"; +import path from "node:path"; + +export const LOCK_FILES = { + npm: "package-lock.json", + bun: "bun.lockb", + yarn: "yarn.lock", + pnpm: "pnpm-lock.yaml" +}; + +export function scanLockFiles(dirPath: string) { + const result: { [k: string]: string; } = {}; + for (const [k, v] of Object.entries(LOCK_FILES)) { + const filePath = path.join(dirPath, v); + if (fs.existsSync(filePath)) { + result[k] = filePath; + } + } + + return result; +} From c247691723a2b4ba99ce3c8cd7f90366f4053dd5 Mon Sep 17 00:00:00 2001 From: anas-codx Date: Sun, 7 Dec 2025 18:49:00 +0530 Subject: [PATCH 2/8] add: exporting scanLockFiles --- workspaces/mama/src/index.ts | 1 + workspaces/mama/src/utils/index.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/workspaces/mama/src/index.ts b/workspaces/mama/src/index.ts index 1dabb0ab..4ed42332 100644 --- a/workspaces/mama/src/index.ts +++ b/workspaces/mama/src/index.ts @@ -3,5 +3,6 @@ export { packageJSONIntegrityHash, parseNpmSpec, inspectModuleType, + scanLockFiles, type PackageModuleType } from "./utils/index.ts"; diff --git a/workspaces/mama/src/utils/index.ts b/workspaces/mama/src/utils/index.ts index d081be93..3f2648ff 100644 --- a/workspaces/mama/src/utils/index.ts +++ b/workspaces/mama/src/utils/index.ts @@ -1,3 +1,4 @@ export * from "./integrity-hash.ts"; export * from "./inspectModuleType.ts"; export * from "./parseNpmSpec.ts"; +export * from "./scan-lockfiles.ts"; From 3a407d91213d3674c53205006179a559a839b16a Mon Sep 17 00:00:00 2001 From: anas-codx Date: Mon, 8 Dec 2025 17:02:33 +0530 Subject: [PATCH 3/8] add: scanLockFiles tests --- workspaces/mama/src/utils/scan-lockfiles.ts | 6 ++-- workspaces/mama/test/scan-lockfiles.spec.ts | 31 +++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 workspaces/mama/test/scan-lockfiles.spec.ts diff --git a/workspaces/mama/src/utils/scan-lockfiles.ts b/workspaces/mama/src/utils/scan-lockfiles.ts index aaba194d..98380fb8 100644 --- a/workspaces/mama/src/utils/scan-lockfiles.ts +++ b/workspaces/mama/src/utils/scan-lockfiles.ts @@ -9,7 +9,7 @@ export const LOCK_FILES = { pnpm: "pnpm-lock.yaml" }; -export function scanLockFiles(dirPath: string) { +export function scanLockFiles(dirPath: string): null | object { const result: { [k: string]: string; } = {}; for (const [k, v] of Object.entries(LOCK_FILES)) { const filePath = path.join(dirPath, v); @@ -18,5 +18,7 @@ export function scanLockFiles(dirPath: string) { } } - return result; + const isEmpty = Object.keys(result).length === 0; + + return isEmpty ? null : result; } diff --git a/workspaces/mama/test/scan-lockfiles.spec.ts b/workspaces/mama/test/scan-lockfiles.spec.ts new file mode 100644 index 00000000..a8d09ad3 --- /dev/null +++ b/workspaces/mama/test/scan-lockfiles.spec.ts @@ -0,0 +1,31 @@ +// Import Node.js Dependencies +import { describe, test } from "node:test"; +import assert from "node:assert"; +import fs from "node:fs"; + +// Import Internal Dependencies +import { scanLockFiles } from "../src/index.ts"; + +describe("scanLockFiles", () => { + test("should scan lock files", () => { + fs.writeFileSync("package-lock.json", ""); + + assert.deepEqual(scanLockFiles(""), { + npm: "package-lock.json" + }); + + fs.writeFileSync("pnpm-lock.yaml", ""); + + assert.deepEqual(scanLockFiles(""), { + npm: "package-lock.json", + pnpm: "pnpm-lock.yaml" + }); + + fs.unlinkSync("package-lock.json"); + fs.unlinkSync("pnpm-lock.yaml"); + }); + + test("should return null no lockfiles", () => { + assert.deepEqual(scanLockFiles(""), null); + }); +}); From 9bc89eb31233e5001134247a1d1f9a143735d6e7 Mon Sep 17 00:00:00 2001 From: anas-codx Date: Mon, 8 Dec 2025 22:17:52 +0530 Subject: [PATCH 4/8] add: test with secure file write --- workspaces/mama/src/index.ts | 1 + workspaces/mama/test/scan-lockfiles.spec.ts | 27 ++++++++++----------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/workspaces/mama/src/index.ts b/workspaces/mama/src/index.ts index 4ed42332..750ff967 100644 --- a/workspaces/mama/src/index.ts +++ b/workspaces/mama/src/index.ts @@ -4,5 +4,6 @@ export { parseNpmSpec, inspectModuleType, scanLockFiles, + LOCK_FILES, type PackageModuleType } from "./utils/index.ts"; diff --git a/workspaces/mama/test/scan-lockfiles.spec.ts b/workspaces/mama/test/scan-lockfiles.spec.ts index a8d09ad3..29a5e882 100644 --- a/workspaces/mama/test/scan-lockfiles.spec.ts +++ b/workspaces/mama/test/scan-lockfiles.spec.ts @@ -2,30 +2,29 @@ import { describe, test } from "node:test"; import assert from "node:assert"; import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; // Import Internal Dependencies -import { scanLockFiles } from "../src/index.ts"; +import { scanLockFiles, LOCK_FILES } from "../src/index.ts"; describe("scanLockFiles", () => { test("should scan lock files", () => { - fs.writeFileSync("package-lock.json", ""); + const output: typeof LOCK_FILES = {} as typeof LOCK_FILES; + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "/")); - assert.deepEqual(scanLockFiles(""), { - npm: "package-lock.json" - }); + for (const [k, v] of Object.entries(LOCK_FILES)) { + const filepath = path.join(tmpDir, v); - fs.writeFileSync("pnpm-lock.yaml", ""); + fs.writeFileSync(filepath, ""); + output[k as keyof typeof LOCK_FILES] = filepath; + } - assert.deepEqual(scanLockFiles(""), { - npm: "package-lock.json", - pnpm: "pnpm-lock.yaml" - }); - - fs.unlinkSync("package-lock.json"); - fs.unlinkSync("pnpm-lock.yaml"); + assert.deepEqual(scanLockFiles(tmpDir), output); }); test("should return null no lockfiles", () => { - assert.deepEqual(scanLockFiles(""), null); + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "/")); + assert.deepEqual(scanLockFiles(tmpDir), null); }); }); From 1e7bbb7842c8cf6ed6d57c64dbab0631641660ac Mon Sep 17 00:00:00 2001 From: anas-codx Date: Mon, 8 Dec 2025 22:29:49 +0530 Subject: [PATCH 5/8] update: changeset --- .changeset/shy-dingos-grin.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/shy-dingos-grin.md diff --git a/.changeset/shy-dingos-grin.md b/.changeset/shy-dingos-grin.md new file mode 100644 index 00000000..6fa94583 --- /dev/null +++ b/.changeset/shy-dingos-grin.md @@ -0,0 +1,5 @@ +--- +"@nodesecure/mama": minor +--- + +Added lockfile scanning utils From 02913b2206018d0370479a5cc39fc9ad1c293ee0 Mon Sep 17 00:00:00 2001 From: anas-codx Date: Mon, 15 Dec 2025 16:56:15 +0530 Subject: [PATCH 6/8] update: add deno.lock --- workspaces/mama/src/utils/scan-lockfiles.ts | 32 +++++++++++---------- workspaces/mama/test/scan-lockfiles.spec.ts | 6 ++-- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/workspaces/mama/src/utils/scan-lockfiles.ts b/workspaces/mama/src/utils/scan-lockfiles.ts index 98380fb8..8fb24fd7 100644 --- a/workspaces/mama/src/utils/scan-lockfiles.ts +++ b/workspaces/mama/src/utils/scan-lockfiles.ts @@ -1,24 +1,26 @@ // Import Node.js Dependencies import fs from "node:fs"; -import path from "node:path"; -export const LOCK_FILES = { - npm: "package-lock.json", - bun: "bun.lockb", - yarn: "yarn.lock", - pnpm: "pnpm-lock.yaml" -}; +export const LOCK_FILES = [ + ["npm", "package-lock.json"], + ["bun", "bun.lockb"], + ["pnpm", "pnpm-lock.yaml"], + ["yarn", "yarn.lock"], + ["deno", "deno.lock"] +]; export function scanLockFiles(dirPath: string): null | object { - const result: { [k: string]: string; } = {}; - for (const [k, v] of Object.entries(LOCK_FILES)) { - const filePath = path.join(dirPath, v); - if (fs.existsSync(filePath)) { - result[k] = filePath; - } + const dir = fs.readdirSync(dirPath); + if (dir.length === 0) { + return null; } - const isEmpty = Object.keys(result).length === 0; + const result: [string, string][] = []; + for (const [k, v] of LOCK_FILES) { + if (dir.includes(v)) { + result.push([k, v]); + } + } - return isEmpty ? null : result; + return result.length === 0 ? null : result; } diff --git a/workspaces/mama/test/scan-lockfiles.spec.ts b/workspaces/mama/test/scan-lockfiles.spec.ts index 29a5e882..dd7a3055 100644 --- a/workspaces/mama/test/scan-lockfiles.spec.ts +++ b/workspaces/mama/test/scan-lockfiles.spec.ts @@ -10,14 +10,14 @@ import { scanLockFiles, LOCK_FILES } from "../src/index.ts"; describe("scanLockFiles", () => { test("should scan lock files", () => { - const output: typeof LOCK_FILES = {} as typeof LOCK_FILES; + const output: [string, string][] = []; const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "/")); - for (const [k, v] of Object.entries(LOCK_FILES)) { + for (const [k, v] of LOCK_FILES) { const filepath = path.join(tmpDir, v); fs.writeFileSync(filepath, ""); - output[k as keyof typeof LOCK_FILES] = filepath; + output.push([k, v]); } assert.deepEqual(scanLockFiles(tmpDir), output); From 70cb87461f21471ab963eadeaa3d7458b135af70 Mon Sep 17 00:00:00 2001 From: anas-codx Date: Fri, 19 Dec 2025 21:28:06 +0530 Subject: [PATCH 7/8] update: add flatMap to filter the lockfiles --- workspaces/mama/src/utils/scan-lockfiles.ts | 15 +++++++-------- workspaces/mama/test/scan-lockfiles.spec.ts | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/workspaces/mama/src/utils/scan-lockfiles.ts b/workspaces/mama/src/utils/scan-lockfiles.ts index 8fb24fd7..7624cc7f 100644 --- a/workspaces/mama/src/utils/scan-lockfiles.ts +++ b/workspaces/mama/src/utils/scan-lockfiles.ts @@ -9,18 +9,17 @@ export const LOCK_FILES = [ ["deno", "deno.lock"] ]; -export function scanLockFiles(dirPath: string): null | object { +export function scanLockFiles(dirPath: string): null | Record { const dir = fs.readdirSync(dirPath); if (dir.length === 0) { return null; } - const result: [string, string][] = []; - for (const [k, v] of LOCK_FILES) { - if (dir.includes(v)) { - result.push([k, v]); - } - } + const filteredEntries = Array.from(LOCK_FILES).flatMap(function x([k, v]) { + return dir.includes(v) ? [[k, v]] : []; + }); - return result.length === 0 ? null : result; + return filteredEntries.length === 0 + ? null + : Object.fromEntries(filteredEntries); } diff --git a/workspaces/mama/test/scan-lockfiles.spec.ts b/workspaces/mama/test/scan-lockfiles.spec.ts index dd7a3055..4b96513b 100644 --- a/workspaces/mama/test/scan-lockfiles.spec.ts +++ b/workspaces/mama/test/scan-lockfiles.spec.ts @@ -20,7 +20,7 @@ describe("scanLockFiles", () => { output.push([k, v]); } - assert.deepEqual(scanLockFiles(tmpDir), output); + assert.deepEqual(scanLockFiles(tmpDir), Object.fromEntries(output)); }); test("should return null no lockfiles", () => { From a49adb51cb7918b2be6bc1888cf2aab494b5acd9 Mon Sep 17 00:00:00 2001 From: anas-codx Date: Fri, 19 Dec 2025 21:40:59 +0530 Subject: [PATCH 8/8] add: documentation --- workspaces/mama/README.md | 1 + workspaces/mama/docs/scan-lockfiles.md | 13 +++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 workspaces/mama/docs/scan-lockfiles.md diff --git a/workspaces/mama/README.md b/workspaces/mama/README.md index 1e24389a..1b48f9f9 100644 --- a/workspaces/mama/README.md +++ b/workspaces/mama/README.md @@ -38,6 +38,7 @@ This package exports a set of standalone utilities that are internally used by t - [packageJSONIntegrityHash](./docs/packageJSONIntegrityHash.md) - [parseNpmSpec](./docs/parseNpmSpec.md) - [inspectModuleType](./docs/inspectModuleType.md) +- [scanLockFiles](./docs/scan-lockfiles.md) ## ManifestManager API diff --git a/workspaces/mama/docs/scan-lockfiles.md b/workspaces/mama/docs/scan-lockfiles.md new file mode 100644 index 00000000..f03b56d0 --- /dev/null +++ b/workspaces/mama/docs/scan-lockfiles.md @@ -0,0 +1,13 @@ +# scanLockFiles + +Scans for lock files in the given path. + +This function searches for common package manager lock files +such as package-lock.json, yarn.lock, pnpm-lock.yaml, etc. +returns an Object of found lock file with their paths + +## Function Signature + +```ts +export function scanLockFiles(dirPath: string): null | Record; +```