@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.
Files changed (95) hide show
  1. package/README.md +83 -0
  2. package/package.json +28 -0
  3. package/src/DocsSiteError.d.ts +24 -0
  4. package/src/DocsSiteError.js +33 -0
  5. package/src/DocsSiteError.js.map +1 -0
  6. package/src/assets/SiteScript.d.ts +11 -0
  7. package/src/assets/SiteScript.js +88 -0
  8. package/src/assets/SiteScript.js.map +1 -0
  9. package/src/assets/SiteStyles.d.ts +13 -0
  10. package/src/assets/SiteStyles.js +106 -0
  11. package/src/assets/SiteStyles.js.map +1 -0
  12. package/src/cli/DocsSiteCli.d.ts +37 -0
  13. package/src/cli/DocsSiteCli.js +116 -0
  14. package/src/cli/DocsSiteCli.js.map +1 -0
  15. package/src/cli/WpDocsSiteMain.d.ts +21 -0
  16. package/src/cli/WpDocsSiteMain.js +60 -0
  17. package/src/cli/WpDocsSiteMain.js.map +1 -0
  18. package/src/cli/wp-docs-site.d.ts +2 -0
  19. package/src/cli/wp-docs-site.js +22 -0
  20. package/src/cli/wp-docs-site.js.map +1 -0
  21. package/src/emit/SiteWriter.d.ts +13 -0
  22. package/src/emit/SiteWriter.js +34 -0
  23. package/src/emit/SiteWriter.js.map +1 -0
  24. package/src/index.d.ts +41 -0
  25. package/src/index.js +105 -0
  26. package/src/index.js.map +1 -0
  27. package/src/manifest/DocsManifest.d.ts +28 -0
  28. package/src/manifest/DocsManifest.js +44 -0
  29. package/src/manifest/DocsManifest.js.map +1 -0
  30. package/src/manifest/ProseLoader.d.ts +23 -0
  31. package/src/manifest/ProseLoader.js +81 -0
  32. package/src/manifest/ProseLoader.js.map +1 -0
  33. package/src/markdown/Markdown.d.ts +47 -0
  34. package/src/markdown/Markdown.js +178 -0
  35. package/src/markdown/Markdown.js.map +1 -0
  36. package/src/render/CardRenderer.d.ts +28 -0
  37. package/src/render/CardRenderer.js +70 -0
  38. package/src/render/CardRenderer.js.map +1 -0
  39. package/src/render/FieldTreeRenderer.d.ts +34 -0
  40. package/src/render/FieldTreeRenderer.js +91 -0
  41. package/src/render/FieldTreeRenderer.js.map +1 -0
  42. package/src/render/Html.d.ts +14 -0
  43. package/src/render/Html.js +28 -0
  44. package/src/render/Html.js.map +1 -0
  45. package/src/render/Layout.d.ts +21 -0
  46. package/src/render/Layout.js +74 -0
  47. package/src/render/Layout.js.map +1 -0
  48. package/src/render/NavModel.d.ts +47 -0
  49. package/src/render/NavModel.js +86 -0
  50. package/src/render/NavModel.js.map +1 -0
  51. package/src/render/OperationPageRenderer.d.ts +44 -0
  52. package/src/render/OperationPageRenderer.js +102 -0
  53. package/src/render/OperationPageRenderer.js.map +1 -0
  54. package/src/render/SitePage.d.ts +30 -0
  55. package/src/render/SitePage.js +46 -0
  56. package/src/render/SitePage.js.map +1 -0
  57. package/src/render/SiteRenderer.d.ts +34 -0
  58. package/src/render/SiteRenderer.js +121 -0
  59. package/src/render/SiteRenderer.js.map +1 -0
  60. package/src/render/SiteUrls.d.ts +24 -0
  61. package/src/render/SiteUrls.js +37 -0
  62. package/src/render/SiteUrls.js.map +1 -0
  63. package/src/samples/CodeSamples.d.ts +49 -0
  64. package/src/samples/CodeSamples.js +166 -0
  65. package/src/samples/CodeSamples.js.map +1 -0
  66. package/src/serve/DevServer.d.ts +35 -0
  67. package/src/serve/DevServer.js +109 -0
  68. package/src/serve/DevServer.js.map +1 -0
  69. package/src/spec/ApiSpec.d.ts +110 -0
  70. package/src/spec/ApiSpec.js +165 -0
  71. package/src/spec/ApiSpec.js.map +1 -0
  72. package/src/spec/ExampleBuilder.d.ts +32 -0
  73. package/src/spec/ExampleBuilder.js +119 -0
  74. package/src/spec/ExampleBuilder.js.map +1 -0
  75. package/src/spec/FieldTree.d.ts +75 -0
  76. package/src/spec/FieldTree.js +155 -0
  77. package/src/spec/FieldTree.js.map +1 -0
  78. package/src/spec/JsonNode.d.ts +53 -0
  79. package/src/spec/JsonNode.js +93 -0
  80. package/src/spec/JsonNode.js.map +1 -0
  81. package/src/spec/SchemaLens.d.ts +57 -0
  82. package/src/spec/SchemaLens.js +162 -0
  83. package/src/spec/SchemaLens.js.map +1 -0
  84. package/src/spec/SchemaShape.d.ts +22 -0
  85. package/src/spec/SchemaShape.js +61 -0
  86. package/src/spec/SchemaShape.js.map +1 -0
  87. package/src/spec/Slug.d.ts +16 -0
  88. package/src/spec/Slug.js +43 -0
  89. package/src/spec/Slug.js.map +1 -0
  90. package/src/spec/SpecLoader.d.ts +26 -0
  91. package/src/spec/SpecLoader.js +64 -0
  92. package/src/spec/SpecLoader.js.map +1 -0
  93. package/src/spec/SpecReader.d.ts +46 -0
  94. package/src/spec/SpecReader.js +174 -0
  95. package/src/spec/SpecReader.js.map +1 -0
package/README.md ADDED
@@ -0,0 +1,83 @@
1
+ # @webpieces/docs-site
2
+
3
+ A static, three-pane API reference site from **any** OpenAPI 3.0-or-3.1 document.
4
+
5
+ ```bash
6
+ wp-docs-site --spec path/to/public-openapi.json --prose path/to/docs --out path/to/site
7
+ wp-docs-site --spec path/to/public-openapi.json --out path/to/site --serve
8
+ ```
9
+
10
+ **Node builtins only** — no framework, no markdown library, no bundler. Adding an API reference to
11
+ your project does not grow your dependency surface, and this package's own build is `tsc`.
12
+
13
+ ## What you get
14
+
15
+ ```
16
+ site/
17
+ index.html the overview: info.description, plus every tag and its operations
18
+ styles.css site.js one stylesheet, one script
19
+ prose/<kebab-title>/index.html one per page docs.manifest.json names, in ITS order
20
+ reference/<kebab-operation>/index.html
21
+ schemas/<kebab-name>/index.html one per named OBJECT dto
22
+ ```
23
+
24
+ One PRE-RENDERED file per URL. A deep link resolves without JavaScript, the folder works on any
25
+ static host (and under a path prefix, and over `file://`), and a JSDoc edit shows up as a diff on the
26
+ one page it affects. The client script does three things only: disclosure state, the language tabs,
27
+ and the theme.
28
+
29
+ ## The prose directory
30
+
31
+ ```json
32
+ {
33
+ "title": "Example Orders API",
34
+ "pages": [
35
+ { "file": "getting-started.md", "title": "Getting started" },
36
+ { "file": "authentication.md", "title": "Authentication" }
37
+ ]
38
+ }
39
+ ```
40
+
41
+ **The order of `pages` is the published order.** So is the document's `tags[]` for the reference.
42
+ Neither is sorted — alphabetising either reorders what a partner reads first.
43
+
44
+ A page the manifest names but the directory does not hold fails the run. A markdown file the manifest
45
+ does NOT name is simply not published: the manifest is the published list.
46
+
47
+ The markdown is a documented SUBSET of CommonMark — headings, fenced code, tables, lists,
48
+ blockquotes, rules, paragraphs, and inline `code` / `**bold**` / `*italic*` / `[links](url)` — over
49
+ trusted, PR-reviewed input. Everything is HTML-escaped before any mark is applied.
50
+
51
+ ## Reading the reference
52
+
53
+ - **Every tag is its own nav section.** A tag missing from the sidebar is an operation a partner
54
+ cannot find, which reads exactly like an operation that does not exist.
55
+ - **A webhook** files into the section of the tag it shares, appended after that tag's operations and
56
+ badged, with no method, no path and no code samples. The url is yours, not ours.
57
+ - **A `oneOf` request body** renders every branch expanded and labelled by its discriminator value —
58
+ never tabbed, because a tab is invisible to Ctrl-F and to print.
59
+ - **A named object DTO gets its own page**, and every reference to it is a LINK. The generated
60
+ example body resolves the same reference fully, because a hyperlink cannot live in a JSON sample.
61
+ - **An enum** shows the COMPLETE `Possible values:` chip line, inline, with no page of its own.
62
+ - A nullable field shows the NON-null type; a `format` renders as `string<date-time>`.
63
+
64
+ ## Code samples
65
+
66
+ Built FROM THE SPEC — the server url, the path, the header names your `securitySchemes` declare, and
67
+ an example body generated from the request schema — in HTTP, JavaScript, Go and Java. Nothing is
68
+ authored per operation, so a rename cannot leave a partner pasting a header the server stopped
69
+ reading.
70
+
71
+ The languages are labelled in words. The gopher and the Java cup are other companies' trademarks.
72
+
73
+ ## `--serve` is a preview, not a host
74
+
75
+ It binds `127.0.0.1` and nothing else. It is not hardened and not authenticated: no logging, no rate
76
+ limiting, no TLS, no access control. Put the generated FOLDER on a real static host.
77
+
78
+ ## It does not depend on the generator
79
+
80
+ This package does not import `@webpieces/openapi-generator`, even though the two model the same
81
+ document. A compile-time dependency would make "generated by us" a precondition for "renderable by
82
+ us". The document is read structurally, and the suite renders a 3.0 document written in 3.0's dialect
83
+ to keep that honest. See `responsibilities.md`.
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@webpieces/docs-site",
3
+ "version": "0.0.1",
4
+ "description": "Render a static, three-pane API reference site from any OpenAPI 3.0-or-3.1 document plus a directory of markdown prose. Node builtins only. Ships the wp-docs-site bin.",
5
+ "type": "commonjs",
6
+ "main": "./src/index.js",
7
+ "types": "./src/index.d.ts",
8
+ "author": "Dean Hiller",
9
+ "license": "Apache-2.0",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/deanhiller/webpieces-ts.git",
13
+ "directory": "packages/docs/docs-site"
14
+ },
15
+ "keywords": [
16
+ "webpieces",
17
+ "openapi",
18
+ "docs",
19
+ "static-site",
20
+ "typescript"
21
+ ],
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "bin": {
26
+ "wp-docs-site": "./src/cli/wp-docs-site.js"
27
+ }
28
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * The ONE failure type this package throws. A run either renders a complete site or throws this — it
3
+ * never prints a warning, never writes a half-site, and never guesses at a document it cannot read.
4
+ *
5
+ * It is a structured throw to the single top-level handler in `WpDocsSiteMain`, per
6
+ * `.claude/review/error-output.md`: `location` and `cure` are carried as FIELDS rather than baked
7
+ * into `message`, so the CLI renders them per audience and this class hand-numbers nothing.
8
+ *
9
+ * It is NOT `RuleFailError`, and for the same reason `@webpieces/openapi-generator` gave: that type
10
+ * lives in `@webpieces/rules-config`, the TOOLING stream a repo pins one release behind on purpose,
11
+ * while `packages/docs/*` publish with the SERVER libs. It is also not `OpenApiGenerationError` —
12
+ * this package renders ANY conforming document and therefore depends on the generator not at all.
13
+ */
14
+ export declare class DocsSiteError extends Error {
15
+ /** Where the problem IS — a spec path, a prose directory, or `<spec>#/paths/~1orders`. */
16
+ readonly location: string;
17
+ /** What to do instead, in one sentence. No numbering — the CLI owns that. */
18
+ readonly cure: string;
19
+ constructor(message: string,
20
+ /** Where the problem IS — a spec path, a prose directory, or `<spec>#/paths/~1orders`. */
21
+ location: string,
22
+ /** What to do instead, in one sentence. No numbering — the CLI owns that. */
23
+ cure: string);
24
+ }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DocsSiteError = void 0;
4
+ /**
5
+ * The ONE failure type this package throws. A run either renders a complete site or throws this — it
6
+ * never prints a warning, never writes a half-site, and never guesses at a document it cannot read.
7
+ *
8
+ * It is a structured throw to the single top-level handler in `WpDocsSiteMain`, per
9
+ * `.claude/review/error-output.md`: `location` and `cure` are carried as FIELDS rather than baked
10
+ * into `message`, so the CLI renders them per audience and this class hand-numbers nothing.
11
+ *
12
+ * It is NOT `RuleFailError`, and for the same reason `@webpieces/openapi-generator` gave: that type
13
+ * lives in `@webpieces/rules-config`, the TOOLING stream a repo pins one release behind on purpose,
14
+ * while `packages/docs/*` publish with the SERVER libs. It is also not `OpenApiGenerationError` —
15
+ * this package renders ANY conforming document and therefore depends on the generator not at all.
16
+ */
17
+ class DocsSiteError extends Error {
18
+ location;
19
+ cure;
20
+ constructor(message,
21
+ /** Where the problem IS — a spec path, a prose directory, or `<spec>#/paths/~1orders`. */
22
+ location,
23
+ /** What to do instead, in one sentence. No numbering — the CLI owns that. */
24
+ cure) {
25
+ super(`${message} (${location})`);
26
+ this.location = location;
27
+ this.cure = cure;
28
+ this.name = 'DocsSiteError';
29
+ Object.setPrototypeOf(this, new.target.prototype);
30
+ }
31
+ }
32
+ exports.DocsSiteError = DocsSiteError;
33
+ //# sourceMappingURL=DocsSiteError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DocsSiteError.js","sourceRoot":"","sources":["../../../../../packages/docs/docs-site/src/DocsSiteError.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;GAYG;AACH,MAAa,aAAc,SAAQ,KAAK;IAIvB;IAEA;IALb,YACI,OAAe;IACf,0FAA0F;IACjF,QAAgB;IACzB,6EAA6E;IACpE,IAAY;QAErB,KAAK,CAAC,GAAG,OAAO,KAAK,QAAQ,GAAG,CAAC,CAAC;QAJzB,aAAQ,GAAR,QAAQ,CAAQ;QAEhB,SAAI,GAAJ,IAAI,CAAQ;QAGrB,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACtD,CAAC;CACJ;AAZD,sCAYC","sourcesContent":["/**\n * The ONE failure type this package throws. A run either renders a complete site or throws this — it\n * never prints a warning, never writes a half-site, and never guesses at a document it cannot read.\n *\n * It is a structured throw to the single top-level handler in `WpDocsSiteMain`, per\n * `.claude/review/error-output.md`: `location` and `cure` are carried as FIELDS rather than baked\n * into `message`, so the CLI renders them per audience and this class hand-numbers nothing.\n *\n * It is NOT `RuleFailError`, and for the same reason `@webpieces/openapi-generator` gave: that type\n * lives in `@webpieces/rules-config`, the TOOLING stream a repo pins one release behind on purpose,\n * while `packages/docs/*` publish with the SERVER libs. It is also not `OpenApiGenerationError` —\n * this package renders ANY conforming document and therefore depends on the generator not at all.\n */\nexport class DocsSiteError extends Error {\n constructor(\n message: string,\n /** Where the problem IS — a spec path, a prose directory, or `<spec>#/paths/~1orders`. */\n readonly location: string,\n /** What to do instead, in one sentence. No numbering — the CLI owns that. */\n readonly cure: string,\n ) {\n super(`${message} (${location})`);\n this.name = 'DocsSiteError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n"]}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The whole client script, as one string written to `site.js` beside the pages.
3
+ *
4
+ * It does THREE things, and the list is deliberately short: disclosure state, the language tabs and
5
+ * the theme. Every URL in this site is already a file on disk, so routing, fetching and rendering
6
+ * are not its job — with JS off the page still reads, only the tabs stop switching and every sample
7
+ * is visible at once, which is a worse page rather than a blank one.
8
+ */
9
+ export declare class SiteScript {
10
+ static readonly JS = "\n(function () {\n var KEY = 'webpieces-docs-theme';\n var LANG = 'webpieces-docs-language';\n\n function readStored(key) {\n // Reading storage THROWS in a private window, so every access is guarded at the boundary.\n // webpieces-disable no-unmanaged-exceptions -- this IS the boundary; the page must read with storage unavailable\n try {\n return window.localStorage.getItem(key);\n } catch (err) {\n return null;\n }\n }\n\n function store(key, value) {\n // webpieces-disable no-unmanaged-exceptions -- same boundary, writing; a refused write is not an error here\n try {\n window.localStorage.setItem(key, value);\n } catch (err) {\n /* a private window refuses storage; the page works without it */\n }\n }\n\n function applyTheme(theme) {\n document.documentElement.setAttribute('data-theme', theme);\n store(KEY, theme);\n }\n\n function selectLanguage(id) {\n var tabs = document.querySelectorAll('.tab');\n for (var i = 0; i < tabs.length; i++) {\n tabs[i].setAttribute('aria-selected', tabs[i].getAttribute('data-language') === id ? 'true' : 'false');\n }\n var samples = document.querySelectorAll('.sample');\n for (var j = 0; j < samples.length; j++) {\n samples[j].hidden = samples[j].getAttribute('data-language') !== id;\n }\n store(LANG, id);\n }\n\n document.addEventListener('click', function (event) {\n var target = event.target;\n if (!target || !target.getAttribute) {\n return;\n }\n if (target.classList.contains('tab')) {\n selectLanguage(target.getAttribute('data-language'));\n }\n if (target.classList.contains('collapse-all')) {\n var open = document.querySelectorAll('.pane-body details[open]');\n for (var i = 0; i < open.length; i++) {\n open[i].open = false;\n }\n }\n if (target.classList.contains('theme-toggle')) {\n applyTheme(document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark');\n }\n });\n\n var storedTheme = readStored(KEY);\n if (storedTheme) {\n applyTheme(storedTheme);\n } else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {\n applyTheme('dark');\n }\n\n var storedLanguage = readStored(LANG);\n if (storedLanguage && document.querySelector('.tab[data-language=\"' + storedLanguage + '\"]')) {\n selectLanguage(storedLanguage);\n }\n})();\n";
11
+ }
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SiteScript = void 0;
4
+ /**
5
+ * The whole client script, as one string written to `site.js` beside the pages.
6
+ *
7
+ * It does THREE things, and the list is deliberately short: disclosure state, the language tabs and
8
+ * the theme. Every URL in this site is already a file on disk, so routing, fetching and rendering
9
+ * are not its job — with JS off the page still reads, only the tabs stop switching and every sample
10
+ * is visible at once, which is a worse page rather than a blank one.
11
+ */
12
+ class SiteScript {
13
+ static JS = `
14
+ (function () {
15
+ var KEY = 'webpieces-docs-theme';
16
+ var LANG = 'webpieces-docs-language';
17
+
18
+ function readStored(key) {
19
+ // Reading storage THROWS in a private window, so every access is guarded at the boundary.
20
+ // webpieces-disable no-unmanaged-exceptions -- this IS the boundary; the page must read with storage unavailable
21
+ try {
22
+ return window.localStorage.getItem(key);
23
+ } catch (err) {
24
+ return null;
25
+ }
26
+ }
27
+
28
+ function store(key, value) {
29
+ // webpieces-disable no-unmanaged-exceptions -- same boundary, writing; a refused write is not an error here
30
+ try {
31
+ window.localStorage.setItem(key, value);
32
+ } catch (err) {
33
+ /* a private window refuses storage; the page works without it */
34
+ }
35
+ }
36
+
37
+ function applyTheme(theme) {
38
+ document.documentElement.setAttribute('data-theme', theme);
39
+ store(KEY, theme);
40
+ }
41
+
42
+ function selectLanguage(id) {
43
+ var tabs = document.querySelectorAll('.tab');
44
+ for (var i = 0; i < tabs.length; i++) {
45
+ tabs[i].setAttribute('aria-selected', tabs[i].getAttribute('data-language') === id ? 'true' : 'false');
46
+ }
47
+ var samples = document.querySelectorAll('.sample');
48
+ for (var j = 0; j < samples.length; j++) {
49
+ samples[j].hidden = samples[j].getAttribute('data-language') !== id;
50
+ }
51
+ store(LANG, id);
52
+ }
53
+
54
+ document.addEventListener('click', function (event) {
55
+ var target = event.target;
56
+ if (!target || !target.getAttribute) {
57
+ return;
58
+ }
59
+ if (target.classList.contains('tab')) {
60
+ selectLanguage(target.getAttribute('data-language'));
61
+ }
62
+ if (target.classList.contains('collapse-all')) {
63
+ var open = document.querySelectorAll('.pane-body details[open]');
64
+ for (var i = 0; i < open.length; i++) {
65
+ open[i].open = false;
66
+ }
67
+ }
68
+ if (target.classList.contains('theme-toggle')) {
69
+ applyTheme(document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark');
70
+ }
71
+ });
72
+
73
+ var storedTheme = readStored(KEY);
74
+ if (storedTheme) {
75
+ applyTheme(storedTheme);
76
+ } else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
77
+ applyTheme('dark');
78
+ }
79
+
80
+ var storedLanguage = readStored(LANG);
81
+ if (storedLanguage && document.querySelector('.tab[data-language="' + storedLanguage + '"]')) {
82
+ selectLanguage(storedLanguage);
83
+ }
84
+ })();
85
+ `;
86
+ }
87
+ exports.SiteScript = SiteScript;
88
+ //# sourceMappingURL=SiteScript.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SiteScript.js","sourceRoot":"","sources":["../../../../../../packages/docs/docs-site/src/assets/SiteScript.ts"],"names":[],"mappings":";;;AAAA;;;;;;;GAOG;AACH,MAAa,UAAU;IACnB,MAAM,CAAU,EAAE,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwExB,CAAC;;AAzEF,gCA0EC","sourcesContent":["/**\n * The whole client script, as one string written to `site.js` beside the pages.\n *\n * It does THREE things, and the list is deliberately short: disclosure state, the language tabs and\n * the theme. Every URL in this site is already a file on disk, so routing, fetching and rendering\n * are not its job — with JS off the page still reads, only the tabs stop switching and every sample\n * is visible at once, which is a worse page rather than a blank one.\n */\nexport class SiteScript {\n static readonly JS = `\n(function () {\n var KEY = 'webpieces-docs-theme';\n var LANG = 'webpieces-docs-language';\n\n function readStored(key) {\n // Reading storage THROWS in a private window, so every access is guarded at the boundary.\n // webpieces-disable no-unmanaged-exceptions -- this IS the boundary; the page must read with storage unavailable\n try {\n return window.localStorage.getItem(key);\n } catch (err) {\n return null;\n }\n }\n\n function store(key, value) {\n // webpieces-disable no-unmanaged-exceptions -- same boundary, writing; a refused write is not an error here\n try {\n window.localStorage.setItem(key, value);\n } catch (err) {\n /* a private window refuses storage; the page works without it */\n }\n }\n\n function applyTheme(theme) {\n document.documentElement.setAttribute('data-theme', theme);\n store(KEY, theme);\n }\n\n function selectLanguage(id) {\n var tabs = document.querySelectorAll('.tab');\n for (var i = 0; i < tabs.length; i++) {\n tabs[i].setAttribute('aria-selected', tabs[i].getAttribute('data-language') === id ? 'true' : 'false');\n }\n var samples = document.querySelectorAll('.sample');\n for (var j = 0; j < samples.length; j++) {\n samples[j].hidden = samples[j].getAttribute('data-language') !== id;\n }\n store(LANG, id);\n }\n\n document.addEventListener('click', function (event) {\n var target = event.target;\n if (!target || !target.getAttribute) {\n return;\n }\n if (target.classList.contains('tab')) {\n selectLanguage(target.getAttribute('data-language'));\n }\n if (target.classList.contains('collapse-all')) {\n var open = document.querySelectorAll('.pane-body details[open]');\n for (var i = 0; i < open.length; i++) {\n open[i].open = false;\n }\n }\n if (target.classList.contains('theme-toggle')) {\n applyTheme(document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark');\n }\n });\n\n var storedTheme = readStored(KEY);\n if (storedTheme) {\n applyTheme(storedTheme);\n } else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {\n applyTheme('dark');\n }\n\n var storedLanguage = readStored(LANG);\n if (storedLanguage && document.querySelector('.tab[data-language=\"' + storedLanguage + '\"]')) {\n selectLanguage(storedLanguage);\n }\n})();\n`;\n}\n"]}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The whole stylesheet, as one string written to `styles.css` beside the pages.
3
+ *
4
+ * It is hand-written rather than generated by a framework because #985 puts the framework, the
5
+ * bundler and the markdown library out of scope: adding an API reference to an upstream project must
6
+ * not grow that project's dependency surface, and this package's own build has to stay `tsc`.
7
+ *
8
+ * Colours are declared once as custom properties and redefined for dark mode, so a theme toggle is
9
+ * one attribute on `<html>` and nothing else in the site knows about it.
10
+ */
11
+ export declare class SiteStyles {
12
+ static readonly CSS = "\n:root {\n --bg: #ffffff;\n --bg-soft: #f6f7f9;\n --fg: #1d2129;\n --fg-soft: #5b6472;\n --line: #e2e5ea;\n --accent: #2f5cff;\n --code-bg: #f2f3f6;\n --badge-fg: #ffffff;\n}\n:root[data-theme='dark'] {\n --bg: #14171c;\n --bg-soft: #1b1f26;\n --fg: #e7eaf0;\n --fg-soft: #9aa4b2;\n --line: #2a2f38;\n --accent: #7f9bff;\n --code-bg: #10131a;\n --badge-fg: #14171c;\n}\n* { box-sizing: border-box; }\nbody {\n margin: 0;\n background: var(--bg);\n color: var(--fg);\n font: 15px/1.6 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;\n}\na { color: var(--accent); text-decoration: none; }\na:hover { text-decoration: underline; }\ncode, pre { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 13px; }\ncode { background: var(--code-bg); padding: 1px 4px; border-radius: 3px; }\npre { background: var(--code-bg); padding: 12px; border-radius: 6px; overflow-x: auto; }\npre code { background: none; padding: 0; }\ntable { border-collapse: collapse; width: 100%; margin: 12px 0; }\nth, td { border: 1px solid var(--line); padding: 6px 10px; text-align: left; vertical-align: top; }\nth { background: var(--bg-soft); }\nhr { border: 0; border-top: 1px solid var(--line); margin: 24px 0; }\nblockquote { margin: 12px 0; padding: 4px 14px; border-left: 3px solid var(--line); color: var(--fg-soft); }\n\n.site { display: grid; grid-template-columns: 260px minmax(0, 1fr) 360px; gap: 0; min-height: 100vh; }\n.pane-nav { border-right: 1px solid var(--line); padding: 20px 16px; background: var(--bg-soft); }\n.pane-body { padding: 28px 32px; min-width: 0; }\n.pane-cards { border-left: 1px solid var(--line); padding: 20px 16px; background: var(--bg-soft); }\n.pane-cards-inner { position: sticky; top: 20px; }\n\n.site-title { font-weight: 700; font-size: 15px; margin-bottom: 4px; display: block; color: var(--fg); }\n.site-version { color: var(--fg-soft); font-size: 12px; margin-bottom: 16px; }\n.nav-group { margin-bottom: 10px; }\n.nav-group > summary { cursor: pointer; font-weight: 600; font-size: 12px; letter-spacing: .06em; text-transform: uppercase; color: var(--fg-soft); }\n.nav-group ul { list-style: none; margin: 6px 0 0; padding: 0 0 0 4px; }\n.nav-group li { margin: 2px 0; }\n.nav-group a { display: flex; gap: 6px; align-items: center; padding: 3px 6px; border-radius: 4px; color: var(--fg); }\n.nav-group a.current { background: var(--accent); color: var(--badge-fg); }\n.nav-group a.current .badge { background: var(--badge-fg); color: var(--accent); }\n\n.badge { font-size: 10px; font-weight: 700; letter-spacing: .04em; padding: 1px 5px; border-radius: 3px; background: var(--accent); color: var(--badge-fg); }\n.op-head { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 4px; }\n.op-path { background: var(--code-bg); padding: 3px 8px; border-radius: 4px; }\n\n.card { border: 1px solid var(--line); border-radius: 8px; background: var(--bg); margin-bottom: 16px; }\n.card-head { display: flex; justify-content: space-between; align-items: center; padding: 8px 12px; border-bottom: 1px solid var(--line); font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--fg-soft); }\n.card-body { padding: 12px; }\n.card-body pre { margin: 0; }\nbutton { font: inherit; font-size: 11px; cursor: pointer; background: none; border: 1px solid var(--line); border-radius: 4px; padding: 2px 8px; color: var(--fg-soft); }\nbutton:hover { color: var(--fg); }\n.tabs { display: flex; gap: 4px; flex-wrap: wrap; padding: 8px 12px 0; }\n.tab[aria-selected='true'] { border-color: var(--accent); color: var(--accent); }\n.sample[hidden] { display: none; }\n\n.field { border-top: 1px solid var(--line); padding: 10px 0; }\n.field-head { display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; }\n.field-name { font-weight: 600; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }\n.field-type { color: var(--fg-soft); font-size: 12px; }\n.field-required { color: #c0392b; font-size: 11px; font-weight: 700; letter-spacing: .04em; }\n.field-doc { margin: 4px 0 0; color: var(--fg-soft); }\n.field-children { margin-left: 16px; border-left: 1px solid var(--line); padding-left: 12px; }\n.chips { margin-top: 6px; font-size: 12px; color: var(--fg-soft); }\n.chip { display: inline-block; background: var(--code-bg); border: 1px solid var(--line); border-radius: 10px; padding: 0 8px; margin: 2px 4px 2px 0; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }\n.variant { border: 1px solid var(--line); border-radius: 8px; padding: 10px 14px; margin: 12px 0; }\n.variant-head { font-weight: 600; }\ndetails.disclosure > summary { cursor: pointer; color: var(--fg-soft); font-size: 12px; }\n\n@media (max-width: 1100px) {\n .site { grid-template-columns: 1fr; }\n .pane-nav, .pane-cards { border: 0; }\n .pane-cards-inner { position: static; }\n}\n";
13
+ }
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SiteStyles = void 0;
4
+ /**
5
+ * The whole stylesheet, as one string written to `styles.css` beside the pages.
6
+ *
7
+ * It is hand-written rather than generated by a framework because #985 puts the framework, the
8
+ * bundler and the markdown library out of scope: adding an API reference to an upstream project must
9
+ * not grow that project's dependency surface, and this package's own build has to stay `tsc`.
10
+ *
11
+ * Colours are declared once as custom properties and redefined for dark mode, so a theme toggle is
12
+ * one attribute on `<html>` and nothing else in the site knows about it.
13
+ */
14
+ class SiteStyles {
15
+ static CSS = `
16
+ :root {
17
+ --bg: #ffffff;
18
+ --bg-soft: #f6f7f9;
19
+ --fg: #1d2129;
20
+ --fg-soft: #5b6472;
21
+ --line: #e2e5ea;
22
+ --accent: #2f5cff;
23
+ --code-bg: #f2f3f6;
24
+ --badge-fg: #ffffff;
25
+ }
26
+ :root[data-theme='dark'] {
27
+ --bg: #14171c;
28
+ --bg-soft: #1b1f26;
29
+ --fg: #e7eaf0;
30
+ --fg-soft: #9aa4b2;
31
+ --line: #2a2f38;
32
+ --accent: #7f9bff;
33
+ --code-bg: #10131a;
34
+ --badge-fg: #14171c;
35
+ }
36
+ * { box-sizing: border-box; }
37
+ body {
38
+ margin: 0;
39
+ background: var(--bg);
40
+ color: var(--fg);
41
+ font: 15px/1.6 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
42
+ }
43
+ a { color: var(--accent); text-decoration: none; }
44
+ a:hover { text-decoration: underline; }
45
+ code, pre { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 13px; }
46
+ code { background: var(--code-bg); padding: 1px 4px; border-radius: 3px; }
47
+ pre { background: var(--code-bg); padding: 12px; border-radius: 6px; overflow-x: auto; }
48
+ pre code { background: none; padding: 0; }
49
+ table { border-collapse: collapse; width: 100%; margin: 12px 0; }
50
+ th, td { border: 1px solid var(--line); padding: 6px 10px; text-align: left; vertical-align: top; }
51
+ th { background: var(--bg-soft); }
52
+ hr { border: 0; border-top: 1px solid var(--line); margin: 24px 0; }
53
+ blockquote { margin: 12px 0; padding: 4px 14px; border-left: 3px solid var(--line); color: var(--fg-soft); }
54
+
55
+ .site { display: grid; grid-template-columns: 260px minmax(0, 1fr) 360px; gap: 0; min-height: 100vh; }
56
+ .pane-nav { border-right: 1px solid var(--line); padding: 20px 16px; background: var(--bg-soft); }
57
+ .pane-body { padding: 28px 32px; min-width: 0; }
58
+ .pane-cards { border-left: 1px solid var(--line); padding: 20px 16px; background: var(--bg-soft); }
59
+ .pane-cards-inner { position: sticky; top: 20px; }
60
+
61
+ .site-title { font-weight: 700; font-size: 15px; margin-bottom: 4px; display: block; color: var(--fg); }
62
+ .site-version { color: var(--fg-soft); font-size: 12px; margin-bottom: 16px; }
63
+ .nav-group { margin-bottom: 10px; }
64
+ .nav-group > summary { cursor: pointer; font-weight: 600; font-size: 12px; letter-spacing: .06em; text-transform: uppercase; color: var(--fg-soft); }
65
+ .nav-group ul { list-style: none; margin: 6px 0 0; padding: 0 0 0 4px; }
66
+ .nav-group li { margin: 2px 0; }
67
+ .nav-group a { display: flex; gap: 6px; align-items: center; padding: 3px 6px; border-radius: 4px; color: var(--fg); }
68
+ .nav-group a.current { background: var(--accent); color: var(--badge-fg); }
69
+ .nav-group a.current .badge { background: var(--badge-fg); color: var(--accent); }
70
+
71
+ .badge { font-size: 10px; font-weight: 700; letter-spacing: .04em; padding: 1px 5px; border-radius: 3px; background: var(--accent); color: var(--badge-fg); }
72
+ .op-head { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 4px; }
73
+ .op-path { background: var(--code-bg); padding: 3px 8px; border-radius: 4px; }
74
+
75
+ .card { border: 1px solid var(--line); border-radius: 8px; background: var(--bg); margin-bottom: 16px; }
76
+ .card-head { display: flex; justify-content: space-between; align-items: center; padding: 8px 12px; border-bottom: 1px solid var(--line); font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--fg-soft); }
77
+ .card-body { padding: 12px; }
78
+ .card-body pre { margin: 0; }
79
+ button { font: inherit; font-size: 11px; cursor: pointer; background: none; border: 1px solid var(--line); border-radius: 4px; padding: 2px 8px; color: var(--fg-soft); }
80
+ button:hover { color: var(--fg); }
81
+ .tabs { display: flex; gap: 4px; flex-wrap: wrap; padding: 8px 12px 0; }
82
+ .tab[aria-selected='true'] { border-color: var(--accent); color: var(--accent); }
83
+ .sample[hidden] { display: none; }
84
+
85
+ .field { border-top: 1px solid var(--line); padding: 10px 0; }
86
+ .field-head { display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; }
87
+ .field-name { font-weight: 600; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
88
+ .field-type { color: var(--fg-soft); font-size: 12px; }
89
+ .field-required { color: #c0392b; font-size: 11px; font-weight: 700; letter-spacing: .04em; }
90
+ .field-doc { margin: 4px 0 0; color: var(--fg-soft); }
91
+ .field-children { margin-left: 16px; border-left: 1px solid var(--line); padding-left: 12px; }
92
+ .chips { margin-top: 6px; font-size: 12px; color: var(--fg-soft); }
93
+ .chip { display: inline-block; background: var(--code-bg); border: 1px solid var(--line); border-radius: 10px; padding: 0 8px; margin: 2px 4px 2px 0; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
94
+ .variant { border: 1px solid var(--line); border-radius: 8px; padding: 10px 14px; margin: 12px 0; }
95
+ .variant-head { font-weight: 600; }
96
+ details.disclosure > summary { cursor: pointer; color: var(--fg-soft); font-size: 12px; }
97
+
98
+ @media (max-width: 1100px) {
99
+ .site { grid-template-columns: 1fr; }
100
+ .pane-nav, .pane-cards { border: 0; }
101
+ .pane-cards-inner { position: static; }
102
+ }
103
+ `;
104
+ }
105
+ exports.SiteStyles = SiteStyles;
106
+ //# sourceMappingURL=SiteStyles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SiteStyles.js","sourceRoot":"","sources":["../../../../../../packages/docs/docs-site/src/assets/SiteStyles.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;GASG;AACH,MAAa,UAAU;IACnB,MAAM,CAAU,GAAG,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwFzB,CAAC;;AAzFF,gCA0FC","sourcesContent":["/**\n * The whole stylesheet, as one string written to `styles.css` beside the pages.\n *\n * It is hand-written rather than generated by a framework because #985 puts the framework, the\n * bundler and the markdown library out of scope: adding an API reference to an upstream project must\n * not grow that project's dependency surface, and this package's own build has to stay `tsc`.\n *\n * Colours are declared once as custom properties and redefined for dark mode, so a theme toggle is\n * one attribute on `<html>` and nothing else in the site knows about it.\n */\nexport class SiteStyles {\n static readonly CSS = `\n:root {\n --bg: #ffffff;\n --bg-soft: #f6f7f9;\n --fg: #1d2129;\n --fg-soft: #5b6472;\n --line: #e2e5ea;\n --accent: #2f5cff;\n --code-bg: #f2f3f6;\n --badge-fg: #ffffff;\n}\n:root[data-theme='dark'] {\n --bg: #14171c;\n --bg-soft: #1b1f26;\n --fg: #e7eaf0;\n --fg-soft: #9aa4b2;\n --line: #2a2f38;\n --accent: #7f9bff;\n --code-bg: #10131a;\n --badge-fg: #14171c;\n}\n* { box-sizing: border-box; }\nbody {\n margin: 0;\n background: var(--bg);\n color: var(--fg);\n font: 15px/1.6 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;\n}\na { color: var(--accent); text-decoration: none; }\na:hover { text-decoration: underline; }\ncode, pre { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 13px; }\ncode { background: var(--code-bg); padding: 1px 4px; border-radius: 3px; }\npre { background: var(--code-bg); padding: 12px; border-radius: 6px; overflow-x: auto; }\npre code { background: none; padding: 0; }\ntable { border-collapse: collapse; width: 100%; margin: 12px 0; }\nth, td { border: 1px solid var(--line); padding: 6px 10px; text-align: left; vertical-align: top; }\nth { background: var(--bg-soft); }\nhr { border: 0; border-top: 1px solid var(--line); margin: 24px 0; }\nblockquote { margin: 12px 0; padding: 4px 14px; border-left: 3px solid var(--line); color: var(--fg-soft); }\n\n.site { display: grid; grid-template-columns: 260px minmax(0, 1fr) 360px; gap: 0; min-height: 100vh; }\n.pane-nav { border-right: 1px solid var(--line); padding: 20px 16px; background: var(--bg-soft); }\n.pane-body { padding: 28px 32px; min-width: 0; }\n.pane-cards { border-left: 1px solid var(--line); padding: 20px 16px; background: var(--bg-soft); }\n.pane-cards-inner { position: sticky; top: 20px; }\n\n.site-title { font-weight: 700; font-size: 15px; margin-bottom: 4px; display: block; color: var(--fg); }\n.site-version { color: var(--fg-soft); font-size: 12px; margin-bottom: 16px; }\n.nav-group { margin-bottom: 10px; }\n.nav-group > summary { cursor: pointer; font-weight: 600; font-size: 12px; letter-spacing: .06em; text-transform: uppercase; color: var(--fg-soft); }\n.nav-group ul { list-style: none; margin: 6px 0 0; padding: 0 0 0 4px; }\n.nav-group li { margin: 2px 0; }\n.nav-group a { display: flex; gap: 6px; align-items: center; padding: 3px 6px; border-radius: 4px; color: var(--fg); }\n.nav-group a.current { background: var(--accent); color: var(--badge-fg); }\n.nav-group a.current .badge { background: var(--badge-fg); color: var(--accent); }\n\n.badge { font-size: 10px; font-weight: 700; letter-spacing: .04em; padding: 1px 5px; border-radius: 3px; background: var(--accent); color: var(--badge-fg); }\n.op-head { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 4px; }\n.op-path { background: var(--code-bg); padding: 3px 8px; border-radius: 4px; }\n\n.card { border: 1px solid var(--line); border-radius: 8px; background: var(--bg); margin-bottom: 16px; }\n.card-head { display: flex; justify-content: space-between; align-items: center; padding: 8px 12px; border-bottom: 1px solid var(--line); font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--fg-soft); }\n.card-body { padding: 12px; }\n.card-body pre { margin: 0; }\nbutton { font: inherit; font-size: 11px; cursor: pointer; background: none; border: 1px solid var(--line); border-radius: 4px; padding: 2px 8px; color: var(--fg-soft); }\nbutton:hover { color: var(--fg); }\n.tabs { display: flex; gap: 4px; flex-wrap: wrap; padding: 8px 12px 0; }\n.tab[aria-selected='true'] { border-color: var(--accent); color: var(--accent); }\n.sample[hidden] { display: none; }\n\n.field { border-top: 1px solid var(--line); padding: 10px 0; }\n.field-head { display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; }\n.field-name { font-weight: 600; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }\n.field-type { color: var(--fg-soft); font-size: 12px; }\n.field-required { color: #c0392b; font-size: 11px; font-weight: 700; letter-spacing: .04em; }\n.field-doc { margin: 4px 0 0; color: var(--fg-soft); }\n.field-children { margin-left: 16px; border-left: 1px solid var(--line); padding-left: 12px; }\n.chips { margin-top: 6px; font-size: 12px; color: var(--fg-soft); }\n.chip { display: inline-block; background: var(--code-bg); border: 1px solid var(--line); border-radius: 10px; padding: 0 8px; margin: 2px 4px 2px 0; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }\n.variant { border: 1px solid var(--line); border-radius: 8px; padding: 10px 14px; margin: 12px 0; }\n.variant-head { font-weight: 600; }\ndetails.disclosure > summary { cursor: pointer; color: var(--fg-soft); font-size: 12px; }\n\n@media (max-width: 1100px) {\n .site { grid-template-columns: 1fr; }\n .pane-nav, .pane-cards { border: 0; }\n .pane-cards-inner { position: static; }\n}\n`;\n}\n"]}
@@ -0,0 +1,37 @@
1
+ import { RenderedSite } from '../render/SitePage';
2
+ export declare const USAGE: string;
3
+ /** What one run produced, so a caller can print it or assert on it. */
4
+ export declare class CliResult {
5
+ readonly written: readonly string[];
6
+ readonly site: RenderedSite;
7
+ readonly outDir: string;
8
+ readonly serve: boolean;
9
+ readonly port: number;
10
+ constructor(written: readonly string[], site: RenderedSite, outDir: string, serve: boolean, port: number);
11
+ }
12
+ /**
13
+ * `wp-docs-site`'s argument parsing and composition, with NO process-level concerns in it — no
14
+ * `process.exit`, no `console` — so the suite exercises the whole command exactly as a user runs it.
15
+ *
16
+ * `--spec` and `--out` are REQUIRED and have no defaults. A defaulted `--out` writes a site
17
+ * somewhere the caller did not name; a defaulted `--spec` documents whichever service the command
18
+ * happened to start in.
19
+ */
20
+ export declare class DocsSiteCli {
21
+ private readonly specs;
22
+ private readonly reader;
23
+ private readonly prose;
24
+ private readonly renderer;
25
+ private readonly writer;
26
+ /** @param argv the arguments AFTER the program name. @param cwd what relative paths resolve against. */
27
+ run(argv: readonly string[], cwd: string): CliResult;
28
+ wantsHelp(argv: readonly string[]): boolean;
29
+ private proseDirOf;
30
+ private portOf;
31
+ /**
32
+ * The value after a flag. An UNKNOWN flag is refused rather than ignored: a mistyped `--spce`
33
+ * would otherwise fall through to "needs both flags", which sends the reader looking at the
34
+ * wrong thing.
35
+ */
36
+ private valueOf;
37
+ }
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DocsSiteCli = exports.CliResult = exports.USAGE = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const path = tslib_1.__importStar(require("node:path"));
6
+ const DocsSiteError_1 = require("../DocsSiteError");
7
+ const ProseLoader_1 = require("../manifest/ProseLoader");
8
+ const SiteRenderer_1 = require("../render/SiteRenderer");
9
+ const SiteWriter_1 = require("../emit/SiteWriter");
10
+ const SpecLoader_1 = require("../spec/SpecLoader");
11
+ const SpecReader_1 = require("../spec/SpecReader");
12
+ const SPEC = '--spec';
13
+ const PROSE = '--prose';
14
+ const OUT = '--out';
15
+ const PORT = '--port';
16
+ const SERVE = '--serve';
17
+ const HELP = '--help';
18
+ const VALUE_FLAGS = [SPEC, PROSE, OUT, PORT];
19
+ const BARE_FLAGS = [SERVE, HELP];
20
+ exports.USAGE = [
21
+ 'wp-docs-site --spec <openapi.json> [--prose <dir>] --out <dir> [--serve] [--port <n>]',
22
+ '',
23
+ 'Renders a static, three-pane API reference site: one PRE-RENDERED HTML file per URL, so a deep',
24
+ 'link resolves without JavaScript and the output is a folder any static host serves.',
25
+ '',
26
+ ' --spec an OpenAPI 3.0 or 3.1 JSON document. Any conforming document — this renderer does',
27
+ ' not require the document to have been generated by webpieces.',
28
+ ' --prose a directory holding docs.manifest.json and the markdown it names, in ITS order.',
29
+ ' --out where to write the site. Nothing in it is deleted.',
30
+ ' --serve after writing, preview the result on 127.0.0.1 and nothing else. It is a PREVIEW:',
31
+ ' not hardened, not authenticated, and not a way to host the site.',
32
+ ' --port the preview port. 0, the default, asks the operating system for a free one.',
33
+ ].join('\n');
34
+ /** What one run produced, so a caller can print it or assert on it. */
35
+ class CliResult {
36
+ written;
37
+ site;
38
+ outDir;
39
+ serve;
40
+ port;
41
+ constructor(written, site, outDir, serve, port) {
42
+ this.written = written;
43
+ this.site = site;
44
+ this.outDir = outDir;
45
+ this.serve = serve;
46
+ this.port = port;
47
+ }
48
+ }
49
+ exports.CliResult = CliResult;
50
+ /**
51
+ * `wp-docs-site`'s argument parsing and composition, with NO process-level concerns in it — no
52
+ * `process.exit`, no `console` — so the suite exercises the whole command exactly as a user runs it.
53
+ *
54
+ * `--spec` and `--out` are REQUIRED and have no defaults. A defaulted `--out` writes a site
55
+ * somewhere the caller did not name; a defaulted `--spec` documents whichever service the command
56
+ * happened to start in.
57
+ */
58
+ class DocsSiteCli {
59
+ specs = new SpecLoader_1.SpecLoader();
60
+ reader = new SpecReader_1.SpecReader();
61
+ prose = new ProseLoader_1.ProseLoader();
62
+ renderer = new SiteRenderer_1.SiteRenderer();
63
+ writer = new SiteWriter_1.SiteWriter();
64
+ /** @param argv the arguments AFTER the program name. @param cwd what relative paths resolve against. */
65
+ run(argv, cwd) {
66
+ const spec = this.valueOf(argv, SPEC);
67
+ const out = this.valueOf(argv, OUT);
68
+ if (spec === undefined || out === undefined) {
69
+ throw new DocsSiteError_1.DocsSiteError(`wp-docs-site needs both ${SPEC} and ${OUT}`, 'wp-docs-site', `Run: wp-docs-site ${SPEC} <openapi.json> ${OUT} <dir>`);
70
+ }
71
+ const proseDir = this.proseDirOf(argv, cwd);
72
+ const document = this.specs.load(path.resolve(cwd, spec));
73
+ const site = this.renderer.render(this.reader.read(document), this.prose.load(proseDir), this.prose.titleOf(proseDir));
74
+ const outDir = path.resolve(cwd, out);
75
+ return new CliResult(this.writer.write(outDir, site), site, outDir, argv.includes(SERVE), this.portOf(argv));
76
+ }
77
+ wantsHelp(argv) {
78
+ return argv.includes(HELP);
79
+ }
80
+ proseDirOf(argv, cwd) {
81
+ const declared = this.valueOf(argv, PROSE);
82
+ return declared === undefined ? undefined : path.resolve(cwd, declared);
83
+ }
84
+ portOf(argv) {
85
+ const declared = this.valueOf(argv, PORT);
86
+ if (declared === undefined) {
87
+ return 0;
88
+ }
89
+ const port = Number(declared);
90
+ if (!Number.isInteger(port) || port < 0 || port > 65535) {
91
+ throw new DocsSiteError_1.DocsSiteError(`${PORT} '${declared}' is not a port number`, 'wp-docs-site', `${PORT} takes a whole number from 0 to 65535. 0 asks the operating system for a free one.`);
92
+ }
93
+ return port;
94
+ }
95
+ /**
96
+ * The value after a flag. An UNKNOWN flag is refused rather than ignored: a mistyped `--spce`
97
+ * would otherwise fall through to "needs both flags", which sends the reader looking at the
98
+ * wrong thing.
99
+ */
100
+ valueOf(argv, flag) {
101
+ for (let i = 0; i < argv.length; i++) {
102
+ const argument = argv[i];
103
+ if (argument === flag) {
104
+ return argv[i + 1];
105
+ }
106
+ if (argument.startsWith('--') &&
107
+ !VALUE_FLAGS.includes(argument) &&
108
+ !BARE_FLAGS.includes(argument)) {
109
+ throw new DocsSiteError_1.DocsSiteError(`unknown flag '${argument}'`, 'wp-docs-site', `wp-docs-site takes ${[...VALUE_FLAGS, ...BARE_FLAGS].join(', ')}, and nothing else.`);
110
+ }
111
+ }
112
+ return undefined;
113
+ }
114
+ }
115
+ exports.DocsSiteCli = DocsSiteCli;
116
+ //# sourceMappingURL=DocsSiteCli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DocsSiteCli.js","sourceRoot":"","sources":["../../../../../../packages/docs/docs-site/src/cli/DocsSiteCli.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'node:path';\nimport { DocsSiteError } from '../DocsSiteError';\nimport { ProseLoader } from '../manifest/ProseLoader';\nimport { RenderedSite } from '../render/SitePage';\nimport { SiteRenderer } from '../render/SiteRenderer';\nimport { SiteWriter } from '../emit/SiteWriter';\nimport { SpecLoader } from '../spec/SpecLoader';\nimport { SpecReader } from '../spec/SpecReader';\n\nconst SPEC = '--spec';\nconst PROSE = '--prose';\nconst OUT = '--out';\nconst PORT = '--port';\nconst SERVE = '--serve';\nconst HELP = '--help';\n\nconst VALUE_FLAGS = [SPEC, PROSE, OUT, PORT];\nconst BARE_FLAGS = [SERVE, HELP];\n\nexport const USAGE = [\n 'wp-docs-site --spec <openapi.json> [--prose <dir>] --out <dir> [--serve] [--port <n>]',\n '',\n 'Renders a static, three-pane API reference site: one PRE-RENDERED HTML file per URL, so a deep',\n 'link resolves without JavaScript and the output is a folder any static host serves.',\n '',\n ' --spec an OpenAPI 3.0 or 3.1 JSON document. Any conforming document — this renderer does',\n ' not require the document to have been generated by webpieces.',\n ' --prose a directory holding docs.manifest.json and the markdown it names, in ITS order.',\n ' --out where to write the site. Nothing in it is deleted.',\n ' --serve after writing, preview the result on 127.0.0.1 and nothing else. It is a PREVIEW:',\n ' not hardened, not authenticated, and not a way to host the site.',\n ' --port the preview port. 0, the default, asks the operating system for a free one.',\n].join('\\n');\n\n/** What one run produced, so a caller can print it or assert on it. */\nexport class CliResult {\n constructor(\n readonly written: readonly string[],\n readonly site: RenderedSite,\n readonly outDir: string,\n readonly serve: boolean,\n readonly port: number,\n ) {}\n}\n\n/**\n * `wp-docs-site`'s argument parsing and composition, with NO process-level concerns in it — no\n * `process.exit`, no `console` — so the suite exercises the whole command exactly as a user runs it.\n *\n * `--spec` and `--out` are REQUIRED and have no defaults. A defaulted `--out` writes a site\n * somewhere the caller did not name; a defaulted `--spec` documents whichever service the command\n * happened to start in.\n */\nexport class DocsSiteCli {\n private readonly specs = new SpecLoader();\n private readonly reader = new SpecReader();\n private readonly prose = new ProseLoader();\n private readonly renderer = new SiteRenderer();\n private readonly writer = new SiteWriter();\n\n /** @param argv the arguments AFTER the program name. @param cwd what relative paths resolve against. */\n run(argv: readonly string[], cwd: string): CliResult {\n const spec = this.valueOf(argv, SPEC);\n const out = this.valueOf(argv, OUT);\n if (spec === undefined || out === undefined) {\n throw new DocsSiteError(\n `wp-docs-site needs both ${SPEC} and ${OUT}`,\n 'wp-docs-site',\n `Run: wp-docs-site ${SPEC} <openapi.json> ${OUT} <dir>`,\n );\n }\n const proseDir = this.proseDirOf(argv, cwd);\n const document = this.specs.load(path.resolve(cwd, spec));\n const site = this.renderer.render(\n this.reader.read(document),\n this.prose.load(proseDir),\n this.prose.titleOf(proseDir),\n );\n const outDir = path.resolve(cwd, out);\n return new CliResult(\n this.writer.write(outDir, site),\n site,\n outDir,\n argv.includes(SERVE),\n this.portOf(argv),\n );\n }\n\n wantsHelp(argv: readonly string[]): boolean {\n return argv.includes(HELP);\n }\n\n private proseDirOf(argv: readonly string[], cwd: string): string | undefined {\n const declared = this.valueOf(argv, PROSE);\n return declared === undefined ? undefined : path.resolve(cwd, declared);\n }\n\n private portOf(argv: readonly string[]): number {\n const declared = this.valueOf(argv, PORT);\n if (declared === undefined) {\n return 0;\n }\n const port = Number(declared);\n if (!Number.isInteger(port) || port < 0 || port > 65535) {\n throw new DocsSiteError(\n `${PORT} '${declared}' is not a port number`,\n 'wp-docs-site',\n `${PORT} takes a whole number from 0 to 65535. 0 asks the operating system for a free one.`,\n );\n }\n return port;\n }\n\n /**\n * The value after a flag. An UNKNOWN flag is refused rather than ignored: a mistyped `--spce`\n * would otherwise fall through to \"needs both flags\", which sends the reader looking at the\n * wrong thing.\n */\n private valueOf(argv: readonly string[], flag: string): string | undefined {\n for (let i = 0; i < argv.length; i++) {\n const argument = argv[i]!;\n if (argument === flag) {\n return argv[i + 1];\n }\n if (\n argument.startsWith('--') &&\n !VALUE_FLAGS.includes(argument) &&\n !BARE_FLAGS.includes(argument)\n ) {\n throw new DocsSiteError(\n `unknown flag '${argument}'`,\n 'wp-docs-site',\n `wp-docs-site takes ${[...VALUE_FLAGS, ...BARE_FLAGS].join(', ')}, and nothing else.`,\n );\n }\n }\n return undefined;\n }\n}\n"]}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The body of the `wp-docs-site` bin: argument handling, the SINGLE top-level handler, and the exit
3
+ * code — with nothing that touches the process itself, so the suite runs the whole command.
4
+ *
5
+ * Everything below here THROWS {@link DocsSiteError} and prints nothing, so this is the only
6
+ * renderer of a failure and the only writer of an exit code (`.claude/review/error-output.md`). It
7
+ * renders from the error's FIELDS — the message, the location, the cure — rather than from a string
8
+ * somebody baked a cure into, so one audience's formatting never freezes into a thrower.
9
+ */
10
+ export declare class WpDocsSiteMain {
11
+ private readonly cli;
12
+ /** @returns the process exit code. 0 on success, 1 on a stated failure. */
13
+ run(argv: readonly string[], cwd: string, out: NodeJS.WritableStream): Promise<number>;
14
+ /**
15
+ * `--serve` starts the localhost preview and RESOLVES when it is listening, rather than blocking
16
+ * forever. The bin keeps the process alive because the socket is open, and the suite can start
17
+ * one, read a page and stop it — a run method that never returned would be untestable.
18
+ */
19
+ private preview;
20
+ private render;
21
+ }