Skip to content

Commit dad75b1

Browse files
committed
breaking: update Redocly CLI to 2.x
1 parent 5cdcca0 commit dad75b1

File tree

12 files changed

+355
-54
lines changed

12 files changed

+355
-54
lines changed

.changeset/salty-results-wait.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"openapi-typescript": major
3+
---
4+
5+
⚠️ Breaking change: Update Redocly CLI to 2.x. See https://redocly.com/docs/cli/guides/migrate-to-v2 for migration guide.

packages/openapi-typescript/biome.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
"$schema": "https://biomejs.dev/schemas/2.2.4/schema.json",
44
"extends": "//",
55
"files": {
6-
"includes": ["bin/**", "!examples/**", "src/**", "test/**", "!**/fixtures/**/*"]
6+
"includes": ["bin/**", "!examples/**", "src/**", "test/**", "!**/fixtures/**/*", "!src/lib/redocly.ts"]
77
}
88
}

packages/openapi-typescript/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@
6262
"typescript": "^5.x"
6363
},
6464
"dependencies": {
65-
"@redocly/openapi-core": "^1.34.5",
65+
"@redocly/openapi-core": "^2.1.2",
66+
"ajv": "^8.17.1",
6667
"ansi-colors": "^4.1.3",
6768
"change-case": "^5.4.4",
6869
"parse-json": "^8.3.0",

packages/openapi-typescript/src/index.ts

Lines changed: 224 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { performance } from "node:perf_hooks";
22
import type { Readable } from "node:stream";
33
import { createConfig } from "@redocly/openapi-core";
4+
import type { RawGovernanceConfig } from "@redocly/openapi-core/lib/config/config";
45
import type ts from "typescript";
56
import { validateAndBundle } from "./lib/redoc.js";
67
import { debug, resolveRef, scanDiscriminators } from "./lib/utils.js";
@@ -51,14 +52,13 @@ export default async function openapiTS(
5152

5253
const redoc =
5354
options.redocly ??
54-
(await createConfig(
55-
{
56-
rules: {
57-
"operation-operationId-unique": { severity: "error" }, // throw error on duplicate operationIDs
58-
},
55+
(await createConfig({
56+
...REDOCLY_MINIMAL_CONFIG,
57+
rules: {
58+
...REDOCLY_MINIMAL_CONFIG.rules,
59+
"operation-operationId-unique": { severity: "error" }, // throw error on duplicate operationIDs
5960
},
60-
{ extends: ["minimal"] },
61-
));
61+
}));
6262

6363
const schema = await validateAndBundle(source, {
6464
redoc,
@@ -102,3 +102,220 @@ export default async function openapiTS(
102102

103103
return result;
104104
}
105+
106+
export const REDOCLY_MINIMAL_CONFIG: RawGovernanceConfig<"built-in"> = {
107+
rules: {
108+
struct: "error",
109+
"no-unresolved-refs": "error",
110+
},
111+
oas2Rules: {
112+
"boolean-parameter-prefixes": "off",
113+
"info-contact": "off",
114+
"info-license": "off",
115+
"info-license-strict": "off",
116+
"no-path-trailing-slash": "warn",
117+
"no-identical-paths": "warn",
118+
"no-ambiguous-paths": "warn",
119+
"no-invalid-schema-examples": "off",
120+
"no-invalid-parameter-examples": "off",
121+
"no-http-verbs-in-paths": "off",
122+
"no-enum-type-mismatch": "warn",
123+
"no-required-schema-properties-undefined": "warn",
124+
"no-schema-type-mismatch": "warn",
125+
"operation-summary": "warn",
126+
"operation-operationId": "warn",
127+
"operation-operationId-unique": "warn",
128+
"operation-operationId-url-safe": "warn",
129+
"operation-description": "off",
130+
"operation-2xx-response": "warn",
131+
"operation-4xx-response": "off",
132+
"operation-parameters-unique": "warn",
133+
"operation-tag-defined": "off",
134+
"operation-singular-tag": "off",
135+
"parameter-description": "off",
136+
"path-declaration-must-exist": "warn",
137+
"path-not-include-query": "warn",
138+
"path-parameters-defined": "warn",
139+
"paths-kebab-case": "off",
140+
"path-http-verbs-order": "off",
141+
"path-params-defined": "off",
142+
"path-segment-plural": "off",
143+
"required-string-property-missing-min-length": "off",
144+
"response-contains-header": "off",
145+
"request-mime-type": "off",
146+
"response-contains-property": "off",
147+
"response-mime-type": "off",
148+
"security-defined": "warn",
149+
"spec-strict-refs": "off",
150+
"scalar-property-missing-example": "off",
151+
"tag-description": "warn",
152+
"tags-alphabetical": "off",
153+
"no-duplicated-tag-names": "off",
154+
},
155+
oas3_0Rules: {
156+
"array-parameter-serialization": "off",
157+
"boolean-parameter-prefixes": "off",
158+
"component-name-unique": "off",
159+
"info-contact": "off",
160+
"info-license": "off",
161+
"info-license-strict": "off",
162+
"no-ambiguous-paths": "warn",
163+
"no-path-trailing-slash": "warn",
164+
"no-identical-paths": "warn",
165+
"no-invalid-schema-examples": "off",
166+
"no-invalid-parameter-examples": "off",
167+
"no-http-verbs-in-paths": "off",
168+
"no-enum-type-mismatch": "warn",
169+
"no-required-schema-properties-undefined": "warn",
170+
"no-schema-type-mismatch": "warn",
171+
"no-invalid-media-type-examples": {
172+
severity: "warn",
173+
allowAdditionalProperties: false,
174+
},
175+
"no-server-example.com": "warn",
176+
"no-server-trailing-slash": "error",
177+
"no-empty-servers": "warn",
178+
"no-example-value-and-externalValue": "warn",
179+
"no-unused-components": "warn",
180+
"no-undefined-server-variable": "warn",
181+
"no-server-variables-empty-enum": "error",
182+
"nullable-type-sibling": "warn",
183+
"operation-summary": "warn",
184+
"operation-operationId": "warn",
185+
"operation-operationId-unique": "warn",
186+
"operation-operationId-url-safe": "warn",
187+
"operation-description": "off",
188+
"operation-2xx-response": "warn",
189+
"operation-4xx-response": "off",
190+
"operation-4xx-problem-details-rfc7807": "off",
191+
"operation-parameters-unique": "warn",
192+
"operation-tag-defined": "off",
193+
"operation-singular-tag": "off",
194+
"parameter-description": "off",
195+
"path-declaration-must-exist": "warn",
196+
"path-not-include-query": "warn",
197+
"path-parameters-defined": "warn",
198+
"paths-kebab-case": "off",
199+
"path-http-verbs-order": "off",
200+
"path-params-defined": "off",
201+
"path-segment-plural": "off",
202+
"required-string-property-missing-min-length": "off",
203+
"response-contains-header": "off",
204+
"request-mime-type": "off",
205+
"response-contains-property": "off",
206+
"response-mime-type": "off",
207+
"security-defined": "warn",
208+
"spec-strict-refs": "off",
209+
"scalar-property-missing-example": "off",
210+
"spec-components-invalid-map-name": "warn",
211+
"tag-description": "warn",
212+
"tags-alphabetical": "off",
213+
"no-duplicated-tag-names": "off",
214+
},
215+
oas3_1Rules: {
216+
"array-parameter-serialization": "off",
217+
"boolean-parameter-prefixes": "off",
218+
"component-name-unique": "off",
219+
"info-contact": "off",
220+
"info-license": "off",
221+
"info-license-strict": "off",
222+
"no-path-trailing-slash": "warn",
223+
"no-identical-paths": "warn",
224+
"no-ambiguous-paths": "warn",
225+
"no-invalid-schema-examples": "off",
226+
"no-invalid-parameter-examples": "off",
227+
"no-http-verbs-in-paths": "off",
228+
"no-enum-type-mismatch": "warn",
229+
"no-required-schema-properties-undefined": "warn",
230+
"no-schema-type-mismatch": "warn",
231+
"no-invalid-media-type-examples": "warn",
232+
"no-server-example.com": "warn",
233+
"no-server-trailing-slash": "error",
234+
"no-empty-servers": "warn",
235+
"no-example-value-and-externalValue": "warn",
236+
"no-unused-components": "warn",
237+
"no-undefined-server-variable": "warn",
238+
"no-server-variables-empty-enum": "error",
239+
"operation-summary": "warn",
240+
"operation-operationId": "warn",
241+
"operation-operationId-unique": "warn",
242+
"operation-operationId-url-safe": "warn",
243+
"operation-description": "off",
244+
"operation-2xx-response": "warn",
245+
"operation-4xx-response": "off",
246+
"operation-4xx-problem-details-rfc7807": "off",
247+
"operation-parameters-unique": "warn",
248+
"operation-tag-defined": "off",
249+
"operation-singular-tag": "off",
250+
"parameter-description": "off",
251+
"path-declaration-must-exist": "warn",
252+
"path-not-include-query": "warn",
253+
"path-parameters-defined": "warn",
254+
"paths-kebab-case": "off",
255+
"path-http-verbs-order": "off",
256+
"path-params-defined": "off",
257+
"path-segment-plural": "off",
258+
"required-string-property-missing-min-length": "off",
259+
"response-contains-header": "off",
260+
"request-mime-type": "off",
261+
"response-contains-property": "off",
262+
"response-mime-type": "off",
263+
"security-defined": "warn",
264+
"spec-strict-refs": "off",
265+
"scalar-property-missing-example": "off",
266+
"spec-components-invalid-map-name": "warn",
267+
"tag-description": "warn",
268+
"tags-alphabetical": "off",
269+
"no-duplicated-tag-names": "off",
270+
},
271+
async2Rules: {
272+
"channels-kebab-case": "off",
273+
"info-contact": "off",
274+
"info-license-strict": "off",
275+
"no-channel-trailing-slash": "off",
276+
"operation-operationId": "warn",
277+
"tag-description": "warn",
278+
"tags-alphabetical": "off",
279+
"no-duplicated-tag-names": "off",
280+
"no-required-schema-properties-undefined": "warn",
281+
"no-enum-type-mismatch": "warn",
282+
"no-schema-type-mismatch": "warn",
283+
},
284+
async3Rules: {
285+
"channels-kebab-case": "off",
286+
"info-contact": "off",
287+
"info-license-strict": "off",
288+
"no-channel-trailing-slash": "off",
289+
"operation-operationId": "warn",
290+
"tag-description": "warn",
291+
"tags-alphabetical": "off",
292+
"no-duplicated-tag-names": "off",
293+
"no-required-schema-properties-undefined": "warn",
294+
"no-enum-type-mismatch": "warn",
295+
"no-schema-type-mismatch": "warn",
296+
},
297+
arazzo1Rules: {
298+
"criteria-unique": "off",
299+
"no-criteria-xpath": "off",
300+
"parameters-unique": "off",
301+
"requestBody-replacements-unique": "off",
302+
"sourceDescription-type": "off",
303+
"sourceDescriptions-not-empty": "off",
304+
"step-onSuccess-unique": "off",
305+
"step-onFailure-unique": "off",
306+
"stepId-unique": "error",
307+
"sourceDescription-name-unique": "off",
308+
"respect-supported-versions": "off",
309+
"workflowId-unique": "error",
310+
"workflow-dependsOn": "off",
311+
"no-x-security-scheme-name-without-openapi": "off",
312+
"x-security-scheme-required-values": "off",
313+
"no-x-security-scheme-name-in-workflow": "off",
314+
"no-required-schema-properties-undefined": "warn",
315+
"no-enum-type-mismatch": "warn",
316+
"no-schema-type-mismatch": "warn",
317+
},
318+
overlay1Rules: {
319+
"info-contact": "off",
320+
},
321+
};

packages/openapi-typescript/src/lib/redoc.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ function _processProblems(problems: NormalizedProblem[], options: { silent: bool
107107
export async function validateAndBundle(
108108
source: string | URL | OpenAPI3 | Readable | Buffer,
109109
options: ValidateAndBundleOptions,
110-
) {
110+
): Promise<OpenAPI3> {
111111
const redocConfigT = performance.now();
112112
debug("Loaded Redoc config", "redoc", performance.now() - redocConfigT);
113113
const redocParseT = performance.now();
@@ -116,22 +116,22 @@ export async function validateAndBundle(
116116
absoluteRef = source.protocol === "file:" ? fileURLToPath(source) : source.href;
117117
}
118118
const resolver = new BaseResolver(options.redoc.resolve);
119-
const document = await parseSchema(source, {
119+
const document = (await parseSchema(source, {
120120
absoluteRef,
121121
resolver,
122-
});
122+
})) as Document<OpenAPI3>;
123123
debug("Parsed schema", "redoc", performance.now() - redocParseT);
124124

125125
// 1. check for OpenAPI 3 or greater
126126
const openapiVersion = Number.parseFloat(document.parsed.openapi);
127127
if (
128-
document.parsed.swagger ||
128+
(document.parsed as any).swagger ||
129129
!document.parsed.openapi ||
130130
Number.isNaN(openapiVersion) ||
131131
openapiVersion < 3 ||
132132
openapiVersion >= 4
133133
) {
134-
if (document.parsed.swagger) {
134+
if ((document.parsed as any).swagger) {
135135
throw new Error("Unsupported Swagger version: 2.x. Use OpenAPI 3.x instead.");
136136
}
137137
if (document.parsed.openapi || openapiVersion < 3 || openapiVersion >= 4) {
@@ -144,7 +144,7 @@ export async function validateAndBundle(
144144
const redocLintT = performance.now();
145145
const problems = await lintDocument({
146146
document,
147-
config: options.redoc.styleguide,
147+
config: options.redoc,
148148
externalRefResolver: resolver,
149149
});
150150
_processProblems(problems, options);
@@ -160,5 +160,5 @@ export async function validateAndBundle(
160160
_processProblems(bundled.problems, options);
161161
debug("Bundled schema", "bundle", performance.now() - redocBundleT);
162162

163-
return bundled.bundle.parsed;
163+
return bundled.bundle.parsed as OpenAPI3;
164164
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 2019 Redocly Inc.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4+
//
5+
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6+
//
7+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8+
export type Falsy = undefined | null | false | '' | 0;
9+
10+
export function isTruthy<Truthy>(value: Truthy | Falsy): value is Truthy {
11+
return !!value;
12+
}
13+
14+
export function parseRef(ref: string): { uri: string | null; pointer: string[] } {
15+
const [uri, pointer = ''] = ref.split('#/');
16+
return {
17+
uri: (uri.endsWith('#') ? uri.slice(0, -1) : uri) || null,
18+
pointer: parsePointer(pointer),
19+
};
20+
}
21+
22+
export function parsePointer(pointer: string) {
23+
return pointer.split('/').map(unescapePointer).filter(isTruthy);
24+
}
25+
26+
export function unescapePointer(fragment: string): string {
27+
return decodeURIComponent(fragment.replace(/~1/g, '/').replace(/~0/g, '~'));
28+
}
29+
30+
export function escapePointer<T extends string | number>(fragment: T): T {
31+
if (typeof fragment === 'number') return fragment;
32+
return (fragment as string).replace(/~/g, '~0').replace(/\//g, '~1') as T;
33+
}

packages/openapi-typescript/src/lib/ts.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { OasRef, Referenced } from "@redocly/openapi-core";
2-
import { parseRef } from "@redocly/openapi-core/lib/ref-utils.js";
32
import ts, { type LiteralTypeNode, type TypeLiteralNode } from "typescript";
43
import type { ParameterObject } from "../types.js";
4+
import { parseRef } from "./redocly.js";
55

66
export const JS_PROPERTY_INDEX_RE = /^[A-Za-z_$][A-Za-z_$0-9]*$/;
77
export const JS_ENUM_INVALID_CHARS_RE = /[^A-Za-z_$0-9]+(.)?/g;

packages/openapi-typescript/src/lib/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { escapePointer, parseRef } from "@redocly/openapi-core/lib/ref-utils.js";
21
import c from "ansi-colors";
32
import supportsColor from "supports-color";
43
import ts from "typescript";
4+
import { escapePointer, parseRef } from "../lib/redocly.js";
55
import type { DiscriminatorObject, OpenAPI3, OpenAPITSOptions, ReferenceObject, SchemaObject } from "../types.js";
66
import { tsLiteral, tsModifiers, tsPropertyIndex } from "./ts.js";
77

packages/openapi-typescript/src/transform/header-object.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { escapePointer } from "@redocly/openapi-core/lib/ref-utils.js";
21
import ts from "typescript";
2+
import { escapePointer } from "../lib/redocly.js";
33
import { addJSDocComment, tsModifiers, tsPropertyIndex, UNKNOWN } from "../lib/ts.js";
44
import { getEntries } from "../lib/utils.js";
55
import type { HeaderObject, TransformNodeOptions } from "../types.js";

packages/openapi-typescript/src/transform/schema-object.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { parseRef } from "@redocly/openapi-core/lib/ref-utils.js";
21
import ts from "typescript";
2+
import { parseRef } from "../lib/redocly.js";
33
import {
44
addJSDocComment,
55
BOOLEAN,

0 commit comments

Comments
 (0)