1
0
mirror of https://github.com/docker/docs.git synced 2026-10-23 18:09:02 +03:00
Files
docs/hack/flatten-and-resolve.js
David Karlsson 9b1f7de05f docs: unify Hub, DVP, and Registry API references (#26048)
@netlify /reference/api/

[Preview the API
reference](https://deploy-preview-26048--docsdocker.netlify.app/reference/api/)

Adds a shared API reference experience for Hub, DVP, and Registry: a
catalog, operation navigation, linked schemas, request and response
examples, and matching HTML and Markdown output. A validation and
generation pipeline prepares the reference data for Hugo and runs as
part of Docker and Netlify builds.

Migrates the three specifications to OpenAPI 3.2, correcting response
shapes, nullable fields, and authentication and token examples. The Hub
migration also corrects SCIM contracts and CSV exports; DVP gains
complete report response schemas and examples. Registry keeps its
documented Hub-specific subset, with authentication and media
negotiation expressed through OpenAPI security and content fields.

Validation is strict for both checks and generation, with no exception
baseline or bypass flag. The build uses the migrated sources directly,
without migration scripts or records.

Page and specification URLs remain stable, including the DVP alias.
Operation and schema anchors change. Engine and Governance retain their
existing references.

Validated locally with Go tests, strict validation of all three
specifications (zero diagnostics), scoped formatting and lint, and a
full Hugo build checking all 176 HTML/Markdown page pairs, example
parity, reference links, and byte-identical published specifications. A
browser check confirmed raw CSV rendering and the generated request
header. No live API requests were made.
2026-09-17 16:56:32 +02:00

100 lines
2.9 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**
* Resolve Markdown links relative to each original file, then flatten index.md.
* Only link destinations change; examples and other text stay byte-identical.
* Usage: node hack/flatten-and-resolve.js [public-dir]
*/
const fs = require("node:fs");
const path = require("node:path");
const publicDir = path.resolve(process.argv[2] || "public");
function markdownFiles(dir) {
return fs.readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
const file = path.join(dir, entry.name);
return entry.isDirectory()
? markdownFiles(file)
: file.endsWith(".md")
? [file]
: [];
});
}
function resolveLink(link, file) {
// External URLs, protocol-relative URLs, and same-page anchors are complete.
if (/^(?:[a-z][a-z0-9+.-]*:|\/\/|#)/i.test(link)) return link;
const [, pathname, suffix] = link.match(/^([^?#]*)(.*)$/s);
if (!pathname) return link;
let url = pathname.startsWith("/")
? pathname
: "/" +
path
.relative(publicDir, path.resolve(path.dirname(file), pathname))
.split(path.sep)
.join("/");
url = url.replace(/^\/manuals\//, "/");
url = url.replace(/\/_?index\.md$/, "/").replace(/\.md$/, "/");
return url + suffix;
}
// Micromark provides exact destination spans, excluding labels, titles, and code.
// Apply edits backwards so offsets stay valid and formatting stays untouched.
function rewriteLinks(content, events, file) {
const destinations = events
.filter(
([event, token]) =>
event === "enter" &&
["resourceDestinationString", "definitionDestinationString"].includes(
token.type,
),
)
.map(([, token]) => token)
.sort((a, b) => b.start.offset - a.start.offset);
for (const token of destinations) {
const start = token.start.offset;
const end = token.end.offset;
const destination = content.slice(start, end);
content =
content.slice(0, start) +
resolveLink(destination, file) +
content.slice(end);
}
return content;
}
async function main() {
const { parse, preprocess, postprocess } = await import("micromark");
const files = markdownFiles(publicDir);
let rewritten = 0;
let flattened = 0;
for (const file of files) {
const original = fs.readFileSync(file, "utf8");
const events = postprocess(
parse()
.document()
.write(preprocess()(original, "utf8", true)),
);
const content = rewriteLinks(original, events, file);
if (content !== original) {
fs.writeFileSync(file, content);
rewritten++;
}
if (
path.basename(file) === "index.md" &&
file !== path.join(publicDir, "index.md")
) {
fs.renameSync(file, path.dirname(file) + ".md");
flattened++;
}
}
console.log(
`Markdown: rewrote links in ${rewritten} files; flattened ${flattened} index.md files`,
);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});