Skip to content

prefer-structured-clone: autofix suggestion throws DataCloneError on live call site that clones an object with a function-valued [Content truncated due to length] #50795

Description

@github-actions

Rule

prefer-structured-clone (eslint-factory/src/rules/prefer-structured-clone.ts) - first review of this rule; shipped since the last refinement pass and not yet covered by any prior issue.

Summary

The rule flags JSON.parse(JSON.stringify(x)) and suggests replacing it with structuredClone(x). This is presented as a strict improvement, but the two are not semantically equivalent when x can contain a function-valued property: JSON.stringify silently drops function values, while structuredClone throws a synchronous DataCloneError the moment it encounters one. The rule offers the suggestion unconditionally, with no static check for this case.

Grounded live example (not hypothetical)

actions/setup/js/safe_outputs_tools_loader.cjs around line 244:

toolToRegister = JSON.parse(JSON.stringify(tool));
...
if (tool.handler) {
  toolToRegister.handler = tool.handler;
}

By the time this line runs, tool.handler has already been set to a live function by attachHandlers() (same file, lines ~130-211). The surrounding code deep-copies with JSON.parse(JSON.stringify(tool)) specifically because that silently strips handler, then re-attaches the original function afterward on purpose.

This exact call expression matches the rule's flagged pattern and receives the structuredClone(tool) suggestion. Applying that suggestion would replace working code with code that throws DataCloneError synchronously on every call.

By contrast, actions/setup/js/generate_safe_outputs_tools.cjs around line 280 (enhancedTool = JSON.parse(JSON.stringify(tool))) is a genuinely safe application of the suggestion: there, tool comes from a static JSON file on disk, so it can never contain a function. Both sites match the rule identically today, but only one is safe to rewrite.

Why this matters

  • hasSuggestions: true means editors/agents can apply this suggestion directly; it looks like a safe mechanical improvement but silently changes drop-on-serialize semantics into throw-on-clone semantics.
  • The rule's docs treat 'drops functions' purely as a JSON.stringify shortcoming that structuredClone fixes, without acknowledging that some code intentionally relies on that drop-and-reattach idiom.

Suggested fix (pick one or combine)

  1. Keep the diagnostic report but drop the suggest array (report-only, no autofix), since 'is this object JSON-safe' is not always statically decidable.
  2. Add a conservative static guard that skips the suggestion when the cloned expression is an Identifier that has a property assigned a function value (FunctionExpression/ArrowFunctionExpression) anywhere before the clone site in the same scope - matching the safe_outputs_tools_loader.cjs shape.
  3. At minimum, add a regression test reproducing this shape and update rule docs / README to call out the function-property hazard explicitly.

Acceptance criteria

  • The suggestion is no longer offered (or is demonstrably guarded) for the safe_outputs_tools_loader.cjs:244 shape.
  • A regression test encodes the function-property hazard (valid case with no suggestion, or invalid case with empty suggestions array).
  • generate_safe_outputs_tools.cjs:280 (JSON-sourced tool, no functions) continues to receive the suggestion - don't regress the safe case.
  • Rule docs (meta.docs.description and/or README) call out that the suggestion assumes the cloned value never carries function-valued properties.

Scope: eslint-factory/** only (rule logic + tests + docs). No changes to actions/setup/js/** app code are in scope for this issue.

Generated by 🤖 ESLint Refiner · agent · 231.1 AIC · ⌖ 43.7 AIC · ⊞ 4.9K ·

  • expires on Aug 12, 2026, 10:21 PM UTC-08:00

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions