@scalar/openapi-to-markdown 1.5.0 → 1.5.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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @scalar/openapi-to-markdown
|
|
2
2
|
|
|
3
|
+
## 1.5.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#10457](https://github.com/scalar/scalar/pull/10457): Render a selected operation, model, tag or webhook in time proportional to the selection instead of the whole document.
|
|
8
|
+
|
|
3
9
|
## 1.5.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
|
@@ -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,eAAe,EAAE,MAAM,8DAA8D,CAAA;AAInG,OAAO,EAAE,KAAK,oBAAoB,
|
|
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,EAAwC,MAAM,mBAAmB,CAAA;AAEnG,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;CAC5D,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,6BAA6B,GAAU,OAAO,WAAW,KAAG,OAAO,CAAC,uBAAuB,CAQvG,CAAA;AAED,qFAAqF;AACrF,eAAO,MAAM,yBAAyB,GACpC,OAAO,WAAW,EAClB,UAAU,oBAAoB,KAC7B,OAAO,CAAC,MAAM,CAGhB,CAAA"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { loadDocument } from './load-document.js';
|
|
2
2
|
import { createDocumentRenderer } from './render-document.js';
|
|
3
|
-
import { selectDocument } from './select-document.js';
|
|
3
|
+
import { createDocumentLookup, selectDocument } from './select-document.js';
|
|
4
4
|
/**
|
|
5
5
|
* Load and resolve an API description once, then render any number of selections.
|
|
6
6
|
* Each renderer owns its document; create a new renderer to pick up source changes.
|
|
@@ -8,7 +8,8 @@ import { selectDocument } from './select-document.js';
|
|
|
8
8
|
export const createOpenApiMarkdownRenderer = async (input) => {
|
|
9
9
|
const content = await loadDocument(input);
|
|
10
10
|
const renderDocument = createDocumentRenderer();
|
|
11
|
-
const
|
|
11
|
+
const lookup = createDocumentLookup(content);
|
|
12
|
+
const render = async (options) => await renderDocument(selectDocument(content, options, lookup), options);
|
|
12
13
|
return { render };
|
|
13
14
|
};
|
|
14
15
|
/** Generate Markdown from an API description, optionally scoped to a single page. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { OperationMethod } from '@scalar/workspace-store/schemas/navigation';
|
|
2
|
-
import type { OpenApiDocument } from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document';
|
|
2
|
+
import type { OpenApiDocument, PathItemObject } from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document';
|
|
3
3
|
/** Identify one operation by path and method, operation ID, or JSON pointer. */
|
|
4
4
|
export type OperationSelector = {
|
|
5
5
|
path: string;
|
|
@@ -40,7 +40,33 @@ type PageSelectors = {
|
|
|
40
40
|
};
|
|
41
41
|
introduction: true;
|
|
42
42
|
};
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
type OperationMatch = {
|
|
44
|
+
path: string;
|
|
45
|
+
method: OperationMethod;
|
|
46
|
+
};
|
|
47
|
+
type TaggedPathItem = {
|
|
48
|
+
path: string;
|
|
49
|
+
pathItem: PathItemObject;
|
|
50
|
+
methods: string[];
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Lookups that every selection from one document shares, built on first use.
|
|
54
|
+
* The document must not change while its lookup is in use.
|
|
55
|
+
*/
|
|
56
|
+
type DocumentLookup = {
|
|
57
|
+
/** Operations by operation ID, in document order. */
|
|
58
|
+
operationsById: () => ReadonlyMap<string, OperationMatch[]>;
|
|
59
|
+
/** Path items with the methods that carry each tag, in document order. */
|
|
60
|
+
operationsByTag: () => ReadonlyMap<string, TaggedPathItem[]>;
|
|
61
|
+
/** The position of each schema in `components.schemas`. */
|
|
62
|
+
schemaPositions: () => ReadonlyMap<string, number>;
|
|
63
|
+
};
|
|
64
|
+
/** Index a document once so that each selection costs time in proportion to what it selects. */
|
|
65
|
+
export declare const createDocumentLookup: (document: OpenApiDocument) => DocumentLookup;
|
|
66
|
+
/**
|
|
67
|
+
* Scope after resolving references and migrating older documents.
|
|
68
|
+
* Pass the same lookup to every selection from one document to reuse its indexes.
|
|
69
|
+
*/
|
|
70
|
+
export declare const selectDocument: (document: OpenApiDocument, options?: OpenApiRenderOptions, lookup?: DocumentLookup) => OpenApiDocument;
|
|
45
71
|
export {};
|
|
46
72
|
//# sourceMappingURL=select-document.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select-document.d.ts","sourceRoot":"","sources":["../src/select-document.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4CAA4C,CAAA;AACjF,OAAO,KAAK,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"select-document.d.ts","sourceRoot":"","sources":["../src/select-document.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4CAA4C,CAAA;AACjF,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,8DAA8D,CAAA;AAEnH,gFAAgF;AAChF,MAAM,MAAM,iBAAiB,GACzB;IACE,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,eAAe,CAAA;CACxB,GACD;IACE,WAAW,EAAE,MAAM,CAAA;CACpB,GACD;IACE,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AACL,2EAA2E;AAC3E,MAAM,MAAM,oBAAoB,GAAG,sBAAsB,GACvD,CACI;KACG,GAAG,IAAI,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,aAAa,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC,GACrF,IAAI,CAAC,aAAa,EAAE,GAAG,CAAC;CAC3B,CAAC,MAAM,aAAa,CAAC,GACtB,OAAO,CAAC,MAAM,CAAC,MAAM,aAAa,EAAE,KAAK,CAAC,CAAC,CAC9C,CAAA;AAEH,mFAAmF;AACnF,MAAM,MAAM,sBAAsB,GAAG;IACnC,4GAA4G;IAC5G,gBAAgB,CAAC,EAAE;QACjB,IAAI,EAAE,QAAQ,CAAA;QACd,oFAAoF;QACpF,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,KAAK,MAAM,GAAG,SAAS,CAAA;QAC7E;;;WAGG;QACH,gBAAgB,CAAC,EAAE,OAAO,CAAA;KAC3B,CAAA;CACF,CAAA;AAED,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,eAAe,CAAA;KAAE,CAAA;IAClD,YAAY,EAAE,IAAI,CAAA;CACnB,CAAA;AACD,KAAK,cAAc,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,CAAA;AA+G/D,KAAK,cAAc,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAA;AAEnF;;;GAGG;AACH,KAAK,cAAc,GAAG;IACpB,qDAAqD;IACrD,cAAc,EAAE,MAAM,WAAW,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC,CAAA;IAC3D,0EAA0E;IAC1E,eAAe,EAAE,MAAM,WAAW,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC,CAAA;IAC5D,2DAA2D;IAC3D,eAAe,EAAE,MAAM,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACnD,CAAA;AA8BD,gGAAgG;AAChG,eAAO,MAAM,oBAAoB,GAAI,UAAU,eAAe,KAAG,cAWhE,CAAA;AAwDD;;;GAGG;AACH,eAAO,MAAM,cAAc,GACzB,UAAU,eAAe,EACzB,UAAS,oBAAyB,EAClC,SAAQ,cAA+C,KACtD,eA2NF,CAAA"}
|
package/dist/select-document.js
CHANGED
|
@@ -78,16 +78,43 @@ const findOperationByPathAndMethod = (document, selector) => {
|
|
|
78
78
|
method,
|
|
79
79
|
};
|
|
80
80
|
};
|
|
81
|
-
const
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
});
|
|
90
|
-
|
|
81
|
+
const indexOperations = (document) => {
|
|
82
|
+
const byId = new Map();
|
|
83
|
+
const byTag = new Map();
|
|
84
|
+
for (const [path, pathItem] of getPathEntries(document)) {
|
|
85
|
+
forEachPathItemOperation(pathItem, (method, operationRef) => {
|
|
86
|
+
const operation = getResolvedRef(operationRef);
|
|
87
|
+
if (typeof operation?.operationId === 'string') {
|
|
88
|
+
const matches = byId.get(operation.operationId) ?? [];
|
|
89
|
+
matches.push({ path, method });
|
|
90
|
+
byId.set(operation.operationId, matches);
|
|
91
|
+
}
|
|
92
|
+
for (const tag of new Set(operation?.tags)) {
|
|
93
|
+
const entries = byTag.get(tag) ?? [];
|
|
94
|
+
const last = entries.at(-1);
|
|
95
|
+
if (last?.path === path) {
|
|
96
|
+
last.methods.push(method);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
entries.push({ path, pathItem, methods: [method] });
|
|
100
|
+
}
|
|
101
|
+
byTag.set(tag, entries);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return { byId, byTag };
|
|
106
|
+
};
|
|
107
|
+
/** Index a document once so that each selection costs time in proportion to what it selects. */
|
|
108
|
+
export const createDocumentLookup = (document) => {
|
|
109
|
+
let operations;
|
|
110
|
+
let schemaPositions;
|
|
111
|
+
return {
|
|
112
|
+
operationsById: () => (operations ??= indexOperations(document)).byId,
|
|
113
|
+
operationsByTag: () => (operations ??= indexOperations(document)).byTag,
|
|
114
|
+
schemaPositions: () => (schemaPositions ??= new Map(Object.keys(document.components?.schemas ?? {}).map((name, position) => [name, position]))),
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
const resolveOperationMatch = (document, selector, lookup) => {
|
|
91
118
|
if ('pointer' in selector) {
|
|
92
119
|
const match = getOperationSelectorFromPointer(selector.pointer);
|
|
93
120
|
if (!getPathItemOperation(document.paths?.[match.path], match.method)) {
|
|
@@ -96,7 +123,7 @@ const resolveOperationMatch = (document, selector) => {
|
|
|
96
123
|
return match;
|
|
97
124
|
}
|
|
98
125
|
if ('operationId' in selector) {
|
|
99
|
-
const matches =
|
|
126
|
+
const matches = lookup.operationsById().get(selector.operationId) ?? [];
|
|
100
127
|
if (!matches.length) {
|
|
101
128
|
throw new Error(`Operation with operationId "${selector.operationId}" was not found`);
|
|
102
129
|
}
|
|
@@ -108,9 +135,9 @@ const resolveOperationMatch = (document, selector) => {
|
|
|
108
135
|
}
|
|
109
136
|
return findOperationByPathAndMethod(document, selector);
|
|
110
137
|
};
|
|
111
|
-
const filterDocumentByOperation = (document, selector) => {
|
|
112
|
-
const match = resolveOperationMatch(document, selector);
|
|
113
|
-
const pathItem =
|
|
138
|
+
const filterDocumentByOperation = (document, selector, lookup) => {
|
|
139
|
+
const match = resolveOperationMatch(document, selector, lookup);
|
|
140
|
+
const pathItem = getResolvedPathItem(document.paths?.[match.path]);
|
|
114
141
|
if (!pathItem) {
|
|
115
142
|
throw new Error(`Operation not found for path "${match.path}" and method "${match.method.toUpperCase()}"`);
|
|
116
143
|
}
|
|
@@ -121,8 +148,11 @@ const filterDocumentByOperation = (document, selector) => {
|
|
|
121
148
|
},
|
|
122
149
|
};
|
|
123
150
|
};
|
|
124
|
-
/**
|
|
125
|
-
|
|
151
|
+
/**
|
|
152
|
+
* Scope after resolving references and migrating older documents.
|
|
153
|
+
* Pass the same lookup to every selection from one document to reuse its indexes.
|
|
154
|
+
*/
|
|
155
|
+
export const selectDocument = (document, options = {}, lookup = createDocumentLookup(document)) => {
|
|
126
156
|
if (!isObject(options)) {
|
|
127
157
|
throw new Error('Render options must be an object');
|
|
128
158
|
}
|
|
@@ -159,7 +189,7 @@ export const selectDocument = (document, options = {}) => {
|
|
|
159
189
|
const selected = { ...document, paths: {}, webhooks: {}, tags: [] };
|
|
160
190
|
const modelRoots = [];
|
|
161
191
|
if (options.operation) {
|
|
162
|
-
selected.paths = filterDocumentByOperation(document, options.operation).paths;
|
|
192
|
+
selected.paths = filterDocumentByOperation(document, options.operation, lookup).paths;
|
|
163
193
|
}
|
|
164
194
|
if (options.tag !== undefined) {
|
|
165
195
|
const metadata = document.tags?.filter((tag) => tag.name === options.tag) ?? [];
|
|
@@ -167,16 +197,8 @@ export const selectDocument = (document, options = {}) => {
|
|
|
167
197
|
throw new Error(`Multiple tags found for "${options.tag}"`);
|
|
168
198
|
}
|
|
169
199
|
selected.tags = metadata.length ? metadata : [{ name: options.tag }];
|
|
170
|
-
for (const
|
|
171
|
-
|
|
172
|
-
forEachPathItemOperation(item, (method, operation) => {
|
|
173
|
-
if (getResolvedRef(operation)?.tags?.includes(options.tag)) {
|
|
174
|
-
methods.push(method);
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
if (methods.length) {
|
|
178
|
-
selected.paths[path] = filterPathItemOperations(item, methods);
|
|
179
|
-
}
|
|
200
|
+
for (const { path, pathItem, methods } of lookup.operationsByTag().get(options.tag) ?? []) {
|
|
201
|
+
selected.paths[path] = filterPathItemOperations(pathItem, methods);
|
|
180
202
|
}
|
|
181
203
|
if (!metadata.length && !Object.keys(selected.paths ?? {}).length) {
|
|
182
204
|
throw new Error(`Tag "${options.tag}" was not found`);
|
|
@@ -327,9 +349,14 @@ export const selectDocument = (document, options = {}) => {
|
|
|
327
349
|
visit(root);
|
|
328
350
|
}
|
|
329
351
|
}
|
|
352
|
+
const positions = lookup.schemaPositions();
|
|
330
353
|
selected.components = {
|
|
331
354
|
...document.components,
|
|
332
|
-
|
|
355
|
+
// Keep the document's schema order, which decides the order of the page's model sections.
|
|
356
|
+
schemas: Object.fromEntries([...needed]
|
|
357
|
+
.filter((name) => positions.has(name))
|
|
358
|
+
.sort((a, b) => positions.get(a) - positions.get(b))
|
|
359
|
+
.map((name) => [name, schemas[name]])),
|
|
333
360
|
securitySchemes: Object.fromEntries(Object.entries(document.components?.securitySchemes ?? {}).filter(([name]) => securityNames.has(name))),
|
|
334
361
|
};
|
|
335
362
|
return selected;
|
package/package.json
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"llm",
|
|
17
17
|
"swagger"
|
|
18
18
|
],
|
|
19
|
-
"version": "1.5.
|
|
19
|
+
"version": "1.5.1",
|
|
20
20
|
"engines": {
|
|
21
21
|
"node": ">=22"
|
|
22
22
|
},
|
|
@@ -40,11 +40,11 @@
|
|
|
40
40
|
"CHANGELOG.md"
|
|
41
41
|
],
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@scalar/code-highlight": "0.4.
|
|
43
|
+
"@scalar/code-highlight": "0.4.8",
|
|
44
44
|
"@scalar/helpers": "0.16.0",
|
|
45
|
-
"@scalar/json-magic": "0.15.
|
|
45
|
+
"@scalar/json-magic": "0.15.4",
|
|
46
46
|
"@scalar/openapi-upgrader": "0.4.1",
|
|
47
|
-
"@scalar/workspace-store": "0.
|
|
47
|
+
"@scalar/workspace-store": "0.68.0",
|
|
48
48
|
"rehype-parse": "^9.0.1",
|
|
49
49
|
"rehype-remark": "^10.0.1",
|
|
50
50
|
"rehype-sanitize": "^6.0.0",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"unified": "^11.0.5"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@scalar/components": "0.30.
|
|
57
|
+
"@scalar/components": "0.30.6",
|
|
58
58
|
"@scalar/galaxy": "0.7.1",
|
|
59
59
|
"@scalar/themes": "0.18.1",
|
|
60
60
|
"@tailwindcss/vite": "^4.3.3",
|