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
27 changes: 14 additions & 13 deletions www/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,20 @@ Run `deno run -A main.tsx --help` for the current list. Each parameter is
addressable two ways — as a `--kebab-case` flag or as a `SCREAMING_SNAKE_CASE`
environment variable — and the command line wins when both are supplied.

| Flag | Environment | Default | Purpose |
| ---------------------- | -------------------- | ----------------- | ----------------------------------------------------- |
| `--port` | `PORT` | `8000` | Port the HTTP server listens on. `0` picks a free one |
| `--github-token` | `GITHUB_TOKEN` | _unauthenticated_ | GitHub access token for the API |
| `--jsr-api` | `JSR_API` | _none_ | JSR API token; the score card is skipped without it |
| `--deno-deployment-id` | `DENO_DEPLOYMENT_ID` | fresh id per boot | Deployment identity behind the `ETag` |
| `--effectionx-dir` | `EFFECTIONX_DIR` | _none_ | Local effectionx checkout to read instead of cloning |
| `--clones-dir` | `CLONES_DIR` | `build/clones` | Git clones of the documented repositories |
| `--worktrees-dir` | `WORKTREES_DIR` | `build/worktrees` | A git worktree per documented series |
| `--pagefind-dir` | `PAGEFIND_DIR` | `pagefind` | Generated Pagefind search bundle |
| `--tailwind-input` | `TAILWIND_INPUT` | `main.css` | Tailwind entry stylesheet |
| `--tailwind-outdir` | `TAILWIND_OUTDIR` | `tailwind` | Where the compiled stylesheet is written |
| `--verbose` | `VERBOSE` | `false` | Emit debug and warning logs, not just info and error |
| Flag | Environment | Default | Purpose |
| ---------------------- | -------------------- | --------------------------------- | ----------------------------------------------------- |
| `--port` | `PORT` | `8000` | Port the HTTP server listens on. `0` picks a free one |
| `--github-token` | `GITHUB_TOKEN` | _unauthenticated_ | GitHub access token for the API |
| `--jsr-api` | `JSR_API` | _none_ | JSR API token; the score card is skipped without it |
| `--deno-deployment-id` | `DENO_DEPLOYMENT_ID` | fresh id per boot | Deployment identity behind the `ETag` |
| `--effectionx-dir` | `EFFECTIONX_DIR` | _none_ | Local effectionx checkout to read instead of cloning |
| `--base` | `BASE` | `https://frontside.com/effection` | Origin every page names as the original |
| `--clones-dir` | `CLONES_DIR` | `build/clones` | Git clones of the documented repositories |
| `--worktrees-dir` | `WORKTREES_DIR` | `build/worktrees` | A git worktree per documented series |
| `--pagefind-dir` | `PAGEFIND_DIR` | `pagefind` | Generated Pagefind search bundle |
| `--tailwind-input` | `TAILWIND_INPUT` | `main.css` | Tailwind entry stylesheet |
| `--tailwind-outdir` | `TAILWIND_OUTDIR` | `tailwind` | Where the compiled stylesheet is written |
| `--verbose` | `VERBOSE` | `false` | Emit debug and warning logs, not just info and error |

The defaults reproduce what used to be hardcoded, so `deno task dev` and the
deployment workflow need no flags.
Expand Down
7 changes: 7 additions & 0 deletions www/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ export const www = command(
),
schema(fallback("")),
),
option(
name("base"),
description(
"Origin every page names as the original, whatever origin served it.",
),
schema(fallback("https://frontside.com/effection")),
),
option(
name("clonesDir"),
description("Directory holding git clones of the documented repositories."),
Expand Down
73 changes: 0 additions & 73 deletions www/context/context-api.ts

This file was deleted.

2 changes: 1 addition & 1 deletion www/context/fetch.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Operation, until } from "effection";
import { createApi } from "./context-api.ts";
import { createApi } from "@effectionx/context-api";
import { rewrite } from "./url-rewrite.ts";
import { log } from "./logging.ts";

Expand Down
2 changes: 1 addition & 1 deletion www/context/logging.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Operation } from "effection";
import { createApi } from "./context-api.ts";
import { createApi } from "@effectionx/context-api";

export interface Logger {
info: (message: string, ...args: unknown[]) => Operation<void>;
Expand Down
2 changes: 1 addition & 1 deletion www/context/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { Operation, Stream } from "effection";
import { each, spawn, until, withResolvers } from "effection";
import md5 from "md5";
import { regex } from "arktype";
import { createApi } from "./context-api.ts";
import { createApi } from "@effectionx/context-api";
import { exec, ExecOptions, ProcessResult } from "@effectionx/process";
import { log } from "./logging.ts";
import { cwd, useCwd } from "./shell.ts";
Expand Down
2 changes: 1 addition & 1 deletion www/context/url-rewrite.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Operation } from "effection";
import { createApi } from "./context-api.ts";
import { createApi } from "@effectionx/context-api";

interface UrlRewrite {
rewrite(
Expand Down
62 changes: 62 additions & 0 deletions www/context/url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { type Operation } from "effection";
import { posixNormalize } from "_posixNormalize";
import { type Api, createApi } from "@effectionx/context-api";
import { CurrentRequest } from "./request.ts";

export interface UrlApi {
/**
* Where the site says it lives: the origin every page names as the
* original, whatever origin actually served it. `main` supplies it from
* configuration; `urlApi.around` can rebase it for a narrower scope.
*/
base: string;

/**
* Fully qualify a path against the origin that served the current request,
* so that a document advertises the site a reader is actually on.
*
* Middleware installed with `urlApi.around` sees each path, so an override
* can rebase some paths and leave others alone.
*/
url(path: string): Operation<string>;

/**
* The canonical url for the current path, under `base`.
*
* A preview and the dev server serve the same page from their own origin,
* but every copy names the same original.
*/
canonical(): Operation<string>;
}

// annotated, because `canonical` reads `base` back through the api
export const urlApi: Api<UrlApi> = createApi<UrlApi>("url", {
base: "https://frontside.com/effection",

*url(path) {
let request = yield* CurrentRequest.expect();
let absolute = new URL(path, new URL(request.url).origin);

absolute.pathname = posixNormalize(absolute.pathname);

return absolute.toString();
},

*canonical() {
let request = yield* CurrentRequest.expect();
// through the api, so that an override of `base` reaches here too
let base = yield* urlApi.operations.base;

let requested = new URL(request.url);
let original = new URL(base);

// normalized, because a base without a path contributes its own "/"
original.pathname = posixNormalize(
`${original.pathname}${requested.pathname}`,
);

return String(original);
},
});

export const { base, canonical, url } = urlApi.operations;
1 change: 1 addition & 0 deletions www/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"@std/http": "jsr:@std/http@^1.0.22",
"@types/hast": "npm:@types/hast@3.0.4",
"effection": "npm:effection@^3.6.1",
"@effectionx/context-api": "npm:@effectionx/context-api@^0.6.0",
"@effectionx/deno-deploy": "jsr:@effectionx/deno-deploy@^0.3.1",
"@effectionx/process": "jsr:@effectionx/process@^0.6.2",
"hast": "npm:hast@^1.0.0",
Expand Down
6 changes: 6 additions & 0 deletions www/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { initClones } from "./lib/clones.ts";
import { initOctokitContext } from "./lib/octokit.ts";
import { currentRequestPlugin } from "./plugins/current-request.ts";
import { verboseLogging } from "./context/logging.ts";
import { urlApi } from "./context/url.ts";

// Learn more at https://docs.deno.com/runtime/manual/examples/module_metadata#concepts
if (import.meta.main) {
Expand All @@ -61,6 +62,11 @@ if (import.meta.main) {
function* serve(options: Options) {
yield* verboseLogging(options.verbose);

// the origin pages name as the original, wherever this copy is served
yield* urlApi.around({
base: () => options.base,
});

let { current, series } = yield* useConfig();

// Get stable series (no prereleases) for guides
Expand Down
36 changes: 0 additions & 36 deletions www/plugins/current-request.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import type { RevolutionPlugin } from "revolution";
import { posixNormalize } from "_posixNormalize";
import { type Operation } from "effection";
import { CurrentRequest } from "../context/request.ts";

export function currentRequestPlugin(): RevolutionPlugin {
Expand All @@ -11,37 +9,3 @@ export function currentRequestPlugin(): RevolutionPlugin {
},
};
}

/**
* Convert a non fully qualified url into a fully qualified url, complete
* with protocol.
*/
export function* useAbsoluteUrl(path: string = "/"): Operation<string> {
let absolute = yield* useAbsoluteUrlFactory();

return absolute(path);
}

export function* useAbsoluteUrlFactory(): Operation<(path: string) => string> {
let request = yield* CurrentRequest.expect();

let origin = new URL(request.url).origin;

return (path) => {
let url = new URL(path, origin);
url.pathname = posixNormalize(url.pathname);
return url.toString();
};
}

/**
* Get the canonical url for the current path.
*/
export function* useCanonicalUrl(options: { base: string }): Operation<string> {
let request = yield* CurrentRequest.expect();

let req = new URL(request.url);
let url = new URL(options.base);
url.pathname = `${url.pathname}${req.pathname}`;
return String(url);
}
26 changes: 13 additions & 13 deletions www/plugins/sitemap.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import type { Middleware, RevolutionPlugin } from "revolution";
import { route as revolutionRoute, useRevolutionOptions } from "revolution";
import type { Operation } from "effection";
import { all, type Operation } from "effection";
import { stringify } from "@libs/xml";
import { compile } from "path-to-regexp";
import { useAbsoluteUrlFactory } from "./current-request.ts";
import { url } from "../context/url.ts";

export function sitemapPlugin(): RevolutionPlugin {
return {
*http(request, next) {
let options = yield* useRevolutionOptions();
let url = new URL(request.url);
let requested = new URL(request.url);

if (url.pathname === "/sitemap.xml") {
if (requested.pathname === "/sitemap.xml") {
let app = options.app ?? [];
let paths: RoutePath[] = [];
for (let middleware of app) {
Expand All @@ -21,21 +21,21 @@ export function sitemapPlugin(): RevolutionPlugin {
}
}

let absolute = yield* useAbsoluteUrlFactory();
let entries = yield* all(paths.map(function* (path) {
let { pathname, ...entry } = path;

return {
loc: yield* url(pathname),
...entry,
};
}));

let xml = stringify({
"@version": "1.0",
"@encoding": "UTF-8",
urlset: {
"@xmlns": "http://www.sitemaps.org/schemas/sitemap/0.9",
url: paths.map((path) => {
let { pathname, ...entry } = path;

return {
loc: absolute(pathname),
...entry,
};
}),
url: entries,
},
});

Expand Down
8 changes: 3 additions & 5 deletions www/routes/app.html.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { JSXChild } from "revolution";

import { Footer } from "../components/footer.tsx";
import { Header, type HeaderProps } from "../components/header.tsx";
import { useAbsoluteUrl, useCanonicalUrl } from "../plugins/current-request.ts";
import { canonical, url } from "../context/url.ts";
import { JSXElement } from "revolution/jsx-runtime";

export type Options = {
Expand All @@ -25,11 +25,9 @@ export function* useAppHtml({
head,
image = "/assets/images/meta-effection.png",
}: Options): Operation<({ children, search }: AppHtmlProps) => JSX.Element> {
let ogImageURL = yield* useAbsoluteUrl(image);
let ogImageURL = yield* url(image);

let canonicalURL = yield* useCanonicalUrl({
base: "https://frontside.com/effection",
});
let canonicalURL = yield* canonical();

let header = yield* Header({ hasLeftSidebar });

Expand Down
Loading