Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"main.ts",
"staticalize.ts",
"downloader.ts",
"rebase.ts",
"task-buffer.ts",
"config.ts",
"progress.ts",
Expand Down
19 changes: 4 additions & 15 deletions downloader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { fromHtml } from "hast-util-from-html";
import { toHtml } from "hast-util-to-html";
import { selectAll } from "hast-util-select";
import { useTaskBuffer } from "./task-buffer.ts";
import { rebase } from "./rebase.ts";
import { createApi } from "@effectionx/context-api";

export interface Downloader extends Operation<void> {
Expand Down Expand Up @@ -69,11 +70,7 @@ export const DownloadApi = createApi("@staticalize/download", {

// replace self-referencing absolute urls with the destination site
if (href.startsWith(host.origin)) {
let url = new URL(href);
url.host = base.host;
url.port = base.port;
url.protocol = base.protocol;
link.properties.href = url.href;
link.properties.href = rebase(new URL(href), base).href;
}
}

Expand All @@ -85,11 +82,7 @@ export const DownloadApi = createApi("@staticalize/download", {

// replace self-referencing absolute urls with the destination site
if (src.startsWith(host.origin)) {
let url = new URL(src);
url.host = base.host;
url.port = base.port;
url.protocol = base.protocol;
element.properties.src = url.href;
element.properties.src = rebase(new URL(src), base).href;
}
}

Expand All @@ -98,11 +91,7 @@ export const DownloadApi = createApi("@staticalize/download", {
let attr = String(element.properties.content);
if (attr.startsWith(host.origin)) {
yield* downloader.download(attr, source);
let url = new URL(attr);
url.host = base.host;
url.port = base.port;
url.protocol = base.protocol;
element.properties.content = url.href;
element.properties.content = rebase(new URL(attr), base).href;
}
}

Expand Down
22 changes: 22 additions & 0 deletions rebase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Map a url on the site being crawled onto the public base url.
*
* Everything the base says about where the site lives is honored, including
* its path, so a site published under a subpath such as
* `https://frontside.com/effection` gets that prefix on every url rewritten
* into it. The source's own path, query and fragment are preserved.
*
* @param source url on the crawled site
* @param base public base url of the site, path included
* @returns the equivalent url on the base
*/
export function rebase(source: URL, base: URL): URL {
let url = new URL(source.href);
url.protocol = base.protocol;
url.host = base.host;
url.port = base.port;
// `source.pathname` always begins with a `/`, so the prefix must not end
// with one. A base without a path contributes `/`, and therefore nothing.
url.pathname = `${base.pathname.replace(/\/$/, "")}${source.pathname}`;
return url;
}
11 changes: 4 additions & 7 deletions staticalize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ensureDir } from "@std/fs/ensure-dir";
import { stringify } from "@libs/xml/stringify";
import { parse } from "@libs/xml/parse";
import { useDownloader } from "./downloader.ts";
import { rebase } from "./rebase.ts";

export interface StaticalizeOptions {
host: URL;
Expand Down Expand Up @@ -93,13 +94,9 @@ export function useStaticalizer(
let xml = stringify({
urlset: {
"@xmlns": "http://www.sitemaps.org/schemas/sitemap/0.9",
"url": [...urls].map((url) => {
let loc = new URL(url);
loc.host = base.host;
loc.port = base.port;
loc.protocol = base.protocol;
return { loc: { "#text": loc } };
}),
"url": [...urls].map((url) => ({
loc: { "#text": rebase(new URL(url), base) },
})),
},
});
yield* call(() =>
Expand Down
46 changes: 46 additions & 0 deletions test/staticalize.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,52 @@ describe("staticalize", () => {
`<meta property="og:url" content="https://fs.com/image.png">`,
);
});

it("carries the path of the base url into every rewritten url", async () => {
app.get(
"/",
(c) =>
c.html(`
<html>
<head>
<link rel="canonical" href="${host}"/>
<meta property="og:url" content="${host}image.png"/>
<script src="${host}main.js"></script>
</head>
<body></body>
</html>
`),
)
.get("/about", (c) => c.html("<h1>About</h1>"))
.get("/image.png", (c) => c.text(""))
.get("/main.js", (c) => c.text("console.log('hi')"))
.get(...sitemap(["/", "/about"]));

await staticalize({
base: new URL("https://frontside.com/effection"),
host,
dir: "test/dist",
});

let index = await content("test/dist/index.html");
expect(index).toContain(
`<link rel="canonical" href="https://frontside.com/effection/">`,
);
expect(index).toContain(
`<script src="https://frontside.com/effection/main.js">`,
);
expect(index).toContain(
`<meta property="og:url" content="https://frontside.com/effection/image.png">`,
);

let sitemapXML = await Deno.readTextFile("test/dist/sitemap.xml");
expect(sitemapXML).toContain(
"<loc>https://frontside.com/effection/</loc>",
);
expect(sitemapXML).toContain(
"<loc>https://frontside.com/effection/about</loc>",
);
});
});

async function content(path: string): Promise<string> {
Expand Down
Loading