@scalar/openapi-to-markdown 1.5.3 → 1.5.4

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,12 @@
1
1
  # @scalar/openapi-to-markdown
2
2
 
3
+ ## 1.5.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [#10559](https://github.com/scalar/scalar/pull/10559): Clean up Biome warnings in package code and tests. Handle rejected debounced callbacks and await streaming writes.
8
+ - [#10505](https://github.com/scalar/scalar/pull/10505): Apply formatting and lint compatibility changes for Biome 2.5.15.
9
+
3
10
  ## 1.5.3
4
11
 
5
12
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"document-anchors.d.ts","sourceRoot":"","sources":["../src/document-anchors.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,OAAO,CAAA;AAEjC,kGAAkG;AAClG,eAAO,MAAM,qBAAqB,QAAO;IACvC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;CAmBhE,CAAA;AAED,qFAAqF;AACrF,eAAO,MAAM,MAAM,GAAI,IAAI,MAAM,KAAG,IAAuD,CAAA"}
1
+ {"version":3,"file":"document-anchors.d.ts","sourceRoot":"","sources":["../src/document-anchors.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,OAAO,CAAA;AAEjC,kGAAkG;AAClG,eAAO,MAAM,qBAAqB,QAAO;IACvC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;CAuBhE,CAAA;AAED,qFAAqF;AACrF,eAAO,MAAM,MAAM,GAAI,IAAI,MAAM,KAAG,IAAuD,CAAA"}
@@ -8,13 +8,15 @@ export const createDocumentAnchors = () => {
8
8
  get: (kind, identity, label = identity) => {
9
9
  const key = JSON.stringify([kind, identity]);
10
10
  const previous = ids.get(key);
11
- if (previous !== undefined)
11
+ if (previous !== undefined) {
12
12
  return previous;
13
+ }
13
14
  const base = `scalar-${kind}-${label}`;
14
15
  let id = slugs.slug(base);
15
16
  // A literal suffix in a later name can collide with the slugger's numeric suffix.
16
- while (used.has(id))
17
+ while (used.has(id)) {
17
18
  id = slugs.slug(base);
19
+ }
18
20
  used.add(id);
19
21
  ids.set(key, id);
20
22
  return id;
@@ -1 +1 @@
1
- {"version":3,"file":"document-examples.d.ts","sourceRoot":"","sources":["../src/document-examples.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,OAAO,CAAA;AAEjC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAC/D,OAAO,EAAE,KAAK,aAAa,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AAEjF,oFAAoF;AACpF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,OAAO,mBAAmB,CAAA;IAC/B,IAAI,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,KAAK;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAA;CAC9F,CAAA;AAaD,uGAAuG;AACvG,eAAO,MAAM,sBAAsB,GAAI,SAAS,UAAU,CAAC,OAAO,qBAAqB,CAAC,KAAG,gBAqC1F,CAAA"}
1
+ {"version":3,"file":"document-examples.d.ts","sourceRoot":"","sources":["../src/document-examples.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,OAAO,CAAA;AAEjC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAC/D,OAAO,EAAE,KAAK,aAAa,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AAEjF,oFAAoF;AACpF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,OAAO,mBAAmB,CAAA;IAC/B,IAAI,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,KAAK;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAA;CAC9F,CAAA;AAeD,uGAAuG;AACvG,eAAO,MAAM,sBAAsB,GAAI,SAAS,UAAU,CAAC,OAAO,qBAAqB,CAAC,KAAG,gBA4C1F,CAAA"}
@@ -4,8 +4,9 @@ import { getMarkdownExamples } from './get-markdown-examples.js';
4
4
  const bookkeeping = new Set(['$ref', '$ref-value', '$global', '$status', '__scalar_']);
5
5
  /** Sibling constraints make a reference a different example source from its target. */
6
6
  const identity = (source) => {
7
- if (!isObject(source.schema))
7
+ if (!isObject(source.schema)) {
8
8
  return undefined;
9
+ }
9
10
  const schema = source.schema;
10
11
  const target = '$ref' in schema && Object.keys(schema).every((key) => bookkeeping.has(key)) ? getResolvedRef(schema) : schema;
11
12
  return isObject(target) ? target : schema;
@@ -20,12 +21,14 @@ export const createDocumentExamples = (anchors) => {
20
21
  get: (...args) => {
21
22
  const [source, ...settings] = args;
22
23
  const schema = identity(source);
23
- if (!schema || source.example !== undefined || Object.keys(source.examples ?? {}).length)
24
+ if (!schema || source.example !== undefined || Object.keys(source.examples ?? {}).length) {
24
25
  return getMarkdownExamples(...args);
26
+ }
25
27
  const scope = JSON.stringify(settings);
26
28
  const previous = cache.get(schema)?.get(scope);
27
- if (previous)
29
+ if (previous) {
28
30
  return previous;
31
+ }
29
32
  // Large APIs must not retain every generated object until the export finishes.
30
33
  if (cachedCount >= 256) {
31
34
  cache.clear();
@@ -42,12 +45,14 @@ export const createDocumentExamples = (anchors) => {
42
45
  const schema = identity(source);
43
46
  const entries = schema ? (shown.get(schema) ?? new Map()) : new Map();
44
47
  const previous = entries.get(scope);
45
- if (previous?.value === code.value)
48
+ if (previous?.value === code.value) {
46
49
  return { id: previous.id, previous: true };
50
+ }
47
51
  const id = anchors.get('example', String(++nextId));
48
52
  entries.set(scope, { id, value: code.value });
49
- if (schema)
53
+ if (schema) {
50
54
  shown.set(schema, entries);
55
+ }
51
56
  return { id, previous: false };
52
57
  },
53
58
  };
@@ -1 +1 @@
1
- {"version":3,"file":"get-markdown-examples.d.ts","sourceRoot":"","sources":["../src/get-markdown-examples.ts"],"names":[],"mappings":"AAMA,4FAA4F;AAC5F,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnC,CAAA;AAED,gFAAgF;AAChF,KAAK,eAAe,GAAG;IACrB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oFAAoF;IACpF,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,GAAG,CACA;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAClB;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,GACzB;IAAE,eAAe,EAAE,MAAM,CAAA;CAAE,GAC3B;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GACjB;IAAE,SAAS,EAAE,OAAO,CAAA;CAAE,GACtB;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CACpB,CAAA;AAYD;;;GAGG;AACH,eAAO,MAAM,2BAA2B,GAAI,MAAM,OAAO,EAAE,cAAoC,KAAG,MA2CjG,CAAA;AAED,yFAAyF;AACzF,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,aAAa,EACrB,WAAW,MAAM,EACjB,OAAO,MAAM,GAAG,OAAO,EACvB,uBAAwB,EAExB,6BAAqC,EACrC,gBAAc,KACb,eAAe,EAyCjB,CAAA"}
1
+ {"version":3,"file":"get-markdown-examples.d.ts","sourceRoot":"","sources":["../src/get-markdown-examples.ts"],"names":[],"mappings":"AAMA,4FAA4F;AAC5F,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnC,CAAA;AAED,gFAAgF;AAChF,KAAK,eAAe,GAAG;IACrB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oFAAoF;IACpF,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,GAAG,CACA;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAClB;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,GACzB;IAAE,eAAe,EAAE,MAAM,CAAA;CAAE,GAC3B;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GACjB;IAAE,SAAS,EAAE,OAAO,CAAA;CAAE,GACtB;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CACpB,CAAA;AAYD;;;GAGG;AACH,eAAO,MAAM,2BAA2B,GAAI,MAAM,OAAO,EAAE,cAAoC,KAAG,MAmEjG,CAAA;AAED,yFAAyF;AACzF,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,aAAa,EACrB,WAAW,MAAM,EACjB,OAAO,MAAM,GAAG,OAAO,EACvB,uBAAwB,EAExB,6BAAqC,EACrC,gBAAc,KACb,eAAe,EA6DjB,CAAA"}
@@ -17,35 +17,47 @@ const MAX_GENERATED_EXAMPLE_VALUES = 10_000;
17
17
  export const countGeneratedExampleValues = (root, limit = MAX_GENERATED_EXAMPLE_VALUES) => {
18
18
  const counts = new WeakMap();
19
19
  const count = (input, level) => {
20
- if (level > EXAMPLE_DEPTH || !isObject(input))
20
+ if (level > EXAMPLE_DEPTH || !isObject(input)) {
21
21
  return 1;
22
+ }
22
23
  const cached = counts.get(input)?.get(level);
23
- if (cached !== undefined)
24
+ if (cached !== undefined) {
24
25
  return cached;
26
+ }
25
27
  const schema = getResolvedRef(input, mergeSiblingReferences);
26
- if (!isObject(schema))
28
+ if (!isObject(schema)) {
27
29
  return 1;
30
+ }
28
31
  // Supplied examples bypass schema expansion in the generator too.
29
- if (schema.example !== undefined || (Array.isArray(schema.examples) && schema.examples.length > 0))
32
+ if (schema.example !== undefined || (Array.isArray(schema.examples) && schema.examples.length > 0)) {
30
33
  return 1;
34
+ }
31
35
  let total = 1;
32
36
  const add = (child) => {
33
- if (total <= limit)
37
+ if (total <= limit) {
34
38
  total += count(child, level + 1);
39
+ }
35
40
  };
36
- if (isObject(schema.properties))
41
+ if (isObject(schema.properties)) {
37
42
  Object.values(schema.properties).forEach(add);
38
- if (isObject(schema.patternProperties))
43
+ }
44
+ if (isObject(schema.patternProperties)) {
39
45
  Object.values(schema.patternProperties).forEach(add);
40
- if (isObject(schema.additionalProperties))
46
+ }
47
+ if (isObject(schema.additionalProperties)) {
41
48
  add(schema.additionalProperties);
42
- if (schema.items !== undefined)
49
+ }
50
+ if (schema.items !== undefined) {
43
51
  add(schema.items);
44
- if (Array.isArray(schema.prefixItems))
52
+ }
53
+ if (Array.isArray(schema.prefixItems)) {
45
54
  schema.prefixItems.forEach(add);
46
- if (Array.isArray(schema.allOf))
55
+ }
56
+ if (Array.isArray(schema.allOf)) {
47
57
  schema.allOf.forEach(add);
48
- const variants = Array.isArray(schema.oneOf) ? schema.oneOf : Array.isArray(schema.anyOf) ? schema.anyOf : [];
58
+ }
59
+ const anyOf = Array.isArray(schema.anyOf) ? schema.anyOf : [];
60
+ const variants = Array.isArray(schema.oneOf) ? schema.oneOf : anyOf;
49
61
  // Object and array generation can use the first variant, while other unions skip null.
50
62
  // A discriminator can choose any variant, so bound the largest candidate in that case.
51
63
  const nonNull = variants.find((variant) => {
@@ -57,8 +69,9 @@ export const countGeneratedExampleValues = (root, limit = MAX_GENERATED_EXAMPLE_
57
69
  : [variants[0], nonNull];
58
70
  let variantCount = 0;
59
71
  for (const candidate of candidates) {
60
- if (candidate !== undefined && total <= limit && variantCount <= limit)
72
+ if (candidate !== undefined && total <= limit && variantCount <= limit) {
61
73
  variantCount = Math.max(variantCount, count(candidate, level + 1));
74
+ }
62
75
  }
63
76
  total += variantCount;
64
77
  const levels = counts.get(input) ?? new Map();
@@ -72,40 +85,51 @@ export const countGeneratedExampleValues = (root, limit = MAX_GENERATED_EXAMPLE_
72
85
  export const getMarkdownExamples = (source, mediaType, mode, openapiVersion = '3.2.0',
73
86
  // Schema metadata can be upgraded while example fields still follow the original version.
74
87
  schemaOpenapiVersion = openapiVersion, linked = false) => {
75
- if (source.example !== undefined)
88
+ if (source.example !== undefined) {
76
89
  return [{ value: source.example }];
90
+ }
77
91
  if (source.examples && Object.keys(source.examples).length) {
78
92
  return Object.entries(source.examples).flatMap(([name, reference]) => {
79
93
  const example = getResolvedRef(reference);
80
- if (!isObject(example))
94
+ if (!isObject(example)) {
81
95
  return [];
96
+ }
82
97
  const metadata = {
83
98
  name,
84
99
  summary: typeof example.summary === 'string' ? example.summary : undefined,
85
100
  description: typeof example.description === 'string' ? example.description : undefined,
86
101
  };
87
- if (example.value !== undefined)
102
+ if (example.value !== undefined) {
88
103
  return [{ ...metadata, value: example.value }];
89
- if (/^3\.2\./.test(openapiVersion) && typeof example.serializedValue === 'string')
104
+ }
105
+ if (/^3\.2\./.test(openapiVersion) && typeof example.serializedValue === 'string') {
90
106
  return [{ ...metadata, serializedValue: example.serializedValue }];
91
- if (typeof example.externalValue === 'string')
107
+ }
108
+ if (typeof example.externalValue === 'string') {
92
109
  return [{ ...metadata, externalValue: example.externalValue }];
93
- if (/^3\.2\./.test(openapiVersion) && example.dataValue !== undefined)
110
+ }
111
+ if (/^3\.2\./.test(openapiVersion) && example.dataValue !== undefined) {
94
112
  return [{ ...metadata, dataValue: example.dataValue }];
113
+ }
95
114
  return [];
96
115
  });
97
116
  }
98
117
  const schema = getResolvedRef(source.schema, mergeSiblingReferences);
99
- if (!isObject(schema))
118
+ if (!isObject(schema)) {
100
119
  return [];
101
- if (schema.example !== undefined)
120
+ }
121
+ if (schema.example !== undefined) {
102
122
  return [{ value: schema.example }];
103
- if (Array.isArray(schema.examples) && schema.examples.length)
123
+ }
124
+ if (Array.isArray(schema.examples) && schema.examples.length) {
104
125
  return schema.examples.map((value) => ({ value }));
105
- if (linked)
126
+ }
127
+ if (linked) {
106
128
  return [];
107
- if (countGeneratedExampleValues(source.schema) > MAX_GENERATED_EXAMPLE_VALUES)
129
+ }
130
+ if (countGeneratedExampleValues(source.schema) > MAX_GENERATED_EXAMPLE_VALUES) {
108
131
  return [{ omitted: true }];
132
+ }
109
133
  if (isXmlMediaType(mediaType)) {
110
134
  const result = getXmlBodyExample(source.schema, undefined, {
111
135
  mode,
@@ -1 +1 @@
1
- {"version":3,"file":"load-document.d.ts","sourceRoot":"","sources":["../src/load-document.ts"],"names":[],"mappings":"AAWA,OAAO,EAEL,KAAK,eAAe,EAErB,MAAM,8DAA8D,CAAA;AA6LrE,8EAA8E;AAC9E,eAAO,MAAM,YAAY,GACvB,OAAO,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,KACxD,OAAO,CAAC,eAAe,CAgGzB,CAAA"}
1
+ {"version":3,"file":"load-document.d.ts","sourceRoot":"","sources":["../src/load-document.ts"],"names":[],"mappings":"AAWA,OAAO,EAEL,KAAK,eAAe,EAErB,MAAM,8DAA8D,CAAA;AAiMrE,8EAA8E;AAC9E,eAAO,MAAM,YAAY,GACvB,OAAO,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,KACxD,OAAO,CAAC,eAAe,CAgGzB,CAAA"}
@@ -40,8 +40,9 @@ const attachRefValues = (document, schemas = getSchemas(document), { detectOnly
40
40
  kind = kind && getChildKind(kind, key);
41
41
  position = Array.isArray(parent) ? position : position && getChildPosition(position, key);
42
42
  // Arbitrary extension and example data has not passed an OpenAPI object schema.
43
- if (position === 'document' && kind === undefined)
43
+ if (position === 'document' && kind === undefined) {
44
44
  position = undefined;
45
+ }
45
46
  parent = getValueByPath(parent, [key]).value;
46
47
  }
47
48
  const schema = getTargetSchema(position, kind);
@@ -78,15 +79,20 @@ const attachRefValues = (document, schemas = getSchemas(document), { detectOnly
78
79
  // support summary and description; casting them as their targets would add defaults.
79
80
  // Path Item Objects declare their own $ref field, so their siblings are cast normally.
80
81
  const reference = typeof value.$ref === 'string' ? value.$ref : undefined;
81
- const cast = reference === undefined || schema === referenceTargetSchemas.pathItem
82
- ? coerceValue(schema, value)
83
- : position === 'schema'
84
- ? { ...coerceValue(SchemaObjectSchema, value), $ref: reference }
85
- : {
86
- $ref: reference,
87
- ...(typeof value.summary === 'string' ? { summary: value.summary } : {}),
88
- ...(typeof value.description === 'string' ? { description: value.description } : {}),
89
- };
82
+ let cast;
83
+ if (reference === undefined || schema === referenceTargetSchemas.pathItem) {
84
+ cast = coerceValue(schema, value);
85
+ }
86
+ else if (position === 'schema') {
87
+ cast = { ...coerceValue(SchemaObjectSchema, value), $ref: reference };
88
+ }
89
+ else {
90
+ cast = {
91
+ $ref: reference,
92
+ ...(typeof value.summary === 'string' ? { summary: value.summary } : {}),
93
+ ...(typeof value.description === 'string' ? { description: value.description } : {}),
94
+ };
95
+ }
90
96
  if (fallback?.booleanSchemas) {
91
97
  restoreBooleanSchemas(value, cast, position);
92
98
  }
@@ -1 +1 @@
1
- {"version":3,"file":"parse-description.d.ts","sourceRoot":"","sources":["../src/parse-description.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAuB,WAAW,EAAE,MAAM,OAAO,CAAA;AAkF7D,0FAA0F;AAC1F,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC,CAAA;AAE1E,mGAAmG;AACnG,eAAO,MAAM,uBAAuB,QAAO,CAAC,MAAM,iBAAiB,CA4ClE,CAAA;AAED,6FAA6F;AAC7F,eAAO,MAAM,kBAAkB,GAAU,OAAO,WAAW,EAAE,EAAE,aAAa,iBAAiB,KAAG,OAAO,CAAC,IAAI,CAe3G,CAAA"}
1
+ {"version":3,"file":"parse-description.d.ts","sourceRoot":"","sources":["../src/parse-description.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAuB,WAAW,EAAE,MAAM,OAAO,CAAA;AAsG7D,0FAA0F;AAC1F,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC,CAAA;AAE1E,mGAAmG;AACnG,eAAO,MAAM,uBAAuB,QAAO,CAAC,MAAM,iBAAiB,CAuDlE,CAAA;AAED,6FAA6F;AAC7F,eAAO,MAAM,kBAAkB,GAAU,OAAO,WAAW,EAAE,EAAE,aAAa,iBAAiB,KAAG,OAAO,CAAC,IAAI,CAkB3G,CAAA"}
@@ -10,8 +10,9 @@ const clean = (node) => {
10
10
  node.url = safeUrl(node.url);
11
11
  }
12
12
  if ('children' in node) {
13
- for (const child of node.children)
13
+ for (const child of node.children) {
14
14
  clean(child);
15
+ }
15
16
  // The filter preserves the parent's existing child types.
16
17
  const previousLength = node.children.length;
17
18
  node.children = node.children.filter((child) => child.type !== 'image' &&
@@ -33,10 +34,12 @@ const clean = (node) => {
33
34
  if (node.children.length !== previousLength) {
34
35
  const first = node.children.at(0);
35
36
  const last = node.children.at(-1);
36
- if (first?.type === 'text')
37
+ if (first?.type === 'text') {
37
38
  first.value = first.value.trimStart();
38
- if (last?.type === 'text')
39
+ }
40
+ if (last?.type === 'text') {
39
41
  last.value = last.value.trimEnd();
42
+ }
40
43
  }
41
44
  }
42
45
  delete node.position;
@@ -47,24 +50,31 @@ const pruneDefinitions = (tree) => {
47
50
  const footnotes = new Set();
48
51
  const definitions = new Map(tree.children.filter((node) => node.type === 'footnoteDefinition').map((node) => [node.identifier, node]));
49
52
  const collect = (node) => {
50
- if (node.type === 'linkReference')
53
+ if (node.type === 'linkReference') {
51
54
  links.add(node.identifier);
52
- if (node.type === 'footnoteReference')
55
+ }
56
+ if (node.type === 'footnoteReference') {
53
57
  footnotes.add(node.identifier);
54
- if ('children' in node)
58
+ }
59
+ if ('children' in node) {
55
60
  node.children.forEach(collect);
61
+ }
56
62
  };
57
63
  tree.children.filter((node) => node.type !== 'footnoteDefinition').forEach(collect);
58
64
  // Set iteration also visits footnotes discovered inside another footnote.
59
- for (const identifier of footnotes)
65
+ for (const identifier of footnotes) {
60
66
  definitions.get(identifier)?.children.forEach(collect);
67
+ }
61
68
  tree.children = tree.children.filter((node) => {
62
- if (node.type === 'definition')
69
+ if (node.type === 'definition') {
63
70
  return links.has(node.identifier);
64
- if (node.type === 'footnoteDefinition')
71
+ }
72
+ if (node.type === 'footnoteDefinition') {
65
73
  return footnotes.has(node.identifier);
66
- if (node.type === 'paragraph')
74
+ }
75
+ if (node.type === 'paragraph') {
67
76
  return node.children.some((child) => child.type !== 'text' || child.value.trim());
77
+ }
68
78
  return true;
69
79
  });
70
80
  };
@@ -78,8 +88,9 @@ export const createDescriptionParser = () => {
78
88
  const cache = new Map();
79
89
  const parse = async (value) => {
80
90
  // Most schema descriptions are one plain sentence. Parsing them costs more than the page's other text.
81
- if (isPlainText(value))
91
+ if (isPlainText(value)) {
82
92
  return [{ type: 'paragraph', children: [{ type: 'text', value: value.trim() }] }];
93
+ }
83
94
  const parsed = parser.parse(value);
84
95
  // Raw HTML and GitHub alerts retain Scalar's existing conversion behavior.
85
96
  const tree = containsHtml(parsed) || /^\s*>\s*\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION|SUCCESS)\]/im.test(value)
@@ -93,26 +104,31 @@ export const createDescriptionParser = () => {
93
104
  const copy = { ...node };
94
105
  if ('identifier' in copy) {
95
106
  copy.identifier = `${prefix}${copy.identifier}`;
96
- if ('label' in copy)
107
+ if ('label' in copy) {
97
108
  copy.label = copy.identifier;
98
- if ('referenceType' in copy)
109
+ }
110
+ if ('referenceType' in copy) {
99
111
  copy.referenceType = 'full';
112
+ }
100
113
  }
101
- if ('children' in copy)
114
+ if ('children' in copy) {
102
115
  copy.children = copy.children.map((child) => namespace(child, prefix));
116
+ }
103
117
  return copy;
104
118
  };
105
119
  const hasReference = (node) => 'identifier' in node || ('children' in node && node.children.some(hasReference));
106
120
  return () => {
107
121
  const state = { nextId: 0 };
108
122
  return async (value) => {
109
- if (!value)
123
+ if (!value) {
110
124
  return [];
125
+ }
111
126
  const prefix = `description-${state.nextId++}-`;
112
127
  const cached = cache.get(value) ?? parse(value);
113
128
  if (!cache.has(value)) {
114
- if (cache.size >= 8192)
129
+ if (cache.size >= 8192) {
115
130
  cache.clear();
131
+ }
116
132
  cache.set(value, cached);
117
133
  }
118
134
  const nodes = await cached;
@@ -127,10 +143,12 @@ export const expandDescriptions = async (nodes, description) => {
127
143
  const found = [];
128
144
  const collect = (siblings) => {
129
145
  for (const node of siblings) {
130
- if (node.type === 'descriptionPlaceholder')
146
+ if (node.type === 'descriptionPlaceholder') {
131
147
  found.push({ siblings, node });
132
- else if ('children' in node)
148
+ }
149
+ else if ('children' in node) {
133
150
  collect(node.children);
151
+ }
134
152
  }
135
153
  };
136
154
  collect(nodes);
@@ -1 +1 @@
1
- {"version":3,"file":"render-document.d.ts","sourceRoot":"","sources":["../src/render-document.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,eAAe,EAGhB,MAAM,8DAA8D,CAAA;AAcrE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AA+C7D,2FAA2F;AAC3F,eAAO,MAAM,sBAAsB,QAAO,CAAC,CACzC,QAAQ,EAAE,eAAe,EACzB,OAAO,CAAC,EAAE,oBAAoB,KAC3B,OAAO,CAAC,MAAM,CAAC,CAuPnB,CAAA"}
1
+ {"version":3,"file":"render-document.d.ts","sourceRoot":"","sources":["../src/render-document.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,eAAe,EAGhB,MAAM,8DAA8D,CAAA;AAcrE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAwD7D,2FAA2F;AAC3F,eAAO,MAAM,sBAAsB,QAAO,CAAC,CACzC,QAAQ,EAAE,eAAe,EACzB,OAAO,CAAC,EAAE,oBAAoB,KAC3B,OAAO,CAAC,MAAM,CAAC,CA0RnB,CAAA"}
@@ -18,14 +18,17 @@ const serializer = unified()
18
18
  bullet: '-',
19
19
  join: [
20
20
  (left, right, parent) => {
21
- if (!('spread' in parent))
21
+ if (!('spread' in parent)) {
22
22
  return undefined;
23
+ }
23
24
  // Inside a tight list item, content after a nested list would otherwise continue its last item.
24
- if (left.type === 'list' && right.type !== 'list')
25
+ if (left.type === 'list' && right.type !== 'list') {
25
26
  return 1;
27
+ }
26
28
  // Code blocks in descriptions read better apart from the prose around them.
27
- if (left.type === 'code' || right.type === 'code')
29
+ if (left.type === 'code' || right.type === 'code') {
28
30
  return 1;
31
+ }
29
32
  return undefined;
30
33
  },
31
34
  ],
@@ -43,12 +46,14 @@ const renderShownModel = async (name, view, previous, description) => {
43
46
  ? [strong(text(view.title)), text(' ('), inlineCode(name), text(')')]
44
47
  : [inlineCode(name)];
45
48
  label.push(text(' — shown above'));
46
- if (previous.name !== name)
49
+ if (previous.name !== name) {
47
50
  label.push(text(' as '), inlineCode(previous.name));
51
+ }
48
52
  label.push(text('.'));
49
53
  const blocks = [paragraph(...label)];
50
- if (view.description && view.description !== previous.description)
54
+ if (view.description && view.description !== previous.description) {
51
55
  blocks.push(...(await description(view.description)));
56
+ }
52
57
  return item(...blocks);
53
58
  };
54
59
  /** Build Markdown directly, retaining caches only for this immutable document snapshot. */
@@ -81,8 +86,9 @@ export const createDocumentRenderer = () => {
81
86
  const entries = [];
82
87
  for (const [path, reference] of Object.entries(group.paths ?? {})) {
83
88
  const pathItem = getResolvedPathItem(reference);
84
- if (!pathItem)
89
+ if (!pathItem) {
85
90
  continue;
91
+ }
86
92
  forEachPathItemOperation(reference, (method, operation) => {
87
93
  const label = `${formatOperationMethod(method)} ${path}`;
88
94
  entries.push({
@@ -107,8 +113,9 @@ export const createDocumentRenderer = () => {
107
113
  const nodes = [];
108
114
  const sections = [];
109
115
  const flush = async () => {
110
- if (!nodes.length)
116
+ if (!nodes.length) {
111
117
  return;
118
+ }
112
119
  const children = nodes.splice(0);
113
120
  await expandDescriptions(children, description);
114
121
  sections.push(serializer.stringify({ type: 'root', children }).trimEnd());
@@ -116,63 +123,74 @@ export const createDocumentRenderer = () => {
116
123
  if (!single) {
117
124
  const { info } = document;
118
125
  const metadata = [field('OpenAPI Version', inlineCode(document.openapi))];
119
- if (info.version)
126
+ if (info.version) {
120
127
  metadata.push(field('API Version', inlineCode(info.version)));
121
- if (info.termsOfService)
128
+ }
129
+ if (info.termsOfService) {
122
130
  metadata.push(field('Terms of service', link(info.termsOfService, info.termsOfService)));
131
+ }
123
132
  if (info.contact) {
124
133
  const contact = [text(info.contact.name ?? '')];
125
134
  metadata.push(item(paragraph(strong(text('Contact:')), text(' '), ...contact, ...(info.contact.url ? [text(' '), link(info.contact.url, info.contact.url)] : []), ...(info.contact.email ? [text(' '), link(`mailto:${info.contact.email}`, info.contact.email)] : []))));
126
135
  }
127
- if (info.license)
136
+ if (info.license) {
128
137
  metadata.push(field('License', info.license.url ? link(info.license.url, info.license.name ?? '') : text(info.license.name)));
138
+ }
129
139
  nodes.push(heading(1, text(info.title)), list(metadata), ...(await description(info.description)));
130
140
  if (whole) {
131
141
  const contents = [];
132
142
  for (const group of groups) {
133
- if (!group.entries.length)
143
+ if (!group.entries.length) {
134
144
  continue;
145
+ }
135
146
  contents.push(paragraph(strong(text(group.title))), list(group.entries.map(({ id, label }) => item(paragraph(link(`#${encodeURIComponent(id)}`, label))))));
136
147
  }
137
148
  if (destinations?.size) {
138
149
  contents.push(paragraph(strong(text('Schemas'))), list([...destinations].map(([name, url]) => item(paragraph(link(url, name))))));
139
150
  }
140
- if (contents.length)
151
+ if (contents.length) {
141
152
  nodes.push(heading(2, text('Contents')), ...contents);
153
+ }
142
154
  }
143
155
  if (document.servers?.length || (whole && document.servers !== undefined)) {
144
- if (documentContext?.servers)
156
+ if (documentContext?.servers) {
145
157
  nodes.push(anchor(documentContext.servers));
158
+ }
146
159
  nodes.push(heading(2, text('Servers')));
147
160
  const effectiveServers = document.servers?.length
148
161
  ? document.servers
149
162
  : [{ url: '/' }];
150
163
  const servers = effectiveServers.map((server) => {
151
164
  const nested = [];
152
- if (server.description)
165
+ if (server.description) {
153
166
  nested.push(field('Description', text(server.description)));
167
+ }
154
168
  const variables = Object.entries(server.variables ?? {});
155
- if (variables.length)
169
+ if (variables.length) {
156
170
  nested.push(item(paragraph(strong(text('Variables:'))), list(variables.map(([name, variable]) => item(paragraph(inlineCode(name), text(' (default: '), inlineCode(variable.default), text(')'), ...(whole && variable.enum?.length
157
171
  ? [text(', possible values: '), inlineCode(variable.enum.join(', '))]
158
172
  : []), text(variable.description ? `: ${variable.description}` : '')))))));
173
+ }
159
174
  const entry = field('URL', inlineCode(server.url));
160
- if (nested.length)
175
+ if (nested.length) {
161
176
  entry.children.push(list(nested));
177
+ }
162
178
  return entry;
163
179
  });
164
180
  nodes.push(list(servers));
165
181
  }
166
- if (documentContext?.authentication)
182
+ if (documentContext?.authentication) {
167
183
  nodes.push(anchor(documentContext.authentication));
184
+ }
168
185
  nodes.push(...(await renderSecurity(document.security, document.components?.securitySchemes, description, 2)));
169
186
  if (document.tags?.length) {
170
187
  nodes.push(heading(2, text('Tags')));
171
188
  for (const tag of document.tags) {
172
189
  const title = /^3\.2\./.test(openapiVersion) ? (tag.summary ?? tag.name) : tag.name;
173
190
  nodes.push(heading(3, text(title)), ...(await description(tag.description)));
174
- if (tag.externalDocs)
191
+ if (tag.externalDocs) {
175
192
  nodes.push(paragraph(link(tag.externalDocs.url, tag.externalDocs.description ?? tag.externalDocs.url)));
193
+ }
176
194
  }
177
195
  }
178
196
  await flush();
@@ -180,12 +198,14 @@ export const createDocumentRenderer = () => {
180
198
  for (const group of groups) {
181
199
  let hasOperations = false;
182
200
  for (const { path, pathItem, method, operation, id } of group.entries) {
183
- if (!hasOperations && !single)
201
+ if (!hasOperations && !single) {
184
202
  nodes.push(heading(2, text(group.title)));
203
+ }
185
204
  hasOperations = true;
186
205
  schemas.beginSection();
187
- if (id)
206
+ if (id) {
188
207
  nodes.push(anchor(id));
208
+ }
189
209
  nodes.push(...(await renderOperation(document, path, method, pathItem, operation, group.webhook, {
190
210
  description,
191
211
  schemas,
@@ -201,11 +221,13 @@ export const createDocumentRenderer = () => {
201
221
  const renderModel = async (name, schema, level) => {
202
222
  const view = schemas.view(schema);
203
223
  const summary = schemas.summarize(schema);
204
- if (whole)
224
+ if (whole) {
205
225
  nodes.push(anchor(anchors.get('schema', name)));
226
+ }
206
227
  nodes.push(heading(level, text(view.title ?? name)));
207
- if (summary.length)
228
+ if (summary.length) {
208
229
  nodes.push(paragraph(strong(text('Type:')), text('\u00a0'), ...summary));
230
+ }
209
231
  nodes.push(...(await description(view.description)), ...schemas.render(schema, 0, [], { hideDetails: true, name }));
210
232
  if (view.type === 'object' || hasAuthoredExamples(view)) {
211
233
  // A model page's own schema is bounded, so it gets a generated example even in linked mode.
@@ -224,13 +246,15 @@ export const createDocumentRenderer = () => {
224
246
  await renderModel(selected[0], selected[1], 1);
225
247
  }
226
248
  const dependencies = models.filter(([name]) => name !== options.model);
227
- if (dependencies.length)
249
+ if (dependencies.length) {
228
250
  nodes.push(heading(2, text('Schemas')));
251
+ }
229
252
  // Models the page already expanded cost one line each, grouped into a single list.
230
253
  const shownAbove = [];
231
254
  const flushShownAbove = () => {
232
- if (shownAbove.length)
255
+ if (shownAbove.length) {
233
256
  nodes.push(list(shownAbove.splice(0)));
257
+ }
234
258
  };
235
259
  for (const [name, schema] of dependencies) {
236
260
  schemas.beginSection();
@@ -1 +1 @@
1
- {"version":3,"file":"render-examples.d.ts","sourceRoot":"","sources":["../src/render-examples.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAQ,WAAW,EAAE,MAAM,OAAO,CAAA;AAG9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAC3D,OAAO,EAAE,KAAK,aAAa,EAAuB,MAAM,yBAAyB,CAAA;AAEjF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAE5D,+EAA+E;AAC/E,eAAO,MAAM,cAAc,GACzB,QAAQ,aAAa,EACrB,aAAa,iBAAiB,EAC9B,kBAA8B,EAC9B,OAAO,MAAM,GAAG,OAAO,EACvB,uBAAwB,EAExB,6BAAqC,EACrC,+BAIG;IACD,yFAAyF;IACzF,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,gFAAgF;IAChF,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,oFAAoF;IACpF,QAAQ,CAAC,EAAE,gBAAgB,CAAA;CACvB,KACL,OAAO,CAAC,WAAW,EAAE,CA8EvB,CAAA"}
1
+ {"version":3,"file":"render-examples.d.ts","sourceRoot":"","sources":["../src/render-examples.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAQ,WAAW,EAAE,MAAM,OAAO,CAAA;AAG9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAC3D,OAAO,EAAE,KAAK,aAAa,EAAuB,MAAM,yBAAyB,CAAA;AAEjF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAE5D,+EAA+E;AAC/E,eAAO,MAAM,cAAc,GACzB,QAAQ,aAAa,EACrB,aAAa,iBAAiB,EAC9B,kBAA8B,EAC9B,OAAO,MAAM,GAAG,OAAO,EACvB,uBAAwB,EAExB,6BAAqC,EACrC,+BAIG;IACD,yFAAyF;IACzF,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,gFAAgF;IAChF,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,oFAAoF;IACpF,QAAQ,CAAC,EAAE,gBAAgB,CAAA;CACvB,KACL,OAAO,CAAC,WAAW,EAAE,CAgFvB,CAAA"}