diff --git a/_components/Footer.tsx b/_components/Footer.tsx
index 5365b3691..85a3b41e1 100644
--- a/_components/Footer.tsx
+++ b/_components/Footer.tsx
@@ -59,7 +59,7 @@ const data = [
},
{
label: "Deno API Reference",
- href: "/api/deno/~/Deno",
+ href: "/api/deno/",
},
],
},
diff --git a/_components/SidebarNav/comp.tsx b/_components/SidebarNav/comp.tsx
index f08f52d06..4e44d7c50 100644
--- a/_components/SidebarNav/comp.tsx
+++ b/_components/SidebarNav/comp.tsx
@@ -4,7 +4,7 @@ import ReferenceSidebarNav from "../../reference/_components/ReferenceSidebarNav
export default function (data: Lume.Data) {
const sectionData = data.sectionData;
const currentUrl = data.currentUrl.replace(/\/$/, "");
- const isReference = currentUrl.startsWith("/api/");
+ const isReference = currentUrl === "/api" || currentUrl.startsWith("/api/");
if (isReference) {
return ;
diff --git a/_includes/layout.tsx b/_includes/layout.tsx
index f79bd04d7..b022c362b 100644
--- a/_includes/layout.tsx
+++ b/_includes/layout.tsx
@@ -148,7 +148,7 @@ export default function Layout(data: Lume.Data) {
/>
+
+
+ {data.intro}
+
+
+ {data.groups.map(({ title, url, slug, group }) => (
+
+
+
+ {group.sections.flatMap((section) =>
+ section.nodes.map((node) => (
+
+
+
+ {node.name}
+
+ {node.docs && (
+
+ )}
+
+ ))
+ )}
+
+
+ ))}
+
+ );
+}
diff --git a/_includes/reference/landing.tsx b/_includes/reference/landing.tsx
new file mode 100644
index 000000000..84dcca9d5
--- /dev/null
+++ b/_includes/reference/landing.tsx
@@ -0,0 +1,86 @@
+export const layout = "doc.tsx";
+
+export interface LandingTile {
+ title: string;
+ href: string;
+ description: string | null;
+}
+
+export interface LandingSection {
+ key: string;
+ title: string;
+ description: string;
+ href: string;
+ allSymbolsHref: string;
+ tiles: LandingTile[];
+ /** Render tiles in a denser grid (used for the long Node module list). */
+ dense?: boolean;
+}
+
+export interface LandingData {
+ intro: string;
+ sections: LandingSection[];
+}
+
+/** The /api/ hub: every API group presented as a tile grid, generated from
+ * the same grouping data as the reference pages themselves. */
+export default function ApiLanding(
+ { data }: { data: LandingData } & Lume.Data,
+ _helpers: Lume.Helpers,
+) {
+ return (
+
+ API reference
+ {data.intro}
+
+ {data.sections.map((section) => (
+
+
+
+ {section.description}
+
+
+
+ ))}
+
+ );
+}
diff --git a/_includes/reference/multiSymbol.tsx b/_includes/reference/multiSymbol.tsx
new file mode 100644
index 000000000..088efd8ab
--- /dev/null
+++ b/_includes/reference/multiSymbol.tsx
@@ -0,0 +1,52 @@
+import type { Group } from "../../reference/_lib/group.ts";
+
+export const layout = "doc.tsx";
+
+/** A grouped reference page: every symbol of a category (Deno, Web) or
+ * module (Node) documented on a single page โ a symbol index up top,
+ * full documentation below. */
+export default function MultiSymbol(
+ { data, comp }: { data: Group } & Lume.Data,
+ _helpers: Lume.Helpers,
+) {
+ return (
+
+
+
+ {data.title}
+ {data.docs &&
}
+
+
+
+
+ {data.sections.map((section) => (
+
+
{section.title}
+
+
+ ))}
+
+
+
+
+
+ {data.symbols.map((symbol) => (
+
+ ))}
+
+
+
+ );
+}
+
+function SymbolDivider(
+ // deno-lint-ignore no-explicit-any
+ { comp, symbol }: { comp: any; symbol: Group["symbols"][number] },
+) {
+ return (
+ <>
+
+
+ >
+ );
+}
diff --git a/api/deno/index.md b/api/deno/index.md
index 35572b869..e3557127a 100644
--- a/api/deno/index.md
+++ b/api/deno/index.md
@@ -2,6 +2,7 @@
title: "Deno Namespace APIs"
description: "A guide to Deno's built-in runtime APIs. Learn about file system operations, network functionality, permissions management, and other core capabilities available through the global Deno namespace."
layout: doc.tsx
+url: /api/deno/about/
oldUrl:
- /runtime/manual/runtime/
- /runtime/manual/runtime/builtin_apis/
diff --git a/api/node/index.md b/api/node/index.md
index ac5afd6df..b7e497de6 100644
--- a/api/node/index.md
+++ b/api/node/index.md
@@ -2,6 +2,7 @@
title: "Node.js Built-in APIs"
description: "Complete reference for Node.js built-in modules and globals supported in Deno. Explore Node.js APIs including fs, http, crypto, process, buffer, and more with compatibility information."
layout: doc.tsx
+url: /api/node/about/
oldUrl:
- /runtime/manual/node/compatibility/
- /runtime/manual/npm_nodejs/compatibility_mode/
diff --git a/api/web/index.md b/api/web/index.md
index a819cec84..1e9776794 100644
--- a/api/web/index.md
+++ b/api/web/index.md
@@ -2,6 +2,7 @@
title: "Web Platform APIs"
description: "A guide to the Web Platform APIs available in Deno. Learn about fetch, events, workers, storage, and other web standard APIs, including implementation details and deviations from browser specifications."
layout: doc.tsx
+url: /api/web/about/
oldUrl:
- /runtime/manual/runtime/navigator_api/
- /runtime/manual/runtime/web_platform_apis/
diff --git a/middleware/redirects.ts b/middleware/redirects.ts
index 6a7f8af92..1e01e5f97 100644
--- a/middleware/redirects.ts
+++ b/middleware/redirects.ts
@@ -140,6 +140,23 @@ function loadFromJson() {
);
}
+ // Redirects from old per-symbol API reference URLs to their anchor on the
+ // grouped reference pages, generated by reference/reference.page.ts. The
+ // file does not exist yet (it is produced by a later step of the reference
+ // rearchitecture), in which case this is a no-op.
+ if (existsSync("./_site/api/_redirects.json")) {
+ const apiRedirects = JSON.parse(
+ new TextDecoder().decode(
+ Deno.readFileSync("./_site/api/_redirects.json"),
+ ),
+ ) as Record
;
+ redirects = { ...apiRedirects, ...redirects };
+ } else {
+ log.warn(
+ `๐ redirectsMiddleware : No './_site/api/_redirects.json' found.`,
+ );
+ }
+
log.info(
`๐ redirectsMiddleware : Total number of redirects loaded: ${
Object.keys(redirects).length
@@ -154,8 +171,6 @@ function addGoLinksAndRedirectLinks(redirects: Record) {
`๐ addGoLinksAndRedirectLinks: Adding additional redirects...`,
);
- redirects["/api/"] = "/api/deno/";
-
log.debug(
`๐ redirectsMiddleware : Reading redirects from 'go.json'...`,
);
diff --git a/orama/generate_orama_index_full.ts b/orama/generate_orama_index_full.ts
index b9b70f1ab..4bd9039dc 100644
--- a/orama/generate_orama_index_full.ts
+++ b/orama/generate_orama_index_full.ts
@@ -16,6 +16,7 @@
import { walk } from "@std/fs";
import { fromFileUrl, join, relative } from "@std/path";
+import { buildReference } from "../reference/_lib/group.ts";
import { MarkdownIndexer } from "./indexing/MarkdownIndexer.ts";
import { FileSelector } from "./identification/FileSelector.ts";
import { IndexCollection } from "./indexing/IndexCollection.ts";
@@ -316,6 +317,8 @@ function processApiSymbol(
async function processApiReference(): Promise {
console.log("\n๐ง Processing API reference documentation...");
const apiDocs: OramaFullDocument[] = [];
+ // deno-lint-ignore no-explicit-any
+ const referenceJsons: Record = {};
for (const refFile of REFERENCE_FILES) {
const fullPath = join(ROOT_DIR, refFile.path);
@@ -326,6 +329,7 @@ async function processApiReference(): Promise {
const content = await Deno.readTextFile(fullPath);
const data = JSON.parse(content);
+ referenceJsons[refFile.apiType] = data;
let processedCount = 0;
let skippedCount = 0;
@@ -362,6 +366,26 @@ async function processApiReference(): Promise {
}
}
+ // Symbols now live as anchors on grouped reference pages; remap the old
+ // per-symbol URLs so search results land directly on the right anchor.
+ if (referenceJsons.deno && referenceJsons.web && referenceJsons.node) {
+ try {
+ const { redirects } = buildReference(referenceJsons);
+ let remapped = 0;
+ for (const doc of apiDocs) {
+ const target = redirects[doc.path];
+ if (target) {
+ doc.path = target;
+ doc.url = `${BASE_URL}${target}`;
+ remapped++;
+ }
+ }
+ console.log(` Remapped ${remapped} API doc URLs to grouped pages.`);
+ } catch (error) {
+ console.warn(`Could not remap API URLs to grouped pages: ${error}`);
+ }
+ }
+
return apiDocs;
}
diff --git a/reference/_components/ReferenceSidebarNav.tsx b/reference/_components/ReferenceSidebarNav.tsx
index 635403572..c5f84629a 100644
--- a/reference/_components/ReferenceSidebarNav.tsx
+++ b/reference/_components/ReferenceSidebarNav.tsx
@@ -8,9 +8,8 @@ export default function ReferenceSidebarNav(data: Lume.Data) {
title: "Deno APIs",
key: "deno",
basePath: "/api/deno",
- categoryLabel: "Deno APIs by category",
items: [
- { href: "/api/deno", title: "Deno specific APIs" },
+ { href: "/api/deno/about/", title: "About", isBottom: true },
{
href: "/api/deno/all_symbols",
title: "All Deno symbols",
@@ -22,9 +21,8 @@ export default function ReferenceSidebarNav(data: Lume.Data) {
title: "Web APIs",
key: "web",
basePath: "/api/web",
- categoryLabel: "Web APIs by category",
items: [
- { href: "/api/web", title: "Web Platform Support" },
+ { href: "/api/web/about/", title: "About", isBottom: true },
{
href: "/api/web/all_symbols",
title: "All web symbols",
@@ -36,9 +34,8 @@ export default function ReferenceSidebarNav(data: Lume.Data) {
title: "Node APIs",
key: "node",
basePath: "/api/node",
- categoryLabel: "Node APIs by namespace",
items: [
- { href: "/api/node", title: "Node support in deno" },
+ { href: "/api/node/about/", title: "About", isBottom: true },
{
href: "/api/node/all_symbols",
title: "All node symbols",
@@ -87,7 +84,11 @@ function ApiSection({ section, currentUrl, apiCategories }: {
return (
-
+
{/* Main section items */}
{topItems.map((item: any) => (
@@ -100,26 +101,14 @@ function ApiSection({ section, currentUrl, apiCategories }: {
))}
- {/* Category accordion */}
-
-
- {section.categoryLabel}
-
-
-
-
-
+ {/* Categories, flat */}
+
- {/* Bottom items (All symbols) */}
+ {/* Bottom items (About, All symbols) */}
{bottomItems.map((item: any) => {
const isActive = item.href.replace(/\/$/, "") === currentUrl;
return (
diff --git a/reference/_components/SymbolArticle.tsx b/reference/_components/SymbolArticle.tsx
new file mode 100644
index 000000000..30b92faca
--- /dev/null
+++ b/reference/_components/SymbolArticle.tsx
@@ -0,0 +1,62 @@
+import type { GroupSymbol } from "../_lib/group.ts";
+
+/** Full documentation for one root symbol on a grouped reference page.
+ * Adapted from SymbolGroup.tsx, but heading at h2 level with a stable anchor
+ * so many symbols can share a page. */
+export default function (
+ // deno-lint-ignore no-explicit-any
+ { comp, symbol }: { comp: any; symbol: GroupSymbol },
+) {
+ const group = symbol.symbolGroup;
+ return (
+
+ {group.symbols.map((item, i) => (
+
+
+
+ {item.kind.title_lowercase}
+ {" "}
+
+ {group.name}
+
+
+ {item.subtitle && (
+
+ {item.subtitle.kind === "class"
+ ?
+ : (
+
+ )}
+
+ )}
+ {(item.tags && item.tags.length > 0) && (
+
+ {item.tags.map((tag) => (
+
+ ))}
+
+ )}
+
+ {i === 0 &&
}
+
+ {item.deprecated && (
+
+ )}
+
+
+ {item.content.map((contentItem) => (
+ contentItem.kind === "function"
+ ?
+ :
+ ))}
+
+
+ ))}
+
+ );
+}
diff --git a/reference/reference.page.ts b/reference/reference.page.ts
index ea34ab154..8dcc16deb 100644
--- a/reference/reference.page.ts
+++ b/reference/reference.page.ts
@@ -1,4 +1,11 @@
import type { Page } from "@deno/doc";
+import { type ApiKind, buildReference, type Group } from "./_lib/group.ts";
+import denoCategories from "../reference_gen/deno-categories.json" with {
+ type: "json",
+};
+import webCategories from "../reference_gen/web-categories.json" with {
+ type: "json",
+};
export const layout = "raw.tsx";
@@ -21,123 +28,229 @@ export const sidebar = [
},
];
-const kinds = [
- { path: "reference_gen/gen/deno.json", name: "Deno" },
- { path: "reference_gen/gen/web.json", name: "Web" },
- { path: "reference_gen/gen/node.json", name: "Node" },
-] as const;
+const kinds: { path: string; kind: ApiKind; title: string }[] = [
+ { path: "reference_gen/gen/deno.json", kind: "deno", title: "Deno" },
+ { path: "reference_gen/gen/web.json", kind: "web", title: "Web" },
+ { path: "reference_gen/gen/node.json", kind: "node", title: "Node" },
+];
export default function* () {
+ if (Deno.env.get("SKIP_REFERENCE") === "1") {
+ console.warn("โญ๏ธ Reference docs generation skipped (SKIP_REFERENCE set)");
+ return;
+ }
+
+ const jsons = {} as Record>;
try {
- if (Deno.env.get("SKIP_REFERENCE") === "1") {
- console.warn("โญ๏ธ Reference docs generation skipped (SKIP_REFERENCE set)");
- return;
+ for (const { path, kind } of kinds) {
+ console.log(`๐ Loading ${kind} reference docs from ${path}...`);
+ jsons[kind] = JSON.parse(Deno.readTextFileSync(path));
+ console.log(
+ `โ
Loaded ${kind} reference docs (${
+ Object.keys(jsons[kind]).length
+ } entries)`,
+ );
+ }
+ } catch (ex) {
+ console.warn(
+ "โ ๏ธ Reference docs were not generated. Run 'deno task generate:reference' first.",
+ ex,
+ );
+ return;
+ }
+
+ const { groups, redirects, warnings, rewritePage } = buildReference(jsons, {
+ deno: denoCategories,
+ web: webCategories,
+ });
+
+ if (warnings.length > 0) {
+ const logPath = "reference_gen/gen/reference-warnings.log";
+ Deno.writeTextFileSync(logPath, warnings.join("\n") + "\n");
+ console.warn(
+ `โ ๏ธ ${warnings.length} reference link warnings (dead links in source docs). Full list: ${logPath}`,
+ );
+ }
+
+ for (const { kind, title } of kinds) {
+ for (const group of groups[kind]) {
+ yield {
+ url: group.url,
+ title: `${group.title} - ${title} documentation`,
+ description: descriptionFor(group),
+ layout: "reference/multiSymbol.tsx",
+ data: {
+ ...group,
+ toc_ctx: {
+ usages: null,
+ top_symbols: null,
+ document_navigation_str: null,
+ document_navigation: group.toc,
+ },
+ },
+ };
}
- for (const { path, name } of kinds) {
- console.log(`๐ Loading ${name} reference docs from ${path}...`);
-
- let json: Record;
- try {
- const fileContent = Deno.readTextFileSync(path);
- json = JSON.parse(fileContent);
- console.log(
- `โ
Successfully loaded ${name} reference docs (${
- Object.keys(json).length
- } entries)`,
- );
- } catch (readError) {
- console.error(`โ Failed to read ${path}:`, readError);
- console.error(` Current working directory: ${Deno.cwd()}`);
-
- // Check if the file exists
- try {
- const stat = Deno.statSync(path);
- console.error(
- ` File exists but read failed. Size: ${stat.size} bytes`,
- );
- } catch {
- console.error(` File does not exist at: ${path}`);
-
- // Check if the gen directory exists
- try {
- Deno.statSync("reference_gen/gen");
- console.error(` Gen directory exists, contents:`);
- for (const entry of Deno.readDirSync("reference_gen/gen")) {
- console.error(
- ` - ${entry.name} (${entry.isFile ? "file" : "dir"})`,
- );
- }
- } catch {
- console.error(
- ` Gen directory does not exist at reference_gen/gen`,
- );
- console.error(
- ` Run 'deno task generate:reference' to generate the reference docs`,
- );
- }
- }
- throw readError;
- }
- for (
- const [filepath, content] of Object.entries(json)
- ) {
- if (content.kind === "search") {
- continue;
- }
-
- // Skip generating index pages since we have static versions
- if (
- (name === "Deno" || name === "Web" || name === "Node") &&
- filepath === "./index.json"
- ) {
- continue;
- }
-
- const trailingLength = filepath.endsWith("index.json")
- ? -"index.json".length
- : -".json".length;
-
- // Remove leading "./" if present
- let normalizedPath = filepath.slice(0, trailingLength);
- if (normalizedPath.startsWith("./")) {
- normalizedPath = normalizedPath.slice(2);
- }
-
- const url = `/api/${name.toLowerCase()}/${normalizedPath}`;
-
- if ("path" in content) {
- // TODO: handle redirects in a more integrated manner
-
- yield {
- url,
- content:
- ` `,
- };
-
- continue;
- }
-
- let layout;
- if (content.kind === "IndexCtx") {
- layout = "index";
- } else if (content.kind === "AllSymbolsCtx") {
- layout = "allSymbols";
- } else if (content.kind === "SymbolPageCtx") {
- layout = "symbol";
- } else {
- throw `unknown page kind: ${(content as { kind: string }).kind}`;
- }
-
- yield {
- url,
- title: content.html_head_ctx.title,
- layout: `reference/${layout}.tsx`,
- data: content,
- };
- }
+ // The "view all symbols" page, with links rewritten to the new URLs.
+ for (const [filepath, content] of Object.entries(jsons[kind])) {
+ if (content.kind !== "AllSymbolsCtx") continue;
+ const normalized = filepath.replace(/^\.\//, "").replace(/\.json$/, "");
+ yield {
+ url: `/api/${kind}/${normalized}`,
+ title: `All Symbols - ${title} documentation`,
+ layout: "reference/allSymbols.tsx",
+ data: rewritePage(kind, filepath, content),
+ };
}
- } catch (ex) {
- console.warn("โ ๏ธ Reference docs were not generated." + ex);
}
+
+ // Landing page per API kind: a dense Ctrl+F-able index of every symbol.
+ // The hand-written guides that previously lived at these URLs moved to
+ // /api//about/ (see api/*/index.md).
+ const kindMeta = {
+ deno: {
+ title: "Deno APIs",
+ intro:
+ "Every API in the global Deno namespace, by category. Use your browser's find-in-page to locate a symbol, then click through for full documentation.",
+ aboutLabel: "About the Deno namespace",
+ },
+ web: {
+ title: "Web APIs",
+ intro:
+ "Every web platform API implemented by Deno, by category. Use your browser's find-in-page to locate a symbol, then click through for full documentation.",
+ aboutLabel: "About web platform support",
+ },
+ node: {
+ title: "Node APIs",
+ intro:
+ "Every Node.js built-in module supported by Deno and its exports. Use your browser's find-in-page to locate a symbol, then click through for full documentation.",
+ aboutLabel: "About Node.js compatibility",
+ },
+ } as const;
+
+ for (const { kind } of kinds) {
+ const meta = kindMeta[kind];
+ yield {
+ url: `/api/${kind}/`,
+ title: `${meta.title} reference`,
+ description: meta.intro,
+ fullWidth: true,
+ layout: "reference/apiIndex.tsx",
+ data: {
+ title: meta.title,
+ intro: meta.intro,
+ aboutUrl: `/api/${kind}/about/`,
+ aboutLabel: meta.aboutLabel,
+ allSymbolsUrl: `/api/${kind}/all_symbols`,
+ groups: groups[kind].map((group) => ({
+ title: group.title === "Uncategorized" ? "Other APIs" : (
+ kind === "node" ? `node:${group.slug}` : group.title
+ ),
+ url: group.url,
+ slug: group.slug.replace(/\//g, "-"),
+ group,
+ })),
+ },
+ };
+ }
+
+ // The /api/ hub: tile grids for every API group, derived from the same
+ // grouping data as the pages themselves.
+ yield {
+ url: "/api/",
+ title: "API reference",
+ description:
+ "Complete API reference for Deno: built-in Deno APIs, supported Web APIs, and Node.js compatibility modules.",
+ fullWidth: true,
+ layout: "reference/landing.tsx",
+ data: {
+ intro:
+ "The complete API reference for Deno: runtime APIs available in the Deno namespace, supported web platform APIs, and the Node.js built-in modules Deno provides for compatibility.",
+ sections: [
+ {
+ key: "deno",
+ title: "Deno APIs",
+ description:
+ "Non-standard APIs in the global Deno namespace: file system, network, subprocesses, testing, FFI, and more.",
+ href: "/api/deno/",
+ allSymbolsHref: "/api/deno/all_symbols",
+ tiles: categoryTiles(groups.deno),
+ },
+ {
+ key: "web",
+ title: "Web APIs",
+ description:
+ "Web platform standards implemented by Deno: fetch, streams, workers, crypto, and other browser-compatible APIs.",
+ href: "/api/web/",
+ allSymbolsHref: "/api/web/all_symbols",
+ tiles: categoryTiles(groups.web),
+ },
+ {
+ key: "node",
+ title: "Node APIs",
+ description:
+ "Node.js built-in modules supported in Deno for backwards compatibility, importable via the node: scheme.",
+ href: "/api/node/",
+ allSymbolsHref: "/api/node/all_symbols",
+ tiles: groups.node
+ .map((group) => ({
+ title: `node:${group.slug}`,
+ href: group.url,
+ description: tileDescription(group.docs),
+ }))
+ .sort((a, b) => a.title.localeCompare(b.title)),
+ dense: true,
+ },
+ ],
+ },
+ };
+
+ console.log(
+ `๐ Reference pages: ${
+ Object.values(groups).reduce((a, g) => a + g.length, 0)
+ } grouped pages, ${Object.keys(redirects).length} redirects`,
+ );
+
+ // Consumed by middleware/redirects.ts to 301 old per-symbol URLs to their
+ // anchor on the grouped pages.
+ yield {
+ url: "/api/_redirects.json",
+ content: JSON.stringify(redirects),
+ };
+}
+
+/** First sentence of a rendered HTML docs block, as plain text. */
+function tileDescription(docs: string | null): string | null {
+ if (!docs) return null;
+ const text = docs.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
+ if (text.length === 0) return null;
+ const sentence = text.match(/^.*?[.!?](?=\s|$)/)?.[0] ?? text;
+ return sentence.length > 140 ? sentence.slice(0, 137) + "..." : sentence;
+}
+
+function categoryTiles(groups: Group[]) {
+ return groups
+ .map((group) => ({
+ // The generated catch-all category reads poorly on a landing page.
+ title: group.title === "Uncategorized" ? "Other APIs" : group.title,
+ href: group.url,
+ description: tileDescription(group.docs),
+ }))
+ .sort((a, b) =>
+ (a.title === "Other APIs" ? 1 : 0) - (b.title === "Other APIs" ? 1 : 0) ||
+ a.title.localeCompare(b.title)
+ );
+}
+
+function descriptionFor(group: Group): string {
+ const fallbacks: Record = {
+ deno: `API reference for Deno's built-in ${group.title} APIs.`,
+ web: `API reference for the ${group.title} Web APIs available in Deno.`,
+ node: `API reference for the node:${group.slug} module in Deno.`,
+ };
+ const fallback = fallbacks[group.apiKind];
+ if (!group.docs) return fallback;
+ const text = group.docs.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
+ if (text.length === 0) return fallback;
+ return text.length > 160 ? text.slice(0, 157) + "..." : text;
}
diff --git a/server.ts b/server.ts
index 749821a8a..b0afa2415 100644
--- a/server.ts
+++ b/server.ts
@@ -10,7 +10,9 @@ import expires from "lume/middlewares/expires.ts";
import createLlmsFilesMiddleware from "./middleware/llmsFiles.ts";
import createMarkdownSourceMiddleware from "./middleware/markdownSource.ts";
-export const server = new Server({ root: "_site" });
+const port = Number(Deno.env.get("PORT") ?? 8000);
+
+export const server = new Server({ root: "_site", port });
server.use(redirectsMiddleware);
server.use(createMarkdownSourceMiddleware({ root: "_site" }));
@@ -28,4 +30,4 @@ server.use(expires({
server.start();
-console.log("Listening on http://localhost:8000");
+console.log(`Listening on http://localhost:${port}`);