Skip to content
This repository was archived by the owner on Apr 21, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"scripts": {
"build": "tsup",
"dev": "tsx src/index.ts",
"pretest": "npm run build",
"test": "node --experimental-vm-modules node_modules/.bin/jest",
"test:coverage": "node --experimental-vm-modules node_modules/.bin/jest --coverage",
"lint": "eslint src/",
Expand Down
49 changes: 49 additions & 0 deletions tests/bin-entries.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { existsSync, readFileSync } from 'node:fs';
import { join } from 'node:path';

const repoRoot = process.cwd();
const pkg = JSON.parse(readFileSync(join(repoRoot, 'package.json'), 'utf-8')) as {
name: string;
bin?: string | Record<string, string>;
files?: string[];
};

function normalizeBin(bin: typeof pkg.bin, name: string): Array<[string, string]> {
if (typeof bin === 'string') return [[name, bin]];
if (bin && typeof bin === 'object') return Object.entries(bin);
return [];
}

function pkgFilesCoversPath(files: string[], relPath: string): boolean {
const clean = relPath.replace(/^\.\//, '');
const top = clean.split('/')[0];
return files.some((f) => {
const c = f.replace(/\/$/, '').replace(/^\.\//, '');
return c === top;
});
Comment on lines +17 to +23
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

pkg.files coverage check is too narrow and can reject valid package configs.

Matching only the top-level directory misses valid files entries like explicit file paths (e.g., dist/index.js) and common pattern forms. That can create false failures when this guard is rolled out across packages.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/bin-entries.test.ts` around lines 17 - 23, The pkgFilesCoversPath
function is too strict by only comparing the top-level segment; update
pkgFilesCoversPath to treat package "files" entries as covering a path if any
entry (1) exactly equals the cleaned relPath, (2) equals the top-level segment
(current behavior), (3) is a directory prefix that matches the start of the
cleaned relPath (e.g., entry "dist/" covers "dist/index.js"), or (4) matches
common glob patterns—use a glob/match library (e.g., minimatch) or simple
suffix/prefix checks to handle entries like "dist/*.js" and explicit file paths
so valid package configs are not rejected.

}

describe('package.json bin entries', () => {
const entries = normalizeBin(pkg.bin, pkg.name);
const files: string[] = pkg.files ?? [];

it('declares at least one bin entry', () => {
expect(entries.length).toBeGreaterThan(0);
});

it.each(entries)('bin "%s" -> "%s" exists on disk', (_name, rel) => {
const abs = join(repoRoot, rel.replace(/^\.\//, ''));
expect(existsSync(abs)).toBe(true);
});
Comment on lines +34 to +37
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Build-order dependency is currently breaking CI.

This assertion depends on prebuilt dist artifacts, and CI is already failing on this exact check. Make this suite self-contained (fixture/temp artifact) or guarantee npm run build always runs before this test job.

🧰 Tools
🪛 GitHub Actions: CI

[error] 34-36: Jest failed: package.json bin entries expects "forge-ai-init" -> "./dist/index.js" to exist on disk. expect(existsSync(abs)).toBe(true) received false.

🪛 GitHub Actions: SonarCloud Analysis

[error] 34-36: Jest test failed: package.json bin entries expected the target file to exist on disk. Failed assertion expect(existsSync(abs)).toBe(true) for bin "forge-ai-init" -> "./dist/index.js". Received false.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/bin-entries.test.ts` around lines 34 - 37, The test relies on prebuilt
dist artifacts and should be made self-contained: in tests/bin-entries.test.ts
update the suite that uses it.each(entries) (and the repoRoot/existsSync check)
to create a temporary test fixture directory in a beforeAll (using
fs.mkdtempSync or fs.promises.mkdtemp) and write dummy files for each rel from
entries into that temp dir (preserving the same relative paths), then point
repoRoot to that temp dir for the assertions; remove dependence on global build
artifacts and clean up the temp directory in afterAll. Ensure references to
entries, repoRoot, and the it.each test remain but change the path resolution to
use the new temp fixture directory.


it.each(entries)('bin "%s" -> "%s" starts with a node shebang', (_name, rel) => {
const abs = join(repoRoot, rel.replace(/^\.\//, ''));
if (!existsSync(abs)) return;
const firstLine = readFileSync(abs, 'utf-8').split('\n', 1)[0] ?? '';
expect(firstLine).toMatch(/^#!.*\bnode\b/);
});
Comment on lines +39 to +44
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Shebang test can pass even when the bin target is missing.

Early-return on missing file masks the failure when this test is run in isolation. Assert file existence inside this test too, then check the shebang.

Suggested fix
 it.each(entries)('bin "%s" -> "%s" starts with a node shebang', (_name, rel) => {
   const abs = join(repoRoot, rel.replace(/^\.\//, ''));
-  if (!existsSync(abs)) return;
+  expect(existsSync(abs)).toBe(true);
   const firstLine = readFileSync(abs, 'utf-8').split('\n', 1)[0] ?? '';
   expect(firstLine).toMatch(/^#!.*\bnode\b/);
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it.each(entries)('bin "%s" -> "%s" starts with a node shebang', (_name, rel) => {
const abs = join(repoRoot, rel.replace(/^\.\//, ''));
if (!existsSync(abs)) return;
const firstLine = readFileSync(abs, 'utf-8').split('\n', 1)[0] ?? '';
expect(firstLine).toMatch(/^#!.*\bnode\b/);
});
it.each(entries)('bin "%s" -> "%s" starts with a node shebang', (_name, rel) => {
const abs = join(repoRoot, rel.replace(/^\.\//, ''));
expect(existsSync(abs)).toBe(true);
const firstLine = readFileSync(abs, 'utf-8').split('\n', 1)[0] ?? '';
expect(firstLine).toMatch(/^#!.*\bnode\b/);
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/bin-entries.test.ts` around lines 39 - 44, The test currently
early-returns when the computed bin path (abs) is missing, masking failures;
instead assert the file exists inside the test by replacing the early return
with an explicit assertion (e.g.,
expect(existsSync(abs)).toBeTruthy()/toBe(true)) before reading firstLine, then
proceed to read the file and assert the shebang; update the test in the
it.each(entries) block that computes abs from repoRoot/rel and uses firstLine so
missing bin targets cause a test failure rather than being skipped.


it.each(entries)('bin "%s" -> "%s" lives under a directory in pkg.files', (_name, rel) => {
expect(pkgFilesCoversPath(files, rel)).toBe(true);
});
});
Loading