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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@

content/upstream/docs/
content/upstream/docs-subset/
content/upstream/static/
content/upstream/sidebars.ts
content/upstream/CHANGELOG.md
prototypes/docusaurus/docs/
prototypes/docusaurus/static/llms.txt
prototypes/docusaurus/static/llms-full.txt
prototypes/docusaurus/static/llms-full-pro.txt

.DS_Store
npm-debug.log*
Expand Down
29 changes: 29 additions & 0 deletions scripts/prepare-docs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
excludeNamesForRootCopy,
exists,
} from "./docs-layout.mjs";
import { syncedStaticFiles } from "./synced-static-files.mjs";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
Expand Down Expand Up @@ -847,9 +848,30 @@ async function prepareSidebars(siteRoot, hasArchive) {
}
}

export async function copySyncedStaticFiles(sourceStaticDir, targetStaticDir) {
await fs.mkdir(targetStaticDir, { recursive: true });

const copied = [];
for (const fileName of syncedStaticFiles) {
const sourceFile = path.join(sourceStaticDir, fileName);
const targetFile = path.join(targetStaticDir, fileName);

await fs.rm(targetFile, { force: true });
if (!(await exists(sourceFile))) {
continue;
}

await fs.copyFile(sourceFile, targetFile);
Comment on lines +859 to +864

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Missing Source Deletes Endpoint

When content/upstream/static is missing one of the synced llms*.txt files, this removes the existing Docusaurus static file before the missing-source check skips the copy. A standalone prepare:docs run or a partial sync can silently drop a previously generated endpoint from the site build and make /llms-full-pro.txt or the other static paths return 404.

Suggested change
await fs.rm(targetFile, { force: true });
if (!(await exists(sourceFile))) {
continue;
}
await fs.copyFile(sourceFile, targetFile);
if (!(await exists(sourceFile))) {
continue;
}
await fs.copyFile(sourceFile, targetFile);

copied.push(fileName);
}

return copied;
}

async function prepareDocusaurus() {
const siteRoot = path.join(workspaceRoot, "prototypes", "docusaurus");
const docsRoot = path.join(siteRoot, "docs");
const staticRoot = path.join(siteRoot, "static");
const layout = await detectDocsLayout(sourceDocs);
const layoutPaths = docsLayoutPaths(sourceDocs, layout);
const excludedRootEntries = excludeNamesForRootCopy(layout);
Expand Down Expand Up @@ -894,6 +916,13 @@ async function prepareDocusaurus() {
await convertGitHubAlerts(docsRoot);

await prepareSidebars(siteRoot, hasArchive);
const copiedStaticFiles = await copySyncedStaticFiles(
path.join(workspaceRoot, "content", "upstream", "static"),
staticRoot
);
if (copiedStaticFiles.length > 0) {
console.log(`Prepared static root files: ${copiedStaticFiles.join(", ")}`);
}

console.log(`Prepared docusaurus docs from ${sourceDocs} (${layout} layout, pro -> /pro)`);
}
Expand Down
33 changes: 33 additions & 0 deletions scripts/prepare-docs.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import test from "node:test";

import {
changelogMarkdown,
copySyncedStaticFiles,
docsHomeMarkdown,
fixProNodeRendererMdx,
injectProFriendlyNotice,
Expand Down Expand Up @@ -208,3 +209,35 @@ test("Pro node renderer table comparisons are escaped for MDX", () => {
"| First request on fresh deploy | 410->retry | Direct render: <50ms |\n"
);
});

test("prepare docs copies synced llms files to the Docusaurus static root", async () => {
await withTempDir(async (tmpDir) => {
const upstreamStatic = path.join(tmpDir, "upstream-static");
const docusaurusStatic = path.join(tmpDir, "docusaurus-static");
await fs.mkdir(upstreamStatic, { recursive: true });
await fs.mkdir(docusaurusStatic, { recursive: true });

await fs.writeFile(path.join(upstreamStatic, "llms.txt"), "llms index\n", "utf8");
await fs.writeFile(path.join(upstreamStatic, "llms-full.txt"), "oss full\n", "utf8");
await fs.writeFile(path.join(upstreamStatic, "llms-full-pro.txt"), "pro full\n", "utf8");
await fs.writeFile(path.join(upstreamStatic, "ignored.txt"), "ignored\n", "utf8");
await fs.writeFile(path.join(docusaurusStatic, "llms-full.txt"), "stale\n", "utf8");

const copied = await copySyncedStaticFiles(upstreamStatic, docusaurusStatic);

assert.deepEqual(copied.sort(), ["llms-full-pro.txt", "llms-full.txt", "llms.txt"]);
assert.equal(await fs.readFile(path.join(docusaurusStatic, "llms.txt"), "utf8"), "llms index\n");
assert.equal(
await fs.readFile(path.join(docusaurusStatic, "llms-full.txt"), "utf8"),
"oss full\n"
);
assert.equal(
await fs.readFile(path.join(docusaurusStatic, "llms-full-pro.txt"), "utf8"),
"pro full\n"
);
await assert.rejects(
fs.access(path.join(docusaurusStatic, "ignored.txt")),
/ENOENT/
);
});
});
26 changes: 26 additions & 0 deletions scripts/sync-docs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import os from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { detectDocsLayout, docsLayoutPaths, exists, subsetPathsForLayout } from "./docs-layout.mjs";
import { syncedStaticFiles } from "./synced-static-files.mjs";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
Expand All @@ -16,6 +17,7 @@ const buildSubset = args.has("--subset");
const upstreamRoot = path.join(workspaceRoot, "content", "upstream");
const fullDocsTarget = path.join(upstreamRoot, "docs");
const subsetDocsTarget = path.join(upstreamRoot, "docs-subset");
const staticTarget = path.join(upstreamRoot, "static");

function cloneRepo(repoUrl, ref) {
const tmpDir = mkdtempSync(path.join(os.tmpdir(), "react-on-rails-docs-"));
Expand Down Expand Up @@ -77,6 +79,25 @@ async function countFiles(rootDir) {
return count;
}

async function syncRootStaticFiles(sourceRepo) {
await fs.rm(staticTarget, { recursive: true, force: true });
await fs.mkdir(staticTarget, { recursive: true });

const copied = [];
for (const fileName of syncedStaticFiles) {
const sourceFile = path.join(sourceRepo, fileName);
if (!(await exists(sourceFile))) {
console.warn(`Warning: ${fileName} not found in source repo`);
continue;
}

await fs.copyFile(sourceFile, path.join(staticTarget, fileName));
copied.push(fileName);
}

return copied;
}

async function main() {
const configuredRepo = process.env.REACT_ON_RAILS_REPO;
const localRepo = configuredRepo
Expand Down Expand Up @@ -125,6 +146,11 @@ async function main() {
console.warn("Warning: CHANGELOG.md not found in source repo");
}

const copiedStaticFiles = await syncRootStaticFiles(sourceRepo);
if (copiedStaticFiles.length > 0) {
console.log(`Synced static root files: ${copiedStaticFiles.join(", ")}`);
}

let subsetStats = null;
if (buildSubset) {
subsetStats = await writeSubset(fullDocsTarget, subsetDocsTarget, layout);
Expand Down
64 changes: 64 additions & 0 deletions scripts/sync-docs.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import assert from "node:assert/strict";
import { execFile } from "node:child_process";
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import test from "node:test";
import { promisify } from "node:util";
import { fileURLToPath } from "node:url";

const execFileAsync = promisify(execFile);
const __filename = fileURLToPath(import.meta.url);
const workspaceRoot = path.resolve(path.dirname(__filename), "..");

async function withTempDir(callback) {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "sync-docs-test-"));
try {
return await callback(tmpDir);
} finally {
await fs.rm(tmpDir, { recursive: true, force: true });
}
}

async function writeFakeUpstream(upstreamRoot) {
await fs.mkdir(path.join(upstreamRoot, "docs"), { recursive: true });
await fs.writeFile(
path.join(upstreamRoot, "docs", "README.md"),
"# React on Rails Docs\n",
"utf8"
);
await fs.writeFile(path.join(upstreamRoot, "llms.txt"), "llms index\n", "utf8");
await fs.writeFile(path.join(upstreamRoot, "llms-full.txt"), "oss full\n", "utf8");
await fs.writeFile(path.join(upstreamRoot, "llms-full-pro.txt"), "pro full\n", "utf8");
}

test("sync docs stages root llms files for static publishing", async () => {
await withTempDir(async (tmpDir) => {
const upstreamRoot = path.join(tmpDir, "react_on_rails");
await writeFakeUpstream(upstreamRoot);

const upstreamContent = path.join(workspaceRoot, "content", "upstream");
await fs.rm(upstreamContent, { recursive: true, force: true });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Test Deletes Workspace State

This deletes content/upstream in the real checkout, while the temp-dir cleanup only covers the fake source repo. Running this test leaves the workspace populated with fake upstream docs on success, and an interruption or sync failure after this line can remove the developer's generated docs/static/changelog state until they run a full sync again.


await execFileAsync(process.execPath, [path.join(workspaceRoot, "scripts", "sync-docs.mjs")], {
cwd: workspaceRoot,
env: {
...process.env,
REACT_ON_RAILS_REPO: upstreamRoot,
},
});

assert.equal(
await fs.readFile(path.join(upstreamContent, "static", "llms.txt"), "utf8"),
"llms index\n"
);
assert.equal(
await fs.readFile(path.join(upstreamContent, "static", "llms-full.txt"), "utf8"),
"oss full\n"
);
assert.equal(
await fs.readFile(path.join(upstreamContent, "static", "llms-full-pro.txt"), "utf8"),
"pro full\n"
);
});
});
1 change: 1 addition & 0 deletions scripts/synced-static-files.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const syncedStaticFiles = ["llms.txt", "llms-full.txt", "llms-full-pro.txt"];
Loading