Skip to content

Commit

Permalink
Merge branch 'add-promise-support' of github.com:doug-wade/eleventy-f…
Browse files Browse the repository at this point in the history
…etch into doug-wade-add-promise-support
  • Loading branch information
zachleat committed Oct 22, 2024
2 parents 2d5d21a + 80673c8 commit e45f7c2
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 24 deletions.
10 changes: 9 additions & 1 deletion eleventy-fetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,19 @@ function isFullUrl(url) {
}
}

function isAwaitable(maybeAwaitable) {
return (typeof maybeAwaitable === "object" && typeof maybeAwaitable.then === "function") || (maybeAwaitable.constructor.name === "AsyncFunction");
}

async function save(source, options) {
if (!isFullUrl(source)) {
if(!(isFullUrl(source) || isAwaitable(source))) {
return Promise.reject(new Error("Caching an already local asset is not yet supported."));
}

if (isAwaitable(source) && !options.formatUrlForDisplay) {
return Promise.reject(new Error("formatUrlForDisplay must be implemented, as a Promise has been provided."));
}

let asset = new RemoteAssetCache(source, options.directory, options);
return asset.fetch(options);
}
Expand Down
10 changes: 8 additions & 2 deletions src/AssetCache.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@ const { createHash } = require("crypto");
const debug = require("debug")("Eleventy:Fetch");

class AssetCache {
constructor(uniqueKey, cacheDirectory, options = {}) {
this.uniqueKey = uniqueKey;
constructor(url, cacheDirectory, options = {}) {
let uniqueKey;
if ((typeof url === "object" && typeof url.then === "function") || (typeof url === "function" && url.constructor.name === "AsyncFunction")) {
uniqueKey = options.formatUrlForDisplay();
} else {
uniqueKey = url;
}

this.hash = AssetCache.getHash(uniqueKey, options.hashLength);
this.cacheDirectory = cacheDirectory || ".cache";
this.defaultDuration = "1d";
Expand Down
29 changes: 19 additions & 10 deletions src/RemoteAssetCache.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,17 +78,26 @@ class RemoteAssetCache extends AssetCache {
this.log(`${isDryRun ? "Fetching" : "Cache miss for"} ${this.displayUrl}`);

let fetchOptions = optionsOverride.fetchOptions || this.options.fetchOptions || {};
let response = await fetch(this.url, fetchOptions);
if (!response.ok) {
throw new Error(
`Bad response for ${this.displayUrl} (${response.status}): ${response.statusText}`,
{ cause: response },
);
}

let type = optionsOverride.type || this.options.type;
let body = await this.getResponseValue(response, type);
if (!isDryRun) {

let body;
if(typeof this.url === "object" && typeof this.url.then === "function") {
body = await this.url;
} else if (typeof this.url === "function" && this.url.constructor.name === "AsyncFunction") {
body = await this.url();
} else {
let response = await fetch(this.url, fetchOptions);
if (!response.ok) {
throw new Error(
`Bad response for ${this.displayUrl} (${response.status}): ${response.statusText}`,
{ cause: response },
);
}

let type = optionsOverride.type || this.options.type;
body = await this.getResponseValue(response, type);
}
if(!isDryRun) {
await super.save(body, type);
}
return body;
Expand Down
20 changes: 20 additions & 0 deletions test/AssetCacheTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,25 @@ test("Cache path should handle slashes without creating directories, issue #14",
let cache = new AssetCache("lksdjflk/jsdf", "/tmp/.cache");
let cachePath = normalizePath(cache.cachePath);


t.is(cachePath, "/tmp/.cache/eleventy-fetch-135797dbf5ab1187e5003c49162602");
});

test("Uses formatUrlForDisplay when caching a promise", async t => {
let promise = Promise.resolve();
let displayUrl = 'mock-display-url'
let asset = new AssetCache(promise, ".customcache", {
formatUrlForDisplay() {
return displayUrl;
}
});
let cachePath = normalizePath(asset.cachePath);
let jsonCachePath = normalizePath(asset.getCachedContentsPath("json"));

await asset.save({name: "Sophia Smith" }, "json");

t.truthy(fs.existsSync(jsonCachePath));

fs.unlinkSync(cachePath);
fs.unlinkSync(jsonCachePath);
});
87 changes: 76 additions & 11 deletions test/RemoteAssetCacheTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const { Util } = require("../");
const AssetCache = require("../src/AssetCache");
const RemoteAssetCache = require("../src/RemoteAssetCache");

test("getDurationMs", (t) => {
test("getDurationMs", t => {
let cache = new RemoteAssetCache("lksdjflkjsdf");
t.is(cache.getDurationMs("1s"), 1000);
t.is(cache.getDurationMs("1m"), 60 * 1000);
Expand Down Expand Up @@ -190,16 +190,81 @@ test("Error with `cause`", async (t) => {
let asset = new RemoteAssetCache(finalUrl);

try {
await asset.fetch({
headers: {
"user-agent":
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36",
},
});
} catch (e) {
t.truthy(
e.message.startsWith(`Bad response for https://example.com/207115/photos/243-0-1.jpg`),
);
await asset.fetch();
} catch(e) {
t.is(e.message, `Bad response for https://example.com/207115/photos/243-0-1.jpg (404): Not Found`)
t.truthy(e.cause);
}
});

test("supports promises that resolve", async (t) => {
let expected = { mockKey: "mockValue" };
let promise = Promise.resolve(expected);
let asset = new RemoteAssetCache(promise, undefined, {
type: "json",
formatUrlForDisplay() {
return "resolve-promise";
},
});

let actual = await asset.fetch();

t.deepEqual(actual, expected);
});

test("supports promises that reject", async (t) => {
let expected = "mock error message";
let cause = new Error("mock cause");
let promise = Promise.reject(new Error(expected, { cause }));

let asset = new RemoteAssetCache(promise, undefined, {
formatUrlForDisplay() {
return "reject-promise";
},
});

try {
await asset.fetch();
} catch (e) {
t.is(e.message, expected);
t.is(e.cause, cause);
}
});

test("supports async functions that return data", async (t) => {
let expected = { mockKey: "mockValue" };
let asyncFunction = async () => {
return Promise.resolve(expected);
};
let asset = new RemoteAssetCache(asyncFunction, undefined, {
type: "json",
formatUrlForDisplay() {
return "async-return";
},
});

let actual = await asset.fetch();

t.deepEqual(actual, expected);
});

test("supports async functions that throw", async (t) => {
let expected = "mock error message";
let cause = new Error("mock cause");
let asyncFunction = async () => {
throw new Error(expected, { cause });
};

let asset = new RemoteAssetCache(asyncFunction, undefined, {
formatUrlForDisplay() {
return "async-throws";
},
});

try {
await asset.fetch();
} catch (e) {
t.is(e.message, expected);
t.is(e.cause, cause);
}
});

0 comments on commit e45f7c2

Please sign in to comment.