Skip to content

Add max-params checker and aggregate checks runner; wire into lint pipeline#2445

Open
andrew-bierman wants to merge 1 commit into
developmentfrom
codex/update-lint-rules-for-max-params-zcdi3i
Open

Add max-params checker and aggregate checks runner; wire into lint pipeline#2445
andrew-bierman wants to merge 1 commit into
developmentfrom
codex/update-lint-rules-for-max-params-zcdi3i

Conversation

@andrew-bierman
Copy link
Copy Markdown
Collaborator

@andrew-bierman andrew-bierman commented May 18, 2026

Motivation

  • Enforce a repository policy preferring a single typed object parameter by detecting functions with more than one parameter.
  • Provide a single entrypoint to run all custom checks sequentially so CI and local tooling can run them consistently.
  • Integrate the new check into the existing lint pipeline so lint:custom includes the new checks.

Description

  • Add packages/checks/src/check-max-params.ts, a TypeScript script that scans apps and packages for *.ts/*.tsx files (excluding tests, build dirs, and node_modules) and reports functions with more than one parameter while allowing exceptions via a leading @allow-multi-param comment, exiting non-zero on violations.
  • Add packages/checks/src/check-all.ts, a small runner that executes the individual check scripts (check:casts, check:magic-strings, check:max-params) and exits non-zero if any check fails.
  • Update packages/checks/package.json to include check:max-params and a check script that runs the aggregated checks, and update the repo root package.json to add check:max-params, a check:custom shortcut wired to packages/checks, and include bun run check:custom in lint:custom so the checks run with the lint task.

Testing

  • No automated tests were run as part of this change.

Codex Task

Summary by CodeRabbit

  • New Features

    • Added static code quality checks for TypeScript files, including parameter count validation.
    • Implemented unified check runner to execute all code quality checks together.
  • Chores

    • Updated custom lint scripts to include new quality checks.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 18, 2026

Walkthrough

This PR introduces a multi-parameter function checker that scans the codebase for functions with more than one parameter and enforces a single-parameter (typed object) pattern. The check is wired into the root lint:custom script via a new unified check orchestrator.

Changes

Max-params check system

Layer / File(s) Summary
Check script wiring
package.json, packages/checks/package.json
Root lint:custom script is extended to invoke check:custom. New scripts check:max-params and check:custom are added to root, and check:max-params and check scripts are added to packages/checks, establishing the entry points for the new check.
Check orchestration runner
packages/checks/src/check-all.ts
A new Bun entrypoint synchronously spawns a fixed array of check commands (type casts, magic strings, max params), logs per-check pass/fail status, and exits with code 1 if any check fails.
Max-params check implementation
packages/checks/src/check-max-params.ts
Scans apps/ and packages/ directories using the TypeScript compiler API to traverse function-like AST nodes, filters by extension (.ts/.tsx/.mts/.cts) and excluded patterns (tests, __tests__, node_modules, dist, build), collects violations for functions with >1 parameter while respecting @allow-multi-param annotations and call-expression callback exclusions, and outputs grouped violations or a success message.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • PackRat-AI/PackRat#2430: Adds a different custom check (check-drizzle-migrations) to the root lint:custom pipeline, establishing a pattern for integrating additional checks into the lint workflow.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: adding a max-params checker, creating an aggregate checks runner, and integrating them into the lint pipeline.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/update-lint-rules-for-max-params-zcdi3i
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch codex/update-lint-rules-for-max-params-zcdi3i

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@andrew-bierman andrew-bierman marked this pull request as ready for review May 18, 2026 06:27
Copilot AI review requested due to automatic review settings May 18, 2026 06:27
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new check-max-params script that flags any function with more than one parameter (with an @allow-multi-param opt-out), plus a check-all aggregator that runs all custom checks, and wires the aggregator into lint:custom.

Changes:

  • New packages/checks/src/check-max-params.ts walking apps/ and packages/ for .ts(x) files and exiting non-zero on multi-param functions.
  • New packages/checks/src/check-all.ts runner that sequentially shells out to check:casts, check:magic-strings, and check:max-params.
  • Adds check, check:max-params, and check:custom scripts, and appends bun run check:custom to the root lint:custom.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
packages/checks/src/check-max-params.ts New AST-based scanner that uses ts.isFunctionLike and reports functions with parameters.length > 1.
packages/checks/src/check-all.ts Sequential runner over the three individual check scripts via Bun.spawnSync.
packages/checks/package.json Registers check:max-params and aggregate check.
package.json Adds check:max-params / check:custom (appended out of alphabetical order) and chains check:custom into lint:custom.
Comments suppressed due to low confidence (1)

packages/checks/src/check-max-params.ts:87

  • ts.isFunctionLike returns true for type-only constructs such as FunctionType, MethodSignature, CallSignature, ConstructSignature, and IndexSignature nodes. That means TypeScript type/interface declarations like type Handler = (req: Request, res: Response) => void or interface Foo { bar(a: number, b: number): void } will be reported as violations even though they are purely type-level and cannot be rewritten as a single object parameter without changing the public type contract. Consider narrowing the check to actual implementation nodes (e.g., FunctionDeclaration, FunctionExpression, ArrowFunction, MethodDeclaration, ConstructorDeclaration, GetAccessor/SetAccessor) and skipping signature-only nodes.
    if (
      ts.isFunctionLike(node) &&
      node.parameters.length > 1 &&
      !hasAllowAnnotation(source, node) &&
      !isCallbackForInvocation(node)
    ) {

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread package.json
"lefthook": "lefthook install",
"lint": "biome check --write",
"lint:custom": "bun run scripts/lint/no-raw-typeof.ts && bun run scripts/lint/no-raw-regex.ts && bun run packages/env/scripts/no-raw-process-env.ts && bun run scripts/lint/no-duplicate-guards.ts && bun run scripts/lint/no-unauth-routes.ts",
"lint:custom": "bun run scripts/lint/no-raw-typeof.ts && bun run scripts/lint/no-raw-regex.ts && bun run packages/env/scripts/no-raw-process-env.ts && bun run scripts/lint/no-duplicate-guards.ts && bun run scripts/lint/no-unauth-routes.ts && bun run check:custom",
Comment thread package.json
Comment on lines +52 to +54
"web": "bun run --cwd apps/web dev",
"check:max-params": "bun run --cwd packages/checks check:max-params",
"check:custom": "bun run --cwd packages/checks check"
Comment on lines +59 to +63
function isCallbackForInvocation(node: ts.Node): boolean {
const parent = node.parent;
if (!parent || !ts.isCallExpression(parent)) return false;
return parent.arguments.some((arg) => arg === node);
}
Comment on lines +3 to +14
const checks = [
{ name: 'check:casts', cmd: ['bun', './check-type-casts.ts'] },
{ name: 'check:magic-strings', cmd: ['bun', './check-magic-strings.ts'] },
{ name: 'check:max-params', cmd: ['bun', './check-max-params.ts'] },
];

let hasFailure = false;

for (const check of checks) {
console.log(`\n▶ Running ${check.name}`);
const proc = Bun.spawnSync(check.cmd, {
cwd: import.meta.dir,
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/checks/src/check-max-params.ts`:
- Around line 21-32: The exclusion regexes in EXCLUDED_FILE_PATTERNS will fail
on Windows because they expect forward slashes; update isTargetFile to normalize
incoming file paths (e.g., use path.normalize or replace all backslashes with
'/') and convert path separators to forward slashes before testing against
EXCLUDED_FILE_PATTERNS so patterns like /\/node_modules\// match on Windows as
well.
- Around line 76-105: In collectViolations replace the brittle string replace of
filePath (filePath.replace(`${ROOT}/`, '')) with a robust relative path using
path.relative(ROOT, filePath); ensure you import/require path if not present and
use that value when setting the violation.file property so ROOT, filePath and
collectViolations are referenced and path normalization is handled
cross-platform.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f39c0ec8-29d3-422d-a261-23813d1b90a4

📥 Commits

Reviewing files that changed from the base of the PR and between c66e287 and b46d002.

📒 Files selected for processing (4)
  • package.json
  • packages/checks/package.json
  • packages/checks/src/check-all.ts
  • packages/checks/src/check-max-params.ts

Comment on lines +21 to +32
const EXCLUDED_FILE_PATTERNS = [
/\.test\./,
/\.spec\./,
/\.stories\./,
/\.d\.ts$/,
/\/__tests__\//,
/\/test\//,
/\/tests\//,
/\/node_modules\//,
/\/dist\//,
/\/build\//,
];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Path patterns may not work correctly on Windows.

The regex patterns use forward slashes (e.g., /\/node_modules\//), but on Windows, file paths use backslashes. This could cause the exclusion patterns to fail, leading to unnecessary scanning or false positives. Normalize paths to use forward slashes before testing against these patterns.

🔧 Suggested fix

Update isTargetFile to normalize the path separator:

 function isTargetFile(filePath: string): boolean {
   if (!TARGET_EXTENSIONS.has(extname(filePath))) return false;
-  return !EXCLUDED_FILE_PATTERNS.some((p) => p.test(filePath));
+  const normalized = filePath.replace(/\\/g, '/');
+  return !EXCLUDED_FILE_PATTERNS.some((p) => p.test(normalized));
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/checks/src/check-max-params.ts` around lines 21 - 32, The exclusion
regexes in EXCLUDED_FILE_PATTERNS will fail on Windows because they expect
forward slashes; update isTargetFile to normalize incoming file paths (e.g., use
path.normalize or replace all backslashes with '/') and convert path separators
to forward slashes before testing against EXCLUDED_FILE_PATTERNS so patterns
like /\/node_modules\// match on Windows as well.

Comment on lines +76 to +105
function collectViolations(filePath: string): Violation[] {
const code = readFileSync(filePath, 'utf8');
const source = ts.createSourceFile(filePath, code, ts.ScriptTarget.Latest, true);
const violations: Violation[] = [];

const visit = (node: ts.Node): void => {
if (
ts.isFunctionLike(node) &&
node.parameters.length > 1 &&
!hasAllowAnnotation(source, node) &&
!isCallbackForInvocation(node)
) {
const start = source.getLineAndCharacterOfPosition(node.getStart());
const end = Math.min(node.getEnd(), node.getStart() + 120);
const snippet = source.getText().slice(node.getStart(), end).replace(/\s+/g, ' ').trim();
violations.push({
file: filePath.replace(`${ROOT}/`, ''),
line: start.line + 1,
col: start.character + 1,
params: node.parameters.length,
kind: getKindLabel(node),
snippet,
});
}
ts.forEachChild(node, visit);
};

visit(source);
return violations;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Use path.relative for more robust path handling.

Line 92 uses string replacement to make paths relative (filePath.replace(\${ROOT}/`, '')). This approach can fail if there are path normalization differences (e.g., symbolic links, double slashes, or case differences on case-insensitive filesystems). Use path.relative(ROOT, filePath)` instead for robust, cross-platform path relativization.

♻️ Refactor with path.relative
+import { extname, join, relative } from 'node:path';
 
 function collectViolations(filePath: string): Violation[] {
   // ...
   violations.push({
-    file: filePath.replace(`${ROOT}/`, ''),
+    file: relative(ROOT, filePath),
     line: start.line + 1,
     col: start.character + 1,
     params: node.parameters.length,
     kind: getKindLabel(node),
     snippet,
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/checks/src/check-max-params.ts` around lines 76 - 105, In
collectViolations replace the brittle string replace of filePath
(filePath.replace(`${ROOT}/`, '')) with a robust relative path using
path.relative(ROOT, filePath); ensure you import/require path if not present and
use that value when setting the violation.file property so ROOT, filePath and
collectViolations are referenced and path normalization is handled
cross-platform.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants