@webpieces/docs-site 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -0
- package/package.json +28 -0
- package/src/DocsSiteError.d.ts +24 -0
- package/src/DocsSiteError.js +33 -0
- package/src/DocsSiteError.js.map +1 -0
- package/src/assets/SiteScript.d.ts +11 -0
- package/src/assets/SiteScript.js +88 -0
- package/src/assets/SiteScript.js.map +1 -0
- package/src/assets/SiteStyles.d.ts +13 -0
- package/src/assets/SiteStyles.js +106 -0
- package/src/assets/SiteStyles.js.map +1 -0
- package/src/cli/DocsSiteCli.d.ts +37 -0
- package/src/cli/DocsSiteCli.js +116 -0
- package/src/cli/DocsSiteCli.js.map +1 -0
- package/src/cli/WpDocsSiteMain.d.ts +21 -0
- package/src/cli/WpDocsSiteMain.js +60 -0
- package/src/cli/WpDocsSiteMain.js.map +1 -0
- package/src/cli/wp-docs-site.d.ts +2 -0
- package/src/cli/wp-docs-site.js +22 -0
- package/src/cli/wp-docs-site.js.map +1 -0
- package/src/emit/SiteWriter.d.ts +13 -0
- package/src/emit/SiteWriter.js +34 -0
- package/src/emit/SiteWriter.js.map +1 -0
- package/src/index.d.ts +41 -0
- package/src/index.js +105 -0
- package/src/index.js.map +1 -0
- package/src/manifest/DocsManifest.d.ts +28 -0
- package/src/manifest/DocsManifest.js +44 -0
- package/src/manifest/DocsManifest.js.map +1 -0
- package/src/manifest/ProseLoader.d.ts +23 -0
- package/src/manifest/ProseLoader.js +81 -0
- package/src/manifest/ProseLoader.js.map +1 -0
- package/src/markdown/Markdown.d.ts +47 -0
- package/src/markdown/Markdown.js +178 -0
- package/src/markdown/Markdown.js.map +1 -0
- package/src/render/CardRenderer.d.ts +28 -0
- package/src/render/CardRenderer.js +70 -0
- package/src/render/CardRenderer.js.map +1 -0
- package/src/render/FieldTreeRenderer.d.ts +34 -0
- package/src/render/FieldTreeRenderer.js +91 -0
- package/src/render/FieldTreeRenderer.js.map +1 -0
- package/src/render/Html.d.ts +14 -0
- package/src/render/Html.js +28 -0
- package/src/render/Html.js.map +1 -0
- package/src/render/Layout.d.ts +21 -0
- package/src/render/Layout.js +74 -0
- package/src/render/Layout.js.map +1 -0
- package/src/render/NavModel.d.ts +47 -0
- package/src/render/NavModel.js +86 -0
- package/src/render/NavModel.js.map +1 -0
- package/src/render/OperationPageRenderer.d.ts +44 -0
- package/src/render/OperationPageRenderer.js +102 -0
- package/src/render/OperationPageRenderer.js.map +1 -0
- package/src/render/SitePage.d.ts +30 -0
- package/src/render/SitePage.js +46 -0
- package/src/render/SitePage.js.map +1 -0
- package/src/render/SiteRenderer.d.ts +34 -0
- package/src/render/SiteRenderer.js +121 -0
- package/src/render/SiteRenderer.js.map +1 -0
- package/src/render/SiteUrls.d.ts +24 -0
- package/src/render/SiteUrls.js +37 -0
- package/src/render/SiteUrls.js.map +1 -0
- package/src/samples/CodeSamples.d.ts +49 -0
- package/src/samples/CodeSamples.js +166 -0
- package/src/samples/CodeSamples.js.map +1 -0
- package/src/serve/DevServer.d.ts +35 -0
- package/src/serve/DevServer.js +109 -0
- package/src/serve/DevServer.js.map +1 -0
- package/src/spec/ApiSpec.d.ts +110 -0
- package/src/spec/ApiSpec.js +165 -0
- package/src/spec/ApiSpec.js.map +1 -0
- package/src/spec/ExampleBuilder.d.ts +32 -0
- package/src/spec/ExampleBuilder.js +119 -0
- package/src/spec/ExampleBuilder.js.map +1 -0
- package/src/spec/FieldTree.d.ts +75 -0
- package/src/spec/FieldTree.js +155 -0
- package/src/spec/FieldTree.js.map +1 -0
- package/src/spec/JsonNode.d.ts +53 -0
- package/src/spec/JsonNode.js +93 -0
- package/src/spec/JsonNode.js.map +1 -0
- package/src/spec/SchemaLens.d.ts +57 -0
- package/src/spec/SchemaLens.js +162 -0
- package/src/spec/SchemaLens.js.map +1 -0
- package/src/spec/SchemaShape.d.ts +22 -0
- package/src/spec/SchemaShape.js +61 -0
- package/src/spec/SchemaShape.js.map +1 -0
- package/src/spec/Slug.d.ts +16 -0
- package/src/spec/Slug.js +43 -0
- package/src/spec/Slug.js.map +1 -0
- package/src/spec/SpecLoader.d.ts +26 -0
- package/src/spec/SpecLoader.js +64 -0
- package/src/spec/SpecLoader.js.map +1 -0
- package/src/spec/SpecReader.d.ts +46 -0
- package/src/spec/SpecReader.js +174 -0
- package/src/spec/SpecReader.js.map +1 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ApiSpec } from '../spec/ApiSpec';
|
|
2
|
+
import { ProsePage } from '../manifest/DocsManifest';
|
|
3
|
+
import { SiteUrls } from './SiteUrls';
|
|
4
|
+
/** The nav group the prose pages sit in, above the reference. */
|
|
5
|
+
export declare const GUIDES_GROUP = "Guides";
|
|
6
|
+
/** The nav group every named object DTO gets a page under. */
|
|
7
|
+
export declare const SCHEMAS_GROUP = "Schemas";
|
|
8
|
+
/** One line in the nav tree. `badge` is drawn beside the title — `WEBHOOK`, or an HTTP method. */
|
|
9
|
+
export declare class NavLink {
|
|
10
|
+
readonly title: string;
|
|
11
|
+
/** Relative to the site ROOT; each page prepends its own `../` prefix. */
|
|
12
|
+
readonly url: string;
|
|
13
|
+
readonly badge: string;
|
|
14
|
+
constructor(title: string,
|
|
15
|
+
/** Relative to the site ROOT; each page prepends its own `../` prefix. */
|
|
16
|
+
url: string, badge: string);
|
|
17
|
+
}
|
|
18
|
+
/** One collapsible section of the nav tree. */
|
|
19
|
+
export declare class NavGroup {
|
|
20
|
+
readonly title: string;
|
|
21
|
+
readonly links: readonly NavLink[];
|
|
22
|
+
constructor(title: string, links: readonly NavLink[]);
|
|
23
|
+
}
|
|
24
|
+
/** The nav tree, as data. {@link NavBuilder} is what puts it in this order. */
|
|
25
|
+
export declare class NavModel {
|
|
26
|
+
readonly groups: readonly NavGroup[];
|
|
27
|
+
constructor(groups: readonly NavGroup[]);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Builds the nav tree: prose first, then ONE SECTION PER OPENAPI TAG in the document's own `tags[]`
|
|
31
|
+
* order, then the schema pages.
|
|
32
|
+
*
|
|
33
|
+
* **Every tag is its own section.** A tag missing from the sidebar is an operation a partner cannot
|
|
34
|
+
* find, which reads exactly like an operation that does not exist — so the sections come from the
|
|
35
|
+
* tags rather than from a hand-maintained list a new tag would have to be added to.
|
|
36
|
+
*
|
|
37
|
+
* Webhooks appear inside the section of the tag they share, appended after that tag's operations and
|
|
38
|
+
* badged, because the events are the consequence of the calls above them.
|
|
39
|
+
*/
|
|
40
|
+
export declare class NavBuilder {
|
|
41
|
+
private readonly urls;
|
|
42
|
+
constructor(urls: SiteUrls);
|
|
43
|
+
build(spec: ApiSpec, prose: readonly ProsePage[], overviewTitle: string): NavModel;
|
|
44
|
+
private guideLinks;
|
|
45
|
+
private referenceLinks;
|
|
46
|
+
private schemaLinks;
|
|
47
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NavBuilder = exports.NavModel = exports.NavGroup = exports.NavLink = exports.SCHEMAS_GROUP = exports.GUIDES_GROUP = void 0;
|
|
4
|
+
/** The nav group the prose pages sit in, above the reference. */
|
|
5
|
+
exports.GUIDES_GROUP = 'Guides';
|
|
6
|
+
/** The nav group every named object DTO gets a page under. */
|
|
7
|
+
exports.SCHEMAS_GROUP = 'Schemas';
|
|
8
|
+
/** One line in the nav tree. `badge` is drawn beside the title — `WEBHOOK`, or an HTTP method. */
|
|
9
|
+
class NavLink {
|
|
10
|
+
title;
|
|
11
|
+
url;
|
|
12
|
+
badge;
|
|
13
|
+
constructor(title,
|
|
14
|
+
/** Relative to the site ROOT; each page prepends its own `../` prefix. */
|
|
15
|
+
url, badge) {
|
|
16
|
+
this.title = title;
|
|
17
|
+
this.url = url;
|
|
18
|
+
this.badge = badge;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.NavLink = NavLink;
|
|
22
|
+
/** One collapsible section of the nav tree. */
|
|
23
|
+
class NavGroup {
|
|
24
|
+
title;
|
|
25
|
+
links;
|
|
26
|
+
constructor(title, links) {
|
|
27
|
+
this.title = title;
|
|
28
|
+
this.links = links;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
exports.NavGroup = NavGroup;
|
|
32
|
+
/** The nav tree, as data. {@link NavBuilder} is what puts it in this order. */
|
|
33
|
+
class NavModel {
|
|
34
|
+
groups;
|
|
35
|
+
constructor(groups) {
|
|
36
|
+
this.groups = groups;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.NavModel = NavModel;
|
|
40
|
+
/**
|
|
41
|
+
* Builds the nav tree: prose first, then ONE SECTION PER OPENAPI TAG in the document's own `tags[]`
|
|
42
|
+
* order, then the schema pages.
|
|
43
|
+
*
|
|
44
|
+
* **Every tag is its own section.** A tag missing from the sidebar is an operation a partner cannot
|
|
45
|
+
* find, which reads exactly like an operation that does not exist — so the sections come from the
|
|
46
|
+
* tags rather than from a hand-maintained list a new tag would have to be added to.
|
|
47
|
+
*
|
|
48
|
+
* Webhooks appear inside the section of the tag they share, appended after that tag's operations and
|
|
49
|
+
* badged, because the events are the consequence of the calls above them.
|
|
50
|
+
*/
|
|
51
|
+
class NavBuilder {
|
|
52
|
+
urls;
|
|
53
|
+
constructor(urls) {
|
|
54
|
+
this.urls = urls;
|
|
55
|
+
}
|
|
56
|
+
build(spec, prose, overviewTitle) {
|
|
57
|
+
const groups = [
|
|
58
|
+
new NavGroup(exports.GUIDES_GROUP, this.guideLinks(prose, overviewTitle)),
|
|
59
|
+
];
|
|
60
|
+
for (const section of spec.sections) {
|
|
61
|
+
groups.push(new NavGroup(section.name, this.referenceLinks(section)));
|
|
62
|
+
}
|
|
63
|
+
const schemaLinks = this.schemaLinks(spec);
|
|
64
|
+
if (schemaLinks.length > 0) {
|
|
65
|
+
groups.push(new NavGroup(exports.SCHEMAS_GROUP, schemaLinks));
|
|
66
|
+
}
|
|
67
|
+
return new NavModel(groups);
|
|
68
|
+
}
|
|
69
|
+
guideLinks(prose, overviewTitle) {
|
|
70
|
+
const links = [new NavLink(overviewTitle, this.urls.home(), '')];
|
|
71
|
+
for (const page of prose) {
|
|
72
|
+
links.push(new NavLink(page.title, this.urls.prose(page), ''));
|
|
73
|
+
}
|
|
74
|
+
return links;
|
|
75
|
+
}
|
|
76
|
+
referenceLinks(section) {
|
|
77
|
+
return section.operations.map((operation) => new NavLink(operation.name, this.urls.operation(operation), operation.isWebhook ? 'WEBHOOK' : operation.httpMethod));
|
|
78
|
+
}
|
|
79
|
+
schemaLinks(spec) {
|
|
80
|
+
return spec.schemas
|
|
81
|
+
.filter((schema) => schema.hasOwnPage)
|
|
82
|
+
.map((schema) => new NavLink(schema.name, this.urls.schema(schema), ''));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
exports.NavBuilder = NavBuilder;
|
|
86
|
+
//# sourceMappingURL=NavModel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NavModel.js","sourceRoot":"","sources":["../../../../../../packages/docs/docs-site/src/render/NavModel.ts"],"names":[],"mappings":"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","sourcesContent":["import { ApiSpec, NamedSchema, OperationInfo, TagSection } from '../spec/ApiSpec';\nimport { ProsePage } from '../manifest/DocsManifest';\nimport { SiteUrls } from './SiteUrls';\n\n/** The nav group the prose pages sit in, above the reference. */\nexport const GUIDES_GROUP = 'Guides';\n\n/** The nav group every named object DTO gets a page under. */\nexport const SCHEMAS_GROUP = 'Schemas';\n\n/** One line in the nav tree. `badge` is drawn beside the title — `WEBHOOK`, or an HTTP method. */\nexport class NavLink {\n constructor(\n readonly title: string,\n /** Relative to the site ROOT; each page prepends its own `../` prefix. */\n readonly url: string,\n readonly badge: string,\n ) {}\n}\n\n/** One collapsible section of the nav tree. */\nexport class NavGroup {\n constructor(\n readonly title: string,\n readonly links: readonly NavLink[],\n ) {}\n}\n\n/** The nav tree, as data. {@link NavBuilder} is what puts it in this order. */\nexport class NavModel {\n constructor(readonly groups: readonly NavGroup[]) {}\n}\n\n/**\n * Builds the nav tree: prose first, then ONE SECTION PER OPENAPI TAG in the document's own `tags[]`\n * order, then the schema pages.\n *\n * **Every tag is its own section.** A tag missing from the sidebar is an operation a partner cannot\n * find, which reads exactly like an operation that does not exist — so the sections come from the\n * tags rather than from a hand-maintained list a new tag would have to be added to.\n *\n * Webhooks appear inside the section of the tag they share, appended after that tag's operations and\n * badged, because the events are the consequence of the calls above them.\n */\nexport class NavBuilder {\n constructor(private readonly urls: SiteUrls) {}\n\n build(spec: ApiSpec, prose: readonly ProsePage[], overviewTitle: string): NavModel {\n const groups: NavGroup[] = [\n new NavGroup(GUIDES_GROUP, this.guideLinks(prose, overviewTitle)),\n ];\n for (const section of spec.sections) {\n groups.push(new NavGroup(section.name, this.referenceLinks(section)));\n }\n const schemaLinks = this.schemaLinks(spec);\n if (schemaLinks.length > 0) {\n groups.push(new NavGroup(SCHEMAS_GROUP, schemaLinks));\n }\n return new NavModel(groups);\n }\n\n private guideLinks(prose: readonly ProsePage[], overviewTitle: string): readonly NavLink[] {\n const links = [new NavLink(overviewTitle, this.urls.home(), '')];\n for (const page of prose) {\n links.push(new NavLink(page.title, this.urls.prose(page), ''));\n }\n return links;\n }\n\n private referenceLinks(section: TagSection): readonly NavLink[] {\n return section.operations.map(\n (operation: OperationInfo): NavLink =>\n new NavLink(\n operation.name,\n this.urls.operation(operation),\n operation.isWebhook ? 'WEBHOOK' : operation.httpMethod,\n ),\n );\n }\n\n private schemaLinks(spec: ApiSpec): readonly NavLink[] {\n return spec.schemas\n .filter((schema: NamedSchema): boolean => schema.hasOwnPage)\n .map(\n (schema: NamedSchema): NavLink =>\n new NavLink(schema.name, this.urls.schema(schema), ''),\n );\n }\n}\n"]}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ApiSpec, OperationInfo } from '../spec/ApiSpec';
|
|
2
|
+
import { CardRenderer } from './CardRenderer';
|
|
3
|
+
import { CodeSampleBuilder } from '../samples/CodeSamples';
|
|
4
|
+
import { ExampleBuilder } from '../spec/ExampleBuilder';
|
|
5
|
+
import { FieldTreeBuilder } from '../spec/FieldTree';
|
|
6
|
+
import { FieldTreeRenderer } from './FieldTreeRenderer';
|
|
7
|
+
import { Layout } from './Layout';
|
|
8
|
+
import { Markdown } from '../markdown/Markdown';
|
|
9
|
+
import { SiteUrls } from './SiteUrls';
|
|
10
|
+
import { SitePage } from './SitePage';
|
|
11
|
+
/** The badge a webhook carries everywhere it appears. */
|
|
12
|
+
export declare const WEBHOOK_BADGE = "WEBHOOK";
|
|
13
|
+
/**
|
|
14
|
+
* One pre-rendered page per operation, at `reference/<kebab-operation>/index.html`.
|
|
15
|
+
*
|
|
16
|
+
* ## What a WEBHOOK page leaves out, and why
|
|
17
|
+
*
|
|
18
|
+
* No method, no path, no code samples. The url is the PARTNER's — they host the endpoint wherever
|
|
19
|
+
* they choose — so a `POST https://api.example.com/...` sample on a webhook page invites them to
|
|
20
|
+
* call something that does not exist. What it keeps is the payload, because the payload is the part
|
|
21
|
+
* that IS ours, and the event name, because that is what they match on the wire.
|
|
22
|
+
*/
|
|
23
|
+
export declare class OperationPageRenderer {
|
|
24
|
+
private readonly spec;
|
|
25
|
+
private readonly fields;
|
|
26
|
+
private readonly trees;
|
|
27
|
+
private readonly markdown;
|
|
28
|
+
private readonly cards;
|
|
29
|
+
private readonly samples;
|
|
30
|
+
private readonly examples;
|
|
31
|
+
private readonly layout;
|
|
32
|
+
private readonly urls;
|
|
33
|
+
private readonly html;
|
|
34
|
+
constructor(spec: ApiSpec, fields: FieldTreeBuilder, trees: FieldTreeRenderer, markdown: Markdown, cards: CardRenderer, samples: CodeSampleBuilder, examples: ExampleBuilder, layout: Layout, urls: SiteUrls);
|
|
35
|
+
page(operation: OperationInfo): SitePage;
|
|
36
|
+
private body;
|
|
37
|
+
private head;
|
|
38
|
+
private requestSection;
|
|
39
|
+
private responseSection;
|
|
40
|
+
private response;
|
|
41
|
+
/** A `oneOf` renders as expanded variants; anything else renders as one flat field tree. */
|
|
42
|
+
private tree;
|
|
43
|
+
private cardColumn;
|
|
44
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OperationPageRenderer = exports.WEBHOOK_BADGE = void 0;
|
|
4
|
+
const Html_1 = require("./Html");
|
|
5
|
+
const SitePage_1 = require("./SitePage");
|
|
6
|
+
/** The badge a webhook carries everywhere it appears. */
|
|
7
|
+
exports.WEBHOOK_BADGE = 'WEBHOOK';
|
|
8
|
+
/**
|
|
9
|
+
* One pre-rendered page per operation, at `reference/<kebab-operation>/index.html`.
|
|
10
|
+
*
|
|
11
|
+
* ## What a WEBHOOK page leaves out, and why
|
|
12
|
+
*
|
|
13
|
+
* No method, no path, no code samples. The url is the PARTNER's — they host the endpoint wherever
|
|
14
|
+
* they choose — so a `POST https://api.example.com/...` sample on a webhook page invites them to
|
|
15
|
+
* call something that does not exist. What it keeps is the payload, because the payload is the part
|
|
16
|
+
* that IS ours, and the event name, because that is what they match on the wire.
|
|
17
|
+
*/
|
|
18
|
+
class OperationPageRenderer {
|
|
19
|
+
spec;
|
|
20
|
+
fields;
|
|
21
|
+
trees;
|
|
22
|
+
markdown;
|
|
23
|
+
cards;
|
|
24
|
+
samples;
|
|
25
|
+
examples;
|
|
26
|
+
layout;
|
|
27
|
+
urls;
|
|
28
|
+
html = new Html_1.Html();
|
|
29
|
+
constructor(spec, fields, trees, markdown, cards, samples, examples, layout, urls) {
|
|
30
|
+
this.spec = spec;
|
|
31
|
+
this.fields = fields;
|
|
32
|
+
this.trees = trees;
|
|
33
|
+
this.markdown = markdown;
|
|
34
|
+
this.cards = cards;
|
|
35
|
+
this.samples = samples;
|
|
36
|
+
this.examples = examples;
|
|
37
|
+
this.layout = layout;
|
|
38
|
+
this.urls = urls;
|
|
39
|
+
}
|
|
40
|
+
page(operation) {
|
|
41
|
+
const url = this.urls.operation(operation);
|
|
42
|
+
const prefix = this.urls.prefixFor(url);
|
|
43
|
+
return new SitePage_1.SitePage(url, operation.name, this.layout.render(url, operation.name, this.body(operation, prefix), this.cardColumn(operation)));
|
|
44
|
+
}
|
|
45
|
+
body(operation, prefix) {
|
|
46
|
+
const parts = [this.head(operation), `<h1>${this.html.escape(operation.name)}</h1>`];
|
|
47
|
+
if (operation.description !== '') {
|
|
48
|
+
parts.push(this.markdown.render(operation.description));
|
|
49
|
+
}
|
|
50
|
+
parts.push(this.requestSection(operation, prefix));
|
|
51
|
+
parts.push(this.responseSection(operation, prefix));
|
|
52
|
+
return parts.join('\n');
|
|
53
|
+
}
|
|
54
|
+
head(operation) {
|
|
55
|
+
if (operation.isWebhook) {
|
|
56
|
+
return `<div class="op-head"><span class="badge">${exports.WEBHOOK_BADGE}</span><code class="op-path">${this.html.escape(operation.eventName)}</code></div>`;
|
|
57
|
+
}
|
|
58
|
+
return `<div class="op-head"><span class="badge">${this.html.escape(operation.httpMethod)}</span><code class="op-path">${this.html.escape(operation.path)}</code></div>`;
|
|
59
|
+
}
|
|
60
|
+
requestSection(operation, prefix) {
|
|
61
|
+
if (operation.requestSchema === undefined) {
|
|
62
|
+
return '';
|
|
63
|
+
}
|
|
64
|
+
return `<h2>Request body</h2>${this.tree(operation.requestSchema, prefix)}`;
|
|
65
|
+
}
|
|
66
|
+
responseSection(operation, prefix) {
|
|
67
|
+
if (operation.responses.length === 0) {
|
|
68
|
+
return '';
|
|
69
|
+
}
|
|
70
|
+
const blocks = operation.responses
|
|
71
|
+
.map((response) => this.response(response, prefix))
|
|
72
|
+
.join('');
|
|
73
|
+
return `<h2>Responses</h2>${blocks}`;
|
|
74
|
+
}
|
|
75
|
+
response(response, prefix) {
|
|
76
|
+
const summary = `${this.html.escape(response.status)} — ${this.html.escape(response.description)}`;
|
|
77
|
+
const body = response.schema === undefined
|
|
78
|
+
? '<p class="field-doc">No body.</p>'
|
|
79
|
+
: this.tree(response.schema, prefix);
|
|
80
|
+
return `<details class="disclosure" open><summary>${summary}</summary>${body}</details>`;
|
|
81
|
+
}
|
|
82
|
+
/** A `oneOf` renders as expanded variants; anything else renders as one flat field tree. */
|
|
83
|
+
tree(schema, prefix) {
|
|
84
|
+
const variants = this.fields.variantsOf(schema);
|
|
85
|
+
if (variants.length > 0) {
|
|
86
|
+
return this.trees.renderVariants(variants, this.fields.discriminatorPropertyOf(schema), prefix);
|
|
87
|
+
}
|
|
88
|
+
return this.trees.render(this.fields.fieldsOf(schema), prefix);
|
|
89
|
+
}
|
|
90
|
+
cardColumn(operation) {
|
|
91
|
+
const payload = this.examples.render(this.examples.build(operation.requestSchema));
|
|
92
|
+
if (operation.isWebhook) {
|
|
93
|
+
return this.cards.example('EVENT PAYLOAD', payload);
|
|
94
|
+
}
|
|
95
|
+
return [
|
|
96
|
+
this.cards.authorization(operation, this.spec.securitySchemes),
|
|
97
|
+
this.cards.samples(this.samples.samplesFor(operation)),
|
|
98
|
+
].join('\n');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
exports.OperationPageRenderer = OperationPageRenderer;
|
|
102
|
+
//# sourceMappingURL=OperationPageRenderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OperationPageRenderer.js","sourceRoot":"","sources":["../../../../../../packages/docs/docs-site/src/render/OperationPageRenderer.ts"],"names":[],"mappings":"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","sourcesContent":["import { ApiSpec, OperationInfo, ResponseInfo } from '../spec/ApiSpec';\nimport { CardRenderer } from './CardRenderer';\nimport { CodeSampleBuilder } from '../samples/CodeSamples';\nimport { ExampleBuilder } from '../spec/ExampleBuilder';\nimport { FieldTreeBuilder } from '../spec/FieldTree';\nimport { FieldTreeRenderer } from './FieldTreeRenderer';\nimport { Html } from './Html';\nimport { JsonNode } from '../spec/JsonNode';\nimport { Layout } from './Layout';\nimport { Markdown } from '../markdown/Markdown';\nimport { SiteUrls } from './SiteUrls';\nimport { SitePage } from './SitePage';\n\n/** The badge a webhook carries everywhere it appears. */\nexport const WEBHOOK_BADGE = 'WEBHOOK';\n\n/**\n * One pre-rendered page per operation, at `reference/<kebab-operation>/index.html`.\n *\n * ## What a WEBHOOK page leaves out, and why\n *\n * No method, no path, no code samples. The url is the PARTNER's — they host the endpoint wherever\n * they choose — so a `POST https://api.example.com/...` sample on a webhook page invites them to\n * call something that does not exist. What it keeps is the payload, because the payload is the part\n * that IS ours, and the event name, because that is what they match on the wire.\n */\nexport class OperationPageRenderer {\n private readonly html = new Html();\n\n constructor(\n private readonly spec: ApiSpec,\n private readonly fields: FieldTreeBuilder,\n private readonly trees: FieldTreeRenderer,\n private readonly markdown: Markdown,\n private readonly cards: CardRenderer,\n private readonly samples: CodeSampleBuilder,\n private readonly examples: ExampleBuilder,\n private readonly layout: Layout,\n private readonly urls: SiteUrls,\n ) {}\n\n page(operation: OperationInfo): SitePage {\n const url = this.urls.operation(operation);\n const prefix = this.urls.prefixFor(url);\n return new SitePage(\n url,\n operation.name,\n this.layout.render(\n url,\n operation.name,\n this.body(operation, prefix),\n this.cardColumn(operation),\n ),\n );\n }\n\n private body(operation: OperationInfo, prefix: string): string {\n const parts = [this.head(operation), `<h1>${this.html.escape(operation.name)}</h1>`];\n if (operation.description !== '') {\n parts.push(this.markdown.render(operation.description));\n }\n parts.push(this.requestSection(operation, prefix));\n parts.push(this.responseSection(operation, prefix));\n return parts.join('\\n');\n }\n\n private head(operation: OperationInfo): string {\n if (operation.isWebhook) {\n return `<div class=\"op-head\"><span class=\"badge\">${WEBHOOK_BADGE}</span><code class=\"op-path\">${this.html.escape(operation.eventName)}</code></div>`;\n }\n return `<div class=\"op-head\"><span class=\"badge\">${this.html.escape(operation.httpMethod)}</span><code class=\"op-path\">${this.html.escape(operation.path)}</code></div>`;\n }\n\n private requestSection(operation: OperationInfo, prefix: string): string {\n if (operation.requestSchema === undefined) {\n return '';\n }\n return `<h2>Request body</h2>${this.tree(operation.requestSchema, prefix)}`;\n }\n\n private responseSection(operation: OperationInfo, prefix: string): string {\n if (operation.responses.length === 0) {\n return '';\n }\n const blocks = operation.responses\n .map((response: ResponseInfo): string => this.response(response, prefix))\n .join('');\n return `<h2>Responses</h2>${blocks}`;\n }\n\n private response(response: ResponseInfo, prefix: string): string {\n const summary = `${this.html.escape(response.status)} — ${this.html.escape(response.description)}`;\n const body =\n response.schema === undefined\n ? '<p class=\"field-doc\">No body.</p>'\n : this.tree(response.schema, prefix);\n return `<details class=\"disclosure\" open><summary>${summary}</summary>${body}</details>`;\n }\n\n /** A `oneOf` renders as expanded variants; anything else renders as one flat field tree. */\n private tree(schema: JsonNode, prefix: string): string {\n const variants = this.fields.variantsOf(schema);\n if (variants.length > 0) {\n return this.trees.renderVariants(\n variants,\n this.fields.discriminatorPropertyOf(schema),\n prefix,\n );\n }\n return this.trees.render(this.fields.fieldsOf(schema), prefix);\n }\n\n private cardColumn(operation: OperationInfo): string {\n const payload = this.examples.render(this.examples.build(operation.requestSchema));\n if (operation.isWebhook) {\n return this.cards.example('EVENT PAYLOAD', payload);\n }\n return [\n this.cards.authorization(operation, this.spec.securitySchemes),\n this.cards.samples(this.samples.samplesFor(operation)),\n ].join('\\n');\n }\n}\n"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One PRE-RENDERED file at one URL.
|
|
3
|
+
*
|
|
4
|
+
* The whole site is a list of these, which is the decision every other property falls out of: a deep
|
|
5
|
+
* link resolves server-side because the file is already there, the output is a folder any static
|
|
6
|
+
* host serves, the page reads correctly with JS off, and a JSDoc edit shows up as a diff on the one
|
|
7
|
+
* page it affects instead of inside a bundle. Client JS is left with disclosure state, the language
|
|
8
|
+
* tabs and the theme — the three things that are genuinely about this reader, right now.
|
|
9
|
+
*/
|
|
10
|
+
export declare class SitePage {
|
|
11
|
+
/** Relative to the output root, always ending in `index.html` so `file://` browsing works. */
|
|
12
|
+
readonly url: string;
|
|
13
|
+
readonly title: string;
|
|
14
|
+
readonly html: string;
|
|
15
|
+
constructor(
|
|
16
|
+
/** Relative to the output root, always ending in `index.html` so `file://` browsing works. */
|
|
17
|
+
url: string, title: string, html: string);
|
|
18
|
+
}
|
|
19
|
+
/** One file that is copied out verbatim beside the pages — the stylesheet and the script. */
|
|
20
|
+
export declare class SiteAsset {
|
|
21
|
+
readonly url: string;
|
|
22
|
+
readonly contents: string;
|
|
23
|
+
constructor(url: string, contents: string);
|
|
24
|
+
}
|
|
25
|
+
/** Everything one run produces. */
|
|
26
|
+
export declare class RenderedSite {
|
|
27
|
+
readonly pages: readonly SitePage[];
|
|
28
|
+
readonly assets: readonly SiteAsset[];
|
|
29
|
+
constructor(pages: readonly SitePage[], assets: readonly SiteAsset[]);
|
|
30
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RenderedSite = exports.SiteAsset = exports.SitePage = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* One PRE-RENDERED file at one URL.
|
|
6
|
+
*
|
|
7
|
+
* The whole site is a list of these, which is the decision every other property falls out of: a deep
|
|
8
|
+
* link resolves server-side because the file is already there, the output is a folder any static
|
|
9
|
+
* host serves, the page reads correctly with JS off, and a JSDoc edit shows up as a diff on the one
|
|
10
|
+
* page it affects instead of inside a bundle. Client JS is left with disclosure state, the language
|
|
11
|
+
* tabs and the theme — the three things that are genuinely about this reader, right now.
|
|
12
|
+
*/
|
|
13
|
+
class SitePage {
|
|
14
|
+
url;
|
|
15
|
+
title;
|
|
16
|
+
html;
|
|
17
|
+
constructor(
|
|
18
|
+
/** Relative to the output root, always ending in `index.html` so `file://` browsing works. */
|
|
19
|
+
url, title, html) {
|
|
20
|
+
this.url = url;
|
|
21
|
+
this.title = title;
|
|
22
|
+
this.html = html;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.SitePage = SitePage;
|
|
26
|
+
/** One file that is copied out verbatim beside the pages — the stylesheet and the script. */
|
|
27
|
+
class SiteAsset {
|
|
28
|
+
url;
|
|
29
|
+
contents;
|
|
30
|
+
constructor(url, contents) {
|
|
31
|
+
this.url = url;
|
|
32
|
+
this.contents = contents;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.SiteAsset = SiteAsset;
|
|
36
|
+
/** Everything one run produces. */
|
|
37
|
+
class RenderedSite {
|
|
38
|
+
pages;
|
|
39
|
+
assets;
|
|
40
|
+
constructor(pages, assets) {
|
|
41
|
+
this.pages = pages;
|
|
42
|
+
this.assets = assets;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.RenderedSite = RenderedSite;
|
|
46
|
+
//# sourceMappingURL=SitePage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SitePage.js","sourceRoot":"","sources":["../../../../../../packages/docs/docs-site/src/render/SitePage.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;GAQG;AACH,MAAa,QAAQ;IAGJ;IACA;IACA;IAJb;IACI,8FAA8F;IACrF,GAAW,EACX,KAAa,EACb,IAAY;QAFZ,QAAG,GAAH,GAAG,CAAQ;QACX,UAAK,GAAL,KAAK,CAAQ;QACb,SAAI,GAAJ,IAAI,CAAQ;IACtB,CAAC;CACP;AAPD,4BAOC;AAED,6FAA6F;AAC7F,MAAa,SAAS;IAEL;IACA;IAFb,YACa,GAAW,EACX,QAAgB;QADhB,QAAG,GAAH,GAAG,CAAQ;QACX,aAAQ,GAAR,QAAQ,CAAQ;IAC1B,CAAC;CACP;AALD,8BAKC;AAED,mCAAmC;AACnC,MAAa,YAAY;IAER;IACA;IAFb,YACa,KAA0B,EAC1B,MAA4B;QAD5B,UAAK,GAAL,KAAK,CAAqB;QAC1B,WAAM,GAAN,MAAM,CAAsB;IACtC,CAAC;CACP;AALD,oCAKC","sourcesContent":["/**\n * One PRE-RENDERED file at one URL.\n *\n * The whole site is a list of these, which is the decision every other property falls out of: a deep\n * link resolves server-side because the file is already there, the output is a folder any static\n * host serves, the page reads correctly with JS off, and a JSDoc edit shows up as a diff on the one\n * page it affects instead of inside a bundle. Client JS is left with disclosure state, the language\n * tabs and the theme — the three things that are genuinely about this reader, right now.\n */\nexport class SitePage {\n constructor(\n /** Relative to the output root, always ending in `index.html` so `file://` browsing works. */\n readonly url: string,\n readonly title: string,\n readonly html: string,\n ) {}\n}\n\n/** One file that is copied out verbatim beside the pages — the stylesheet and the script. */\nexport class SiteAsset {\n constructor(\n readonly url: string,\n readonly contents: string,\n ) {}\n}\n\n/** Everything one run produces. */\nexport class RenderedSite {\n constructor(\n readonly pages: readonly SitePage[],\n readonly assets: readonly SiteAsset[],\n ) {}\n}\n"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ApiSpec } from '../spec/ApiSpec';
|
|
2
|
+
import { ProsePage } from '../manifest/DocsManifest';
|
|
3
|
+
import { RenderedSite, SiteAsset } from './SitePage';
|
|
4
|
+
/** The nav title of the generated home page. */
|
|
5
|
+
export declare const OVERVIEW_TITLE = "Overview";
|
|
6
|
+
/**
|
|
7
|
+
* Turns one {@link ApiSpec} plus its prose into the complete list of files to write.
|
|
8
|
+
*
|
|
9
|
+
* Every URL is a FILE — `index.html`, `prose/<slug>/index.html`, `reference/<slug>/index.html`,
|
|
10
|
+
* `schemas/<slug>/index.html` — so a deep link resolves without JS, the output is a folder any
|
|
11
|
+
* static host serves, and a JSDoc edit is a diff on the one page it affects.
|
|
12
|
+
*
|
|
13
|
+
* ## One page per named object DTO
|
|
14
|
+
*
|
|
15
|
+
* Only OBJECTS get pages. A named enum or scalar alias renders inline at each use site with its
|
|
16
|
+
* chips, because a page holding one line of values costs a reader the context they were reading.
|
|
17
|
+
* An UNDEFINED `$ref` also stays inline — a link to a page the document never defined is a dead
|
|
18
|
+
* link, which is strictly worse than the unresolved type name it replaced.
|
|
19
|
+
*/
|
|
20
|
+
export declare class SiteRenderer {
|
|
21
|
+
private readonly markdown;
|
|
22
|
+
private readonly cards;
|
|
23
|
+
private readonly html;
|
|
24
|
+
private readonly urls;
|
|
25
|
+
/** @param spec the document. @param prose the manifest's pages, in the manifest's order. */
|
|
26
|
+
render(spec: ApiSpec, prose: readonly ProsePage[], titleOverride: string): RenderedSite;
|
|
27
|
+
/** The stylesheet and the script, written beside the pages. */
|
|
28
|
+
assets(): readonly SiteAsset[];
|
|
29
|
+
private overview;
|
|
30
|
+
private sectionIndex;
|
|
31
|
+
private serversCard;
|
|
32
|
+
private prosePage;
|
|
33
|
+
private schemaPage;
|
|
34
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SiteRenderer = exports.OVERVIEW_TITLE = void 0;
|
|
4
|
+
const CardRenderer_1 = require("./CardRenderer");
|
|
5
|
+
const CodeSamples_1 = require("../samples/CodeSamples");
|
|
6
|
+
const ExampleBuilder_1 = require("../spec/ExampleBuilder");
|
|
7
|
+
const FieldTree_1 = require("../spec/FieldTree");
|
|
8
|
+
const FieldTreeRenderer_1 = require("./FieldTreeRenderer");
|
|
9
|
+
const Html_1 = require("./Html");
|
|
10
|
+
const Layout_1 = require("./Layout");
|
|
11
|
+
const Markdown_1 = require("../markdown/Markdown");
|
|
12
|
+
const NavModel_1 = require("./NavModel");
|
|
13
|
+
const OperationPageRenderer_1 = require("./OperationPageRenderer");
|
|
14
|
+
const SitePage_1 = require("./SitePage");
|
|
15
|
+
const SchemaLens_1 = require("../spec/SchemaLens");
|
|
16
|
+
const SiteScript_1 = require("../assets/SiteScript");
|
|
17
|
+
const SiteStyles_1 = require("../assets/SiteStyles");
|
|
18
|
+
const SiteUrls_1 = require("./SiteUrls");
|
|
19
|
+
/** The nav title of the generated home page. */
|
|
20
|
+
exports.OVERVIEW_TITLE = 'Overview';
|
|
21
|
+
/**
|
|
22
|
+
* Turns one {@link ApiSpec} plus its prose into the complete list of files to write.
|
|
23
|
+
*
|
|
24
|
+
* Every URL is a FILE — `index.html`, `prose/<slug>/index.html`, `reference/<slug>/index.html`,
|
|
25
|
+
* `schemas/<slug>/index.html` — so a deep link resolves without JS, the output is a folder any
|
|
26
|
+
* static host serves, and a JSDoc edit is a diff on the one page it affects.
|
|
27
|
+
*
|
|
28
|
+
* ## One page per named object DTO
|
|
29
|
+
*
|
|
30
|
+
* Only OBJECTS get pages. A named enum or scalar alias renders inline at each use site with its
|
|
31
|
+
* chips, because a page holding one line of values costs a reader the context they were reading.
|
|
32
|
+
* An UNDEFINED `$ref` also stays inline — a link to a page the document never defined is a dead
|
|
33
|
+
* link, which is strictly worse than the unresolved type name it replaced.
|
|
34
|
+
*/
|
|
35
|
+
class SiteRenderer {
|
|
36
|
+
markdown = new Markdown_1.Markdown();
|
|
37
|
+
cards = new CardRenderer_1.CardRenderer();
|
|
38
|
+
html = new Html_1.Html();
|
|
39
|
+
urls = new SiteUrls_1.SiteUrls();
|
|
40
|
+
/** @param spec the document. @param prose the manifest's pages, in the manifest's order. */
|
|
41
|
+
render(spec, prose, titleOverride) {
|
|
42
|
+
const lens = new SchemaLens_1.SchemaLens(spec);
|
|
43
|
+
const fields = new FieldTree_1.FieldTreeBuilder(lens);
|
|
44
|
+
const trees = new FieldTreeRenderer_1.FieldTreeRenderer(this.markdown, this.urls);
|
|
45
|
+
const examples = new ExampleBuilder_1.ExampleBuilder(lens);
|
|
46
|
+
const title = titleOverride === '' ? spec.title : titleOverride;
|
|
47
|
+
const nav = new NavModel_1.NavBuilder(this.urls).build(spec, prose, exports.OVERVIEW_TITLE);
|
|
48
|
+
const layout = new Layout_1.Layout(nav, this.urls, title, spec.version);
|
|
49
|
+
const operations = new OperationPageRenderer_1.OperationPageRenderer(spec, fields, trees, this.markdown, this.cards, new CodeSamples_1.CodeSampleBuilder(spec, examples), examples, layout, this.urls);
|
|
50
|
+
const pages = [this.overview(spec, layout)];
|
|
51
|
+
for (const page of prose) {
|
|
52
|
+
pages.push(this.prosePage(page, layout));
|
|
53
|
+
}
|
|
54
|
+
for (const operation of spec.operations()) {
|
|
55
|
+
pages.push(operations.page(operation));
|
|
56
|
+
}
|
|
57
|
+
for (const schema of spec.schemas) {
|
|
58
|
+
if (schema.hasOwnPage) {
|
|
59
|
+
pages.push(this.schemaPage(schema, fields, trees, examples, layout));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return new SitePage_1.RenderedSite(pages, this.assets());
|
|
63
|
+
}
|
|
64
|
+
/** The stylesheet and the script, written beside the pages. */
|
|
65
|
+
assets() {
|
|
66
|
+
return [
|
|
67
|
+
new SitePage_1.SiteAsset('styles.css', SiteStyles_1.SiteStyles.CSS),
|
|
68
|
+
new SitePage_1.SiteAsset('site.js', SiteScript_1.SiteScript.JS),
|
|
69
|
+
];
|
|
70
|
+
}
|
|
71
|
+
overview(spec, layout) {
|
|
72
|
+
const parts = [`<h1>${this.html.escape(spec.title)}</h1>`];
|
|
73
|
+
if (spec.description !== '') {
|
|
74
|
+
parts.push(this.markdown.render(spec.description));
|
|
75
|
+
}
|
|
76
|
+
parts.push(this.sectionIndex(spec));
|
|
77
|
+
const url = this.urls.home();
|
|
78
|
+
return new SitePage_1.SitePage(url, exports.OVERVIEW_TITLE, layout.render(url, exports.OVERVIEW_TITLE, parts.join('\n'), this.serversCard(spec)));
|
|
79
|
+
}
|
|
80
|
+
sectionIndex(spec) {
|
|
81
|
+
return spec.sections
|
|
82
|
+
.map((section) => {
|
|
83
|
+
const items = section.operations
|
|
84
|
+
.map((operation) => `<li>${this.html.link(this.urls.operation(operation), operation.name)}</li>`)
|
|
85
|
+
.join('');
|
|
86
|
+
return `<h2>${this.html.escape(section.name)}</h2>${this.markdown.render(section.description)}<ul>${items}</ul>`;
|
|
87
|
+
})
|
|
88
|
+
.join('\n');
|
|
89
|
+
}
|
|
90
|
+
serversCard(spec) {
|
|
91
|
+
if (spec.servers.length === 0) {
|
|
92
|
+
return '';
|
|
93
|
+
}
|
|
94
|
+
const rows = spec.servers
|
|
95
|
+
.map((server) => `<div class="field"><div class="field-head"><span class="field-name">${this.html.escape(server.url)}</span></div><div class="field-doc">${this.html.escape(server.description ?? '')}</div></div>`)
|
|
96
|
+
.join('');
|
|
97
|
+
return this.cards.panel('SERVERS', rows);
|
|
98
|
+
}
|
|
99
|
+
prosePage(page, layout) {
|
|
100
|
+
const url = this.urls.prose(page);
|
|
101
|
+
const body = `<h1>${this.html.escape(page.title)}</h1>${this.markdown.render(page.markdown)}`;
|
|
102
|
+
return new SitePage_1.SitePage(url, page.title, layout.render(url, page.title, body, ''));
|
|
103
|
+
}
|
|
104
|
+
schemaPage(schema, fields, trees, examples, layout) {
|
|
105
|
+
const url = this.urls.schema(schema);
|
|
106
|
+
const prefix = this.urls.prefixFor(url);
|
|
107
|
+
const parts = [`<h1>${this.html.escape(schema.name)}</h1>`];
|
|
108
|
+
const description = schema.node.text('description');
|
|
109
|
+
if (description !== undefined) {
|
|
110
|
+
parts.push(this.markdown.render(description));
|
|
111
|
+
}
|
|
112
|
+
const variants = fields.variantsOf(schema.node);
|
|
113
|
+
parts.push(variants.length > 0
|
|
114
|
+
? trees.renderVariants(variants, fields.discriminatorPropertyOf(schema.node), prefix)
|
|
115
|
+
: trees.render(fields.fieldsOf(schema.node), prefix));
|
|
116
|
+
const sample = examples.render(examples.build(schema.node));
|
|
117
|
+
return new SitePage_1.SitePage(url, schema.name, layout.render(url, schema.name, parts.join('\n'), this.cards.example('EXAMPLE', sample)));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
exports.SiteRenderer = SiteRenderer;
|
|
121
|
+
//# sourceMappingURL=SiteRenderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SiteRenderer.js","sourceRoot":"","sources":["../../../../../../packages/docs/docs-site/src/render/SiteRenderer.ts"],"names":[],"mappings":"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","sourcesContent":["import { ApiSpec, NamedSchema, OperationInfo, ServerInfo, TagSection } from '../spec/ApiSpec';\nimport { CardRenderer } from './CardRenderer';\nimport { CodeSampleBuilder } from '../samples/CodeSamples';\nimport { ExampleBuilder } from '../spec/ExampleBuilder';\nimport { FieldTreeBuilder } from '../spec/FieldTree';\nimport { FieldTreeRenderer } from './FieldTreeRenderer';\nimport { Html } from './Html';\nimport { Layout } from './Layout';\nimport { Markdown } from '../markdown/Markdown';\nimport { NavBuilder } from './NavModel';\nimport { OperationPageRenderer } from './OperationPageRenderer';\nimport { ProsePage } from '../manifest/DocsManifest';\nimport { RenderedSite, SiteAsset, SitePage } from './SitePage';\nimport { SchemaLens } from '../spec/SchemaLens';\nimport { SiteScript } from '../assets/SiteScript';\nimport { SiteStyles } from '../assets/SiteStyles';\nimport { SiteUrls } from './SiteUrls';\n\n/** The nav title of the generated home page. */\nexport const OVERVIEW_TITLE = 'Overview';\n\n/**\n * Turns one {@link ApiSpec} plus its prose into the complete list of files to write.\n *\n * Every URL is a FILE — `index.html`, `prose/<slug>/index.html`, `reference/<slug>/index.html`,\n * `schemas/<slug>/index.html` — so a deep link resolves without JS, the output is a folder any\n * static host serves, and a JSDoc edit is a diff on the one page it affects.\n *\n * ## One page per named object DTO\n *\n * Only OBJECTS get pages. A named enum or scalar alias renders inline at each use site with its\n * chips, because a page holding one line of values costs a reader the context they were reading.\n * An UNDEFINED `$ref` also stays inline — a link to a page the document never defined is a dead\n * link, which is strictly worse than the unresolved type name it replaced.\n */\nexport class SiteRenderer {\n private readonly markdown = new Markdown();\n private readonly cards = new CardRenderer();\n private readonly html = new Html();\n private readonly urls = new SiteUrls();\n\n /** @param spec the document. @param prose the manifest's pages, in the manifest's order. */\n render(spec: ApiSpec, prose: readonly ProsePage[], titleOverride: string): RenderedSite {\n const lens = new SchemaLens(spec);\n const fields = new FieldTreeBuilder(lens);\n const trees = new FieldTreeRenderer(this.markdown, this.urls);\n const examples = new ExampleBuilder(lens);\n const title = titleOverride === '' ? spec.title : titleOverride;\n const nav = new NavBuilder(this.urls).build(spec, prose, OVERVIEW_TITLE);\n const layout = new Layout(nav, this.urls, title, spec.version);\n const operations = new OperationPageRenderer(\n spec,\n fields,\n trees,\n this.markdown,\n this.cards,\n new CodeSampleBuilder(spec, examples),\n examples,\n layout,\n this.urls,\n );\n const pages: SitePage[] = [this.overview(spec, layout)];\n for (const page of prose) {\n pages.push(this.prosePage(page, layout));\n }\n for (const operation of spec.operations()) {\n pages.push(operations.page(operation));\n }\n for (const schema of spec.schemas) {\n if (schema.hasOwnPage) {\n pages.push(this.schemaPage(schema, fields, trees, examples, layout));\n }\n }\n return new RenderedSite(pages, this.assets());\n }\n\n /** The stylesheet and the script, written beside the pages. */\n assets(): readonly SiteAsset[] {\n return [\n new SiteAsset('styles.css', SiteStyles.CSS),\n new SiteAsset('site.js', SiteScript.JS),\n ];\n }\n\n private overview(spec: ApiSpec, layout: Layout): SitePage {\n const parts = [`<h1>${this.html.escape(spec.title)}</h1>`];\n if (spec.description !== '') {\n parts.push(this.markdown.render(spec.description));\n }\n parts.push(this.sectionIndex(spec));\n const url = this.urls.home();\n return new SitePage(\n url,\n OVERVIEW_TITLE,\n layout.render(url, OVERVIEW_TITLE, parts.join('\\n'), this.serversCard(spec)),\n );\n }\n\n private sectionIndex(spec: ApiSpec): string {\n return spec.sections\n .map((section: TagSection): string => {\n const items = section.operations\n .map(\n (operation: OperationInfo): string =>\n `<li>${this.html.link(this.urls.operation(operation), operation.name)}</li>`,\n )\n .join('');\n return `<h2>${this.html.escape(section.name)}</h2>${this.markdown.render(section.description)}<ul>${items}</ul>`;\n })\n .join('\\n');\n }\n\n private serversCard(spec: ApiSpec): string {\n if (spec.servers.length === 0) {\n return '';\n }\n const rows = spec.servers\n .map(\n (server: ServerInfo): string =>\n `<div class=\"field\"><div class=\"field-head\"><span class=\"field-name\">${this.html.escape(server.url)}</span></div><div class=\"field-doc\">${this.html.escape(server.description ?? '')}</div></div>`,\n )\n .join('');\n return this.cards.panel('SERVERS', rows);\n }\n\n private prosePage(page: ProsePage, layout: Layout): SitePage {\n const url = this.urls.prose(page);\n const body = `<h1>${this.html.escape(page.title)}</h1>${this.markdown.render(page.markdown)}`;\n return new SitePage(url, page.title, layout.render(url, page.title, body, ''));\n }\n\n private schemaPage(\n schema: NamedSchema,\n fields: FieldTreeBuilder,\n trees: FieldTreeRenderer,\n examples: ExampleBuilder,\n layout: Layout,\n ): SitePage {\n const url = this.urls.schema(schema);\n const prefix = this.urls.prefixFor(url);\n const parts = [`<h1>${this.html.escape(schema.name)}</h1>`];\n const description = schema.node.text('description');\n if (description !== undefined) {\n parts.push(this.markdown.render(description));\n }\n const variants = fields.variantsOf(schema.node);\n parts.push(\n variants.length > 0\n ? trees.renderVariants(\n variants,\n fields.discriminatorPropertyOf(schema.node),\n prefix,\n )\n : trees.render(fields.fieldsOf(schema.node), prefix),\n );\n const sample = examples.render(examples.build(schema.node));\n return new SitePage(\n url,\n schema.name,\n layout.render(\n url,\n schema.name,\n parts.join('\\n'),\n this.cards.example('EXAMPLE', sample),\n ),\n );\n }\n}\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { NamedSchema, OperationInfo } from '../spec/ApiSpec';
|
|
2
|
+
import { ProsePage } from '../manifest/DocsManifest';
|
|
3
|
+
/**
|
|
4
|
+
* Every URL the site has, in ONE place.
|
|
5
|
+
*
|
|
6
|
+
* Each is a real FILE ending in `index.html`, and each is stated RELATIVE to the site root; a page
|
|
7
|
+
* prepends its own `../` prefix with {@link prefixFor}. Root-relative links (`/reference/…`) only
|
|
8
|
+
* work when the site is the whole host, and a generated reference is routinely served under a path —
|
|
9
|
+
* GitHub Pages does it by default — where a root-relative link 404s on every page but the home one.
|
|
10
|
+
* Ending each href in `index.html` rather than a bare directory is the same argument one level down:
|
|
11
|
+
* a directory URL needs a server willing to serve the index, and `file://` is not one.
|
|
12
|
+
*/
|
|
13
|
+
export declare class SiteUrls {
|
|
14
|
+
/** The generated home page. */
|
|
15
|
+
home(): string;
|
|
16
|
+
/** `reference/<kebab-operation>/index.html` — one file per operation URL. */
|
|
17
|
+
operation(operation: OperationInfo): string;
|
|
18
|
+
/** `schemas/<kebab-name>/index.html` — one file per named object DTO. */
|
|
19
|
+
schema(schema: NamedSchema): string;
|
|
20
|
+
/** `prose/<kebab-title>/index.html`. */
|
|
21
|
+
prose(page: ProsePage): string;
|
|
22
|
+
/** `../` once per directory between a page and the site root. */
|
|
23
|
+
prefixFor(url: string): string;
|
|
24
|
+
}
|