Skip to content

eslint-factory: extend no-json-stringify-error to cover .then(_, onRejected) and pin scope boundaries#43724

Merged
pelikhan merged 6 commits into
mainfrom
copilot/eslint-factory-fix-no-json-stringify-error
Jul 6, 2026
Merged

eslint-factory: extend no-json-stringify-error to cover .then(_, onRejected) and pin scope boundaries#43724
pelikhan merged 6 commits into
mainfrom
copilot/eslint-factory-fix-no-json-stringify-error

Conversation

Copilot AI commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

The no-json-stringify-error rule silently skipped JSON.stringify(err) inside .then(onFulfilled, onRejected) rejection handlers — semantically identical to .catch(err => ...) but undetected. The named-reference exclusion (p.catch(handler)) also lacked a pinning test and was undocumented.

Rule change

  • isCatchCallback: now also returns true for the second argument of .then() calls (the rejection handler), in addition to the first argument of .catch().
// now flagged — previously silent
p.then(onFulfilled, err => core.error(JSON.stringify(err)));
p.then(null, function(err) { core.error(JSON.stringify(err)); });

// still not flagged — named references remain out of scope
p.catch(handler);
p.then(ok, handler);
  • docs.description: updated to enumerate the three detected scopes (try/catch, .catch(fn), .then(_, fn)) and state explicitly that named-reference handlers are out of scope because cross-file static analysis is required.

Tests (5 new)

Case Result
.then(ok, err => JSON.stringify(err)) arrow inline flagged
.then(null, function(err) { JSON.stringify(err); }) function inline flagged
.then() first argument (onFulfilled) not flagged
p.catch(handler) named reference not flagged
p.then(ok, handler) named-reference rejection handler not flagged

README

Added ### no-json-stringify-error section to eslint-factory/README.md (rule was previously undocumented alongside prefer-number-isnan).


pr-sous-chef run: https://github.com/github/gh-aw/actions/runs/28776499241

Generated by 👨‍🍳 PR Sous Chef · 7.38 AIC · ⌖ 10.2 AIC · ⊞ 5.6K ·
Comment /souschef to run again

Copilot AI and others added 2 commits July 6, 2026 07:09
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…, onRejected) and pin scope boundaries

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix no-json-stringify-error to recognize rejection handlers eslint-factory: extend no-json-stringify-error to cover .then(_, onRejected) and pin scope boundaries Jul 6, 2026
Copilot AI requested a review from pelikhan July 6, 2026 07:19
@pelikhan pelikhan marked this pull request as ready for review July 6, 2026 07:23
Copilot AI review requested due to automatic review settings July 6, 2026 07:23
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR #43724 does not have the 'implementation' label and has 0 new lines of code in business logic directories (threshold: 100).

Copilot AI left a comment

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.

Pull request overview

Extends the eslint-factory no-json-stringify-error rule so it also detects JSON.stringify(err) inside inline Promise rejection handlers passed as the second argument to .then(onFulfilled, onRejected), and documents/tests the intended scope boundaries (inline handlers only; named references out of scope). The PR also includes widespread .lock.yml churn that appears to be mechanical (YAML block chomping / trailing-newline normalization).

Changes:

  • Update rule scope detection to treat .then(_, onRejected) inline callbacks as “catch-like” error scopes.
  • Add new RuleTester cases covering .then(_, onRejected) (arrow + function) and verifying named-reference handlers remain unflagged.
  • Document the rule in eslint-factory/README.md and update the rule’s docs.description; regenerate multiple workflow .lock.yml files.
Show a summary per file
File Description
eslint-factory/src/rules/no-json-stringify-error.ts Extends catch-scope detection to include .then(_, onRejected) inline rejection handlers; updates rule docs text.
eslint-factory/src/rules/no-json-stringify-error.test.ts Adds test cases for .then(_, onRejected) detection and named-reference exclusions.
eslint-factory/README.md Documents no-json-stringify-error rule behavior/scope alongside existing rules.
.github/workflows/workflow-skill-extractor.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in embedded script.
.github/workflows/workflow-health-manager.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/weekly-blog-post-writer.lock.yml Mechanical .lock.yml regeneration (`run:
.github/workflows/unbloat-docs.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/uk-ai-operational-resilience.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/test-quality-sentinel.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/step-name-alignment.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/static-analysis-report.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/stale-repo-identifier.lock.yml Mechanical .lock.yml regeneration (`run:
.github/workflows/spec-extractor.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/schema-consistency-checker.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/repository-quality-improver.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/release.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/prompt-clustering-analysis.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/pr-description-caveman.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/objective-impact-report.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in embedded script.
.github/workflows/mergefest.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/mattpocock-skills-reviewer.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/linter-miner.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/lint-monster.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/issue-arborist.lock.yml Mechanical .lock.yml regeneration (`run:
.github/workflows/impeccable-skills-reviewer.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/gpclean.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/glossary-maintainer.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/eslint-monster.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/docs-noob-tester.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/designer-drift-audit.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/design-decision-gate.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/dependabot-burner.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in embedded script.
.github/workflows/delight.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/dataflow-pr-discussion-dataset.lock.yml Mechanical .lock.yml regeneration (`run:
.github/workflows/daily-spdd-spec-planner.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/daily-security-observability.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/daily-multi-device-docs-tester.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/daily-community-attribution.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/daily-ambient-context-optimizer.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in embedded script.
.github/workflows/copilot-pr-nlp-analysis.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/copilot-centralization-drilldown.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/contribution-check.lock.yml Mechanical .lock.yml regeneration (`run:
.github/workflows/code-simplifier.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/cli-consistency-checker.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/ci-doctor.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/aw-failure-investigator.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/auto-triage-issues.lock.yml Mechanical .lock.yml regeneration (`run:
.github/workflows/architecture-guardian.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/agentic-token-trend-audit.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/agentic-token-optimizer.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.
.github/workflows/agentic-token-audit.lock.yml Mechanical .lock.yml regeneration / newline-chomping normalization in run: scripts.

Review details

Tip

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

  • Files reviewed: 51/51 changed files
  • Comments generated: 2
  • Review effort level: Low

Comment on lines +14 to +15
* - Second argument of `.then(onFulfilled, onRejected)` — semantically
* equivalent to `.catch(onRejected)`.
Comment on lines +17 to +19
* Named-reference handlers (for example `p.catch(handler)`) are intentionally
* out of scope because the rule cannot statically follow the reference to
* inspect its parameter list without cross-file analysis.
@github-actions github-actions Bot mentioned this pull request Jul 6, 2026

@github-actions github-actions Bot left a comment

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.

Review: no-json-stringify-error — .then(_, onRejected) extension

The implementation is correct and well-scoped.

isCatchCallback logic (lines 21–33):

  • .catch(fn) at argument index 0 ✓
  • .then(_, fn) at argument index 1 ✓
  • Computed member access excluded (callee.computed guard) ✓
  • Named references excluded by AST node type — only ArrowFunctionExpression / FunctionExpression nodes reach enterFunction, so p.then(ok, handler) where handler is an Identifier is never matched ✓

Sentinel stack discipline remains intact — non-rejection-handler functions still push a sentinel, blocking outer catch-var names from leaking into unrelated scopes.

Tests (5 new cases):

  • Both arrow and function-expression inline handlers flagged ✓
  • p.then(result => ...) first argument not flagged ✓
  • Named-reference variants for both .catch and .then pinned as valid ✓

README correctly enumerates all three detected scopes and states the named-reference out-of-scope boundary.

No blocking issues found.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · 46.2 AIC · ⌖ 7.41 AIC · ⊞ 4.9K

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 82/100 — Excellent

Analyzed 18 test(s): 18 design, 0 implementation, 0 violation(s).

📊 Metrics (18 tests)
Metric Value
Analyzed 18 (Go: 0, JS/TS: 18)
✅ Design 18 (100%)
⚠️ Implementation 0 (0%)
Edge/error coverage 13 (72%)
Duplicate clusters 0
Inflation YES — 3.45:1 (69 test / 20 prod added lines; expected RuleTester boilerplate)
🚨 Violations 0
Test Classification Issues
valid: non-caught variable not flagged design_test None
valid: non-error var inside catch not flagged design_test None
valid: explicit error properties not flagged design_test None
valid: shadow outer scope not flagged outside catch design_test None — scope invariant, edge case
valid: promise .catch() non-error param not flagged design_test None
valid: bare catch {} without binding not flagged design_test None — edge case
invalid: JSON.stringify(err) in catch flagged design_test None — core behavioral contract
invalid: JSON.stringify(error, null, 2) in catch flagged design_test None — multi-arg edge case
invalid: .catch() arrow callback flagged design_test None
invalid: .catch() function callback flagged design_test Arrow/function pairing intentional (distinct AST nodes)
invalid: nested catch tracked independently design_test None — scope invariant
valid: named-reference .catch(handler) not flagged design_test None — documents scope limitation
valid: named-reference .then(ok, handler) not flagged design_test None
invalid: .then() rejection handler arrow flagged design_test None — new feature coverage
invalid: .then() rejection handler function flagged design_test Arrow/function pairing intentional
valid: .then() first arg not treated as rejection design_test None — scope boundary edge case
invalid: works with ES module syntax design_test None — compatibility coverage
invalid: inner catch variable flagged independently design_test None — nested scope edge case
📝 Notes on inflation flag

Inflation (3.45:1): The 69:20 added-line ratio exceeds the 2:1 threshold mechanically, but this is structurally expected for ESLint RuleTester-based tests — every invalid case requires errors[], suggestions[], and output boilerplate. Not a quality concern.

Arrow/function pairs: Testing both ArrowFunctionExpression and FunctionExpression is necessary; they are distinct AST node types handled by separate visitors. Not a duplicate cluster.

Verdict

Passed. 0% implementation tests (threshold: 30%). No violations. The suite provides strong behavioral coverage for the new .then(_, onRejected) rejection-handler feature and scope-boundary pinning changes.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • awmgmcpg

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "awmgmcpg"

See Network Configuration for more information.

🧪 Test quality analysis by Test Quality Sentinel · 59.9 AIC · ⌖ 9.74 AIC · ⊞ 6.8K ·
Comment /review to run again

@github-actions github-actions Bot left a comment

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.

Test Quality Sentinel: 82/100. 0% implementation tests (threshold: 30%). No violations - all 18 tests are design tests covering behavioral contracts for the new .then(_, onRejected) rejection-handler feature and scope-boundary pinning.

@github-actions github-actions Bot left a comment

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.

Skills-Based Review 🧠

Applied /tdd and /grill-with-docs — solid change with a few targeted improvements available.

📋 Key Themes & Highlights

Key Themes

  • Naming drift: isCatchCallback now covers .then() rejection handlers too; the name no longer matches the semantics and is a future maintenance hazard.
  • Test coverage gap: The two new invalid tests only exercise CJS mode. The existing .catch() and try/catch invalid cases also have ESM counterparts — the new .then() cases should too.
  • False-positive boundary: The onFulfilled test uses a result parameter. The highest-risk false-positive is a first-arg callback that uses err as its param name — this isn't tested.
  • Incidental test correctness: The named-reference tests pass because FunctionDeclaration nodes are not tracked — not because cross-file reference resolution is skipped. The test description is misleading.

Positive Highlights

  • ✅ Precise AST logic: parent.arguments[1] === node correctly pins to the second argument without relying on index arithmetic.
  • ✅ Updated JSDoc on isCatchCallback accurately describes both detection cases and the out-of-scope rationale.
  • ✅ README now explicitly enumerates all three scopes and the out of scope caveat — great for rule consumers.
  • ✅ Five new test cases cover both arrow and function-expression syntax plus both named-reference exclusions.
  • ✅ The docs.description update keeps the rule self-documenting inside the ESLint metadata.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 88.6 AIC · ⌖ 8.5 AIC · ⊞ 6.7K
Comment /matt to run again

* out of scope because the rule cannot statically follow the reference to
* inspect its parameter list without cross-file analysis.
*/
function isCatchCallback(node: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression): boolean {

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.

[/grill-with-docs] isCatchCallback is now a misnomer — the function also returns true for .then() second-arg rejection handlers, but its name implies only .catch(). Future maintainers may assume the sentinel does not cover .then() and widen it incorrectly.

💡 Suggested rename

Consider renaming to isInlineRejectionHandler (or isRejectionHandlerCallback) to match the expanded semantics described in the JSDoc:

function isInlineRejectionHandler(
  node: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression
): boolean {

The callers (enterFunction) and the comment on line 104 would need updating to match.

@copilot please address this.

});
});

it("invalid: JSON.stringify(err) in .then() second-argument rejection handler (arrow) is flagged", () => {

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.

[/tdd] The two .then() invalid tests use cjsRuleTester only — there is no ESM-mode counterpart, unlike the existing try/catch and .catch() tests which exercise both testers. If the ESM parser resolves method calls differently (e.g., in strict mode modules) the gap would go undetected.

💡 Suggested addition

Add a minimal ESM-mode invalid test alongside the existing it("invalid: works with ES module syntax") block:

it("invalid: JSON.stringify(err) in .then() rejection handler works with ES module syntax", () => {
  esmRuleTester.run("no-json-stringify-error", noJsonStringifyErrorRule, {
    valid: [],
    invalid: [
      {
        code: `p.then(null, err => console.error(JSON.stringify(err)));`,
        errors: [{ messageId: "jsonStringifyError", data: { errorVar: "err" } }],
      },
    ],
  });
});

@copilot please address this.

});
});

it("valid: first argument of .then() (onFulfilled) is not treated as a rejection handler", () => {

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.

[/tdd] The onFulfilled non-flagging test uses a single parameter named result — it does not cover the case where the first-arg callback happens to use err as a parameter name (e.g., p.then(err => JSON.stringify(err))). This is the most likely false-positive scenario and should be a dedicated test.

💡 Suggested test
it("valid: p.then(err => JSON.stringify(err)) — first argument is onFulfilled, not flagged", () => {
  cjsRuleTester.run("no-json-stringify-error", noJsonStringifyErrorRule, {
    valid: [`p.then(err => core.info(JSON.stringify(err)));`],
    invalid: [],
  });
});

Without this, a future refactor that accidentally checks argument index >= 1 instead of === 1 would go undetected.

@copilot please address this.

});
});

it("valid: p.catch(handler) named-reference is not flagged (static cross-reference out of scope)", () => {

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.

[/tdd] The named-reference exclusion test (p.catch(handler)) places JSON.stringify(err) inside handler, so the rule would flag the standalone function body. The test only passes because p.catch(handler) itself is not inside a rejection scope — but the test comment says it's about cross-reference analysis. The test would be clearer if handler is defined outside any .catch() call and the JSON.stringify call is confirmed untouched.

💡 Observation

This is a readability/specification issue, not a bug. The current test function handler(err) { JSON.stringify(err); } p.catch(handler); passes because handler is a FunctionDeclaration (not FunctionExpression/ArrowFunctionExpression), so enterFunction is never triggered for it at all. The test description says "static cross-reference out of scope" but the actual mechanism is that FunctionDeclaration nodes are not tracked. A comment or rename clarifying this would prevent confusion.

@copilot please address this.

@github-actions github-actions Bot left a comment

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.

Two test coverage gaps and a stale function name block this merge.

The rule logic itself is correct — the .then(_, onRejected) detection in isCatchCallback is a clean, minimal change and the sentinel-stack scoping is unaffected. The concerns below are about test confidence and maintainability debt introduced alongside the logic change.

Blocking findings
  1. Missing ESM test for the new .then() path (high): All five new invalid/valid cases use cjsRuleTester. The suite already has an esmRuleTester path for try/catch and .catch(). Module vs. script parser differences can mask rule failures, and a regression in ESM mode would pass CI silently.

  2. isCatchCallback name is now wrong (medium): The function covers both .catch(fn) and .then(_, fn), but the name only says catch. Maintainers reading the .then() branch will spend time verifying the coverage is intentional rather than a copy-paste mistake. Rename to isInlineRejectionHandler (or similar) and update the companion comment at the AST visitor registration site.

Non-blocking observations
  • Missing JSON.stringify(err, null, 2) test for the .then() path (medium): The extra-args variant is covered for try/catch but not for the new .then() detection. The rule logic already handles it correctly, but a dedicated test would pin the assumption against future refactors.

  • parent.arguments[1] has no explicit bounds guard (low): undefined === node is always false, so this is safe today. But parent.arguments.length > 1 && parent.arguments[1] === node makes the intent explicit and guards against future pattern-changes.

  • Stale comment in enterFunction else-branch still reads // Non-.catch() function — update to Non-rejection-handler function to stay in sync with the expanded detection scope.

🔎 Code quality review by PR Code Quality Reviewer · 101.6 AIC · ⌖ 5.72 AIC · ⊞ 5.4K
Comment /review to run again

cjsRuleTester.run("no-json-stringify-error", noJsonStringifyErrorRule, {
valid: [`p.then(result => JSON.stringify(result));`, `p.then(function(result) { JSON.stringify(result); });`],
invalid: [],
});

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.

Missing ESM-mode test for .then(_, fn) rejection handler — the new detection path is validated only under CJS, leaving ESM coverage absent while try/catch already has an esmRuleTester case.

💡 Suggested fix

Add an ESM invalid case after the existing CJS tests:

it("invalid: JSON.stringify(err) in .then() rejection handler is flagged (ESM)", () => {
  esmRuleTester.run("no-json-stringify-error", noJsonStringifyErrorRule, {
    valid: [],
    invalid: [
      {
        code: `p.then(ok, err => core.error(JSON.stringify(err)));`,
        errors: [{ messageId: "jsonStringifyError", data: { errorVar: "err" }, suggestions: [{ messageId: "useGetErrorMessage", data: { errorVar: "err" }, output: `p.then(ok, err => core.error(getErrorMessage(err)));` }] }],
      },
    ],
  });
});

Parser configuration differences between module and script mode can affect identifier binding. The suite already has esmRuleTester coverage for try/catch and .catch() — this new .then() path deserves the same treatment. Without it, a regression in ESM mode would pass CI silently.

* out of scope because the rule cannot statically follow the reference to
* inspect its parameter list without cross-file analysis.
*/
function isCatchCallback(node: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression): boolean {

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.

isCatchCallback is now a misleading name — the function detects both .catch(fn) and .then(_, fn) rejection handlers, so the name actively lies to maintainers about its scope.

💡 Suggested fix

Rename to isInlineRejectionHandler (and update the companion comment at line 101):

function isInlineRejectionHandler(
  node: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression
): boolean {
  ...
}

Also update the comment at the AST visitor registration site:

// Track inline rejection-handler parameters (.catch(fn) and .then(onFulfilled, onRejected))
ArrowFunctionExpression: enterFunction,

A maintainer looking at the .then() branch and seeing isCatchCallback will waste time deciding whether the coverage is intentional — a good function name eliminates that question entirely.

});

it("invalid: JSON.stringify(err) in .then() second-argument rejection handler (function) is flagged", () => {
cjsRuleTester.run("no-json-stringify-error", noJsonStringifyErrorRule, {

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.

Missing test: JSON.stringify(err, null, 2) with extra args in .then() rejection handler — the replacer/space-argument variant is already covered for try/catch, but not for the new .then() path.

💡 Suggested fix

Add a case inside the existing .then() invalid test block (or a separate small test):

{
  code: `p.then(null, err => core.error(JSON.stringify(err, null, 2)));`,
  errors: [{
    messageId: "jsonStringifyError",
    data: { errorVar: "err" },
    suggestions: [{ messageId: "useGetErrorMessage", data: { errorVar: "err" }, output: `p.then(null, err => core.error(getErrorMessage(err)));` }],
  }],
},

The rule targets JSON.stringify by matching any call where the first argument is the error variable, so extra arguments are already handled correctly — but without a test, that assumption is unverified and future refactors targeting arguments.length checks have no safety net.

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, address unresolved review comments, and rerun checks once the branch is up to date.
Unresolved reviews:

  • @github-actions requested changes

Generated by 👨‍🍳 PR Sous Chef · 7.38 AIC · ⌖ 10.2 AIC · ⊞ 5.6K ·
Comment /souschef to run again

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

PR Triage

Category refactor
Risk Low
Score 30/100 (impact 14 + urgency 8 + quality 8)
Action defer

CHANGES_REQUESTED by reviewer: missing ESM test coverage for .then(_, onRejected) and isCatchCallback rename needed. Blocked on reviewer feedback; 48 lock file changes add noise. Defer until agent addresses review comments.

Run §28776188550

Generated by 🔧 PR Triage Agent · 98.5 AIC · ⌖ 11.4 AIC · ⊞ 5.5K ·

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>

Copilot AI commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the pr-finisher skill, address unresolved review comments, and rerun checks once the branch is up to date.
Unresolved reviews:

  • @github-actions requested changes...

Addressed in c916cb0. The follow-up updates rename the rejection-handler helper, tighten the inline-scope wording, and add the missing .then(_, onRejected) coverage for ESM, extra-arg JSON.stringify, the first-argument false-positive case, and the named-reference boundary. I re-ran local make fmt, make lint, make test-unit, and make test; GitHub checks on this pushed HEAD still need a maintainer-triggered rerun.

Copilot AI requested a review from gh-aw-bot July 6, 2026 08:20
@pelikhan pelikhan merged commit 2e1ada3 into main Jul 6, 2026
1 check passed
@pelikhan pelikhan deleted the copilot/eslint-factory-fix-no-json-stringify-error branch July 6, 2026 08:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

4 participants