Skip to content
Open
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
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ two answers to what had been one question: where the bytes are, and what the
content is called.

`--canonical` separates them. Urls that _name the page_ —
`<link rel=canonical>`, its `<meta property="og:url">` twin, and
`<link rel=alternate>` — are rebased onto it, while navigation, assets and the
sitemap stay on `--base`:
`<link rel=canonical>`, its `<meta property="og:url">` twin, and a
`<link rel=alternate hreflang>` saying it for another language — are rebased
onto it, while navigation, assets and the sitemap stay on `--base`:

```
$ staticalize --site http://localhost:8000 --output dist \
Expand All @@ -54,6 +54,9 @@ The build still browses on its own at `--base`, and search engines are told to
index `--canonical` instead. That is what a preview wants too: readable at its
own alias, indexed as production.

An `alternate` without `hreflang` is left on `--base`: a feed or an `llms.txt`
is a separate file, and a file lives where the build is served.

Textual bodies follow `--canonical`, because a document like `llms.txt` is a map
telling a reader where the docs live rather than a page of the site.

Expand Down
12 changes: 11 additions & 1 deletion downloader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,17 @@ function names(element: { properties?: Record<string, unknown> }): boolean {
let rel = element.properties?.rel;

if (Array.isArray(rel)) {
return rel.includes("canonical") || rel.includes("alternate");
if (rel.includes("canonical")) {
return true;
}

// `alternate` covers two unrelated things. With `hreflang` it is this page
// in another language, which is a claim about the page. Without it — a
// feed, an `llms.txt` — it points at a separate file, which lives wherever
// the build is served.
if (rel.includes("alternate")) {
return Boolean(element.properties?.hrefLang);
}
}

return element.properties?.property === "og:url";
Expand Down
5 changes: 5 additions & 0 deletions test/staticalize.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@ describe("staticalize", () => {
<head>
<link rel="canonical" href="${host}"/>
<link rel="alternate" href="${host}" hreflang="en"/>
<link rel="alternate" type="application/rss+xml" href="${host}feed.xml"/>
<meta property="og:url" content="${host}"/>
<meta property="og:image" content="${host}card.png"/>
<link rel="stylesheet" href="${host}styles.css"/>
Expand All @@ -473,6 +474,7 @@ describe("staticalize", () => {
`),
)
.get("/about", (c) => c.html("<h1>About</h1>"))
.get("/feed.xml", (c) => c.text("<rss/>"))
.get("/card.png", (c) => c.text(""))
.get("/styles.css", (c) => c.text("body {}"))
.get("/main.js", (c) => c.text("console.log('hi')"))
Expand All @@ -499,6 +501,9 @@ describe("staticalize", () => {
);

// these point at bytes, which live where the build is hosted
expect(index).toContain(
`<link rel="alternate" type="application/rss+xml" href="https://interactors.netlify.app/feed.xml">`,
);
expect(index).toContain(
`<link rel="stylesheet" href="https://interactors.netlify.app/styles.css">`,
);
Expand Down
Loading