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
package/src/cli/cli-arguments.ts
CHANGED
|
@@ -2,51 +2,28 @@ import { kebabCase } from "scule";
|
|
|
2
2
|
import parser from "yargs-parser";
|
|
3
3
|
import type { OutputFixName } from "../domain/output-fixes.js";
|
|
4
4
|
import { cliError } from "./cli-errors.js";
|
|
5
|
+
import {
|
|
6
|
+
ALIASES,
|
|
7
|
+
BOOLEAN_FLAGS,
|
|
8
|
+
GENERATOR_FLAGS,
|
|
9
|
+
type GeneratorFlag,
|
|
10
|
+
type GeneratorFlags,
|
|
11
|
+
KNOWN_FLAGS,
|
|
12
|
+
STRING_FLAGS,
|
|
13
|
+
} from "./cli-flag-names.js";
|
|
14
|
+
import { MULTIPLE_SCHEMAS_DOCS_URL } from "./docs-links.js";
|
|
5
15
|
import { fixNamesOf } from "./fix-option.js";
|
|
16
|
+
import {
|
|
17
|
+
rejectInvalidBooleans,
|
|
18
|
+
rejectNegatedValueFlags,
|
|
19
|
+
rejectRemovedFlags,
|
|
20
|
+
unknownFlagNames,
|
|
21
|
+
writtenFlags,
|
|
22
|
+
} from "./written-flags.js";
|
|
6
23
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
/** Boolean flags that become generator options (camelCase; yargs also accepts kebab-case). */
|
|
10
|
-
export const GENERATOR_FLAGS = [
|
|
11
|
-
"additionalProperties",
|
|
12
|
-
"alphabetize",
|
|
13
|
-
"arrayLength",
|
|
14
|
-
"binaryAsBlob",
|
|
15
|
-
"conditionalEnums",
|
|
16
|
-
"dedupeEnums",
|
|
17
|
-
"defaultNonNullable",
|
|
18
|
-
"emptyObjectsUnknown",
|
|
19
|
-
"enum",
|
|
20
|
-
"enumValues",
|
|
21
|
-
"excludeDeprecated",
|
|
22
|
-
"exportType",
|
|
23
|
-
"generatePathParams",
|
|
24
|
-
"immutable",
|
|
25
|
-
"lint",
|
|
26
|
-
"makePathsEnum",
|
|
27
|
-
"pathParamsAsTypes",
|
|
28
|
-
"propertiesRequiredByDefault",
|
|
29
|
-
"readWriteMarkers",
|
|
30
|
-
"rootTypes",
|
|
31
|
-
"rootTypesKeepCasing",
|
|
32
|
-
"rootTypesNoSchemaPrefix",
|
|
33
|
-
] as const;
|
|
34
|
-
|
|
35
|
-
export type GeneratorFlag = (typeof GENERATOR_FLAGS)[number];
|
|
36
|
-
|
|
37
|
-
export type GeneratorFlags = Readonly<Partial<Record<GeneratorFlag, boolean>>>;
|
|
38
|
-
|
|
39
|
-
// `contentNever` was a boolean flag of the original CLI that no code reads; it stays in the list
|
|
40
|
-
// so that `--content-never <input>` keeps parsing as a flag and an input.
|
|
41
|
-
const CAMEL_CASE_BOOLEAN_FLAGS = [...GENERATOR_FLAGS, "check", "contentNever", "help"];
|
|
24
|
+
export { GENERATOR_FLAGS, type GeneratorFlag, type GeneratorFlags } from "./cli-flag-names.js";
|
|
42
25
|
|
|
43
|
-
|
|
44
|
-
// only, `--root-types schema.yaml` took the path as the flag's value (upstream #2523). Both
|
|
45
|
-
// spellings are listed.
|
|
46
|
-
const BOOLEAN_FLAGS = [
|
|
47
|
-
...CAMEL_CASE_BOOLEAN_FLAGS,
|
|
48
|
-
...CAMEL_CASE_BOOLEAN_FLAGS.map((flag) => kebabCase(flag)),
|
|
49
|
-
];
|
|
26
|
+
/** The CLI's arguments, parsed as the original CLI parsed them (yargs-parser). */
|
|
50
27
|
|
|
51
28
|
/** The `$ref` limits as the user wrote them; unset flags are absent. */
|
|
52
29
|
export interface CliRefLimits {
|
|
@@ -70,101 +47,9 @@ export interface CliArguments {
|
|
|
70
47
|
readonly unknownFlags: readonly string[];
|
|
71
48
|
}
|
|
72
49
|
|
|
73
|
-
/** Flags of older versions that were removed or renamed, with the message for each. */
|
|
74
|
-
const REMOVED_FLAGS: readonly (readonly [string, string])[] = [
|
|
75
|
-
["-ap", `The -ap alias has been deprecated. Use "--additional-properties" instead.`],
|
|
76
|
-
["--immutable-types", `The --immutable-types flag has been renamed to "--immutable".`],
|
|
77
|
-
[
|
|
78
|
-
"--support-array-length",
|
|
79
|
-
`The --support-array-length flag has been renamed to "--array-length".`,
|
|
80
|
-
],
|
|
81
|
-
["-it", `The -it alias has been deprecated. Use "--immutable" instead.`],
|
|
82
|
-
["--redoc", `The --redoc config flag has been renamed to "--redocly" (or -c as shorthand).`],
|
|
83
|
-
];
|
|
84
|
-
|
|
85
50
|
/** Flags that only work together with `--root-types`. */
|
|
86
51
|
const ROOT_TYPES_FLAGS = ["rootTypesNoSchemaPrefix", "rootTypesKeepCasing"] as const;
|
|
87
52
|
|
|
88
|
-
const OPTIONS_END = "--";
|
|
89
|
-
const LONG_PREFIX = "--";
|
|
90
|
-
const NEGATION_PREFIX = "--no-";
|
|
91
|
-
const VALUE_SEPARATOR = "=";
|
|
92
|
-
const BOOLEAN_VALUES: ReadonlySet<string> = new Set(["true", "false"]);
|
|
93
|
-
|
|
94
|
-
/** A flag as written before `--`: its name (`--redoc`, `-o`) and the text after `=`, if any. */
|
|
95
|
-
interface WrittenFlag {
|
|
96
|
-
readonly name: string;
|
|
97
|
-
readonly value: string | undefined;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function writtenFlags(argv: readonly string[]): WrittenFlag[] {
|
|
101
|
-
const end = argv.indexOf(OPTIONS_END);
|
|
102
|
-
return (end === -1 ? argv : argv.slice(0, end))
|
|
103
|
-
.filter((arg) => arg.startsWith("-") && arg !== "-")
|
|
104
|
-
.map((arg) => {
|
|
105
|
-
const at = arg.indexOf(VALUE_SEPARATOR);
|
|
106
|
-
return at === -1
|
|
107
|
-
? { name: arg, value: undefined }
|
|
108
|
-
: { name: arg.slice(0, at), value: arg.slice(at + 1) };
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* A long flag as yargs-parser reads it: in kebab-case and without `--no-`, so `--immutableTypes`
|
|
114
|
-
* and `--no-immutable-types` are `--immutable-types`. A short flag stays as written.
|
|
115
|
-
*/
|
|
116
|
-
function canonicalFlagName(name: string): string {
|
|
117
|
-
if (!name.startsWith(LONG_PREFIX)) {
|
|
118
|
-
return name;
|
|
119
|
-
}
|
|
120
|
-
const bare = name.startsWith(NEGATION_PREFIX)
|
|
121
|
-
? name.slice(NEGATION_PREFIX.length)
|
|
122
|
-
: name.slice(LONG_PREFIX.length);
|
|
123
|
-
return `${LONG_PREFIX}${kebabCase(bare)}`;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/** An old flag fails by its name in any spelling, also with a value (`--redoc=redocly.yaml`). */
|
|
127
|
-
function rejectRemovedFlags(flags: readonly WrittenFlag[]): void {
|
|
128
|
-
const names = new Set(flags.map(({ name }) => canonicalFlagName(name)));
|
|
129
|
-
for (const [flag, message] of REMOVED_FLAGS) {
|
|
130
|
-
if (names.has(flag)) {
|
|
131
|
-
throw cliError("CLI_FLAG_REMOVED", message);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const ALIASES = { redocly: ["c"], exportType: ["t"], output: ["o"] };
|
|
137
|
-
|
|
138
|
-
/** The short aliases of boolean flags (`-t`), which take a value the same way (`-t=true`). */
|
|
139
|
-
const BOOLEAN_ALIASES = Object.entries(ALIASES)
|
|
140
|
-
.filter(([flag]) => CAMEL_CASE_BOOLEAN_FLAGS.includes(flag))
|
|
141
|
-
.flatMap(([, aliases]) => aliases.map((alias) => `-${alias}`));
|
|
142
|
-
|
|
143
|
-
const BOOLEAN_FLAG_NAMES: ReadonlySet<string> = new Set([
|
|
144
|
-
...BOOLEAN_FLAGS.map((flag) => `${LONG_PREFIX}${flag}`),
|
|
145
|
-
...BOOLEAN_ALIASES,
|
|
146
|
-
]);
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* A boolean flag takes `=true` or `=false`, and its `--no-` form takes no value. yargs-parser read
|
|
150
|
-
* any other value as false (`--enum=1`, `--alphabetize=yes`), which turned the option off.
|
|
151
|
-
*/
|
|
152
|
-
function rejectInvalidBooleans(flags: readonly WrittenFlag[]): void {
|
|
153
|
-
for (const { name, value } of flags) {
|
|
154
|
-
const negated = name.startsWith(NEGATION_PREFIX)
|
|
155
|
-
? `${LONG_PREFIX}${name.slice(NEGATION_PREFIX.length)}`
|
|
156
|
-
: undefined;
|
|
157
|
-
const isNegatedWithValue = negated !== undefined && BOOLEAN_FLAG_NAMES.has(negated);
|
|
158
|
-
const isInvalid = BOOLEAN_FLAG_NAMES.has(name) && !BOOLEAN_VALUES.has(value ?? "true");
|
|
159
|
-
if (value !== undefined && (isNegatedWithValue || isInvalid)) {
|
|
160
|
-
throw cliError(
|
|
161
|
-
"CLI_FLAG_INVALID",
|
|
162
|
-
`${name}=${value}: a boolean flag takes no value, or =true or =false.`,
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
53
|
/** Notices for flags that have no effect in this combination, printed before the run. */
|
|
169
54
|
export function ineffectiveFlagNotices(flags: GeneratorFlags): string[] {
|
|
170
55
|
if (flags.rootTypes === true) {
|
|
@@ -221,22 +106,6 @@ function refLimitsOf(parsed: parser.Arguments): CliRefLimits {
|
|
|
221
106
|
};
|
|
222
107
|
}
|
|
223
108
|
|
|
224
|
-
/** String flags, in both spellings, so that a value such as `8080` is not read as a number. */
|
|
225
|
-
const STRING_FLAGS = [
|
|
226
|
-
"output",
|
|
227
|
-
"redocly",
|
|
228
|
-
"fix",
|
|
229
|
-
...["localRefRoot", "remoteRefHosts"].flatMap((flag) => [flag, kebabCase(flag)]),
|
|
230
|
-
];
|
|
231
|
-
|
|
232
|
-
/** Every name yargs-parser can return for a flag this CLI reads. */
|
|
233
|
-
const KNOWN_FLAGS = new Set<string>([
|
|
234
|
-
...BOOLEAN_FLAGS,
|
|
235
|
-
...STRING_FLAGS,
|
|
236
|
-
...Object.values(ALIASES).flat(),
|
|
237
|
-
"version",
|
|
238
|
-
]);
|
|
239
|
-
|
|
240
109
|
/**
|
|
241
110
|
* Flags that no code reads. The original CLI ignored them silently, so a typo such as
|
|
242
111
|
* `--alphabetise` gave other output with no hint; they still do not fail the run.
|
|
@@ -258,22 +127,42 @@ function writtenFlagName(key: string, argv: readonly string[]): string {
|
|
|
258
127
|
return isNegated ? negated : `--${kebabCase(key)}`;
|
|
259
128
|
}
|
|
260
129
|
|
|
130
|
+
/**
|
|
131
|
+
* The one schema argument. More than one fails with `CLI_INPUT_EXTRA`: the original CLI read the
|
|
132
|
+
* first and dropped the rest, so a glob the shell expanded (`g/*.yaml`) generated one schema.
|
|
133
|
+
*/
|
|
134
|
+
function inputOf(positionals: readonly (string | number)[]): string | undefined {
|
|
135
|
+
if (positionals.length > 1) {
|
|
136
|
+
throw cliError(
|
|
137
|
+
"CLI_INPUT_EXTRA",
|
|
138
|
+
`Only one schema argument is allowed, but ${positionals.length} were given: ` +
|
|
139
|
+
`${positionals.join(" ")}. A shell expands an unquoted *; to generate several ` +
|
|
140
|
+
`schemas, list them under \`apis\` in redocly.yaml. See ${MULTIPLE_SCHEMAS_DOCS_URL}`,
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
return optionalString(positionals[0]);
|
|
144
|
+
}
|
|
145
|
+
|
|
261
146
|
/**
|
|
262
147
|
* Parse the arguments after `node cli.js`. Fails with `CLI_FLAG_REMOVED` for an old flag, with
|
|
263
|
-
* `CLI_FLAG_INVALID` for a boolean flag with another value than true or false
|
|
148
|
+
* `CLI_FLAG_INVALID` for a boolean flag with another value than true or false or a `--no-` form
|
|
149
|
+
* of a flag that takes a value, with `CLI_INPUT_EXTRA` for a second schema argument, and with
|
|
264
150
|
* `OPTION_FIX_UNKNOWN` for an unknown `--fix` name.
|
|
265
151
|
*/
|
|
266
152
|
export function parseCliArguments(argv: readonly string[]): CliArguments {
|
|
267
153
|
const flags = writtenFlags(argv);
|
|
268
154
|
rejectRemovedFlags(flags);
|
|
269
155
|
rejectInvalidBooleans(flags);
|
|
156
|
+
rejectNegatedValueFlags(flags);
|
|
270
157
|
const parsed = parser([...argv], {
|
|
271
|
-
boolean: BOOLEAN_FLAGS,
|
|
158
|
+
boolean: [...BOOLEAN_FLAGS, ...unknownFlagNames(flags)],
|
|
272
159
|
string: STRING_FLAGS,
|
|
273
160
|
alias: ALIASES,
|
|
161
|
+
// A schema path is a file name: `1.10` is not the number 1.1.
|
|
162
|
+
configuration: { "parse-positional-numbers": false },
|
|
274
163
|
});
|
|
275
164
|
return {
|
|
276
|
-
input:
|
|
165
|
+
input: inputOf(parsed._),
|
|
277
166
|
output: lastString(parsed.output),
|
|
278
167
|
redocly: lastString(parsed.redocly),
|
|
279
168
|
isCheck: parsed.check === true,
|
package/src/cli/cli-errors.ts
CHANGED
|
@@ -109,6 +109,7 @@ const CLI_ERROR_REPORTS: Readonly<Record<ErrorCode, CliErrorReport>> = {
|
|
|
109
109
|
CLI_CONFIG_OUTPUT_MISSING: { exitCode: EXIT_CODES.USAGE },
|
|
110
110
|
CLI_CHECK_OUTPUT_MISSING: { exitCode: EXIT_CODES.USAGE },
|
|
111
111
|
CLI_INPUT_MISSING: { exitCode: EXIT_CODES.USAGE },
|
|
112
|
+
CLI_INPUT_EXTRA: { exitCode: EXIT_CODES.USAGE },
|
|
112
113
|
CLI_OUTPUT_IS_INPUT: { exitCode: EXIT_CODES.USAGE },
|
|
113
114
|
CLI_OUTPUT_DUPLICATE: { exitCode: EXIT_CODES.USAGE },
|
|
114
115
|
CLI_OUTPUT_INVALID: { exitCode: EXIT_CODES.USAGE },
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { kebabCase } from "scule";
|
|
2
|
+
|
|
3
|
+
/** The names of the CLI's flags, in the spellings yargs-parser is told about. */
|
|
4
|
+
|
|
5
|
+
/** Boolean flags that become generator options (camelCase; yargs also accepts kebab-case). */
|
|
6
|
+
export const GENERATOR_FLAGS = [
|
|
7
|
+
"additionalProperties",
|
|
8
|
+
"alphabetize",
|
|
9
|
+
"arrayLength",
|
|
10
|
+
"binaryAsBlob",
|
|
11
|
+
"conditionalEnums",
|
|
12
|
+
"dedupeEnums",
|
|
13
|
+
"defaultNonNullable",
|
|
14
|
+
"emptyObjectsUnknown",
|
|
15
|
+
"enum",
|
|
16
|
+
"enumValues",
|
|
17
|
+
"excludeDeprecated",
|
|
18
|
+
"exportType",
|
|
19
|
+
"generatePathParams",
|
|
20
|
+
"immutable",
|
|
21
|
+
"lint",
|
|
22
|
+
"makePathsEnum",
|
|
23
|
+
"pathParamsAsTypes",
|
|
24
|
+
"propertiesRequiredByDefault",
|
|
25
|
+
"readWriteMarkers",
|
|
26
|
+
"rootTypes",
|
|
27
|
+
"rootTypesKeepCasing",
|
|
28
|
+
"rootTypesNoSchemaPrefix",
|
|
29
|
+
] as const;
|
|
30
|
+
|
|
31
|
+
export type GeneratorFlag = (typeof GENERATOR_FLAGS)[number];
|
|
32
|
+
|
|
33
|
+
export type GeneratorFlags = Readonly<Partial<Record<GeneratorFlag, boolean>>>;
|
|
34
|
+
|
|
35
|
+
// `contentNever` was a boolean flag of the original CLI that no code reads; it stays in the list
|
|
36
|
+
// so that `--content-never <input>` keeps parsing as a flag and an input.
|
|
37
|
+
const CAMEL_CASE_BOOLEAN_FLAGS = [...GENERATOR_FLAGS, "check", "contentNever", "help"];
|
|
38
|
+
|
|
39
|
+
// yargs-parser matches the `boolean` list by the exact spelling of the flag: with camelCase names
|
|
40
|
+
// only, `--root-types schema.yaml` took the path as the flag's value (upstream #2523). Both
|
|
41
|
+
// spellings are listed.
|
|
42
|
+
export const BOOLEAN_FLAGS = [
|
|
43
|
+
...CAMEL_CASE_BOOLEAN_FLAGS,
|
|
44
|
+
...CAMEL_CASE_BOOLEAN_FLAGS.map((flag) => kebabCase(flag)),
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
export const ALIASES = { redocly: ["c"], exportType: ["t"], output: ["o"] };
|
|
48
|
+
|
|
49
|
+
const CAMEL_CASE_STRING_FLAGS = ["output", "redocly", "fix", "localRefRoot", "remoteRefHosts"];
|
|
50
|
+
|
|
51
|
+
/** String flags, in both spellings, so that a value such as `8080` is not read as a number. */
|
|
52
|
+
export const STRING_FLAGS = [
|
|
53
|
+
...CAMEL_CASE_STRING_FLAGS,
|
|
54
|
+
...CAMEL_CASE_STRING_FLAGS.map((flag) => kebabCase(flag)),
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
/** Every name yargs-parser can return for a flag this CLI reads. */
|
|
58
|
+
export const KNOWN_FLAGS: ReadonlySet<string> = new Set<string>([
|
|
59
|
+
...BOOLEAN_FLAGS,
|
|
60
|
+
...STRING_FLAGS,
|
|
61
|
+
...Object.values(ALIASES).flat(),
|
|
62
|
+
"version",
|
|
63
|
+
]);
|
|
64
|
+
|
|
65
|
+
export const LONG_PREFIX = "--";
|
|
66
|
+
export const SHORT_PREFIX = "-";
|
|
67
|
+
export const NEGATION_PREFIX = "--no-";
|
|
68
|
+
|
|
69
|
+
/** The short aliases of `flags`, as written on the command line (`-t`). */
|
|
70
|
+
function aliasesOf(flags: readonly string[]): string[] {
|
|
71
|
+
return Object.entries(ALIASES)
|
|
72
|
+
.filter(([flag]) => flags.includes(flag))
|
|
73
|
+
.flatMap(([, aliases]) => aliases.map((alias) => `${SHORT_PREFIX}${alias}`));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** The boolean flags as written, `--` and `-` included; they take `=true` or `=false`. */
|
|
77
|
+
export const BOOLEAN_FLAG_NAMES: ReadonlySet<string> = new Set([
|
|
78
|
+
...BOOLEAN_FLAGS.map((flag) => `${LONG_PREFIX}${flag}`),
|
|
79
|
+
...aliasesOf(CAMEL_CASE_BOOLEAN_FLAGS),
|
|
80
|
+
]);
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The flags that take a value, as `canonicalFlagName` gives them: kebab-case, and the short
|
|
84
|
+
* aliases with `--`, since yargs-parser reads `--no-o` as the negation of `-o`.
|
|
85
|
+
*/
|
|
86
|
+
export const VALUE_FLAG_NAMES: ReadonlySet<string> = new Set([
|
|
87
|
+
...CAMEL_CASE_STRING_FLAGS.map((flag) => `${LONG_PREFIX}${kebabCase(flag)}`),
|
|
88
|
+
...aliasesOf(CAMEL_CASE_STRING_FLAGS).map((alias) => `${SHORT_PREFIX}${alias}`),
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
/** The long flags this CLI reads, as `canonicalFlagName` gives them. */
|
|
92
|
+
export const KNOWN_LONG_FLAG_NAMES: ReadonlySet<string> = new Set(
|
|
93
|
+
[...KNOWN_FLAGS]
|
|
94
|
+
.filter((flag) => flag.length > 1)
|
|
95
|
+
.map((flag) => `${LONG_PREFIX}${kebabCase(flag)}`),
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
/** The short flags this CLI reads, without the `-`. */
|
|
99
|
+
export const KNOWN_SHORT_FLAGS: ReadonlySet<string> = new Set(Object.values(ALIASES).flat());
|
package/src/cli/cli-output.ts
CHANGED
|
@@ -187,12 +187,15 @@ export function ensureOutputIsOwn(
|
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
-
/**
|
|
190
|
+
/**
|
|
191
|
+
* `CLI_OUTPUT_DUPLICATE` for `output`, which is also the output of the `others` (named). Only
|
|
192
|
+
* APIs share outputs, and the CLI names the API before the message (`ApiFailure`).
|
|
193
|
+
*/
|
|
191
194
|
export function duplicateOutputError(output: NamedOutput, others: readonly string[]): Error {
|
|
192
195
|
return cliError(
|
|
193
196
|
"CLI_OUTPUT_DUPLICATE",
|
|
194
|
-
`The output ${output.output}
|
|
195
|
-
|
|
197
|
+
`The output ${output.output} is also the output of ${others.join(", ")}: they would ` +
|
|
198
|
+
"overwrite each other. Give each API its own output file.",
|
|
196
199
|
);
|
|
197
200
|
}
|
|
198
201
|
|
|
@@ -216,13 +219,24 @@ function printDone(run: CliRun, output: OutputFile): void {
|
|
|
216
219
|
run.deps.console.info(`🚀 ${target} ${duration}`);
|
|
217
220
|
}
|
|
218
221
|
|
|
222
|
+
const CRLF = /\r\n/g;
|
|
223
|
+
const LF = "\n";
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* The text with LF line ends. Git with `core.autocrlf` checks a committed file out with CRLF on
|
|
227
|
+
* Windows, so `--check` compares lines, not line ends.
|
|
228
|
+
*/
|
|
229
|
+
function withLfLineEnds(text: string): string {
|
|
230
|
+
return text.replace(CRLF, LF);
|
|
231
|
+
}
|
|
232
|
+
|
|
219
233
|
/**
|
|
220
|
-
* With `--check`, fail with `OUTPUT_STALE` when the file differs from `contents`
|
|
221
|
-
* alone otherwise. Without it, write the file and print where it went.
|
|
234
|
+
* With `--check`, fail with `OUTPUT_STALE` when the file differs from `contents` (line ends
|
|
235
|
+
* aside) and leave it alone otherwise. Without it, write the file and print where it went.
|
|
222
236
|
*/
|
|
223
237
|
export function emitOutput(run: CliRun, output: OutputFile, contents: string): void {
|
|
224
238
|
if (run.args.isCheck) {
|
|
225
|
-
if (run.deps.files.readText(output.file) !== contents) {
|
|
239
|
+
if (withLfLineEnds(run.deps.files.readText(output.file)) !== withLfLineEnds(contents)) {
|
|
226
240
|
// The original message, then the file: with several APIs it tells which one is stale.
|
|
227
241
|
throw cliError(
|
|
228
242
|
"OUTPUT_STALE",
|
package/src/cli/main.ts
CHANGED
|
@@ -27,9 +27,10 @@ function readVersion(): string {
|
|
|
27
27
|
return packageJson.version;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
/** The style of the CLI's own lines, which go to stdout. */
|
|
30
31
|
function terminalStyle(): TextStyle {
|
|
31
32
|
const palette = colors.create();
|
|
32
|
-
palette.enabled = terminalSupportsColor();
|
|
33
|
+
palette.enabled = terminalSupportsColor("stdout");
|
|
33
34
|
return {
|
|
34
35
|
bold: (text) => palette.bold(text),
|
|
35
36
|
green: (text) => palette.green(text),
|
package/src/cli/redocly-apis.ts
CHANGED
|
@@ -104,7 +104,8 @@ interface ApiRun {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
/**
|
|
107
|
-
* The generator options of one API: its own settings win over the CLI's flags
|
|
107
|
+
* The generator options of one API: its own settings win over the CLI's flags, and its own lint
|
|
108
|
+
* rules in `redocly.yaml` over the root's (upstream used the root's only). A flag that has
|
|
108
109
|
* no effect with the merged flags is named for this API, since its settings may turn on the flag
|
|
109
110
|
* that the command line lacks, or set one that has no effect themselves.
|
|
110
111
|
*/
|
|
@@ -119,7 +120,7 @@ function apiOptions(run: CliRun, apiRun: ApiRun, target: ApiTarget): GeneratorOp
|
|
|
119
120
|
...apiRun.refLimits,
|
|
120
121
|
...apiRefLimitOptions(name, settings, configRoot),
|
|
121
122
|
...fixOption(apiFixNames(name, settings) ?? run.args.fix),
|
|
122
|
-
redocly: apiRun.redocly,
|
|
123
|
+
redocly: run.deps.redoclyConfigs.forApi(apiRun.redocly, name),
|
|
123
124
|
};
|
|
124
125
|
}
|
|
125
126
|
|
|
@@ -146,9 +147,12 @@ async function generateApi(run: CliRun, apiRun: ApiRun, target: ApiTarget): Prom
|
|
|
146
147
|
run.deps.logger.warn(warning);
|
|
147
148
|
}
|
|
148
149
|
const output = apiOutputOf(target.name, target.settings);
|
|
149
|
-
const outputFile =
|
|
150
|
-
|
|
151
|
-
|
|
150
|
+
const outputFile = failingAsApi(target.name, () => {
|
|
151
|
+
const file = outputFileOf(run, { source: target.name, output }, target.configRoot);
|
|
152
|
+
ensureOutputIsNotSchema(run, file, files.schemas);
|
|
153
|
+
ensureOutputIsOwn(run, file, otherOutputs(files, target.name));
|
|
154
|
+
return file;
|
|
155
|
+
});
|
|
152
156
|
const source = resolveLocationUrl(target.root, target.configRoot);
|
|
153
157
|
const options = apiOptions(run, apiRun, target);
|
|
154
158
|
try {
|
|
@@ -159,6 +163,18 @@ async function generateApi(run: CliRun, apiRun: ApiRun, target: ApiTarget): Prom
|
|
|
159
163
|
}
|
|
160
164
|
}
|
|
161
165
|
|
|
166
|
+
/**
|
|
167
|
+
* The result of a step of one API, whose failure is reported with the API's name. Errors of an
|
|
168
|
+
* API's settings name the API in their own message and are not wrapped.
|
|
169
|
+
*/
|
|
170
|
+
function failingAsApi<T>(apiName: string, step: () => T): T {
|
|
171
|
+
try {
|
|
172
|
+
return step();
|
|
173
|
+
} catch (error) {
|
|
174
|
+
throw new ApiFailure(apiName, error);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
162
178
|
/**
|
|
163
179
|
* The schema and the output of every API, and the config file itself. A location that is no
|
|
164
180
|
* text or no valid URL is left out: it is no file that an output could be, and its own API fails
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { kebabCase } from "scule";
|
|
2
|
+
import { cliError } from "./cli-errors.js";
|
|
3
|
+
import {
|
|
4
|
+
BOOLEAN_FLAG_NAMES,
|
|
5
|
+
KNOWN_LONG_FLAG_NAMES,
|
|
6
|
+
KNOWN_SHORT_FLAGS,
|
|
7
|
+
LONG_PREFIX,
|
|
8
|
+
NEGATION_PREFIX,
|
|
9
|
+
SHORT_PREFIX,
|
|
10
|
+
VALUE_FLAG_NAMES,
|
|
11
|
+
} from "./cli-flag-names.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Checks of the flags as the user wrote them, before yargs-parser reads them: yargs-parser accepts
|
|
15
|
+
* any spelling and value, so what it cannot tell apart is checked here.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/** Flags of older versions that were removed or renamed, with the message for each. */
|
|
19
|
+
const REMOVED_FLAGS: readonly (readonly [string, string])[] = [
|
|
20
|
+
["-ap", `The -ap alias has been deprecated. Use "--additional-properties" instead.`],
|
|
21
|
+
["--immutable-types", `The --immutable-types flag has been renamed to "--immutable".`],
|
|
22
|
+
[
|
|
23
|
+
"--support-array-length",
|
|
24
|
+
`The --support-array-length flag has been renamed to "--array-length".`,
|
|
25
|
+
],
|
|
26
|
+
["-it", `The -it alias has been deprecated. Use "--immutable" instead.`],
|
|
27
|
+
["--redoc", `The --redoc config flag has been renamed to "--redocly" (or -c as shorthand).`],
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
const OPTIONS_END = "--";
|
|
31
|
+
const VALUE_SEPARATOR = "=";
|
|
32
|
+
const BOOLEAN_VALUES: ReadonlySet<string> = new Set(["true", "false"]);
|
|
33
|
+
const LETTER = /^[a-z]$/i;
|
|
34
|
+
|
|
35
|
+
/** A flag as written before `--`: its name (`--redoc`, `-o`) and the text after `=`, if any. */
|
|
36
|
+
export interface WrittenFlag {
|
|
37
|
+
readonly name: string;
|
|
38
|
+
readonly value: string | undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function writtenFlags(argv: readonly string[]): WrittenFlag[] {
|
|
42
|
+
const end = argv.indexOf(OPTIONS_END);
|
|
43
|
+
return (end === -1 ? argv : argv.slice(0, end))
|
|
44
|
+
.filter((arg) => arg.startsWith(SHORT_PREFIX) && arg !== SHORT_PREFIX)
|
|
45
|
+
.map((arg) => {
|
|
46
|
+
const at = arg.indexOf(VALUE_SEPARATOR);
|
|
47
|
+
return at === -1
|
|
48
|
+
? { name: arg, value: undefined }
|
|
49
|
+
: { name: arg.slice(0, at), value: arg.slice(at + 1) };
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* A long flag as yargs-parser reads it: in kebab-case and without `--no-`, so `--immutableTypes`
|
|
55
|
+
* and `--no-immutable-types` are `--immutable-types`. A short flag stays as written.
|
|
56
|
+
*/
|
|
57
|
+
function canonicalFlagName(name: string): string {
|
|
58
|
+
if (!name.startsWith(LONG_PREFIX)) {
|
|
59
|
+
return name;
|
|
60
|
+
}
|
|
61
|
+
const bare = name.startsWith(NEGATION_PREFIX)
|
|
62
|
+
? name.slice(NEGATION_PREFIX.length)
|
|
63
|
+
: name.slice(LONG_PREFIX.length);
|
|
64
|
+
return `${LONG_PREFIX}${kebabCase(bare)}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** An old flag fails by its name in any spelling, also with a value (`--redoc=redocly.yaml`). */
|
|
68
|
+
export function rejectRemovedFlags(flags: readonly WrittenFlag[]): void {
|
|
69
|
+
const names = new Set(flags.map(({ name }) => canonicalFlagName(name)));
|
|
70
|
+
for (const [flag, message] of REMOVED_FLAGS) {
|
|
71
|
+
if (names.has(flag)) {
|
|
72
|
+
throw cliError("CLI_FLAG_REMOVED", message);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* A boolean flag takes `=true` or `=false`, and its `--no-` form takes no value. yargs-parser read
|
|
79
|
+
* any other value as false (`--enum=1`, `--alphabetize=yes`), which turned the option off.
|
|
80
|
+
*/
|
|
81
|
+
export function rejectInvalidBooleans(flags: readonly WrittenFlag[]): void {
|
|
82
|
+
for (const { name, value } of flags) {
|
|
83
|
+
const negated = name.startsWith(NEGATION_PREFIX)
|
|
84
|
+
? `${LONG_PREFIX}${name.slice(NEGATION_PREFIX.length)}`
|
|
85
|
+
: undefined;
|
|
86
|
+
const isNegatedWithValue = negated !== undefined && BOOLEAN_FLAG_NAMES.has(negated);
|
|
87
|
+
const isInvalid = BOOLEAN_FLAG_NAMES.has(name) && !BOOLEAN_VALUES.has(value ?? "true");
|
|
88
|
+
if (value !== undefined && (isNegatedWithValue || isInvalid)) {
|
|
89
|
+
throw cliError(
|
|
90
|
+
"CLI_FLAG_INVALID",
|
|
91
|
+
`${name}=${value}: a boolean flag takes no value, or =true or =false.`,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* A flag that takes a value has no `--no-` form: yargs-parser gave it the value false, which the
|
|
99
|
+
* CLI read as the text "false" (`--no-output` wrote a file named `false`).
|
|
100
|
+
*/
|
|
101
|
+
export function rejectNegatedValueFlags(flags: readonly WrittenFlag[]): void {
|
|
102
|
+
for (const { name } of flags) {
|
|
103
|
+
if (name.startsWith(NEGATION_PREFIX) && VALUE_FLAG_NAMES.has(canonicalFlagName(name))) {
|
|
104
|
+
throw cliError(
|
|
105
|
+
"CLI_FLAG_INVALID",
|
|
106
|
+
`${name}: a flag that takes a value has no --no- form. Leave the flag out instead.`,
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** The bare names of the unknown flags in a written long flag or a group of short flags. */
|
|
113
|
+
function unknownNamesOf({ name }: WrittenFlag): string[] {
|
|
114
|
+
if (name.startsWith(LONG_PREFIX)) {
|
|
115
|
+
return KNOWN_LONG_FLAG_NAMES.has(canonicalFlagName(name))
|
|
116
|
+
? []
|
|
117
|
+
: [name.slice(LONG_PREFIX.length)];
|
|
118
|
+
}
|
|
119
|
+
return [...name.slice(SHORT_PREFIX.length)].filter(
|
|
120
|
+
(letter) => LETTER.test(letter) && !KNOWN_SHORT_FLAGS.has(letter),
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* The unknown flags, to declare as boolean: yargs-parser gives an undeclared flag the next
|
|
126
|
+
* argument as its value, so `--alphabetise api.yaml` took the schema path and read stdin. An
|
|
127
|
+
* unknown flag is ignored, so it takes a value only as `--flag=value`.
|
|
128
|
+
*/
|
|
129
|
+
export function unknownFlagNames(flags: readonly WrittenFlag[]): string[] {
|
|
130
|
+
return [...new Set(flags.flatMap(unknownNamesOf))];
|
|
131
|
+
}
|
package/src/domain/errors.ts
CHANGED
|
@@ -38,6 +38,7 @@ export const ERROR_CODES = {
|
|
|
38
38
|
CLI_CONFIG_OUTPUT_MISSING: "CLI_CONFIG_OUTPUT_MISSING",
|
|
39
39
|
CLI_CHECK_OUTPUT_MISSING: "CLI_CHECK_OUTPUT_MISSING",
|
|
40
40
|
CLI_INPUT_MISSING: "CLI_INPUT_MISSING",
|
|
41
|
+
CLI_INPUT_EXTRA: "CLI_INPUT_EXTRA",
|
|
41
42
|
CLI_OUTPUT_IS_INPUT: "CLI_OUTPUT_IS_INPUT",
|
|
42
43
|
CLI_OUTPUT_DUPLICATE: "CLI_OUTPUT_DUPLICATE",
|
|
43
44
|
CLI_OUTPUT_INVALID: "CLI_OUTPUT_INVALID",
|
|
@@ -6,7 +6,22 @@
|
|
|
6
6
|
*/
|
|
7
7
|
const URL_SCHEME = /^[a-z][a-z\d+.-]+:/i;
|
|
8
8
|
|
|
9
|
-
/**
|
|
9
|
+
/** The schemes the generator reads, which mark a URL also without `//` (`file:api.yaml`). */
|
|
10
|
+
const READ_SCHEMES: ReadonlySet<string> = new Set(["file:", "http:", "https:"]);
|
|
11
|
+
|
|
12
|
+
const AUTHORITY_START = "//";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Whether the location is written as a URL, not as a file path: a scheme the generator reads, or
|
|
16
|
+
* any scheme followed by `//` (`s3://bucket/api.yaml`). Another text before a colon is part of a
|
|
17
|
+
* file name, as it is on POSIX file systems (`out:v2.ts`, `spec:v2.yaml`).
|
|
18
|
+
*/
|
|
10
19
|
export function hasUrlScheme(location: string): boolean {
|
|
11
|
-
|
|
20
|
+
const scheme = URL_SCHEME.exec(location)?.[0];
|
|
21
|
+
if (scheme === undefined) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
return (
|
|
25
|
+
READ_SCHEMES.has(scheme.toLowerCase()) || location.startsWith(AUTHORITY_START, scheme.length)
|
|
26
|
+
);
|
|
12
27
|
}
|