From a2e2c7af22cf081c9e70c8ab64fc9de4bf37e8f3 Mon Sep 17 00:00:00 2001 From: neverland Date: Thu, 6 Aug 2026 22:50:37 +0800 Subject: [PATCH] fix(fmt): avoid reading unsupported cache misses --- packages/rstack/src/fmt/worker.ts | 26 +++++++++++++++++------- packages/rstack/tests/fmt/worker.test.ts | 19 +++++++++++++++++ 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/packages/rstack/src/fmt/worker.ts b/packages/rstack/src/fmt/worker.ts index d695bc5..330e9b2 100644 --- a/packages/rstack/src/fmt/worker.ts +++ b/packages/rstack/src/fmt/worker.ts @@ -25,20 +25,28 @@ const formatFile = async ({ }: FormatFileTask): Promise => { let source: string | undefined; let contentHash: string | undefined; + const fileCache = shouldWrite ? undefined : cache; + + const readSource = (): string => { + if (!fileCache) { + return readFileSync(file.path, 'utf8'); + } - if (cache && !shouldWrite) { const content = readFileSync(file.path); contentHash = hashContent(content); - source = content.toString('utf8'); + return content.toString('utf8'); + }; - const { entry, optionsHash } = cache; - if (entry?.[0] === contentHash && entry[1] === optionsHash) { + if (fileCache?.entry && fileCache.entry[1] === fileCache.optionsHash) { + source = readSource(); + const { entry } = fileCache; + if (entry[0] === contentHash) { return { status: entry[2] === 'clean' ? 'unchanged' : 'changed' }; } } const { formatFmtSource } = await import('./format.ts'); - const result = await formatFmtSource(file, () => (source ??= readFileSync(file.path, 'utf8'))); + const result = await formatFmtSource(file, () => (source ??= readSource())); if (result.status === 'unsupported') { return { status: 'unsupported' }; } @@ -50,11 +58,15 @@ const formatFile = async ({ } const status = unchanged ? 'unchanged' : 'changed'; - if (!cache || contentHash === undefined) { + if (!fileCache || contentHash === undefined) { return { status }; } - const cacheEntry: FmtCacheEntry = [contentHash, cache.optionsHash, unchanged ? 'clean' : 'dirty']; + const cacheEntry: FmtCacheEntry = [ + contentHash, + fileCache.optionsHash, + unchanged ? 'clean' : 'dirty', + ]; return { status, cacheEntry }; }; diff --git a/packages/rstack/tests/fmt/worker.test.ts b/packages/rstack/tests/fmt/worker.test.ts index 7c823d6..c7f5ba5 100644 --- a/packages/rstack/tests/fmt/worker.test.ts +++ b/packages/rstack/tests/fmt/worker.test.ts @@ -1,3 +1,4 @@ +import path from 'node:path'; import { readFileSync } from 'node:fs'; import { expect, test } from 'rstack/test'; import { sha256 } from '../../src/fmt/cacheIdentity.ts'; @@ -72,3 +73,21 @@ test('returns cached states before resolving the parser', async () => { } }); }); + +test('resolves parser support before reading on a cache miss', async () => { + await withTempProject(async (rootPath) => { + await expect( + formatFile({ + file: { + path: path.join(rootPath, 'missing.unknown'), + options: {}, + }, + shouldWrite: false, + cache: { + entry: undefined, + optionsHash: 'options', + }, + }), + ).resolves.toEqual({ status: 'unsupported' }); + }); +});