Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
387bdda
refactor(core): move each CLI command into its own folder (AI-assisted)
slorber Nov 5, 2026
69feb02
test(core): capture how docusaurus serve serves static files (AI-assi…
slorber Nov 5, 2026
b2f3b65
refactor: apply lint autofix
slorber Nov 5, 2026
babf058
test(core): skip the serve snapshot that differs on Windows (AI-assis…
slorber Nov 5, 2026
d2d259b
test(core): check that serve --build builds the --dir it serves (AI-a…
slorber Nov 5, 2026
8a34973
test(core): ignore the encoded path spelling in the serve tests only …
slorber Nov 5, 2026
7c8d318
refactor(core): move listenToServer into the serve command folder (AI…
slorber Nov 5, 2026
7dd25ca
Merge remote-tracking branch 'origin/claude/commands-own-folders' int…
slorber Nov 5, 2026
d705d34
Merge main into claude/serve-test-suite-533e77 (AI-assisted)
slorber Nov 5, 2026
357921d
refactor(core): extract the serve request handler into its own module…
slorber Nov 5, 2026
1e14b38
Merge claude/serve-request-handler into claude/serve-test-suite-533e7…
slorber Nov 5, 2026
9bb24b1
refactor(core): extract the serve redirect rules into a pure function…
slorber Nov 5, 2026
106545d
Merge claude/serve-redirect-location into claude/serve-test-suite-533…
slorber Nov 5, 2026
d335bbd
Merge main into claude/serve-test-suite-533e77 (AI-assisted)
slorber Nov 5, 2026
f34eea3
test(core): focus the serve tests on the request handler, capture its…
slorber Nov 5, 2026
a4797fe
test(core): capture how serve handles asset extensions of 1 to 5 char…
slorber Nov 6, 2026
1770a6b
test(core): capture that serve 404s folders with a dot in their name …
slorber Nov 6, 2026
7b848dd
test(core): move the serve test helpers into testUtils (AI-assisted)
slorber Nov 6, 2026
9f9d40a
test(core): tie the serve Windows skips to the looksLikeAsset bug (AI…
slorber Nov 6, 2026
029d9b1
test(core): capture the serve looksLikeAsset bug on Windows too (AI-a…
slorber Nov 6, 2026
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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file not shown.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Large diffs are not rendered by default.

151 changes: 151 additions & 0 deletions packages/docusaurus/src/commands/serve/__tests__/testUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import http from 'node:http';
import path from 'node:path';
import {pathExists} from '@docusaurus/fs';
import {listenToServer} from '../listenToServer';
import {createServeRequestHandler} from '../requestHandler';
import type {AddressInfo} from 'node:net';
import type {DocusaurusConfig} from '@docusaurus/types';

export type ServeOptions = Pick<DocusaurusConfig, 'baseUrl' | 'trailingSlash'>;

const fixtureOutDir = path.join(__dirname, '__fixtures__/build');

// Added by the test server when the request handler rejects
const CrashHeader = 'x-test-crash';

const MaxRedirects = 5;

// The request paths are appended to the baseUrl
export async function describeServing(
options: ServeOptions,
requestPaths: string[],
): Promise<string> {
const baseUrlPrefix = options.baseUrl.replace(/\/$/, '');
const paths = requestPaths.map((requestPath) => baseUrlPrefix + requestPath);
return describeRequests(options, paths);
}

// One line per request, aligned to be easy to review in snapshots
export async function describeRequests(
options: ServeOptions,
paths: string[],
): Promise<string> {
const descriptions = await withServer(options, (origin) =>
Promise.all(
paths.map((requestPath) => describeRequest(origin, requestPath)),
),
);
const pathWidth = Math.max(...paths.map((requestPath) => requestPath.length));
return paths
.map(
(requestPath, index) =>
`${requestPath.padEnd(pathWidth)} ${descriptions[index]}`,
)
.join('\n');
}

export async function serveRequest(
options: ServeOptions,
requestPath: string,
): Promise<string> {
return withServer(options, (origin) => describeRequest(origin, requestPath));
}

async function withServer<T>(
options: ServeOptions,
callback: (origin: string) => Promise<T>,
): Promise<T> {
const handleRequest = createServeRequestHandler({
outDir: fixtureOutDir,
...options,
});
const server = http.createServer((req, res) => {
handleRequest(req, res).catch((error: NodeJS.ErrnoException) =>
respondWithCrash(res, error),
);
});

await listenToServer({server, host: '127.0.0.1', port: 0});
try {
const {port} = server.address() as AddressInfo;
return await callback(`http://127.0.0.1:${port}`);
} finally {
server.closeAllConnections();
server.close();
}
}

// In production, this rejection is unhandled and crashes the server process
function respondWithCrash(
res: http.ServerResponse,
error: NodeJS.ErrnoException,
) {
if (res.headersSent) {
res.destroy();
return;
}
// A failed writeHead() can leave the Content-Length of the file to serve
res.writeHead(500, {
[CrashHeader]: error.code ?? 'unknown',
'Content-Length': 0,
});
res.end();
}

/**
* Requests a path and follows its redirects, to describe how it's served:
* "301 /file → 200 file.html"
*/
async function describeRequest(
origin: string,
requestPath: string,
): Promise<string> {
const descriptions: string[] = [];

// Concatenated: new URL('//file', origin) would read "file" as a host name
let url = origin + requestPath;
while (descriptions.length <= MaxRedirects) {
const response = await fetch(url, {redirect: 'manual'});
descriptions.push(await describeResponse(response));

const location = response.headers.get('location');
if (location === null) {
break;
}
url = new URL(location, url).href;
}

return descriptions.join(' → ');
}

async function describeResponse(response: Response): Promise<string> {
const body = await response.text();

const crashCode = response.headers.get(CrashHeader);
if (crashCode !== null) {
return `crash ${crashCode}`;
}

const location = response.headers.get('location');
if (location !== null) {
return `${response.status} ${location}`;
}

const servedFile = (await getServedFixtureFile(body)) ?? response.statusText;
return `${response.status} ${servedFile}`;
}

// Each fixture build file contains its own path
async function getServedFixtureFile(body: string): Promise<string | undefined> {
const filePath = body.trim();
const isFixtureFile =
filePath !== '' && (await pathExists(path.join(fixtureOutDir, filePath)));
return isFixtureFile ? filePath : undefined;
}
1 change: 1 addition & 0 deletions project-words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -352,3 +352,4 @@ zedspencermilnes
Zhou
zpao
ödingers
файл
Loading