openapi-typesafe 1.0.0-rc.5 → 1.0.0-rc.6
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 +73 -0
- package/dist/adapters/redocly-loader/query-document-names.cjs +50 -0
- package/dist/adapters/redocly-loader/query-document-names.cjs.map +1 -0
- package/dist/adapters/redocly-loader/query-document-names.mjs +48 -0
- package/dist/adapters/redocly-loader/query-document-names.mjs.map +1 -0
- package/dist/adapters/redocly-loader/query-named-components.cjs +29 -37
- package/dist/adapters/redocly-loader/query-named-components.cjs.map +1 -1
- package/dist/adapters/redocly-loader/query-named-components.mjs +29 -37
- package/dist/adapters/redocly-loader/query-named-components.mjs.map +1 -1
- package/dist/adapters/redocly-loader/redocly-config-source.cjs +3 -0
- package/dist/adapters/redocly-loader/redocly-config-source.cjs.map +1 -1
- package/dist/adapters/redocly-loader/redocly-config-source.mjs +4 -1
- package/dist/adapters/redocly-loader/redocly-config-source.mjs.map +1 -1
- package/dist/adapters/redocly-loader/redocly-schema-loader.cjs +16 -7
- package/dist/adapters/redocly-loader/redocly-schema-loader.cjs.map +1 -1
- package/dist/adapters/redocly-loader/redocly-schema-loader.mjs +17 -8
- package/dist/adapters/redocly-loader/redocly-schema-loader.mjs.map +1 -1
- package/dist/adapters/redocly-loader/ref-access.cjs +9 -1
- package/dist/adapters/redocly-loader/ref-access.cjs.map +1 -1
- package/dist/adapters/redocly-loader/ref-access.mjs +10 -2
- package/dist/adapters/redocly-loader/ref-access.mjs.map +1 -1
- package/dist/adapters/redocly-loader/remote-fetch.cjs +16 -1
- package/dist/adapters/redocly-loader/remote-fetch.cjs.map +1 -1
- package/dist/adapters/redocly-loader/remote-fetch.mjs +16 -2
- package/dist/adapters/redocly-loader/remote-fetch.mjs.map +1 -1
- package/dist/api/cwd-option.cjs +58 -0
- package/dist/api/cwd-option.cjs.map +1 -0
- package/dist/api/cwd-option.mjs +52 -0
- package/dist/api/cwd-option.mjs.map +1 -0
- package/dist/api/default-ports.cjs +15 -7
- package/dist/api/default-ports.cjs.map +1 -1
- package/dist/api/default-ports.mjs +14 -8
- package/dist/api/default-ports.mjs.map +1 -1
- package/dist/api/document-run.cjs +2 -16
- package/dist/api/document-run.cjs.map +1 -1
- package/dist/api/document-run.mjs +2 -12
- package/dist/api/document-run.mjs.map +1 -1
- package/dist/api/legacy-utils.cjs +1 -1
- package/dist/api/legacy-utils.cjs.map +1 -1
- package/dist/api/legacy-utils.mjs +1 -1
- package/dist/api/legacy-utils.mjs.map +1 -1
- package/dist/api/openapi-ts.cjs.map +1 -1
- package/dist/api/openapi-ts.mjs.map +1 -1
- package/dist/api/option-errors.cjs +24 -0
- package/dist/api/option-errors.cjs.map +1 -0
- package/dist/api/option-errors.mjs +21 -0
- package/dist/api/option-errors.mjs.map +1 -0
- package/dist/api/ref-limit-options.cjs +19 -24
- package/dist/api/ref-limit-options.cjs.map +1 -1
- package/dist/api/ref-limit-options.mjs +15 -20
- package/dist/api/ref-limit-options.mjs.map +1 -1
- package/dist/application/transform/document/path-template-refs.cjs +12 -2
- package/dist/application/transform/document/path-template-refs.cjs.map +1 -1
- package/dist/application/transform/document/path-template-refs.mjs +12 -2
- package/dist/application/transform/document/path-template-refs.mjs.map +1 -1
- package/dist/application/transform/ref-pointer/generated-shape.cjs +28 -10
- package/dist/application/transform/ref-pointer/generated-shape.cjs.map +1 -1
- package/dist/application/transform/ref-pointer/generated-shape.mjs +29 -11
- package/dist/application/transform/ref-pointer/generated-shape.mjs.map +1 -1
- package/dist/application/transform/ref-pointer/original-index.cjs +1 -1
- package/dist/application/transform/ref-pointer/original-index.cjs.map +1 -1
- package/dist/application/transform/ref-pointer/original-index.mjs +1 -1
- package/dist/application/transform/ref-pointer/original-index.mjs.map +1 -1
- package/dist/application/transform/ref-pointer/property-shape.cjs +5 -0
- package/dist/application/transform/ref-pointer/property-shape.cjs.map +1 -1
- package/dist/application/transform/ref-pointer/property-shape.mjs +5 -1
- package/dist/application/transform/ref-pointer/property-shape.mjs.map +1 -1
- package/dist/application/transform/ref-pointer/typed-walk.cjs +11 -1
- package/dist/application/transform/ref-pointer/typed-walk.cjs.map +1 -1
- package/dist/application/transform/ref-pointer/typed-walk.mjs +12 -2
- package/dist/application/transform/ref-pointer/typed-walk.mjs.map +1 -1
- package/dist/cli/api-settings.cjs +3 -2
- package/dist/cli/api-settings.cjs.map +1 -1
- package/dist/cli/api-settings.mjs +2 -1
- package/dist/cli/api-settings.mjs.map +1 -1
- package/dist/cli/api-writes.cjs +7 -1
- package/dist/cli/api-writes.cjs.map +1 -1
- package/dist/cli/api-writes.mjs +7 -1
- package/dist/cli/api-writes.mjs.map +1 -1
- package/dist/cli/cli-arguments.cjs +25 -107
- package/dist/cli/cli-arguments.cjs.map +1 -1
- package/dist/cli/cli-arguments.mjs +18 -100
- package/dist/cli/cli-arguments.mjs.map +1 -1
- package/dist/cli/cli-errors.cjs +1 -0
- package/dist/cli/cli-errors.cjs.map +1 -1
- package/dist/cli/cli-errors.mjs +1 -0
- package/dist/cli/cli-errors.mjs.map +1 -1
- package/dist/cli/cli-flag-names.cjs +77 -0
- package/dist/cli/cli-flag-names.cjs.map +1 -0
- package/dist/cli/cli-flag-names.mjs +64 -0
- package/dist/cli/cli-flag-names.mjs.map +1 -0
- package/dist/cli/cli-output.cjs +7 -2
- package/dist/cli/cli-output.cjs.map +1 -1
- package/dist/cli/cli-output.mjs +7 -2
- package/dist/cli/cli-output.mjs.map +1 -1
- package/dist/cli/main.cjs +1 -1
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.mjs +1 -1
- package/dist/cli/main.mjs.map +1 -1
- package/dist/cli/redocly-apis.cjs +14 -4
- package/dist/cli/redocly-apis.cjs.map +1 -1
- package/dist/cli/redocly-apis.mjs +14 -4
- package/dist/cli/redocly-apis.mjs.map +1 -1
- package/dist/cli/written-flags.cjs +83 -0
- package/dist/cli/written-flags.cjs.map +1 -0
- package/dist/cli/written-flags.mjs +77 -0
- package/dist/cli/written-flags.mjs.map +1 -0
- package/dist/domain/errors.cjs +1 -0
- package/dist/domain/errors.cjs.map +1 -1
- package/dist/domain/errors.mjs +1 -0
- package/dist/domain/errors.mjs.map +1 -1
- package/dist/domain/location-text.cjs +7 -1
- package/dist/domain/location-text.cjs.map +1 -1
- package/dist/domain/location-text.mjs +7 -1
- package/dist/domain/location-text.mjs.map +1 -1
- package/dist/index.d.cts +3 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.d.ts +3 -0
- package/package.json +1 -1
- package/src/adapters/redocly-loader/query-document-names.ts +79 -0
- package/src/adapters/redocly-loader/query-named-components.ts +48 -49
- package/src/adapters/redocly-loader/redocly-config-source.ts +11 -1
- package/src/adapters/redocly-loader/redocly-schema-loader.ts +20 -6
- package/src/adapters/redocly-loader/ref-access.ts +16 -2
- package/src/adapters/redocly-loader/remote-fetch.ts +26 -0
- package/src/api/cwd-option.ts +76 -0
- package/src/api/default-ports.ts +33 -10
- package/src/api/document-run.ts +2 -17
- package/src/api/legacy-utils.ts +1 -1
- package/src/api/openapi-ts.ts +14 -1
- package/src/api/option-errors.ts +22 -0
- package/src/api/ref-limit-options.ts +17 -24
- package/src/application/ports/redocly-config-source.ts +5 -0
- package/src/application/transform/document/path-template-refs.ts +28 -7
- package/src/application/transform/ref-pointer/generated-shape.ts +57 -14
- package/src/application/transform/ref-pointer/original-index.ts +6 -3
- package/src/application/transform/ref-pointer/property-shape.ts +9 -0
- package/src/application/transform/ref-pointer/typed-walk.ts +26 -4
- package/src/cli/api-writes.ts +11 -3
- package/src/cli/cli-arguments.ts +42 -153
- package/src/cli/cli-errors.ts +1 -0
- package/src/cli/cli-flag-names.ts +99 -0
- package/src/cli/cli-output.ts +20 -6
- package/src/cli/main.ts +2 -1
- package/src/cli/redocly-apis.ts +21 -5
- package/src/cli/written-flags.ts +131 -0
- package/src/domain/errors.ts +1 -0
- package/src/domain/location-text.ts +17 -2
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { LoadedDocument } from "./unsupported-keys.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Which loaded document with a query each node comes from, and the names Redocly and the loader
|
|
5
|
+
* give that document. Redocly names a component after the text behind the last `/` of the URL,
|
|
6
|
+
* query included, so `pet.json?ref=feature/new-api` and `owner.yaml?ref=feature/new-api` both give
|
|
7
|
+
* `new-api`: the name alone cannot tell the documents apart, the node can.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const REMOTE_URL_RE = /^https?:\/\//;
|
|
11
|
+
const PATH_SEPARATOR_RE = /[/\\]/;
|
|
12
|
+
const EXTENSION_RE = /\.[^.]+$/;
|
|
13
|
+
|
|
14
|
+
/** The base name Redocly gives a document and the one without the query. */
|
|
15
|
+
export interface DocumentNames {
|
|
16
|
+
readonly named: string;
|
|
17
|
+
readonly clean: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** The base name Redocly 1.34 gives a document (`refBaseName`): the last segment, no extension. */
|
|
21
|
+
function redoclyBaseName(ref: string): string {
|
|
22
|
+
return (ref.split(PATH_SEPARATOR_RE).at(-1) ?? "").replace(EXTENSION_RE, "");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The base name of the URL without its query and fragment. A URL with no last segment
|
|
27
|
+
* (`https://host/?key=…`) is named after its host, so the name is never empty.
|
|
28
|
+
*/
|
|
29
|
+
function queryFreeBaseName(url: URL): string {
|
|
30
|
+
const withoutQuery = new URL(url.pathname, url.origin).href;
|
|
31
|
+
return redoclyBaseName(withoutQuery) || url.hostname;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The names of a remote document with a query whose Redocly name keeps it; else undefined. */
|
|
35
|
+
function documentNamesOf(ref: string): DocumentNames | undefined {
|
|
36
|
+
const url = REMOTE_URL_RE.test(ref) && URL.canParse(ref) ? new URL(ref) : undefined;
|
|
37
|
+
if (url === undefined || url.search === "") {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
const names = { named: redoclyBaseName(ref), clean: queryFreeBaseName(url) };
|
|
41
|
+
return names.named === names.clean ? undefined : names;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function isNode(value: unknown): value is object {
|
|
45
|
+
return typeof value === "object" && value !== null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Every object and array of a parsed document, each once (a YAML alias can repeat one). */
|
|
49
|
+
function nodesOf(parsed: unknown): object[] {
|
|
50
|
+
const visited = new Set<object>();
|
|
51
|
+
const pending = isNode(parsed) ? [parsed] : [];
|
|
52
|
+
for (let node = pending.pop(); node !== undefined; node = pending.pop()) {
|
|
53
|
+
if (!visited.has(node)) {
|
|
54
|
+
visited.add(node);
|
|
55
|
+
pending.push(...Object.values(node).filter(isNode));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return [...visited];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The names of the document each node of a loaded document with a query belongs to. The bundle
|
|
63
|
+
* keeps the nodes of the loaded documents as its components, so a component finds its document.
|
|
64
|
+
*/
|
|
65
|
+
export function queryDocumentNames(
|
|
66
|
+
documents: readonly LoadedDocument[],
|
|
67
|
+
): ReadonlyMap<object, DocumentNames> {
|
|
68
|
+
const byNode = new Map<object, DocumentNames>();
|
|
69
|
+
for (const { source, parsed } of documents) {
|
|
70
|
+
const names = documentNamesOf(source);
|
|
71
|
+
if (names === undefined) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
for (const node of nodesOf(parsed)) {
|
|
75
|
+
byNode.set(node, names);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return byNode;
|
|
79
|
+
}
|
|
@@ -4,7 +4,9 @@ import {
|
|
|
4
4
|
parseRefPointer,
|
|
5
5
|
ROOT_POINTER,
|
|
6
6
|
} from "../../domain/openapi/json-pointer.js";
|
|
7
|
+
import { type DocumentNames, queryDocumentNames } from "./query-document-names.js";
|
|
7
8
|
import { findMappings, findRefHolders } from "./ref-holders.js";
|
|
9
|
+
import type { LoadedDocument } from "./unsupported-keys.js";
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* Redocly 1.x bundles an external `$ref` into a component named after the last segment of the
|
|
@@ -13,6 +15,7 @@ import { findMappings, findRefHolders } from "./ref-holders.js";
|
|
|
13
15
|
* API keys and signatures, so `https://host/schemas/pet?token=…` became the component
|
|
14
16
|
* `pet?token=…` in the generated file and in messages (threat model T4b). This renames such a
|
|
15
17
|
* component after the URL without its query: `pet`, with a `-2` suffix when that name is taken.
|
|
18
|
+
* Each component is matched to its document by its node, since two URLs can give one name.
|
|
16
19
|
*/
|
|
17
20
|
|
|
18
21
|
type Components = Record<string, Record<string, unknown>>;
|
|
@@ -24,12 +27,14 @@ export type ComponentKeys = ReadonlyMap<string, ReadonlySet<string>>;
|
|
|
24
27
|
type Renames = Map<string, Map<string, string>>;
|
|
25
28
|
|
|
26
29
|
const COMPONENTS_KEY = "components";
|
|
27
|
-
const REMOTE_URL_RE = /^https?:\/\//;
|
|
28
|
-
const PATH_SEPARATOR_RE = /[/\\]/;
|
|
29
|
-
const EXTENSION_RE = /\.[^.]+$/;
|
|
30
30
|
/** What Redocly puts between the base name and the rest of a component name. */
|
|
31
31
|
const NAME_SUFFIX_STARTS = ["_", "-"];
|
|
32
32
|
const FIRST_SERIAL = 2;
|
|
33
|
+
/**
|
|
34
|
+
* The serial Redocly adds when a name is taken (`pet-2`). Two URLs that differ only in the query
|
|
35
|
+
* took one name; without the query they may not, so the serial is chosen again (`freeName`).
|
|
36
|
+
*/
|
|
37
|
+
const REDOCLY_SERIAL_RE = /-\d+$/;
|
|
33
38
|
|
|
34
39
|
function isContainer(value: unknown): value is Record<string, unknown> {
|
|
35
40
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -58,46 +63,17 @@ export function componentKeysOf(document: unknown): ComponentKeys {
|
|
|
58
63
|
return keys;
|
|
59
64
|
}
|
|
60
65
|
|
|
61
|
-
/** The
|
|
62
|
-
function
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* The base name of the URL without its query and fragment. A URL with no last segment
|
|
68
|
-
* (`https://host/?key=…`) is named after its host, so the name is never empty.
|
|
69
|
-
*/
|
|
70
|
-
function queryFreeBaseName(url: URL): string {
|
|
71
|
-
const withoutQuery = new URL(url.pathname, url.origin).href;
|
|
72
|
-
return redoclyBaseName(withoutQuery) || url.hostname;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/** Each base name Redocly takes from a remote URL with a query, with the name to use instead. */
|
|
76
|
-
function queryBaseNames(documentRefs: Iterable<string>): Map<string, string> {
|
|
77
|
-
const names = new Map<string, string>();
|
|
78
|
-
for (const ref of documentRefs) {
|
|
79
|
-
const url = REMOTE_URL_RE.test(ref) && URL.canParse(ref) ? new URL(ref) : undefined;
|
|
80
|
-
if (url === undefined || url.search === "") {
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
const named = redoclyBaseName(ref);
|
|
84
|
-
const clean = queryFreeBaseName(url);
|
|
85
|
-
if (named !== clean) {
|
|
86
|
-
names.set(named, clean);
|
|
87
|
-
}
|
|
66
|
+
/** The name a component of a document with a query gets; undefined for any other component. */
|
|
67
|
+
function queryFreeName(key: string, names: DocumentNames | undefined): string | undefined {
|
|
68
|
+
if (names === undefined) {
|
|
69
|
+
return undefined;
|
|
88
70
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const matches = [...baseNames.keys()].filter(
|
|
96
|
-
(named) =>
|
|
97
|
-
key === named || NAME_SUFFIX_STARTS.some((start) => key.startsWith(`${named}${start}`)),
|
|
98
|
-
);
|
|
99
|
-
const named = matches.sort((a, b) => b.length - a.length)[0];
|
|
100
|
-
return named === undefined ? undefined : `${baseNames.get(named)}${key.slice(named.length)}`;
|
|
71
|
+
const { named, clean } = names;
|
|
72
|
+
const hasBaseName =
|
|
73
|
+
key === named || NAME_SUFFIX_STARTS.some((start) => key.startsWith(`${named}${start}`));
|
|
74
|
+
return hasBaseName
|
|
75
|
+
? `${clean}${key.slice(named.length).replace(REDOCLY_SERIAL_RE, "")}`
|
|
76
|
+
: undefined;
|
|
101
77
|
}
|
|
102
78
|
|
|
103
79
|
function freeName(name: string, taken: ReadonlySet<string>): string {
|
|
@@ -112,12 +88,14 @@ function freeName(name: string, taken: ReadonlySet<string>): string {
|
|
|
112
88
|
function renameGroup(
|
|
113
89
|
entries: Record<string, unknown>,
|
|
114
90
|
keysBefore: ReadonlySet<string>,
|
|
115
|
-
|
|
91
|
+
documentNames: ReadonlyMap<object, DocumentNames>,
|
|
116
92
|
): Map<string, string> {
|
|
117
93
|
const renames = new Map<string, string>();
|
|
118
94
|
const taken = new Set(Object.keys(entries));
|
|
119
95
|
for (const key of Object.keys(entries)) {
|
|
120
|
-
const
|
|
96
|
+
const node = entries[key];
|
|
97
|
+
const names = typeof node === "object" && node !== null ? documentNames.get(node) : undefined;
|
|
98
|
+
const name = keysBefore.has(key) ? undefined : queryFreeName(key, names);
|
|
121
99
|
if (name !== undefined) {
|
|
122
100
|
taken.delete(key);
|
|
123
101
|
const free = freeName(name, taken);
|
|
@@ -138,6 +116,26 @@ export interface ComponentRenaming {
|
|
|
138
116
|
readonly pointer: (pointer: string) => string;
|
|
139
117
|
/** The text with each renamed component's old name replaced by its new name. */
|
|
140
118
|
readonly text: (text: string) => string;
|
|
119
|
+
/**
|
|
120
|
+
* Whether the problem is Redocly's report of two components with one name that the new names
|
|
121
|
+
* tell apart: it renamed `new-api` to `new-api-2`, and the documents are now `pet` and `owner`.
|
|
122
|
+
*/
|
|
123
|
+
readonly isResolvedCollision: (message: string) => boolean;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** The warning Redocly 1.34 gives when it adds a serial to a component name. */
|
|
127
|
+
const COLLISION_RE =
|
|
128
|
+
/^Two schemas are referenced with the same name but different content\. Renamed (.+) to (.+)\.$/;
|
|
129
|
+
const SERIAL_RE = /^-\d+$/;
|
|
130
|
+
|
|
131
|
+
function resolvesCollision(message: string, renames: Renames): boolean {
|
|
132
|
+
const [, first, second] = COLLISION_RE.exec(message) ?? [];
|
|
133
|
+
if (first === undefined || second === undefined) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
const renamed = new Map([...renames.values()].flatMap((group) => [...group]));
|
|
137
|
+
const [newFirst, newSecond] = [renamed.get(first) ?? first, renamed.get(second) ?? second];
|
|
138
|
+
return !(newSecond.startsWith(newFirst) && SERIAL_RE.test(newSecond.slice(newFirst.length)));
|
|
141
139
|
}
|
|
142
140
|
|
|
143
141
|
function renamedPointer(pointer: string, renames: Renames): string {
|
|
@@ -173,18 +171,18 @@ function rewriteRefs(document: unknown, renaming: ComponentRenaming): void {
|
|
|
173
171
|
|
|
174
172
|
/**
|
|
175
173
|
* Rename, in place, the components the bundle named after a URL with a query, and every local
|
|
176
|
-
* `$ref` and `mapping` value that names them. `
|
|
174
|
+
* `$ref` and `mapping` value that names them. `documents` are the documents the load read.
|
|
177
175
|
*/
|
|
178
176
|
export function renameQueryNamedComponents(
|
|
179
177
|
bundled: unknown,
|
|
180
178
|
keysBefore: ComponentKeys,
|
|
181
|
-
|
|
179
|
+
documents: readonly LoadedDocument[],
|
|
182
180
|
): ComponentRenaming {
|
|
183
|
-
const
|
|
181
|
+
const documentNames = queryDocumentNames(documents);
|
|
184
182
|
const renames: Renames = new Map();
|
|
185
|
-
if (
|
|
183
|
+
if (documentNames.size > 0) {
|
|
186
184
|
for (const [group, entries] of Object.entries(componentsOf(bundled))) {
|
|
187
|
-
const groupRenames = renameGroup(entries, keysBefore.get(group) ?? new Set(),
|
|
185
|
+
const groupRenames = renameGroup(entries, keysBefore.get(group) ?? new Set(), documentNames);
|
|
188
186
|
if (groupRenames.size > 0) {
|
|
189
187
|
renames.set(group, groupRenames);
|
|
190
188
|
}
|
|
@@ -193,6 +191,7 @@ export function renameQueryNamedComponents(
|
|
|
193
191
|
const renaming: ComponentRenaming = {
|
|
194
192
|
pointer: (pointer) => renamedPointer(pointer, renames),
|
|
195
193
|
text: (text) => renamedText(text, renames),
|
|
194
|
+
isResolvedCollision: (message) => resolvesCollision(message, renames),
|
|
196
195
|
};
|
|
197
196
|
if (renames.size > 0) {
|
|
198
197
|
rewriteRefs(bundled, renaming);
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
createConfig,
|
|
5
|
+
findConfig,
|
|
6
|
+
getMergedConfig,
|
|
7
|
+
IGNORE_FILE,
|
|
8
|
+
loadConfig,
|
|
9
|
+
} from "@redocly/openapi-core";
|
|
4
10
|
import type {
|
|
5
11
|
RedoclyConfig,
|
|
6
12
|
RedoclyConfigSource,
|
|
@@ -70,6 +76,10 @@ class RedoclyConfigFiles implements RedoclyConfigSource {
|
|
|
70
76
|
);
|
|
71
77
|
return loadedConfig(create, undefined);
|
|
72
78
|
}
|
|
79
|
+
|
|
80
|
+
forApi(config: RedoclyConfig, apiName: string): RedoclyConfig {
|
|
81
|
+
return getMergedConfig(config, apiName);
|
|
82
|
+
}
|
|
73
83
|
}
|
|
74
84
|
|
|
75
85
|
/** Find and load `redocly.yaml` relative to the directory `cwd`. */
|
|
@@ -31,7 +31,11 @@ import {
|
|
|
31
31
|
import { RefAccessResolver } from "./ref-access.js";
|
|
32
32
|
import { checkRefsResolve } from "./ref-resolution-check.js";
|
|
33
33
|
import { REF_CYCLE_MESSAGE } from "./ref-resolution-steps.js";
|
|
34
|
-
import {
|
|
34
|
+
import {
|
|
35
|
+
RedirectedUrls,
|
|
36
|
+
type RemoteDocumentLimits,
|
|
37
|
+
resolveConfigWithAccess,
|
|
38
|
+
} from "./remote-fetch.js";
|
|
35
39
|
import {
|
|
36
40
|
loadFailedError,
|
|
37
41
|
refCycleError,
|
|
@@ -177,17 +181,21 @@ class RedoclySchemaLoader implements SchemaLoader {
|
|
|
177
181
|
|
|
178
182
|
#resolver(request: SchemaLoadRequest): RefAccessResolver {
|
|
179
183
|
const { redoc, remoteRefTimeoutMs, remoteDocumentLimits } = this.#options;
|
|
184
|
+
// One record per load, as the resolver and its cache are.
|
|
185
|
+
const redirects = new RedirectedUrls();
|
|
180
186
|
const config = resolveConfigWithAccess(redoc.resolve, {
|
|
181
187
|
timeoutMs: remoteRefTimeoutMs,
|
|
182
188
|
access: request.refAccess,
|
|
183
189
|
rootUrl: request.source instanceof URL ? request.source.href : undefined,
|
|
184
190
|
maxParallelFetches: remoteDocumentLimits.maxParallelFetches,
|
|
185
191
|
bodyLimits: remoteDocumentLimits,
|
|
192
|
+
redirects,
|
|
186
193
|
});
|
|
187
194
|
return new RefAccessResolver(config, {
|
|
188
195
|
access: request.refAccess,
|
|
189
196
|
maxRemoteDocuments: remoteDocumentLimits.maxDocuments,
|
|
190
197
|
maxRemoteNodes: remoteDocumentLimits.maxNodes,
|
|
198
|
+
redirects,
|
|
191
199
|
});
|
|
192
200
|
}
|
|
193
201
|
|
|
@@ -229,8 +237,8 @@ class RedoclySchemaLoader implements SchemaLoader {
|
|
|
229
237
|
});
|
|
230
238
|
removeBundledMappingCopies(bundled.bundle.parsed, schemaKeys);
|
|
231
239
|
repairBundledRefs(bundled.bundle.parsed);
|
|
232
|
-
const
|
|
233
|
-
const renaming = renameQueryNamedComponents(bundled.bundle.parsed, componentKeys,
|
|
240
|
+
const documents = await loadedDocuments(document, resolver);
|
|
241
|
+
const renaming = renameQueryNamedComponents(bundled.bundle.parsed, componentKeys, documents);
|
|
234
242
|
// The problems name the components the bundle made, so they are shown by their new names.
|
|
235
243
|
this.#report(bundled.problems, document.source.absoluteRef, renaming);
|
|
236
244
|
this.#options.logger.debug("Bundled schema", "bundle", this.#options.now() - startedAt);
|
|
@@ -240,11 +248,13 @@ class RedoclySchemaLoader implements SchemaLoader {
|
|
|
240
248
|
/**
|
|
241
249
|
* Report every problem; then throw if any of them is an error. A problem that the Redocly
|
|
242
250
|
* config's `.redocly.lint-ignore.yaml` lists is left out, as `redocly lint` leaves it out
|
|
243
|
-
* (upstream #2189).
|
|
251
|
+
* (upstream #2189). A name collision that the renamed components no longer have is dropped.
|
|
244
252
|
*/
|
|
245
253
|
#report(problems: readonly NormalizedProblem[], rootRef: string, renaming = UNRENAMED): void {
|
|
246
254
|
const errorMessages: string[] = [];
|
|
247
|
-
for (const problem of problems
|
|
255
|
+
for (const problem of problems.filter(
|
|
256
|
+
(found) => !renaming.isResolvedCollision(found.message),
|
|
257
|
+
)) {
|
|
248
258
|
if (problem.ignored === true) {
|
|
249
259
|
this.#options.logger.debug(
|
|
250
260
|
`Ignored by the lint ignore file: ${problemMessage(problem, renaming, rootRef)}`,
|
|
@@ -272,7 +282,11 @@ function lintWarningText(message: string, ruleId: string): string {
|
|
|
272
282
|
}
|
|
273
283
|
|
|
274
284
|
/** Problems found before the bundle named any component. */
|
|
275
|
-
const UNRENAMED: ComponentRenaming = {
|
|
285
|
+
const UNRENAMED: ComponentRenaming = {
|
|
286
|
+
pointer: (pointer) => pointer,
|
|
287
|
+
text: (text) => text,
|
|
288
|
+
isResolvedCollision: () => false,
|
|
289
|
+
};
|
|
276
290
|
|
|
277
291
|
/** A problem location that names a JSON Pointer, not a line and column. */
|
|
278
292
|
type PointerLocation = Extract<LocationObject, { pointer: string }>;
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
BaseResolver,
|
|
6
6
|
type Document,
|
|
7
7
|
type Config as RedoclyConfig,
|
|
8
|
-
|
|
8
|
+
Source,
|
|
9
9
|
YamlParseError,
|
|
10
10
|
} from "@redocly/openapi-core";
|
|
11
11
|
import type { RefAccess } from "../../application/ports/schema-loader.js";
|
|
@@ -14,6 +14,7 @@ import { redactUrlSecrets } from "../../domain/url-secrets.js";
|
|
|
14
14
|
import { aliasExpansionFailure } from "./alias-limit.js";
|
|
15
15
|
import { isHostAllowed } from "./allowed-hosts.js";
|
|
16
16
|
import { pointMappingNamesAtSchemas } from "./mapping-schema-names.js";
|
|
17
|
+
import type { RedirectedUrls } from "./remote-fetch.js";
|
|
17
18
|
import { isParsedSource, RemoteNodeLimit } from "./remote-node-limit.js";
|
|
18
19
|
import { refForbiddenError } from "./schema-load-errors.js";
|
|
19
20
|
|
|
@@ -108,6 +109,8 @@ export interface RefAccessLimits {
|
|
|
108
109
|
readonly maxRemoteDocuments: number;
|
|
109
110
|
/** Nodes the remote documents of one load may parse into together (threat model T2). */
|
|
110
111
|
readonly maxRemoteNodes: number;
|
|
112
|
+
/** The final URLs of redirected documents, which their relative `$ref`s resolve against. */
|
|
113
|
+
readonly redirects: RedirectedUrls;
|
|
111
114
|
}
|
|
112
115
|
|
|
113
116
|
/**
|
|
@@ -207,7 +210,7 @@ export class RefAccessResolver extends BaseResolver {
|
|
|
207
210
|
throw refusal;
|
|
208
211
|
}
|
|
209
212
|
try {
|
|
210
|
-
return await super.loadExternalRef(absoluteRef);
|
|
213
|
+
return this.#atFinalUrl(await super.loadExternalRef(absoluteRef));
|
|
211
214
|
} catch (error) {
|
|
212
215
|
// A refused redirect throws inside the fetch, and Redocly wraps it in a ResolveError.
|
|
213
216
|
const cause = (error as { originalError?: unknown }).originalError;
|
|
@@ -218,6 +221,17 @@ export class RefAccessResolver extends BaseResolver {
|
|
|
218
221
|
}
|
|
219
222
|
}
|
|
220
223
|
|
|
224
|
+
/**
|
|
225
|
+
* A redirected document is named by the URL it came from, so its relative `$ref`s, its
|
|
226
|
+
* component names and its problems use that URL. Redocly still finds it by the requested one.
|
|
227
|
+
*/
|
|
228
|
+
#atFinalUrl(source: Source): Source {
|
|
229
|
+
const finalUrl = this.#limits.redirects.finalUrlOf(source.absoluteRef);
|
|
230
|
+
return finalUrl === source.absoluteRef
|
|
231
|
+
? source
|
|
232
|
+
: new Source(finalUrl, source.body, source.mimeType);
|
|
233
|
+
}
|
|
234
|
+
|
|
221
235
|
#accessRefusal(absoluteRef: string): Promise<OpenAPITypesafeError | undefined> {
|
|
222
236
|
return absoluteRef === this.#rootRef
|
|
223
237
|
? Promise.resolve(undefined)
|
|
@@ -109,6 +109,27 @@ function fetchWithBodyLimit(fetchImpl: RemoteFetch, bodyLimit: RemoteBodyLimit):
|
|
|
109
109
|
return async (input, init) => bufferedResponse(await fetchImpl(input, init), input, bodyLimit);
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
/**
|
|
113
|
+
* The URL each remote document was read from in the end, when redirects moved it. A relative
|
|
114
|
+
* `$ref` resolves against the URL the document was retrieved from (RFC 3986, section 5.1.3), so
|
|
115
|
+
* `/latest` that redirects to `/static/v1/root.yaml` loads `./pet.yaml` from `/static/v1/`.
|
|
116
|
+
* Redocly reads only the body of the response, so the fetch records the URL here.
|
|
117
|
+
*/
|
|
118
|
+
export class RedirectedUrls {
|
|
119
|
+
readonly #finalUrls = new Map<string, string>();
|
|
120
|
+
|
|
121
|
+
record(requested: string, final: string): void {
|
|
122
|
+
if (requested !== final) {
|
|
123
|
+
this.#finalUrls.set(requested, final);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** The URL the document of `requested` came from; `requested` when it was not redirected. */
|
|
128
|
+
finalUrlOf(requested: string): string {
|
|
129
|
+
return this.#finalUrls.get(requested) ?? requested;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
112
133
|
/** What every hop of a remote fetch is checked against. */
|
|
113
134
|
export interface RedirectPolicy {
|
|
114
135
|
/** `resolve.http.headers` of the Redocly config; chosen again for the URL of every hop. */
|
|
@@ -120,6 +141,8 @@ export interface RedirectPolicy {
|
|
|
120
141
|
* redirects are limited to the hosts (ADR-017): a spec URL that redirects to a CDN loads.
|
|
121
142
|
*/
|
|
122
143
|
readonly rootUrl: string | undefined;
|
|
144
|
+
/** Where the final URL of each redirected document is recorded. */
|
|
145
|
+
readonly redirects: RedirectedUrls;
|
|
123
146
|
}
|
|
124
147
|
|
|
125
148
|
/**
|
|
@@ -148,6 +171,7 @@ export function fetchWithCheckedRedirects(
|
|
|
148
171
|
const response = await fetchImpl(url.href, { ...init, headers, redirect: "manual" });
|
|
149
172
|
const location = response.headers.get("location");
|
|
150
173
|
if (!REDIRECT_STATUSES.has(response.status) || location === null) {
|
|
174
|
+
policy.redirects.record(input, url.href);
|
|
151
175
|
return response;
|
|
152
176
|
}
|
|
153
177
|
url = new URL(location, url);
|
|
@@ -200,6 +224,7 @@ export interface RemoteFetchOptions {
|
|
|
200
224
|
readonly rootUrl: string | undefined;
|
|
201
225
|
readonly maxParallelFetches: number;
|
|
202
226
|
readonly bodyLimits: RemoteBodyLimits;
|
|
227
|
+
readonly redirects: RedirectedUrls;
|
|
203
228
|
}
|
|
204
229
|
|
|
205
230
|
/**
|
|
@@ -225,6 +250,7 @@ export function resolveConfigWithAccess(
|
|
|
225
250
|
headers: resolve.http.headers,
|
|
226
251
|
hosts: options.access.remoteHosts,
|
|
227
252
|
rootUrl: options.rootUrl,
|
|
253
|
+
redirects: options.redirects,
|
|
228
254
|
});
|
|
229
255
|
// The limit is outside the redirects, so all hops of one document share one slot.
|
|
230
256
|
const customFetch = fetchWithConcurrencyLimit(checked, options.maxParallelFetches);
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
3
|
+
import { hasUrlScheme } from "../domain/location-text.js";
|
|
4
|
+
import type { GeneratorOptions } from "../types.js";
|
|
5
|
+
import { processCwd } from "./default-ports.js";
|
|
6
|
+
import { kindOf, optionInvalid } from "./option-errors.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The `cwd` option: the directory that relative `$ref`s of an inline source resolve against
|
|
10
|
+
* (ADR-023). A string is read as `localRefRoot` is: a URL when it has a scheme, else a path.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const FILE_PROTOCOL = "file:";
|
|
14
|
+
|
|
15
|
+
/** A `file:` cwd is a local directory; an `http:` or `https:` one holds remote documents. */
|
|
16
|
+
const CWD_PROTOCOLS: ReadonlySet<string> = new Set([FILE_PROTOCOL, "http:", "https:"]);
|
|
17
|
+
|
|
18
|
+
const CWD_EXPECTED = "a directory path, or a file:, http: or https: URL";
|
|
19
|
+
|
|
20
|
+
/** A path, resolved against the working directory, as a directory URL; `#` and `%` are encoded. */
|
|
21
|
+
function pathDirectoryUrl(directory: string): URL {
|
|
22
|
+
return pathToFileURL(path.join(path.resolve(directory), path.sep));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** The URL the value names, before its scheme is checked; undefined for another type. */
|
|
26
|
+
function cwdUrlOf(value: unknown): URL | undefined {
|
|
27
|
+
if (value === undefined) {
|
|
28
|
+
return pathDirectoryUrl(processCwd());
|
|
29
|
+
}
|
|
30
|
+
if (typeof value === "string") {
|
|
31
|
+
if (!hasUrlScheme(value)) {
|
|
32
|
+
return pathDirectoryUrl(value);
|
|
33
|
+
}
|
|
34
|
+
return URL.parse(value, pathDirectoryUrl(processCwd()).href) ?? undefined;
|
|
35
|
+
}
|
|
36
|
+
if (Buffer.isBuffer(value)) {
|
|
37
|
+
return pathDirectoryUrl(value.toString());
|
|
38
|
+
}
|
|
39
|
+
return value instanceof URL ? value : undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** A `file:` URL with a host, or with an encoded slash, names no path on this platform. */
|
|
43
|
+
function isLocalPath(url: URL): boolean {
|
|
44
|
+
try {
|
|
45
|
+
fileURLToPath(url);
|
|
46
|
+
return true;
|
|
47
|
+
} catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** What the cwd is instead of a directory, for the error. */
|
|
53
|
+
function cwdKindOf(value: unknown, url: URL | undefined): string {
|
|
54
|
+
if (url?.protocol === FILE_PROTOCOL) {
|
|
55
|
+
return "a file: URL that names no local path";
|
|
56
|
+
}
|
|
57
|
+
return url === undefined && typeof value === "string"
|
|
58
|
+
? "a string that is no valid URL"
|
|
59
|
+
: kindOf(url ?? value);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The cwd as a URL that ends in a slash. Fails with `OPTION_INVALID` for another type, a URL of
|
|
64
|
+
* another scheme or a `file:` URL that names no local path.
|
|
65
|
+
*/
|
|
66
|
+
export function sourceCwdOf(options: GeneratorOptions): URL {
|
|
67
|
+
const url = cwdUrlOf(options.cwd);
|
|
68
|
+
const isUsable =
|
|
69
|
+
url !== undefined &&
|
|
70
|
+
CWD_PROTOCOLS.has(url.protocol) &&
|
|
71
|
+
(url.protocol !== FILE_PROTOCOL || isLocalPath(url));
|
|
72
|
+
if (!isUsable) {
|
|
73
|
+
throw optionInvalid("cwd", CWD_EXPECTED, cwdKindOf(options.cwd, url));
|
|
74
|
+
}
|
|
75
|
+
return url.pathname.endsWith("/") ? url : new URL(`${url.pathname}/`, url);
|
|
76
|
+
}
|
package/src/api/default-ports.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { performance } from "node:perf_hooks";
|
|
2
2
|
import { createConfig, type Config as RedoclyConfig } from "@redocly/openapi-core";
|
|
3
|
-
import supportsColor from "supports-color";
|
|
3
|
+
import supportsColor, { type ColorInfo } from "supports-color";
|
|
4
4
|
import { createNodeDeprecationReporter } from "../adapters/node-deprecations/node-deprecations.js";
|
|
5
5
|
import type { RemoteDocumentLimits } from "../adapters/redocly-loader/remote-fetch.js";
|
|
6
6
|
import {
|
|
7
7
|
createTerminalLogger,
|
|
8
|
+
type TerminalLoggerOptions,
|
|
8
9
|
type TerminalSink,
|
|
9
10
|
} from "../adapters/terminal-logger/terminal-logger.js";
|
|
10
11
|
import type { DeprecationReporter } from "../application/ports/deprecation-reporter.js";
|
|
@@ -15,10 +16,24 @@ import type { Logger } from "../application/ports/logger.js";
|
|
|
15
16
|
* composition root are where the process environment is read.
|
|
16
17
|
*/
|
|
17
18
|
|
|
18
|
-
/**
|
|
19
|
-
export
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
/** The output streams of the process, which can each be a terminal or redirected. */
|
|
20
|
+
export type TerminalStream = "stdout" | "stderr";
|
|
21
|
+
|
|
22
|
+
/** The color support of each stream, as `supports-color` detects it. */
|
|
23
|
+
export type StreamColorSupport = Readonly<Record<TerminalStream, ColorInfo>>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Whether `stream` shows colors. Each stream is asked for itself: with stdout on a terminal and
|
|
27
|
+
* stderr redirected to a log, the log gets no escape sequences.
|
|
28
|
+
*/
|
|
29
|
+
export function streamSupportsColor(stream: TerminalStream, support: StreamColorSupport): boolean {
|
|
30
|
+
const info = support[stream];
|
|
31
|
+
return info !== false && info.hasBasic;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Colors when the terminal of `stream` supports them, as the original decided. */
|
|
35
|
+
export function terminalSupportsColor(stream: TerminalStream): boolean {
|
|
36
|
+
return streamSupportsColor(stream, supportsColor);
|
|
22
37
|
}
|
|
23
38
|
|
|
24
39
|
/**
|
|
@@ -34,14 +49,22 @@ const STDERR_SINK: TerminalSink = {
|
|
|
34
49
|
error: (line) => console.error(line),
|
|
35
50
|
};
|
|
36
51
|
|
|
37
|
-
/** The
|
|
38
|
-
export function
|
|
39
|
-
|
|
52
|
+
/** The options of the default logger: it writes to stderr, so the colors follow stderr. */
|
|
53
|
+
export function defaultLoggerOptions(
|
|
54
|
+
silent: boolean,
|
|
55
|
+
support: StreamColorSupport,
|
|
56
|
+
): TerminalLoggerOptions {
|
|
57
|
+
return {
|
|
40
58
|
silent,
|
|
41
59
|
debugFilter: process.env.DEBUG,
|
|
42
|
-
useColors:
|
|
60
|
+
useColors: streamSupportsColor("stderr", support),
|
|
43
61
|
sink: STDERR_SINK,
|
|
44
|
-
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** The terminal logger with `DEBUG` from the environment, writing to stderr. */
|
|
66
|
+
export function defaultLogger(silent: boolean): Logger {
|
|
67
|
+
return createTerminalLogger(defaultLoggerOptions(silent, supportsColor));
|
|
45
68
|
}
|
|
46
69
|
|
|
47
70
|
/** Deprecations as Node.js `DeprecationWarning`s, each code once per process. */
|
package/src/api/document-run.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { pathToFileURL } from "node:url";
|
|
3
1
|
import type { Config as RedoclyConfig } from "@redocly/openapi-core";
|
|
4
2
|
import { sourceEmptyError } from "../adapters/redocly-loader/schema-load-errors.js";
|
|
5
3
|
import type { DeprecationReporter } from "../application/ports/deprecation-reporter.js";
|
|
@@ -14,7 +12,7 @@ import {
|
|
|
14
12
|
} from "../domain/output-fixes.js";
|
|
15
13
|
import { WARNING_CODES, warningText } from "../domain/warnings.js";
|
|
16
14
|
import type { GeneratorOptions, OpenAPI3, SchemaSource } from "../types.js";
|
|
17
|
-
import {
|
|
15
|
+
import { sourceCwdOf } from "./cwd-option.js";
|
|
18
16
|
import { warnDiscriminatorValueConflicts } from "./discriminator-conflict-warnings.js";
|
|
19
17
|
import { findDiscriminators } from "./discriminator-scan.js";
|
|
20
18
|
import { resolveDocumentRef } from "./document-refs.js";
|
|
@@ -56,19 +54,6 @@ type ContextFlags = Omit<
|
|
|
56
54
|
| "fix"
|
|
57
55
|
>;
|
|
58
56
|
|
|
59
|
-
/**
|
|
60
|
-
* The directory that relative `$ref`s of an inline source resolve against, as a URL that ends in
|
|
61
|
-
* a slash. A path is resolved and encoded (`#`, `%`, spaces); a URL without the slash gets one.
|
|
62
|
-
*/
|
|
63
|
-
function sourceCwd(options: GeneratorOptions): URL {
|
|
64
|
-
const { cwd } = options;
|
|
65
|
-
if (cwd instanceof URL) {
|
|
66
|
-
return cwd.pathname.endsWith("/") ? cwd : new URL(`${cwd.pathname}/`, cwd);
|
|
67
|
-
}
|
|
68
|
-
const directory = path.resolve(cwd === undefined ? processCwd() : cwd.toString());
|
|
69
|
-
return pathToFileURL(path.join(directory, path.sep));
|
|
70
|
-
}
|
|
71
|
-
|
|
72
57
|
function callbackOptions(options: GeneratorOptions) {
|
|
73
58
|
return {
|
|
74
59
|
irCallbacks: options.irCallbacks === true,
|
|
@@ -146,7 +131,7 @@ export async function createDocumentRun(
|
|
|
146
131
|
const { logger } = ports;
|
|
147
132
|
const fix = outputFixesOf(options, logger);
|
|
148
133
|
const silent = options.silent ?? false;
|
|
149
|
-
const cwd =
|
|
134
|
+
const cwd = sourceCwdOf(options);
|
|
150
135
|
const { document: schema, localFiles } = await ports.loader.load({
|
|
151
136
|
source,
|
|
152
137
|
cwd,
|
package/src/api/legacy-utils.ts
CHANGED
|
@@ -15,7 +15,7 @@ import { type ResolveRefOptions, resolveDocumentRef, walkDocument } from "./docu
|
|
|
15
15
|
|
|
16
16
|
// The exported `c` is the shared ansi-colors instance, switched off at import when the terminal has
|
|
17
17
|
// no colors, as the original did. The generator's own logger uses a separate instance.
|
|
18
|
-
if (!terminalSupportsColor()) {
|
|
18
|
+
if (!terminalSupportsColor("stdout")) {
|
|
19
19
|
colors.enabled = false;
|
|
20
20
|
}
|
|
21
21
|
|