Skip to content

fmt: cached check passes while --no-cache fails after formatting #479

Description

@shiyudesu

Description

Immediately after rs fmt formats and writes a file:

  • rs fmt --check exits with code 0 and reports that formatting is correct.
  • rs fmt --check --no-cache exits with code 1 and reports a formatting issue for exactly the same file.
  • Running cached rs fmt again leaves the file unchanged.
  • Running formatting again without cache changes the file.

This reproduces with a fresh cache directory and does not require an existing project or CI environment.

Environment

  • Linux x64
  • Node.js 24.19.0
  • pnpm 12.3.4
  • Rstack 0.7.3
  • Prettier 3.9.6 (Rstack dependency)
  • yuku-parser 0.9.3 (Rstack dependency)

Minimal reproduction

Create the following three files in an empty directory.

package.json

{
  "private": true,
  "type": "module",
  "devDependencies": {
    "rstack": "0.7.3"
  }
}

rstack.config.mjs

import { define } from 'rstack';

define.fmt({
  singleQuote: true,
  trailingComma: 'all',
});

example.ts

Please preserve the exact formatting of this input:

const fetch = rs.fn<typeof globalThis.fetch>().mockImplementation(() => Promise.resolve(
  new Response('cached pixels', { headers: { 'content-type': 'image/webp' } }),
));

Here, rs is only an identifier in the formatting input. The code is never executed, so no test framework is required.

Install dependencies:

pnpm install

Starting with a nonexistent repro-cache directory, run each command separately, in order:

pnpm exec rs fmt --cache-location ./repro-cache example.ts
# Exits with 0 and writes the first formatted output, B.

pnpm exec rs fmt --check --cache-location ./repro-cache example.ts
# Exits with 0: Format check passed.

pnpm exec rs fmt --check --no-cache example.ts
# Exits with 1: Formatting issues found in 1 file.

pnpm exec rs fmt --cache-location ./repro-cache example.ts
# Exits with 0, but output B remains unchanged.

pnpm exec rs fmt --no-cache example.ts
# Exits with 0 and changes B into a different output, C.

pnpm exec rs fmt --check --no-cache example.ts
# Exits with 0: Format check passed.

Expected behavior

Cached and uncached checks should produce consistent results for identical file contents, configuration, and tool versions.

Formatting should be idempotent: formatting its output again should not change it.

Additional investigation

Two observable behaviors combine to produce this result:

  1. For this input, formatting the first output B again produces a different output C.
  2. Rstack caches the first written output B as clean. Subsequent cache hits skip formatting, so they do not detect that B would still be changed by another formatting pass.

In the installed Rstack 0.7.3 package, formatFile in dist/335.js stores the hash of result.formatted after writing and classifies the cache entry using:

shouldWrite || unchanged ? 'clean' : 'dirty'

As a result, a written output is marked clean.

Separately, directly calling Prettier 3.9.6's format() with parser: 'typescript', singleQuote: true, and trailingComma: 'all' also produces the same B and C.

Therefore, the non-idempotent formatting is not specific to Rstack or its Yuku integration. This report concerns how caching the first written output as clean masks that behavior, causing cached and uncached checks to disagree. There is no evidence that this is a cache-hash collision.

Activity

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions