Skip to content

Commit 88191c5

Browse files
committed
refactor(bundle-source): tighten cache.js power usage
1 parent 0973f34 commit 88191c5

File tree

1 file changed

+51
-6
lines changed

1 file changed

+51
-6
lines changed

packages/bundle-source/cache.js

+51-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1+
// @ts-check
12
import { makePromiseKit } from '@endo/promise-kit';
23
import { makeReadPowers } from '@endo/compartment-mapper/node-powers.js';
34

45
import bundleSource from './src/index.js';
56

67
const { Fail, quote: q } = assert;
78

9+
/**
10+
* @typedef {(...args: unknown[]) => void} Logger A message logger.
11+
*/
812

913
/**
1014
* @typedef {object} BundleMeta
@@ -14,22 +18,53 @@ const { Fail, quote: q } = assert;
1418
* @property {Array<{ relativePath: string, mtime: string }>} contents
1519
*/
1620

21+
/**
22+
* @param {string} fileName
23+
* @param {{
24+
* fs: {
25+
* promises: Pick<import('fs/promises'),'readFile' | 'stat'>
26+
* },
27+
* path: Pick<import('path'), 'resolve' | 'relative' | 'normalize'>,
28+
* }} powers
29+
*/
1730
export const makeFileReader = (fileName, { fs, path }) => {
1831
const make = there => makeFileReader(there, { fs, path });
32+
33+
// fs.promises.exists isn't implemented in Node.js apparently because it's pure
34+
// sugar.
35+
const exists = fn =>
36+
fs.promises.stat(fn).then(
37+
() => true,
38+
e => {
39+
if (e.code === 'ENOENT') {
40+
return false;
41+
}
42+
throw e;
43+
},
44+
);
45+
1946
return harden({
2047
toString: () => fileName,
2148
readText: () => fs.promises.readFile(fileName, 'utf-8'),
2249
neighbor: ref => make(path.resolve(fileName, ref)),
2350
stat: () => fs.promises.stat(fileName),
2451
absolute: () => path.normalize(fileName),
2552
relative: there => path.relative(fileName, there),
26-
exists: () => fs.existsSync(fileName),
53+
exists: () => exists(fileName),
2754
});
2855
};
2956

3057
/**
3158
* @param {string} fileName
32-
* @param {{ fs: import('fs'), path: import('path') }} io
59+
* @param {{
60+
* fs: Pick<import('fs'), 'existsSync'> &
61+
* { promises: Pick<
62+
* import('fs/promises'),
63+
* 'readFile' | 'stat' | 'writeFile' | 'mkdir' | 'rm'
64+
* >,
65+
* },
66+
* path: Pick<import('path'), 'resolve' | 'relative' | 'normalize'>,
67+
* }} io
3368
*/
3469
export const makeFileWriter = (fileName, { fs, path }) => {
3570
const make = there => makeFileWriter(there, { fs, path });
@@ -101,7 +136,13 @@ export const makeBundleCache = (wr, cwd, readPowers, opts) => {
101136
if (oops.code !== 'EEXIST') {
102137
throw oops;
103138
}
104-
// The lock exists, so something is already writing the bundle.
139+
140+
// The lock exists, so something is already writing the bundle on our
141+
// behalf.
142+
//
143+
// All we need to do is try validating the bundle, which will first wait
144+
// for the lock to disappear before reading the freshly-written bundle.
145+
105146
// eslint-disable-next-line no-use-before-define
106147
return validate(targetName, rootPath);
107148
}
@@ -209,12 +250,16 @@ export const makeBundleCache = (wr, cwd, readPowers, opts) => {
209250
/**
210251
* @param {string} rootPath
211252
* @param {string} targetName
212-
* @param {(...args: any[]) => void} [log]
253+
* @param {Logger} [log]
213254
* @returns {Promise<BundleMeta>}
214255
*/
215256
const validateOrAdd = async (rootPath, targetName, log = defaultLog) => {
216257
let meta;
217-
if (wr.readOnly().neighbor(toBundleMeta(targetName)).exists()) {
258+
const metaExists = await wr
259+
.readOnly()
260+
.neighbor(toBundleMeta(targetName))
261+
.exists();
262+
if (metaExists) {
218263
try {
219264
meta = await validate(targetName, rootPath, log);
220265
const { bundleTime, contents } = meta;
@@ -251,7 +296,7 @@ export const makeBundleCache = (wr, cwd, readPowers, opts) => {
251296
/**
252297
* @param {string} rootPath
253298
* @param {string} [targetName]
254-
* @param {(...args: any[]) => void} [log]
299+
* @param {Logger} [log]
255300
*/
256301
const load = async (
257302
rootPath,

0 commit comments

Comments
 (0)