Browse Source

fix(vsh): hash wildcard publint outputs

pull/8373/head
kilisamemarisaaa 3 weeks ago
parent
commit
3199561b4b
  1. 33
      scripts/vsh/src/publint/__tests__/cache.test.ts
  2. 38
      scripts/vsh/src/publint/index.ts

33
scripts/vsh/src/publint/__tests__/cache.test.ts

@ -58,4 +58,37 @@ describe('publint cache key', () => {
expect(updatedHash).not.toBe(initialHash);
});
it('invalidates when a wildcard output file is created', async () => {
const packageDir = join(process.cwd(), '.tmp-publint-cache-test');
tempDirectories.push(packageDir);
await mkdir(join(packageDir, 'dist'), { recursive: true });
const packageJson = {
exports: {
'.': './dist/*.mjs',
},
};
const missingOutputHash = await getPackageHash(packageJson, packageDir);
await writeFile(join(packageDir, 'dist/index.mjs'), 'export {}');
const createdOutputHash = await getPackageHash(packageJson, packageDir);
expect(createdOutputHash).not.toBe(missingOutputHash);
});
it('changes when a wildcard output file is modified', async () => {
const packageDir = join(process.cwd(), '.tmp-publint-cache-test');
tempDirectories.push(packageDir);
await mkdir(join(packageDir, 'dist'), { recursive: true });
const outputPath = join(packageDir, 'dist/index.mjs');
await writeFile(outputPath, 'export const value = 1');
const packageJson = { exports: { '.': './dist/*.mjs' } };
const initialHash = await getPackageHash(packageJson, packageDir);
await writeFile(outputPath, 'export const value = 100');
const updatedHash = await getPackageHash(packageJson, packageDir);
expect(updatedHash).not.toBe(initialHash);
});
});

38
scripts/vsh/src/publint/index.ts

@ -1,7 +1,7 @@
import type { CAC } from 'cac';
import type { Result } from 'publint';
import { readFile, stat } from 'node:fs/promises';
import { glob, readFile, stat } from 'node:fs/promises';
import { basename, dirname, join, resolve } from 'node:path';
import {
@ -67,6 +67,34 @@ function collectRelativePaths(value: unknown, paths = new Set<string>()) {
return paths;
}
/**
* Expand wildcard package targets before collecting their filesystem state.
* Keep an unmatched pattern so creating its first match also invalidates the
* cache.
*/
async function expandRelativePath(relativePath: string, pkgDir: string) {
if (!relativePath.includes('*')) {
return [relativePath];
}
const matches: string[] = [];
try {
for await (const match of glob(relativePath, { cwd: pkgDir })) {
matches.push(match);
}
} catch {
return [relativePath];
}
if (matches.length === 0) {
return [relativePath];
}
return matches.map((match) =>
match.startsWith('./') ? match : `./${match}`,
);
}
/**
* Include referenced package files in the cache key. Publint checks the
* filesystem as well as package.json, so a build can change its result without
@ -76,7 +104,13 @@ async function getPackageHash(
pkgJson: Record<string, unknown>,
pkgDir: string,
) {
const relativePaths = [...collectRelativePaths(pkgJson)].toSorted();
const declaredPaths = [...collectRelativePaths(pkgJson)];
const expandedPaths = await Promise.all(
declaredPaths.map((relativePath) =>
expandRelativePath(relativePath, pkgDir),
),
);
const relativePaths = expandedPaths.flat().toSorted();
const fileStates = await Promise.all(
relativePaths.map(async (relativePath) => {
const targetPath = resolve(pkgDir, relativePath);

Loading…
Cancel
Save