commit-cop 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/README.md +72 -15
  2. package/dist/brand.d.ts +4 -0
  3. package/dist/brand.d.ts.map +1 -0
  4. package/{src/brand.ts → dist/brand.js} +4 -3
  5. package/dist/brand.js.map +1 -0
  6. package/dist/checks/binaryFileCheck.js +2 -2
  7. package/dist/checks/binaryFileCheck.js.map +1 -1
  8. package/dist/checks/consoleLogCheck.d.ts.map +1 -1
  9. package/dist/checks/consoleLogCheck.js +2 -2
  10. package/dist/checks/consoleLogCheck.js.map +1 -1
  11. package/dist/checks/debuggerCheck.d.ts.map +1 -1
  12. package/dist/checks/debuggerCheck.js +2 -2
  13. package/dist/checks/debuggerCheck.js.map +1 -1
  14. package/dist/checks/envFileCheck.d.ts.map +1 -1
  15. package/dist/checks/envFileCheck.js +2 -2
  16. package/dist/checks/envFileCheck.js.map +1 -1
  17. package/dist/checks/focusedTestCheck.js +2 -2
  18. package/dist/checks/focusedTestCheck.js.map +1 -1
  19. package/dist/checks/generatedFolderCheck.js +2 -2
  20. package/dist/checks/generatedFolderCheck.js.map +1 -1
  21. package/dist/checks/junkFileCheck.d.ts.map +1 -1
  22. package/dist/checks/junkFileCheck.js +2 -2
  23. package/dist/checks/junkFileCheck.js.map +1 -1
  24. package/dist/checks/largeFileCheck.js +2 -2
  25. package/dist/checks/largeFileCheck.js.map +1 -1
  26. package/dist/checks/localHostCheck.js +2 -2
  27. package/dist/checks/localHostCheck.js.map +1 -1
  28. package/dist/checks/lockfileDriftCheck.d.ts.map +1 -1
  29. package/dist/checks/lockfileDriftCheck.js +4 -4
  30. package/dist/checks/lockfileDriftCheck.js.map +1 -1
  31. package/dist/checks/mergeConflictCheck.d.ts.map +1 -1
  32. package/dist/checks/mergeConflictCheck.js +2 -2
  33. package/dist/checks/mergeConflictCheck.js.map +1 -1
  34. package/dist/checks/secretCheck.js +19 -19
  35. package/dist/checks/secretCheck.js.map +1 -1
  36. package/dist/checks/sensitiveFilenameCheck.d.ts.map +1 -1
  37. package/dist/checks/sensitiveFilenameCheck.js +2 -2
  38. package/dist/checks/sensitiveFilenameCheck.js.map +1 -1
  39. package/dist/fix/debugCode.d.ts +3 -0
  40. package/dist/fix/debugCode.d.ts.map +1 -0
  41. package/dist/fix/debugCode.js +55 -0
  42. package/dist/fix/debugCode.js.map +1 -0
  43. package/dist/fix/focusedTests.d.ts +2 -0
  44. package/dist/fix/focusedTests.d.ts.map +1 -0
  45. package/dist/fix/focusedTests.js +22 -0
  46. package/dist/fix/focusedTests.js.map +1 -0
  47. package/dist/fix/gitignore.d.ts +2 -0
  48. package/dist/fix/gitignore.d.ts.map +1 -0
  49. package/dist/fix/gitignore.js +82 -0
  50. package/dist/fix/gitignore.js.map +1 -0
  51. package/dist/fix/junkFiles.d.ts +2 -0
  52. package/dist/fix/junkFiles.d.ts.map +1 -0
  53. package/dist/fix/junkFiles.js +14 -0
  54. package/dist/fix/junkFiles.js.map +1 -0
  55. package/dist/fix/lockfile.d.ts +2 -0
  56. package/dist/fix/lockfile.d.ts.map +1 -0
  57. package/dist/fix/lockfile.js +18 -0
  58. package/dist/fix/lockfile.js.map +1 -0
  59. package/dist/fix/matchers.d.ts +9 -0
  60. package/dist/fix/matchers.d.ts.map +1 -0
  61. package/dist/fix/matchers.js +118 -0
  62. package/dist/fix/matchers.js.map +1 -0
  63. package/dist/fix/runFix.d.ts +3 -0
  64. package/dist/fix/runFix.d.ts.map +1 -0
  65. package/dist/fix/runFix.js +82 -0
  66. package/dist/fix/runFix.js.map +1 -0
  67. package/dist/fix/unstage.d.ts +2 -0
  68. package/dist/fix/unstage.d.ts.map +1 -0
  69. package/dist/fix/unstage.js +22 -0
  70. package/dist/fix/unstage.js.map +1 -0
  71. package/dist/fix/utils.d.ts +4 -0
  72. package/dist/fix/utils.d.ts.map +1 -0
  73. package/dist/fix/utils.js +39 -0
  74. package/dist/fix/utils.js.map +1 -0
  75. package/dist/git.d.ts.map +1 -1
  76. package/dist/git.js +2 -1
  77. package/dist/git.js.map +1 -1
  78. package/dist/hook.d.ts +3 -0
  79. package/dist/hook.d.ts.map +1 -0
  80. package/dist/hook.js +87 -0
  81. package/dist/hook.js.map +1 -0
  82. package/dist/index.js +38 -21
  83. package/dist/index.js.map +1 -1
  84. package/dist/reporter.d.ts +1 -1
  85. package/dist/reporter.d.ts.map +1 -1
  86. package/dist/reporter.js +86 -28
  87. package/dist/reporter.js.map +1 -1
  88. package/dist/runScan.d.ts +2 -0
  89. package/dist/runScan.d.ts.map +1 -0
  90. package/dist/runScan.js +18 -0
  91. package/dist/runScan.js.map +1 -0
  92. package/dist/scanner.d.ts.map +1 -1
  93. package/dist/scanner.js +4 -1
  94. package/dist/scanner.js.map +1 -1
  95. package/dist/types.d.ts +4 -0
  96. package/dist/types.d.ts.map +1 -1
  97. package/package.json +7 -2
  98. package/src/checks/binaryFileCheck.ts +0 -64
  99. package/src/checks/consoleLogCheck.ts +0 -40
  100. package/src/checks/debuggerCheck.ts +0 -33
  101. package/src/checks/envFileCheck.ts +0 -26
  102. package/src/checks/focusedTestCheck.ts +0 -41
  103. package/src/checks/generatedFolderCheck.ts +0 -45
  104. package/src/checks/junkFileCheck.ts +0 -40
  105. package/src/checks/largeFileCheck.ts +0 -31
  106. package/src/checks/localHostCheck.ts +0 -40
  107. package/src/checks/lockfileDriftCheck.ts +0 -40
  108. package/src/checks/mergeConflictCheck.ts +0 -41
  109. package/src/checks/secretCheck.ts +0 -60
  110. package/src/checks/sensitiveFilenameCheck.ts +0 -51
  111. package/src/checks/utils.ts +0 -62
  112. package/src/fix/debugCode.ts +0 -74
  113. package/src/fix/focusedTests.ts +0 -26
  114. package/src/fix/gitignore.ts +0 -108
  115. package/src/fix/junkFiles.ts +0 -16
  116. package/src/fix/lockfile.ts +0 -23
  117. package/src/fix/matchers.ts +0 -141
  118. package/src/fix/runFix.ts +0 -96
  119. package/src/fix/unstage.ts +0 -25
  120. package/src/fix/utils.ts +0 -50
  121. package/src/git.ts +0 -17
  122. package/src/hook.ts +0 -98
  123. package/src/index.ts +0 -59
  124. package/src/reporter.ts +0 -88
  125. package/src/runScan.ts +0 -35
  126. package/src/scanner.ts +0 -44
  127. package/src/types.ts +0 -25
  128. package/test.ts +0 -6
  129. package/testing.ts +0 -3
  130. package/tsconfig.json +0 -44
@@ -1,41 +0,0 @@
1
- import fs from "node:fs";
2
- import type { Check, Finding } from "../types.js";
3
-
4
- const testPatterns = [
5
- "test.only",
6
- "it.only",
7
- "describe.only",
8
- ];
9
-
10
- export const focusedTestCheck: Check = {
11
- name: "focused-test-check",
12
-
13
- async run(context) {
14
- const findings: Finding[] = [];
15
-
16
- for (const file of context.stagedFiles) {
17
- if (!file.includes("test") && !file.includes("spec")) continue;
18
- if (!fs.existsSync(file)) continue;
19
-
20
- const content = fs.readFileSync(file, "utf-8");
21
- const lines = content.split("\n");
22
-
23
- lines.forEach((line, index) => {
24
- for (const pattern of testPatterns) {
25
- if (line.includes(pattern)) {
26
- findings.push({
27
- severity: "error",
28
- checkName: this.name,
29
- file,
30
- line: index + 1,
31
- message: `${pattern} detected — only that test will run, hiding failures in the rest of the suite.`,
32
- suggestion: `Change ${pattern} back to ${pattern.replace(".only", "")}.`,
33
- });
34
- }
35
- }
36
- });
37
- }
38
-
39
- return findings;
40
- },
41
- };
@@ -1,45 +0,0 @@
1
- import type { Check, Finding } from "../types.js";
2
- import { normalizePath } from "./utils.js";
3
-
4
- const blockedFolders = [
5
- "node_modules/",
6
- "dist/",
7
- "build/",
8
- ".next/",
9
- "coverage/",
10
- ];
11
-
12
- function matchesBlockedFolder(normalizedPath: string, folder: string): boolean {
13
- return (
14
- normalizedPath.startsWith(folder) ||
15
- normalizedPath.includes(`/${folder}`)
16
- );
17
- }
18
-
19
- export const generatedFolderCheck: Check = {
20
- name: "generated-folder-check",
21
-
22
- async run(context) {
23
- const findings: Finding[] = [];
24
-
25
- for (const file of context.stagedFiles) {
26
- const normalized = normalizePath(file);
27
-
28
- const matchedFolder = blockedFolders.find((folder) =>
29
- matchesBlockedFolder(normalized, folder)
30
- );
31
-
32
- if (matchedFolder) {
33
- findings.push({
34
- severity: "error",
35
- checkName: this.name,
36
- file,
37
- message: `Generated folder (${matchedFolder}) — auto-built files bloat the repo and cause merge pain.`,
38
- suggestion: `Add ${matchedFolder} to .gitignore, then: git restore --staged ${file}`,
39
- });
40
- }
41
- }
42
-
43
- return findings;
44
- },
45
- };
@@ -1,40 +0,0 @@
1
- import type { Check, Finding } from "../types.js";
2
- import { getBaseName, matchesAnyPattern } from "./utils.js";
3
-
4
- const junkExactNames = new Set([
5
- ".ds_store",
6
- "thumbs.db",
7
- "desktop.ini",
8
- ]);
9
-
10
- const junkNamePatterns = [/\.swp$/i, /\.bak$/i, /\.tmp$/i, /~$/];
11
-
12
- export const junkFileCheck: Check = {
13
- name: "junk-file-check",
14
-
15
- async run(context) {
16
- const findings: Finding[] = [];
17
-
18
- for (const file of context.stagedFiles) {
19
- const baseName = getBaseName(file);
20
- const normalizedBaseName = baseName.toLowerCase();
21
-
22
- const isJunk =
23
- junkExactNames.has(normalizedBaseName) ||
24
- matchesAnyPattern(baseName, junkNamePatterns);
25
-
26
- if (isJunk) {
27
- findings.push({
28
- severity: "warning",
29
- checkName: this.name,
30
- file,
31
- message:
32
- "OS or editor junk file — adds noise and has no place in the repo.",
33
- suggestion: `Unstage it: git restore --staged ${file}`,
34
- });
35
- }
36
- }
37
-
38
- return findings;
39
- },
40
- };
@@ -1,31 +0,0 @@
1
- import fs from "node:fs";
2
- import type { Check, Finding } from "../types.js";
3
-
4
- const MAX_SIZE_MB = 5;
5
-
6
- export const largeFileCheck: Check = {
7
- name: "large-file-check",
8
-
9
- async run(context) {
10
- const findings: Finding[] = [];
11
-
12
- for (const file of context.stagedFiles) {
13
- if (!fs.existsSync(file)) continue;
14
-
15
- const stats = fs.statSync(file);
16
- const sizeMb = stats.size / 1024 / 1024;
17
-
18
- if (sizeMb > MAX_SIZE_MB) {
19
- findings.push({
20
- severity: "warning",
21
- checkName: this.name,
22
- file,
23
- message: `Large file (${sizeMb.toFixed(2)} MB) — slows clones and may hit GitHub's size limits.`,
24
- suggestion: "Remove it from Git or use Git LFS for big assets.",
25
- });
26
- }
27
- }
28
-
29
- return findings;
30
- },
31
- };
@@ -1,40 +0,0 @@
1
- import fs from "node:fs";
2
- import type { Check, Finding } from "../types.ts";
3
-
4
- const patterns = [
5
- "http://localhost",
6
- "https://localhost",
7
- "127.0.0.1",
8
- ];
9
-
10
- export const localHostCheck: Check = {
11
- name: "localhost-check",
12
-
13
- async run(context) {
14
- const findings: Finding[] = [];
15
-
16
- for (const file of context.stagedFiles) {
17
- if (!fs.existsSync(file)) continue;
18
-
19
- const content = fs.readFileSync(file, "utf-8");
20
- const lines = content.split("\n");
21
-
22
- lines.forEach((line, index) => {
23
- for (const pattern of patterns) {
24
- if (line.includes(pattern)) {
25
- findings.push({
26
- severity: "warning",
27
- checkName: this.name,
28
- file,
29
- line: index + 1,
30
- message: `Hardcoded local URL (${pattern}) — won't work in production or for teammates.`,
31
- suggestion: "Move the URL to an environment variable (e.g. process.env.API_URL).",
32
- });
33
- }
34
- }
35
- });
36
- }
37
-
38
- return findings;
39
- },
40
- };
@@ -1,40 +0,0 @@
1
- import type { Check, Finding } from "../types.js";
2
- import { normalizePath } from "./utils.js";
3
-
4
- function isStaged(stagedFiles: string[], target: string): boolean {
5
- return stagedFiles.some((file) => normalizePath(file) === target);
6
- }
7
-
8
- export const lockfileDriftCheck: Check = {
9
- name: "lockfile-drift-check",
10
-
11
- async run(context) {
12
- const findings: Finding[] = [];
13
- const packageJsonStaged = isStaged(context.stagedFiles, "package.json");
14
- const lockfileStaged = isStaged(context.stagedFiles, "package-lock.json");
15
-
16
- if (packageJsonStaged && !lockfileStaged) {
17
- findings.push({
18
- severity: "warning",
19
- checkName: this.name,
20
- file: "package.json",
21
- message:
22
- "package.json changed without its lockfile — teammates may get different dependency versions.",
23
- suggestion: "Run npm install, then stage package-lock.json.",
24
- });
25
- }
26
-
27
- if (lockfileStaged && !packageJsonStaged) {
28
- findings.push({
29
- severity: "warning",
30
- checkName: this.name,
31
- file: "package-lock.json",
32
- message:
33
- "Lockfile changed without package.json — the lockfile may not match your declared dependencies.",
34
- suggestion: "Stage package.json too, or unstage package-lock.json.",
35
- });
36
- }
37
-
38
- return findings;
39
- },
40
- };
@@ -1,41 +0,0 @@
1
- import type { Check, Finding } from "../types.js";
2
- import { readFileLines, shouldSkipContentScan } from "./utils.js";
3
-
4
- const conflictMarkers = ["<<<<<<<", "=======", ">>>>>>>"];
5
-
6
- export const mergeConflictCheck: Check = {
7
- name: "merge-conflict-check",
8
-
9
- async run(context) {
10
- const findings: Finding[] = [];
11
-
12
- for (const file of context.stagedFiles) {
13
- if (shouldSkipContentScan(file)) continue;
14
-
15
- const lines = readFileLines(file);
16
- if (!lines) continue;
17
-
18
- lines.forEach((line, index) => {
19
- const trimmed = line.trim();
20
-
21
- for (const marker of conflictMarkers) {
22
- if (trimmed.startsWith(marker)) {
23
- findings.push({
24
- severity: "error",
25
- checkName: this.name,
26
- file,
27
- line: index + 1,
28
- message:
29
- "Unresolved merge conflict — this file won't run correctly until fixed.",
30
- suggestion:
31
- "Resolve the conflict, remove the <<<<<<< / ======= / >>>>>>> markers, then restage.",
32
- });
33
- return;
34
- }
35
- }
36
- });
37
- }
38
-
39
- return findings;
40
- },
41
- };
@@ -1,60 +0,0 @@
1
- import type { Check, Finding } from "../types.js";
2
- import {
3
- isCommentLine,
4
- isPlaceholderValue,
5
- readFileLines,
6
- shouldSkipContentScan,
7
- } from "./utils.js";
8
-
9
- const secretRules: { pattern: RegExp; label: string }[] = [
10
- { pattern: /OPENAI_API_KEY\s*=/i, label: "OpenAI API key" },
11
- { pattern: /DATABASE_URL\s*=/i, label: "database connection string" },
12
- { pattern: /JWT_SECRET\s*=/i, label: "JWT secret" },
13
- { pattern: /AUTH_SECRET\s*=/i, label: "auth secret" },
14
- { pattern: /PRIVATE_KEY\s*=/i, label: "private key" },
15
- { pattern: /SECRET_KEY\s*=/i, label: "secret key" },
16
- { pattern: /sk-[A-Za-z0-9_-]{20,}/, label: "API key" },
17
- { pattern: /ghp_[A-Za-z0-9]{36,}/, label: "GitHub personal access token" },
18
- { pattern: /github_pat_[A-Za-z0-9_]+/, label: "GitHub personal access token" },
19
- { pattern: /AKIA[0-9A-Z]{16}/, label: "AWS access key" },
20
- { pattern: /sk_live_[A-Za-z0-9]+/, label: "Stripe live secret key" },
21
- { pattern: /AIza[0-9A-Za-z_-]{35}/, label: "Google API key" },
22
- { pattern: /hooks\.slack\.com\/services\//, label: "Slack webhook URL" },
23
- { pattern: /password\s*=\s*['"][^'"]{8,}['"]/i, label: "hardcoded password" },
24
- ];
25
-
26
- export const secretCheck: Check = {
27
- name: "secret-check",
28
-
29
- async run(context) {
30
- const findings: Finding[] = [];
31
-
32
- for (const file of context.stagedFiles) {
33
- if (shouldSkipContentScan(file)) continue;
34
-
35
- const lines = readFileLines(file);
36
- if (!lines) continue;
37
-
38
- lines.forEach((line, index) => {
39
- if (isCommentLine(line) || isPlaceholderValue(line)) return;
40
-
41
- for (const rule of secretRules) {
42
- if (rule.pattern.test(line)) {
43
- findings.push({
44
- severity: "error",
45
- checkName: this.name,
46
- file,
47
- line: index + 1,
48
- message: `Possible ${rule.label} — credentials pushed to GitHub can be scraped instantly.`,
49
- suggestion:
50
- "Remove it from the code, unstage the file, and rotate the credential if it was ever pushed.",
51
- });
52
- return;
53
- }
54
- }
55
- });
56
- }
57
-
58
- return findings;
59
- },
60
- };
@@ -1,51 +0,0 @@
1
- import type { Check, Finding } from "../types.js";
2
- import { getBaseName, matchesAnyPattern, normalizePath } from "./utils.js";
3
-
4
- const sensitiveExactNames = new Set([
5
- "id_rsa",
6
- "id_ed25519",
7
- "credentials.json",
8
- "serviceaccountkey.json",
9
- ".npmrc",
10
- ".pypirc",
11
- ]);
12
-
13
- const sensitiveNamePatterns = [
14
- /\.pem$/i,
15
- /\.p12$/i,
16
- /\.key$/i,
17
- /^firebase-adminsdk.*\.json$/i,
18
- ];
19
-
20
- export const sensitiveFilenameCheck: Check = {
21
- name: "sensitive-filename-check",
22
-
23
- async run(context) {
24
- const findings: Finding[] = [];
25
-
26
- for (const file of context.stagedFiles) {
27
- const baseName = getBaseName(file);
28
- const normalizedBaseName = baseName.toLowerCase();
29
- const normalizedPath = normalizePath(file).toLowerCase();
30
-
31
- const isSensitive =
32
- sensitiveExactNames.has(normalizedBaseName) ||
33
- matchesAnyPattern(baseName, sensitiveNamePatterns) ||
34
- normalizedPath.endsWith("/credentials.json") ||
35
- normalizedPath.includes("serviceaccountkey.json");
36
-
37
- if (isSensitive) {
38
- findings.push({
39
- severity: "error",
40
- checkName: this.name,
41
- file,
42
- message:
43
- "Credential or key file detected — private keys and auth config should not be in Git.",
44
- suggestion: `Unstage it: git restore --staged ${file}`,
45
- });
46
- }
47
- }
48
-
49
- return findings;
50
- },
51
- };
@@ -1,62 +0,0 @@
1
- import fs from "node:fs";
2
-
3
- const CODE_EXTENSIONS = [".js", ".jsx", ".ts", ".tsx"];
4
-
5
- const SKIP_CONTENT_EXTENSIONS = [".md", ".example", ".sample"];
6
-
7
- const PLACEHOLDER_PATTERNS = [
8
- /your-api-key/i,
9
- /changeme/i,
10
- /xxx+/i,
11
- /TODO/i,
12
- /placeholder/i,
13
- /example/i,
14
- /replace-me/i,
15
- ];
16
-
17
- export function normalizePath(file: string): string {
18
- return file.replaceAll("\\", "/");
19
- }
20
-
21
- export function getBaseName(file: string): string {
22
- return normalizePath(file).split("/").pop() ?? "";
23
- }
24
-
25
- export function isCodeFile(file: string): boolean {
26
- return CODE_EXTENSIONS.some((ext) => file.endsWith(ext));
27
- }
28
-
29
- export function shouldSkipContentScan(file: string): boolean {
30
- return SKIP_CONTENT_EXTENSIONS.some((ext) => file.endsWith(ext));
31
- }
32
-
33
- export function readFileLines(file: string): string[] | null {
34
- if (!fs.existsSync(file)) return null;
35
-
36
- try {
37
- return fs.readFileSync(file, "utf-8").split("\n");
38
- } catch {
39
- return null;
40
- }
41
- }
42
-
43
- export function isCommentLine(line: string): boolean {
44
- const trimmed = line.trim();
45
- return (
46
- trimmed.startsWith("//") ||
47
- trimmed.startsWith("#") ||
48
- trimmed.startsWith("*") ||
49
- trimmed.startsWith("/*")
50
- );
51
- }
52
-
53
- export function isPlaceholderValue(line: string): boolean {
54
- return PLACEHOLDER_PATTERNS.some((pattern) => pattern.test(line));
55
- }
56
-
57
- export function matchesAnyPattern(
58
- value: string,
59
- patterns: RegExp[]
60
- ): boolean {
61
- return patterns.some((pattern) => pattern.test(value));
62
- }
@@ -1,74 +0,0 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import { isCodeFile } from "../checks/utils.js";
4
- import { getStagedFiles } from "../git.js";
5
- import type { WipFixOptions } from "../types.js";
6
-
7
- const CONSOLE_LOG_LINE = /^\s*console\.log\([^)]*\);?\s*$/;
8
- const DEBUGGER_LINE = /^\s*debugger;?\s*$/;
9
-
10
- function stripDebugLines(
11
- content: string,
12
- options: { removeConsoleLog: boolean; removeDebugger: boolean }
13
- ): { updated: string; removed: number } {
14
- const lines = content.split("\n");
15
- const kept: string[] = [];
16
- let removed = 0;
17
-
18
- for (const line of lines) {
19
- const isConsoleLog = CONSOLE_LOG_LINE.test(line);
20
- const isDebugger = DEBUGGER_LINE.test(line);
21
-
22
- if (
23
- (options.removeConsoleLog && isConsoleLog) ||
24
- (options.removeDebugger && isDebugger)
25
- ) {
26
- removed += 1;
27
- continue;
28
- }
29
- kept.push(line);
30
- }
31
-
32
- return { updated: kept.join("\n"), removed };
33
- }
34
-
35
- export function fixDebugCode(
36
- cwd = process.cwd(),
37
- options: WipFixOptions = {}
38
- ): string[] {
39
- const removeConsoleLog = Boolean(options.fixConsoleLog);
40
- const removeDebugger = true;
41
-
42
- if (!removeConsoleLog && !removeDebugger) {
43
- return [];
44
- }
45
-
46
- let stagedFiles: string[];
47
-
48
- try {
49
- stagedFiles = getStagedFiles();
50
- } catch {
51
- return [];
52
- }
53
-
54
- const fixed: string[] = [];
55
-
56
- for (const file of stagedFiles) {
57
- if (!isCodeFile(file)) continue;
58
-
59
- const absolutePath = path.resolve(cwd, file);
60
- if (!fs.existsSync(absolutePath)) continue;
61
-
62
- const content = fs.readFileSync(absolutePath, "utf-8");
63
- const { updated, removed } = stripDebugLines(content, {
64
- removeConsoleLog,
65
- removeDebugger,
66
- });
67
- if (removed === 0) continue;
68
-
69
- fs.writeFileSync(absolutePath, updated, "utf-8");
70
- fixed.push(file);
71
- }
72
-
73
- return fixed;
74
- }
@@ -1,26 +0,0 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import { walkCodeFiles } from "./utils.js";
4
-
5
- const FOCUSED_TEST_PATTERN = /\b(test|it|describe)\.only\b/g;
6
-
7
- function isTestFile(filePath: string): boolean {
8
- return filePath.includes("test") || filePath.includes("spec");
9
- }
10
-
11
- export function fixFocusedTests(cwd = process.cwd()): string[] {
12
- const files = walkCodeFiles(cwd, isTestFile);
13
- const fixed: string[] = [];
14
-
15
- for (const absolutePath of files) {
16
- const content = fs.readFileSync(absolutePath, "utf-8");
17
- if (!FOCUSED_TEST_PATTERN.test(content)) continue;
18
-
19
- FOCUSED_TEST_PATTERN.lastIndex = 0;
20
- const updated = content.replace(FOCUSED_TEST_PATTERN, "$1");
21
- fs.writeFileSync(absolutePath, updated, "utf-8");
22
- fixed.push(path.relative(cwd, absolutePath));
23
- }
24
-
25
- return fixed;
26
- }
@@ -1,108 +0,0 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
-
4
- const GITIGNORE_PATH = ".gitignore";
5
-
6
- type GitignoreEntry = {
7
- line: string;
8
- isPresent: (lines: string[]) => boolean;
9
- };
10
-
11
- const entries: GitignoreEntry[] = [
12
- {
13
- line: ".env",
14
- isPresent: (lines) =>
15
- lines.some((line) => line === ".env" || line.startsWith(".env")),
16
- },
17
- {
18
- line: "node_modules/",
19
- isPresent: (lines) =>
20
- lines.some(
21
- (line) => line === "node_modules" || line === "node_modules/"
22
- ),
23
- },
24
- {
25
- line: "dist/",
26
- isPresent: (lines) =>
27
- lines.some((line) => line === "dist" || line === "dist/"),
28
- },
29
- {
30
- line: "build/",
31
- isPresent: (lines) =>
32
- lines.some((line) => line === "build" || line === "build/"),
33
- },
34
- {
35
- line: ".next/",
36
- isPresent: (lines) =>
37
- lines.some((line) => line === ".next" || line === ".next/"),
38
- },
39
- {
40
- line: "coverage/",
41
- isPresent: (lines) =>
42
- lines.some((line) => line === "coverage" || line === "coverage/"),
43
- },
44
- {
45
- line: ".DS_Store",
46
- isPresent: (lines) =>
47
- lines.some((line) => line.toLowerCase() === ".ds_store"),
48
- },
49
- {
50
- line: "Thumbs.db",
51
- isPresent: (lines) =>
52
- lines.some((line) => line.toLowerCase() === "thumbs.db"),
53
- },
54
- {
55
- line: "desktop.ini",
56
- isPresent: (lines) =>
57
- lines.some((line) => line.toLowerCase() === "desktop.ini"),
58
- },
59
- {
60
- line: "*.swp",
61
- isPresent: (lines) => lines.includes("*.swp"),
62
- },
63
- {
64
- line: "*.bak",
65
- isPresent: (lines) => lines.includes("*.bak"),
66
- },
67
- {
68
- line: "*.tmp",
69
- isPresent: (lines) => lines.includes("*.tmp"),
70
- },
71
- ];
72
-
73
- function parseGitignoreLines(content: string): string[] {
74
- return content
75
- .split("\n")
76
- .map((line) => line.trim())
77
- .filter((line) => line.length > 0 && !line.startsWith("#"));
78
- }
79
-
80
- export function fixGitignore(cwd = process.cwd()): string[] {
81
- const filePath = path.join(cwd, GITIGNORE_PATH);
82
- const applied: string[] = [];
83
-
84
- const existingContent = fs.existsSync(filePath)
85
- ? fs.readFileSync(filePath, "utf-8")
86
- : "";
87
- const existingLines = parseGitignoreLines(existingContent);
88
-
89
- const missing = entries.filter((entry) => !entry.isPresent(existingLines));
90
- if (missing.length === 0) {
91
- return applied;
92
- }
93
-
94
- const additions = missing.map((entry) => entry.line);
95
- const needsLeadingNewline =
96
- existingContent.length > 0 && !existingContent.endsWith("\n");
97
- const block =
98
- (needsLeadingNewline ? "\n" : "") +
99
- (existingContent.length > 0 ? "\n" : "") +
100
- "# Added by commit-cop wip-fix\n" +
101
- additions.join("\n") +
102
- "\n";
103
-
104
- fs.writeFileSync(filePath, existingContent + block, "utf-8");
105
- applied.push(...additions);
106
-
107
- return applied;
108
- }
@@ -1,16 +0,0 @@
1
- import fs from "node:fs";
2
- import { isJunkFile } from "./matchers.js";
3
- import { walkRepo } from "./utils.js";
4
-
5
- export function fixJunkFiles(cwd = process.cwd()): string[] {
6
- const removed: string[] = [];
7
-
8
- walkRepo(cwd, (absolutePath, relativePath) => {
9
- if (!isJunkFile(relativePath)) return;
10
-
11
- fs.unlinkSync(absolutePath);
12
- removed.push(relativePath);
13
- });
14
-
15
- return removed;
16
- }