Skip to content

Commit dbd3440

Browse files
authored
Merge pull request #60 from 11ty/remove-flat-cache
Fixes #59 and #56
2 parents 011bf55 + a8ab91f commit dbd3440

File tree

9 files changed

+297
-190
lines changed

9 files changed

+297
-190
lines changed

package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,14 @@
4141
},
4242
"homepage": "https://github.com/11ty/eleventy-fetch#readme",
4343
"devDependencies": {
44-
"ava": "^6.1.3",
45-
"prettier": "^3.3.3"
44+
"ava": "^6.2.0",
45+
"prettier": "^3.5.3"
4646
},
4747
"dependencies": {
48+
"@11ty/eleventy-utils": "^2.0.1",
4849
"@rgrove/parse-xml": "^4.2.0",
49-
"debug": "^4.3.7",
50-
"flat-cache": "^6.1.1",
50+
"debug": "^4.4.0",
51+
"flatted": "^3.3.3",
5152
"graceful-fs": "^4.2.11",
5253
"p-queue": "6.6.2"
5354
},

src/AssetCache.js

Lines changed: 28 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
const fs = require("graceful-fs");
22
const path = require("path");
3-
const { create: FlatCacheCreate } = require("flat-cache");
43
const { createHash } = require("crypto");
5-
const debugUtil = require("debug");
4+
const { DateCompare } = require("@11ty/eleventy-utils");
65

6+
const FileCache = require("./FileCache.js");
77
const Sources = require("./Sources.js");
8-
const DirectoryManager = require("./DirectoryManager.js");
98

9+
const debugUtil = require("debug");
1010
const debug = debugUtil("Eleventy:Fetch");
11-
const debugAssets = debugUtil("Eleventy:Assets");
1211

1312
class AssetCache {
1413
#source;
@@ -18,7 +17,6 @@ class AssetCache {
1817
#cacheDirectory;
1918
#cacheLocationDirty = false;
2019
#directoryManager;
21-
#rawContents = {}
2220

2321
constructor(source, cacheDirectory, options = {}) {
2422
if(!Sources.isValidSource(source)) {
@@ -173,10 +171,12 @@ class AssetCache {
173171

174172
get cache() {
175173
if (!this.#cache || this.#cacheLocationDirty) {
176-
let cache = FlatCacheCreate({
177-
cacheId: this.cacheFilename,
178-
cacheDir: this.rootDir,
174+
let cache = new FileCache(this.cacheFilename, {
175+
dir: this.rootDir,
176+
source: this.source,
179177
});
178+
cache.setDryRun(this.options.dryRun);
179+
cache.setDirectoryManager(this.#directoryManager);
180180

181181
this.#cache = cache;
182182
this.#cacheLocationDirty = false;
@@ -185,181 +185,58 @@ class AssetCache {
185185
}
186186

187187
getDurationMs(duration = "0s") {
188-
let durationUnits = duration.slice(-1);
189-
let durationMultiplier;
190-
if (durationUnits === "s") {
191-
durationMultiplier = 1;
192-
} else if (durationUnits === "m") {
193-
durationMultiplier = 60;
194-
} else if (durationUnits === "h") {
195-
durationMultiplier = 60 * 60;
196-
} else if (durationUnits === "d") {
197-
durationMultiplier = 60 * 60 * 24;
198-
} else if (durationUnits === "w") {
199-
durationMultiplier = 60 * 60 * 24 * 7;
200-
} else if (durationUnits === "y") {
201-
durationMultiplier = 60 * 60 * 24 * 365;
202-
}
203-
204-
let durationValue = parseInt(duration.slice(0, duration.length - 1), 10);
205-
return durationValue * durationMultiplier * 1000;
206-
}
207-
208-
getCachedContentsPath(type = "buffer") {
209-
if(type === "xml") {
210-
type = "text";
211-
} else if(type === "parsed-xml") {
212-
type = "json";
213-
}
214-
215-
return `${this.cachePath}.${type}`;
188+
return DateCompare.getDurationMs(duration);
216189
}
217190

218191
setDirectoryManager(manager) {
219192
this.#directoryManager = manager;
220193
}
221194

222-
ensureDir() {
223-
if (this.options.dryRun) {
224-
return;
225-
}
226-
227-
if(!this.#directoryManager) {
228-
// standalone fallback (for tests)
229-
this.#directoryManager = new DirectoryManager();
230-
}
231-
232-
this.#directoryManager.create(this.cacheDirectory);
233-
}
234-
235195
async save(contents, type = "buffer", metadata = {}) {
236196
if(!contents) {
237197
throw new Error("save(contents) expects contents (was falsy)");
238198
}
239199

240-
this.cache.set(this.hash, {
241-
cachedAt: Date.now(),
242-
type: type,
243-
metadata,
244-
});
245-
246-
let contentPath = this.getCachedContentsPath(type);
247-
248-
if (type === "json" || type === "parsed-xml") {
249-
contents = JSON.stringify(contents);
250-
}
251-
252-
this.#rawContents[type] = contents;
253-
254-
if(this.options.dryRun) {
255-
debug(`Dry run writing ${contentPath}`);
256-
return;
257-
}
258-
259-
this.ensureDir();
260-
261-
debugAssets("[11ty/eleventy-fetch] Writing %o from %o", contentPath, this.source);
262-
263-
// the contents must exist before the cache metadata are saved below
264-
fs.writeFileSync(contentPath, contents);
265-
debug(`Writing ${contentPath}`);
200+
this.cache.set(type, contents, metadata);
266201

202+
// Dry-run handled downstream
267203
this.cache.save();
268204
}
269205

270-
async #getCachedContents(type) {
271-
let contentPath = this.getCachedContentsPath(type);
272-
273-
debug(`Fetching from cache ${contentPath}`);
274-
275-
if(this.source) {
276-
debugAssets("[11ty/eleventy-fetch] Reading via %o", this.source);
277-
} else {
278-
debugAssets("[11ty/eleventy-fetch] Reading %o", contentPath);
279-
}
280-
281-
if (type === "json" || type === "parsed-xml") {
282-
return require(contentPath);
283-
}
284-
285-
return fs.readFileSync(contentPath, type !== "buffer" ? "utf8" : null);
286-
}
287-
288-
async getCachedContents(type) {
289-
if(!this.#rawContents[type]) {
290-
this.#rawContents[type] = this.#getCachedContents(type);
291-
}
292-
293-
// already saved on this instance in-memory
294-
return this.#rawContents[type];
295-
}
296-
297-
_backwardsCompatibilityGetCachedValue(type) {
298-
if (type === "json") {
299-
return this.cachedObject.contents;
300-
} else if (type === "text") {
301-
return this.cachedObject.contents.toString();
302-
}
303-
304-
// buffer
305-
return Buffer.from(this.cachedObject.contents);
206+
getCachedContents() {
207+
return this.cache.getContents();
306208
}
307209

308-
async getCachedValue() {
309-
let type = this.cachedObject.type;
310-
311-
// backwards compat with old caches
312-
if (this.cachedObject.contents) {
313-
return this._backwardsCompatibilityGetCachedValue(type);
314-
}
315-
210+
getCachedValue() {
316211
if(this.options.returnType === "response") {
317212
return {
318213
...this.cachedObject.metadata?.response,
319-
body: await this.getCachedContents(type),
214+
body: this.getCachedContents(),
320215
cache: "hit",
321216
}
322217
}
323218

324-
// promise
325-
return this.getCachedContents(type);
219+
return this.getCachedContents();
326220
}
327221

328222
getCachedTimestamp() {
329223
return this.cachedObject?.cachedAt;
330224
}
331225

332226
isCacheValid(duration = this.duration) {
333-
if (!this.cachedObject) {
334-
// not cached
227+
if(!this.cachedObject || !this.cachedObject?.cachedAt) {
335228
return false;
336229
}
337230

338-
// in the cache and no duration
339-
if (!duration || duration === "*") {
340-
// no duration specified (plugin default is 1d, but if this is falsy assume infinite)
341-
// "*" is infinite duration
342-
return true;
343-
}
344-
345-
debug("Cache check for: %o %o (duration: %o)", this.hash, this.source, duration);
346-
debug("Cache object: %o", this.cachedObject);
347-
348-
let compareDuration = this.getDurationMs(duration);
349-
let expiration = this.cachedObject.cachedAt + compareDuration;
350-
let expirationRelative = Math.abs(Date.now() - expiration);
351-
352-
if (expiration > Date.now()) {
353-
debug("Cache okay, expires in %o s (%o)", expirationRelative / 1000, new Date(expiration));
354-
return true;
231+
if(DateCompare.isTimestampWithinDuration(this.cachedObject?.cachedAt, duration)) {
232+
return this.cache.hasContents(); // check file system to make files haven’t been purged.
355233
}
356234

357-
debug("Cache expired %o s ago (%o)", expirationRelative / 1000, new Date(expiration));
358235
return false;
359236
}
360237

361238
get cachedObject() {
362-
return this.cache.get(this.hash);
239+
return this.cache.get();
363240
}
364241

365242
// Deprecated
@@ -382,17 +259,18 @@ class AssetCache {
382259
}
383260

384261
// for testing
385-
hasCacheFiles() {
386-
return fs.existsSync(this.cachePath) || fs.existsSync(this.getCachedContentsPath());
262+
hasAnyCacheFiles() {
263+
for(let p of this.cache.getFilePaths()) {
264+
if(fs.existsSync(p)) {
265+
return true;
266+
}
267+
}
268+
return false;
387269
}
388270

389271
// for testing
390272
async destroy() {
391-
let paths = [];
392-
paths.push(this.cachePath);
393-
paths.push(this.getCachedContentsPath("json"));
394-
paths.push(this.getCachedContentsPath("text"));
395-
paths.push(this.getCachedContentsPath("buffer"));
273+
let paths = this.cache.getFilePaths();
396274

397275
await Promise.all(paths.map(path => {
398276
if (fs.existsSync(path)) {

src/DirectoryManager.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
const fs = require("node:fs");
2-
const path = require("node:path");
3-
const debugUtil = require("debug");
4-
const debugAssets = debugUtil("Eleventy:Assets");
2+
const debugAssets = require("debug")("Eleventy:Assets");
53

64
class DirectoryManager {
75
#dirs = new Set();
@@ -16,7 +14,7 @@ class DirectoryManager {
1614
}
1715

1816
this.#dirs.add(dir);
19-
debugAssets("[11ty/eleventy-fetch] Creating directory %o", dir);
17+
debugAssets("Creating directory %o", dir);
2018
fs.mkdirSync(dir, { recursive: true });
2119
}
2220
}

src/ExistsCache.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
const fs = require("node:fs");
2+
// const debug = require("debug")("Eleventy:Assets");
3+
4+
class ExistsCache {
5+
#checks = new Map();
6+
#count = 0;
7+
8+
set(target, value) {
9+
this.#checks.set(target, Boolean(value));
10+
}
11+
12+
exists(target) {
13+
if(this.#checks.has(target)) {
14+
return this.#checks.get(target);
15+
}
16+
17+
let exists = fs.existsSync(target);
18+
this.#count++;
19+
this.#checks.set(target, exists);
20+
return exists;
21+
}
22+
}
23+
24+
module.exports = ExistsCache;

0 commit comments

Comments
 (0)