Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions packages/rstack/src/fmt/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,13 @@ const formatFile = async ({
return { status };
}

const cacheHash =
shouldWrite && !unchanged
? hashContent(result.formatted)
: (contentHash ?? hashContent(result.source));
// Cache only the input we actually checked. Prettier or a plugin may produce
// non-idempotent output, so writing it does not prove that it is clean.
// Keeping the input hash makes the next run verify the newly written content.
const cacheEntry: FmtCacheEntry = [
cacheHash,
contentHash ?? hashContent(result.source),
cache.optionsHash,
shouldWrite || unchanged ? 'clean' : 'dirty',
unchanged ? 'clean' : 'dirty',
];
return { status, cacheEntry };
};
Expand Down
2 changes: 1 addition & 1 deletion packages/rstack/tests/cli/fmt/cache.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ test('excludes the custom cache directory from formatting', () => {

test('uses an explicit config root cache from a subdirectory', () => {
const appPath = resolveProjectPath('packages/app');
writeProjectFile('packages/app/index.ts', 'const value=1');
writeProjectFile('packages/app/index.ts', 'const value = 1;\n');

const result = runFmt(
['index.ts', '--config', '../../rstack.config.ts'],
Expand Down
38 changes: 32 additions & 6 deletions packages/rstack/tests/fmt/runnerCache.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ test('does not cache formatting errors', async () => {
});
});

test('write persists clean results for misses and hits', async () => {
test('write persists checked input results for misses and hits', async () => {
await withTempProject(async (rootPath) => {
const cleanPath = path.join(rootPath, 'clean.ts');
const dirtyPath = path.join(rootPath, 'dirty.ts');
Expand All @@ -333,9 +333,9 @@ test('write persists clean results for misses and hits', async () => {
'clean',
]);
expect(store.get('dirty.ts')).toEqual([
createCacheHash(readFileSync(dirtyPath)),
createCacheHash('const dirty=1'),
expect.any(String),
'clean',
'dirty',
]);

const timestamps = files.map((file) => statSync(file.path).mtimeMs);
Expand All @@ -350,7 +350,7 @@ test('write persists clean results for misses and hits', async () => {
});
});

test('write converts a dirty entry to clean', async () => {
test('write keeps a dirty entry until the output is checked', async () => {
await withTempProject(async (rootPath) => {
const filePath = path.join(rootPath, 'index.ts');
const cache = createFmtCacheContext(rootPath);
Expand All @@ -367,13 +367,39 @@ test('write converts a dirty entry to clean', async () => {

const store = await loadFmtCacheStore(cache.filePath, cacheNamespace);
expect(store.get('index.ts')).toEqual([
createCacheHash(readFileSync(filePath)),
createCacheHash('const value=1'),
expect.any(String),
'clean',
'dirty',
]);
await expect(run([file], 'check', cache)).resolves.toMatchObject({
exitCode: 0,
files: [],
});
});
});

test('cached check matches uncached check after writing non-idempotent output', async () => {
await withTempProject(async (rootPath) => {
// Preserve these line breaks: this input needs two passes in Prettier 3.9.6.
const filePath = writeProjectFile(
rootPath,
'example.ts',
`const fetch = rs.fn<typeof globalThis.fetch>().mockImplementation(() => Promise.resolve(
new Response('cached pixels', { headers: { 'content-type': 'image/webp' } }),
));
`,
);
const files = [
createFmtRequest(filePath, {
parser: 'typescript',
singleQuote: true,
trailingComma: 'all',
}),
];
const cache = createFmtCacheContext(rootPath);

await run(files, 'write', cache);
const uncached = await runFmtFiles({ files, mode: 'check' });
await expect(run(files, 'check', cache)).resolves.toEqual(uncached);
});
});