@scalar/openapi-to-markdown 0.6.0 → 1.0.0

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 (53) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +44 -35
  3. package/dist/create-markdown-from-openapi.d.ts +15 -25
  4. package/dist/create-markdown-from-openapi.d.ts.map +1 -1
  5. package/dist/create-markdown-from-openapi.js +35 -0
  6. package/dist/index.d.ts +4 -2
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1 -850
  9. package/dist/load-document.d.ts +4 -0
  10. package/dist/load-document.d.ts.map +1 -0
  11. package/dist/load-document.js +151 -0
  12. package/dist/markdown-nodes.d.ts +25 -0
  13. package/dist/markdown-nodes.d.ts.map +1 -0
  14. package/dist/markdown-nodes.js +34 -0
  15. package/dist/parse-description.d.ts +6 -0
  16. package/dist/parse-description.d.ts.map +1 -0
  17. package/dist/parse-description.js +116 -0
  18. package/dist/parse-html-description.d.ts +4 -0
  19. package/dist/parse-html-description.d.ts.map +1 -0
  20. package/dist/parse-html-description.js +11 -0
  21. package/dist/render-document.d.ts +4 -0
  22. package/dist/render-document.d.ts.map +1 -0
  23. package/dist/render-document.js +112 -0
  24. package/dist/render-operation.d.ts +13 -0
  25. package/dist/render-operation.d.ts.map +1 -0
  26. package/dist/render-operation.js +89 -0
  27. package/dist/render-schema.d.ts +31 -0
  28. package/dist/render-schema.d.ts.map +1 -0
  29. package/dist/render-schema.js +98 -0
  30. package/dist/render-security.d.ts +6 -0
  31. package/dist/render-security.d.ts.map +1 -0
  32. package/dist/render-security.js +33 -0
  33. package/dist/select-document.d.ts +29 -0
  34. package/dist/select-document.d.ts.map +1 -0
  35. package/dist/select-document.js +312 -0
  36. package/package.json +15 -27
  37. package/dist/components/MarkdownReference.test.d.ts +0 -2
  38. package/dist/components/MarkdownReference.test.d.ts.map +0 -1
  39. package/dist/components/MarkdownReference.vue.d.ts +0 -9
  40. package/dist/components/MarkdownReference.vue.d.ts.map +0 -1
  41. package/dist/components/Schema.test.d.ts +0 -2
  42. package/dist/components/Schema.test.d.ts.map +0 -1
  43. package/dist/components/Schema.vue.d.ts +0 -10
  44. package/dist/components/Schema.vue.d.ts.map +0 -1
  45. package/dist/components/Security.vue.d.ts +0 -9
  46. package/dist/components/Security.vue.d.ts.map +0 -1
  47. package/dist/components/XmlOrJson.vue.d.ts +0 -8
  48. package/dist/components/XmlOrJson.vue.d.ts.map +0 -1
  49. package/dist/create-markdown-from-openapi.test.d.ts +0 -2
  50. package/dist/create-markdown-from-openapi.test.d.ts.map +0 -1
  51. package/dist/index.js.map +0 -1
  52. package/dist/selection.test.d.ts +0 -2
  53. package/dist/selection.test.d.ts.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @scalar/openapi-to-markdown
2
2
 
3
+ ## 1.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [#10233](https://github.com/scalar/scalar/pull/10233): Generate Markdown directly from a Markdown syntax tree, avoiding Vue server rendering and the generated HTML conversion pipeline. Reuse normalized schemas and parsed descriptions across pages while preserving selection, schema coercion, reference resolution, and raw HTML description sanitization.
8
+
9
+ Keep the HTML API through an on-demand Markdown conversion. Markdown and HTML spacing and escaping may differ from previous output.
10
+
11
+ ### Minor Changes
12
+
13
+ - [#10224](https://github.com/scalar/scalar/pull/10224): Add a reusable renderer that loads an OpenAPI document once and generates multiple Markdown or HTML pages from it.
14
+
15
+ ### Patch Changes
16
+
17
+ - [#10241](https://github.com/scalar/scalar/pull/10241): Simplify reference link rebuilding after OpenAPI document coercion.
18
+
3
19
  ## 0.6.0
4
20
 
5
21
  ### Minor Changes
package/README.md CHANGED
@@ -68,6 +68,35 @@ const operationMarkdownByPointer = await createMarkdownFromOpenApi(content, {
68
68
  })
69
69
  ```
70
70
 
71
+ ### Render multiple pages
72
+
73
+ Create a reusable renderer when exporting several pages from the same API description.
74
+ It loads, upgrades, coerces, and resolves the document once. Each call uses the same selectors as
75
+ `createMarkdownFromOpenApi`, and omitting a selector renders the complete document.
76
+
77
+ ```ts
78
+ import { createOpenApiMarkdownRenderer } from '@scalar/openapi-to-markdown'
79
+
80
+ const renderer = await createOpenApiMarkdownRenderer(content)
81
+
82
+ const introduction = await renderer.render({ introduction: true })
83
+ const operation = await renderer.render({
84
+ operation: { path: '/users/{id}', method: 'get' },
85
+ })
86
+ const tag = await renderer.render({ tag: 'Users' })
87
+ const model = await renderer.render({ model: 'User' })
88
+ const webhook = await renderer.render({
89
+ webhook: { name: 'userCreated', method: 'post' },
90
+ })
91
+ ```
92
+
93
+ The factory accepts the same document objects, JSON/YAML strings, file paths, and URLs
94
+ as the one-shot functions. Source files and URLs are read during creation, including
95
+ references. Create a new renderer when the source changes. Reuse one renderer per API
96
+ description during a build, then release it when the build finishes. Renderers do not
97
+ share a global document cache. An invalid selection rejects that call without preventing
98
+ later calls on the same renderer.
99
+
71
100
  ### With Hono
72
101
 
73
102
  You use the package with any Node.js framework. Here is an example for [Hono](https://hono.dev/):
@@ -94,43 +123,21 @@ app.get('/llms.txt', (c) => c.text(markdown))
94
123
  serve(app)
95
124
  ```
96
125
 
97
- ### Generate HTML
126
+ ### Markdown rendering
98
127
 
99
- This is not really the purpose of the package, but maybe good to know: This package actually renders HTML at first, and
100
- transforms the HTML to Markdown then.
128
+ The renderer constructs a Markdown syntax tree directly from the resolved API description.
129
+ It preserves Markdown descriptions, GFM tables and code blocks without rendering a Vue app
130
+ or converting the generated document through HTML. Descriptions containing raw HTML or
131
+ Scalar alerts use a separate sanitization and conversion path. Images remain excluded.
101
132
 
102
- So if you'd like to have a really light-weight HTML API Reference, here you are:
133
+ Schema normalization and description parsing are cached within each renderer. Recursive
134
+ schema expansion still tracks ancestors and stops at a depth of ten. Output may use tighter
135
+ list spacing and normalized Markdown escaping compared with earlier versions.
103
136
 
104
- ```ts
105
- import { createHtmlFromOpenApi } from '@scalar/openapi-to-markdown'
106
- import { Hono } from 'hono'
107
-
108
- // Generate HTML from an OpenAPI document
109
- const html = await createHtmlFromOpenApi(content)
110
-
111
- const app = new Hono()
137
+ ### HTML output
112
138
 
113
- app.get('/', (c) =>
114
- c.html(
115
- `<!doctype html>
116
- <html lang="en" data-theme="light">
117
- <head>
118
- <meta charset="UTF-8" />
119
- <title>Scalar Galaxy</title>
120
- <!-- Basic styling for semantic HTML tags (optional) -->
121
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
122
- </head>
123
- <body>
124
- <main class="container">
125
- ${html}
126
- </main>
127
- </body>
128
- </html>`,
129
- ),
130
- )
131
-
132
- serve(app)
133
- ```
139
+ `createHtmlFromOpenApi` and `renderer.renderHtml` remain available. They convert the
140
+ Markdown output to HTML on demand, without loading a Vue renderer.
134
141
 
135
142
  ## Community
136
143
 
@@ -142,7 +149,7 @@ The source code in this repository is licensed under [MIT](https://github.com/sc
142
149
 
143
150
  ## Individual reference pages
144
151
 
145
- Both `createMarkdownFromOpenApi` and `createHtmlFromOpenApi` accept the same options. Choose one selector per call:
152
+ `createMarkdownFromOpenApi` and `renderer.render` accept the same selection options. Choose one selector per call:
146
153
 
147
154
  ```ts
148
155
  await createMarkdownFromOpenApi(content, { tag: 'pets' })
@@ -151,7 +158,9 @@ await createMarkdownFromOpenApi(content, {
151
158
  webhook: { name: 'petCreated', method: 'post' },
152
159
  })
153
160
  await createMarkdownFromOpenApi(content, { introduction: true })
154
- await createHtmlFromOpenApi(content, { operation: { operationId: 'getUser' } })
161
+ await createMarkdownFromOpenApi(content, {
162
+ operation: { operationId: 'getUser' },
163
+ })
155
164
  ```
156
165
 
157
166
  - **Operation:** One operation, effective parameters, servers and authentication, its tags, and referenced component schemas. Existing path/method, operation ID, and JSON pointer selectors still work. Methods are case insensitive.
@@ -1,29 +1,19 @@
1
- import type { HttpMethod } from '@scalar/helpers/http/http-methods';
2
1
  import type { OpenApiDocument } from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document';
2
+ import { type OpenApiRenderOptions } from './select-document.js';
3
3
  type AnyDocument = OpenApiDocument | Record<string, unknown> | string;
4
- export type { HttpMethod };
5
- export type OperationSelector = {
6
- path: string;
7
- method: HttpMethod | Uppercase<HttpMethod>;
8
- } | {
9
- operationId: string;
10
- } | {
11
- pointer: string;
4
+ /** A resolved API description that can render multiple pages without loading it again. */
5
+ export type OpenApiMarkdownRenderer = {
6
+ render: (options?: OpenApiRenderOptions) => Promise<string>;
7
+ renderHtml: (options?: OpenApiRenderOptions) => Promise<string>;
12
8
  };
13
- /** Select one reference page, or omit selectors for the whole document. */
14
- export type OpenApiRenderOptions = {
15
- [Key in keyof PageSelectors]: Partial<Record<Exclude<keyof PageSelectors, Key>, never>> & Pick<PageSelectors, Key>;
16
- }[keyof PageSelectors] | Partial<Record<keyof PageSelectors, never>>;
17
- type PageSelectors = {
18
- operation: OperationSelector;
19
- tag: string;
20
- model: string;
21
- webhook: {
22
- name: string;
23
- method: HttpMethod | Uppercase<HttpMethod>;
24
- };
25
- introduction: true;
26
- };
27
- export declare function createHtmlFromOpenApi(input: AnyDocument, options?: OpenApiRenderOptions): Promise<string>;
28
- export declare function createMarkdownFromOpenApi(content: AnyDocument, options?: OpenApiRenderOptions): Promise<string>;
9
+ /**
10
+ * Load and resolve an API description once, then render any number of selections.
11
+ * Each renderer owns its document; create a new renderer to pick up source changes.
12
+ */
13
+ export declare const createOpenApiMarkdownRenderer: (input: AnyDocument) => Promise<OpenApiMarkdownRenderer>;
14
+ /** Generate Markdown from an API description, optionally scoped to a single page. */
15
+ export declare const createMarkdownFromOpenApi: (input: AnyDocument, options?: OpenApiRenderOptions) => Promise<string>;
16
+ /** Generate HTML through the optional Markdown conversion path. */
17
+ export declare const createHtmlFromOpenApi: (input: AnyDocument, options?: OpenApiRenderOptions) => Promise<string>;
18
+ export {};
29
19
  //# sourceMappingURL=create-markdown-from-openapi.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-markdown-from-openapi.d.ts","sourceRoot":"","sources":["../src/create-markdown-from-openapi.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAA;AAOnE,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,8DAA8D,CAAA;AAanH,KAAK,WAAW,GAAG,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAA;AACrE,YAAY,EAAE,UAAU,EAAE,CAAA;AAC1B,MAAM,MAAM,iBAAiB,GACzB;IACE,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC,CAAA;CAC3C,GACD;IACE,WAAW,EAAE,MAAM,CAAA;CACpB,GACD;IACE,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AACL,2EAA2E;AAC3E,MAAM,MAAM,oBAAoB,GAC5B;KACG,GAAG,IAAI,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,aAAa,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,aAAa,EAAE,GAAG,CAAC;CACnH,CAAC,MAAM,aAAa,CAAC,GACtB,OAAO,CAAC,MAAM,CAAC,MAAM,aAAa,EAAE,KAAK,CAAC,CAAC,CAAA;AAE/C,KAAK,aAAa,GAAG;IACnB,SAAS,EAAE,iBAAiB,CAAA;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC,CAAA;KAAE,CAAA;IACrE,YAAY,EAAE,IAAI,CAAA;CACnB,CAAA;AA6ZD,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,oBAAoB,mBA2C7F;AAED,wBAAsB,yBAAyB,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,oBAAoB,mBAEnG"}
1
+ {"version":3,"file":"create-markdown-from-openapi.d.ts","sourceRoot":"","sources":["../src/create-markdown-from-openapi.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8DAA8D,CAAA;AAInG,OAAO,EAAE,KAAK,oBAAoB,EAAkB,MAAM,mBAAmB,CAAA;AAE7E,KAAK,WAAW,GAAG,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAA;AACrE,0FAA0F;AAC1F,MAAM,MAAM,uBAAuB,GAAG;IACpC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,oBAAoB,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAC3D,UAAU,EAAE,CAAC,OAAO,CAAC,EAAE,oBAAoB,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;CAChE,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,6BAA6B,GAAU,OAAO,WAAW,KAAG,OAAO,CAAC,uBAAuB,CAoBvG,CAAA;AAED,qFAAqF;AACrF,eAAO,MAAM,yBAAyB,GACpC,OAAO,WAAW,EAClB,UAAU,oBAAoB,KAC7B,OAAO,CAAC,MAAM,CAGhB,CAAA;AAED,mEAAmE;AACnE,eAAO,MAAM,qBAAqB,GAAU,OAAO,WAAW,EAAE,UAAU,oBAAoB,KAAG,OAAO,CAAC,MAAM,CAG9G,CAAA"}
@@ -0,0 +1,35 @@
1
+ import { loadDocument } from './load-document.js';
2
+ import { createDocumentRenderer } from './render-document.js';
3
+ import { selectDocument } from './select-document.js';
4
+ /**
5
+ * Load and resolve an API description once, then render any number of selections.
6
+ * Each renderer owns its document; create a new renderer to pick up source changes.
7
+ */
8
+ export const createOpenApiMarkdownRenderer = async (input) => {
9
+ const content = await loadDocument(input);
10
+ const renderDocument = createDocumentRenderer();
11
+ const render = async (options) => await renderDocument(selectDocument(content, options));
12
+ return {
13
+ render,
14
+ renderHtml: async (options) => {
15
+ const [{ unified }, { default: remarkParse }, { default: remarkRehype }, { default: rehypeStringify }] = await Promise.all([
16
+ import('unified'),
17
+ import('remark-parse'),
18
+ import('remark-rehype'),
19
+ import('rehype-stringify'),
20
+ ]);
21
+ const processor = unified().use(remarkParse).use(remarkRehype).use(rehypeStringify);
22
+ return processor.processSync(await render(options)).toString();
23
+ },
24
+ };
25
+ };
26
+ /** Generate Markdown from an API description, optionally scoped to a single page. */
27
+ export const createMarkdownFromOpenApi = async (input, options) => {
28
+ const renderer = await createOpenApiMarkdownRenderer(input);
29
+ return renderer.render(options);
30
+ };
31
+ /** Generate HTML through the optional Markdown conversion path. */
32
+ export const createHtmlFromOpenApi = async (input, options) => {
33
+ const renderer = await createOpenApiMarkdownRenderer(input);
34
+ return renderer.renderHtml(options);
35
+ };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
- export { createHtmlFromOpenApi, createMarkdownFromOpenApi, } from './create-markdown-from-openapi';
2
- export type { HttpMethod, OpenApiRenderOptions, OperationSelector, } from './create-markdown-from-openapi';
1
+ export type { HttpMethod } from '@scalar/helpers/http/http-methods';
2
+ export type { OpenApiMarkdownRenderer } from './create-markdown-from-openapi.js';
3
+ export { createHtmlFromOpenApi, createMarkdownFromOpenApi, createOpenApiMarkdownRenderer, } from './create-markdown-from-openapi.js';
4
+ export type { OpenApiRenderOptions, OperationSelector } from './select-document.js';
3
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,yBAAyB,GAC1B,MAAM,gCAAgC,CAAA;AACvC,YAAY,EACV,UAAU,EACV,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,gCAAgC,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAA;AAEnE,YAAY,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAA;AAC7E,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,6BAA6B,GAC9B,MAAM,gCAAgC,CAAA;AACvC,YAAY,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA"}