diff --git a/src/usage/scanner.ts b/src/usage/scanner.ts index dfce65b5..a2ae7143 100644 --- a/src/usage/scanner.ts +++ b/src/usage/scanner.ts @@ -1,10 +1,20 @@ import fs from "node:fs"; import path from "node:path"; -// Shared with lockfile scanning in utils/file.ts; additions affect both code paths. import { EXCLUDED_DIRS } from "../constants.js"; const ALLOWED_EXTS = new Set([".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs"]); const MAX_FILES_TO_SCAN = 5000; +const ALL_IMPORTS_EXCLUDED_DIRS = new Set([ + ...EXCLUDED_DIRS, + "__fixtures__", + "__tests__", + "example", + "examples", + "fixture", + "fixtures", + "test", + "tests", +]); // Matches: // import { foo } from 'pkg' @@ -130,7 +140,7 @@ export function scanAllImports(projectPath: string): Map { if (scannedCount >= MAX_FILES_TO_SCAN) return; const fullPath = path.join(dir, entry.name); if (entry.isDirectory()) { - if (!EXCLUDED_DIRS.has(entry.name) && !entry.name.startsWith(".")) { + if (!ALL_IMPORTS_EXCLUDED_DIRS.has(entry.name) && !entry.name.startsWith(".")) { walk(fullPath); } } else if (entry.isFile()) { diff --git a/tests/overrides/context-builder.test.ts b/tests/overrides/context-builder.test.ts index ec4cb2f6..d3e0eade 100644 --- a/tests/overrides/context-builder.test.ts +++ b/tests/overrides/context-builder.test.ts @@ -1,4 +1,4 @@ -import { mkdtempSync, writeFileSync, rmSync } from "node:fs"; +import { mkdirSync, mkdtempSync, writeFileSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { buildOverrideContext } from "../../src/overrides/context-builder.js"; @@ -42,6 +42,37 @@ describe("buildOverrideContext", () => { expect(ctx.lockfilePackageNames.has("postcss")).toBe(true); }); + it("does not count imports from example, test, or fixture directories", () => { + writeFileSync(join(dir, "package.json"), JSON.stringify({ + name: "x", + overrides: { "js-yaml": "^4.1.0" }, + })); + writeFileSync(join(dir, "package-lock.json"), JSON.stringify({ + lockfileVersion: 3, + packages: { + "": { name: "x" }, + "node_modules/js-yaml": { version: "4.1.0" }, + }, + })); + mkdirSync(join(dir, "examples", "demo", "src"), { recursive: true }); + mkdirSync(join(dir, "tests"), { recursive: true }); + mkdirSync(join(dir, "fixtures", "project", "src"), { recursive: true }); + mkdirSync(join(dir, "src"), { recursive: true }); + writeFileSync(join(dir, "examples", "demo", "src", "index.ts"), "import yaml from 'js-yaml';"); + writeFileSync(join(dir, "tests", "usage.test.ts"), "const fixture = `import yaml from 'js-yaml';`;"); + writeFileSync(join(dir, "fixtures", "project", "src", "index.ts"), "require('js-yaml');"); + writeFileSync(join(dir, "src", "index.ts"), "import { parse } from 'semver';"); + + const ctx = buildOverrideContext(dir, { + auditLog: NULL_AUDIT_LOG, + logger: makeNoopLogger() as any, + checkNetwork: false, + }); + + expect(ctx.importedPackageNames.has("js-yaml")).toBe(false); + expect(ctx.importedPackageNames.get("semver")).toEqual([expect.stringMatching(/src.index\.ts/)]); + }); + it("flags OA001/OA004/OA006/OA008 as skipped when node_modules is absent", () => { writeFileSync(join(dir, "package.json"), JSON.stringify({ name: "x", diff --git a/tests/usage.test.ts b/tests/usage.test.ts index 181f59f3..fd4901d6 100644 --- a/tests/usage.test.ts +++ b/tests/usage.test.ts @@ -70,6 +70,23 @@ describe("scanProjectForPackageUsage", () => { expect(results["lodash"][0]).toMatch(/src.index\.js/); }); + it("keeps example, test, and fixture imports for usage filtering", () => { + createTestFile("examples/demo/src/index.ts", "import yaml from 'js-yaml';"); + createTestFile("tests/usage.test.ts", "const fixture = `import yaml from 'js-yaml';`;"); + createTestFile("fixtures/project/src/index.ts", "require('js-yaml');"); + createTestFile("src/index.ts", "import yaml from 'js-yaml';"); + + const results = scanProjectForPackageUsage(tempDir, new Set(["js-yaml"])); + + expect(results["js-yaml"]).toHaveLength(4); + expect(results["js-yaml"]).toEqual(expect.arrayContaining([ + expect.stringMatching(/examples.demo.src.index\.ts/), + expect.stringMatching(/tests.usage\.test\.ts/), + expect.stringMatching(/fixtures.project.src.index\.ts/), + expect.stringMatching(/src.index\.ts/), + ])); + }); + it("should extract bare module names correctly", () => { createTestFile("src/index.js", ` import { x } from 'lodash/fp/map'; @@ -146,4 +163,16 @@ describe("scanAllImports", () => { const result = scanAllImports(tempDir); expect(result.has("bar")).toBe(false); }); + + it("excludes example, test, and fixture imports", () => { + createFile("examples/pd001-override-phantom/src/index.ts", `import yaml from 'js-yaml';`); + createFile("tests/usage.test.ts", `const fixture = \`import yaml from 'js-yaml';\`;`); + createFile("fixtures/project/src/index.ts", `require('js-yaml');`); + createFile("src/index.ts", `import { parse } from 'semver';`); + + const result = scanAllImports(tempDir); + + expect(result.has("js-yaml")).toBe(false); + expect(result.get("semver")).toEqual([expect.stringMatching(/src.index\.ts/)]); + }); });