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
18 changes: 10 additions & 8 deletions scripts/build-search.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import fs from "node:fs";
import path from "node:path";
import matter from "gray-matter";
import * as pagefind from "pagefind";
import { orderedResourceTags } from "./openapi-order.mjs";

/** Read an env var, falling back to .env.local / .env (plain node script —
* Next's automatic env loading doesn't apply here). */
Expand Down Expand Up @@ -109,8 +110,8 @@ function apiRecords(root) {
const specFile = path.join(root, "api", "openapi.json");
if (!fs.existsSync(specFile)) return [];
const spec = JSON.parse(fs.readFileSync(specFile, "utf8"));
const methods = ["get", "post", "put", "patch", "delete", "options", "head"];
const records = [];
const methods = ["get", "post", "put", "patch", "delete", "options", "head"];
for (const [pathStr, item] of Object.entries(spec.paths ?? {})) {
for (const method of methods) {
const op = item[method];
Expand Down Expand Up @@ -178,20 +179,21 @@ function apiResources(root) {
const specFile = path.join(root, "api", "openapi.json");
if (!fs.existsSync(specFile)) return [];
const spec = JSON.parse(fs.readFileSync(specFile, "utf8"));
const tags = new Map();
for (const t of spec.tags ?? []) tags.set(t.name, t.description ? String(t.description).split("\n")[0].trim() : "");
for (const item of Object.values(spec.paths ?? {})) {
for (const op of Object.values(item)) {
if (op && Array.isArray(op.tags)) for (const t of op.tags) if (!tags.has(t)) tags.set(t, "");
}
const descriptions = new Map();
for (const t of spec.tags ?? []) {
descriptions.set(t.name, t.description ? String(t.description).split("\n")[0].trim() : "");
}
const slug = (s) => String(s).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
const pretty = (s) =>
String(s).split("/").pop()
.replace(/[-_]/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2")
.split(/\s+/).filter(Boolean)
.map((w) => w[0].toUpperCase() + w.slice(1).toLowerCase()).join(" ");
return [...tags].map(([name, desc]) => ({ url: `/api-reference/${slug(name)}`, title: pretty(name), desc }));
return orderedResourceTags(spec).map((name) => ({
url: `/api-reference/${slug(name)}`,
title: pretty(name),
desc: descriptions.get(name) ?? "",
}));
}

function writeLlms(root) {
Expand Down
19 changes: 19 additions & 0 deletions scripts/openapi-order.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const METHODS = ["get", "post", "put", "patch", "delete", "options", "head"];

export function orderedResourceTags(spec) {
const declared = (spec.tags ?? []).map((tag) => tag.name);
const declaredSet = new Set(declared);
const discovered = new Set();

for (const item of Object.values(spec.paths ?? {})) {
for (const method of METHODS) {
const op = item[method];
if (!op || !Array.isArray(op.tags)) continue;
for (const tag of op.tags) {
if (!declaredSet.has(tag)) discovered.add(tag);
}
}
}

return [...declared, ...[...discovered].sort((a, b) => a.localeCompare(b))];
}
21 changes: 21 additions & 0 deletions test/build-search.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { test } from "node:test";
import assert from "node:assert/strict";

type OrderedResourceTags = (spec: object) => string[];

test("orderedResourceTags keeps declared order and sorts undeclared tags", async () => {
const modulePath = "../scripts/openapi-order.mjs";
const { orderedResourceTags } = await import(modulePath) as unknown as {
orderedResourceTags: OrderedResourceTags;
};
const tags = orderedResourceTags({
tags: [{ name: "Requests" }, { name: "Customers" }],
paths: {
"/webhooks": { get: { tags: ["Webhooks"] } },
"/events": { post: { tags: ["Events", "Requests"] } },
"/configuration": { patch: { tags: ["Configuration"] } },
},
});

assert.deepEqual(tags, ["Requests", "Customers", "Configuration", "Events", "Webhooks"]);
});