Skip to content

Commit

Permalink
Add --assetrefs option to swap to use absolute asset urls instead of …
Browse files Browse the repository at this point in the history
…relative.
  • Loading branch information
zachleat committed Nov 20, 2024
1 parent f263d74 commit 78265d3
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 25 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ npx @11ty/import [type] [target] --cacheduration=20m
# Change output format (default: markdown)
npx @11ty/import [type] [target] --format=html

# Use absolute asset URLs (default: relative)
npx @11ty/import [type] [target] --assetrefs=absolute

# EXPERIMENTAL: Persist *new* non-draft content
# - `github` persist type requires a `GITHUB_TOKEN` environment variable.
npx @11ty/import [type] [target] --persist=github:zachleat/wp-awesome
Expand Down Expand Up @@ -121,6 +124,7 @@ importer.setSafeMode(false); // --overwrite
importer.setDryRun(false); // --dryrun
importer.setDraftsFolder("drafts");
importer.setAssetsFolder("assets");
importer.setAssetReferenceType("relative"); // --assetrefs

// Sources (one or more)
importer.addSource("bluesky", "@11ty.dev");
Expand Down
10 changes: 9 additions & 1 deletion cli.js
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,15 @@ let { positionals, values } = parseArgs({
type: "string",
default: "",
},
assetrefs: {
type: "string",
default: "relative",
},
},
});

let [ type, target ] = positionals;
let { quiet, dryrun, output, help, version, overwrite, cacheduration, format, persist } = values;
let { quiet, dryrun, output, help, version, overwrite, cacheduration, format, persist, assetrefs } = values;

if(version) {
const require = createRequire(import.meta.url);
Expand Down Expand Up @@ -93,6 +97,9 @@ if(help) {
# Change output format (default: markdown)
npx @11ty/import [type] [target] --format=html
# Use absolute asset URLs (default: relative)
npx @11ty/import [type] [target] --assetrefs=absolute
`);

process.exit();
Expand All @@ -119,6 +126,7 @@ importer.addSource(type, target);
// TODO wire these up to CLI
importer.setDraftsFolder("drafts");
importer.setAssetsFolder("assets");
importer.setAssetReferenceType(assetrefs);

if(persist) {
importer.setPersistTarget(persist);
Expand Down
63 changes: 45 additions & 18 deletions src/Fetcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class Fetcher {
#directoryManager;
#assetsFolder = "assets";
#persistManager;
#outputFolder = ".";

constructor() {
this.fetchedUrls = new Set();
Expand All @@ -69,6 +70,7 @@ class Fetcher {
this.isVerbose = true;
this.dryRun = false;
this.safeMode = true;
this.useRelativeAssets = true;
this.counts = {
assets: 0,
};
Expand All @@ -90,6 +92,14 @@ class Fetcher {
this.#assetsFolder = folder;
}

setUseRelativeAssetPaths(use) {
this.useRelativeAssets = Boolean(use);
}

setOutputFolder(dir) {
this.#outputFolder = dir;
}

getCounts() {
return {
assets: this.counts.assets,
Expand All @@ -109,34 +119,51 @@ class Fetcher {
this.#persistManager = manager;
}

async fetchAsset(url, contextEntry) {
getAssetLocation(assetUrl, assetContentType, contextEntry) {
let filename = Fetcher.getFilenameFromSrc(assetUrl, assetContentType);
let assetUrlLocation = path.join(this.#assetsFolder, filename);

// root /assets folder
if(!this.useRelativeAssets) {
return {
url: `/${assetUrlLocation}`,
filePath: path.join(this.#outputFolder, assetUrlLocation),
};
}

let contextPathname;
if(contextEntry.filePath) {
contextPathname = DirectoryManager.getDirectory(contextEntry.filePath);
} else {
// backwards compatibility
contextPathname = Fetcher.getContextPathname(contextEntry.url);
}

return {
url: assetUrlLocation,
filePath: path.join(contextPathname, assetUrlLocation),
}
}

async fetchAsset(assetUrl, contextEntry) {
// Adds protocol from original page URL if a protocol relative URL
if(url.startsWith("//") && contextEntry.url) {
if(assetUrl.startsWith("//") && contextEntry.url) {
let contextUrl = new URL(contextEntry.url);
if(contextUrl.protocol) {
url = `${contextUrl.protocol}${url}`;
assetUrl = `${contextUrl.protocol}${assetUrl}`;
}
}

// TODO move this upstream as a Fetch `alias` feature.
return this.fetch(url, {
return this.fetch(assetUrl, {
type: "buffer",
returnType: "response",
},
{
verbose: true,
showErrors: true,
}).then(result => {
let contextPathname;
if(contextEntry.filePath) {
contextPathname = DirectoryManager.getDirectory(contextEntry.filePath);
} else {
contextPathname = Fetcher.getContextPathname(contextEntry.url);
}
let filename = Fetcher.getFilenameFromSrc(url, result.headers?.["content-type"]);
let assetUrlLocation = path.join(this.#assetsFolder, filename);
let fullOutputLocation = path.join(contextPathname, assetUrlLocation);
let urlValue = assetUrlLocation;
let { url: urlValue, filePath: fullOutputLocation } = this.getAssetLocation(assetUrl, result.headers?.["content-type"], contextEntry);

if(this.writtenAssetFiles.has(fullOutputLocation)) {
return urlValue;
Expand All @@ -147,7 +174,7 @@ class Fetcher {
// TODO compare file contents and skip
if(this.safeMode && fs.existsSync(fullOutputLocation)) {
if(this.isVerbose) {
Logger.skipping("asset", fullOutputLocation, url);
Logger.skipping("asset", fullOutputLocation, assetUrl);
}
return urlValue;
}
Expand All @@ -157,7 +184,7 @@ class Fetcher {
}

if(this.isVerbose) {
Logger.importing("asset", fullOutputLocation, url, {
Logger.importing("asset", fullOutputLocation, assetUrl, {
size: result.body.length,
dryRun: this.dryRun
});
Expand All @@ -172,7 +199,7 @@ class Fetcher {
// Don’t persist assets if upstream post is a draft
if(contextEntry.status !== "draft" && this.#persistManager.canPersist()) {
this.#persistManager.persistFile(fullOutputLocation, result.body, {
url,
assetUrl,
type: "asset",
});
}
Expand All @@ -181,7 +208,7 @@ class Fetcher {
}, error => {
// Error logging happens in .fetch() upstream
// Fetching the asset failed but we don’t want to fail the upstream document promise
return url;
return assetUrl;
});
}

Expand Down
5 changes: 0 additions & 5 deletions src/HtmlTransformer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,12 @@ import posthtml from "posthtml";
import urls from "@11ty/posthtml-urls";

class HtmlTransformer {
#outputFolder = ".";
#fetcher;

setFetcher(fetcher) {
this.#fetcher = fetcher;
}

setOutputFolder(dir) {
this.#outputFolder = dir;
}

async transform(content, entry) {
let options = {
eachURL: async (rawUrl, attr, tagName) => {
Expand Down
12 changes: 11 additions & 1 deletion src/Importer.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,23 @@ class Importer {
this.fetcher.setAssetsFolder(folder);
}

setAssetReferenceType(refType) {
if(refType === "absolute") {
this.fetcher.setUseRelativeAssetPaths(false);
} else if(refType === "relative") {
this.fetcher.setUseRelativeAssetPaths(true);
} else {
throw new Error(`Invalid value for --assetrefs, must be \`relative\` or \`absolute\`. Received: ${refType} (${typeof refType})`);
}
}

setDraftsFolder(dir) {
this.#draftsFolder = dir;
}

setOutputFolder(dir) {
this.#outputFolder = dir;
this.htmlTransformer.setOutputFolder(dir);
this.fetcher.setOutputFolder(dir);
this.markdownService.setOutputFolder(dir);
}

Expand Down
34 changes: 34 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { createRequire } from "node:module";
import { Importer } from "../src/Importer.js";
import { DataSource } from "../src/DataSource.js";
import { Persist } from "../src/Persist.js";
import { Fetcher } from "../src/Fetcher.js";

const require = createRequire(import.meta.url);

Expand Down Expand Up @@ -129,3 +130,36 @@ test("Persist constructor (gitlab)", async (t) => {
message: "Invalid persist type: gitlab"
});
});

test("Fetcher asset location tests (relative)", async (t) => {
let f = new Fetcher();

let relative1 = f.getAssetLocation("https://example.com/test.png", "image/png", { filePath: "/test.html" });
assert.deepEqual(relative1, {
filePath: "assets/test-NzhbK6MSYu2g.png",
url: "assets/test-NzhbK6MSYu2g.png",
});

let relativeNoExt = f.getAssetLocation("https://example.com/test", "image/png", { filePath: "/test.html" });
assert.deepEqual(relativeNoExt, {
filePath: "assets/test-m4HI5oTdgEt4.png",
url: "assets/test-m4HI5oTdgEt4.png",
});

let relative2 = f.getAssetLocation("https://example.com/subdir/test.png", "image/png", { filePath: "localsubdirectory/test.html" });
assert.deepEqual(relative2, {
filePath: "localsubdirectory/assets/test-slaK8pecO8QR.png",
url: "assets/test-slaK8pecO8QR.png",
});
});

test("Fetcher asset location tests (absolute)", async (t) => {
let f = new Fetcher();
f.setUseRelativeAssetPaths(false);

let abs1 = f.getAssetLocation("https://example.com/test.png", "image/png");
assert.deepEqual(abs1, {
filePath: "assets/test-NzhbK6MSYu2g.png",
url: "/assets/test-NzhbK6MSYu2g.png",
});
});

0 comments on commit 78265d3

Please sign in to comment.