@redocly/cli 1.5.0 → 1.7.0
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 +24 -0
- package/README.md +5 -5
- package/lib/__mocks__/utils.d.ts +1 -0
- package/lib/__mocks__/utils.js +2 -1
- package/lib/__tests__/utils.test.js +18 -0
- package/lib/commands/bundle.js +9 -15
- package/lib/commands/join.d.ts +0 -1
- package/lib/commands/join.js +1 -0
- package/lib/commands/lint.d.ts +3 -2
- package/lib/commands/lint.js +3 -5
- package/lib/commands/preview-project/constants.d.ts +14 -0
- package/lib/commands/preview-project/constants.js +22 -0
- package/lib/commands/preview-project/index.d.ts +2 -0
- package/lib/commands/preview-project/index.js +58 -0
- package/lib/commands/preview-project/types.d.ts +10 -0
- package/lib/commands/preview-project/types.js +2 -0
- package/lib/commands/split/index.js +3 -3
- package/lib/index.js +39 -8
- package/lib/types.d.ts +2 -1
- package/lib/utils.d.ts +4 -2
- package/lib/utils.js +28 -2
- package/package.json +2 -2
- package/src/__mocks__/utils.ts +1 -0
- package/src/__tests__/utils.test.ts +18 -0
- package/src/commands/bundle.ts +9 -21
- package/src/commands/join.ts +3 -1
- package/src/commands/lint.ts +7 -10
- package/src/commands/preview-project/constants.ts +23 -0
- package/src/commands/preview-project/index.ts +58 -0
- package/src/commands/preview-project/types.ts +12 -0
- package/src/commands/split/index.ts +3 -3
- package/src/index.ts +46 -8
- package/src/types.ts +4 -1
- package/src/utils.ts +36 -3
- package/tsconfig.tsbuildinfo +1 -1
package/src/commands/join.ts
CHANGED
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
sortTopLevelKeysForOas,
|
|
30
30
|
getAndValidateFileExtension,
|
|
31
31
|
writeToFileByExtension,
|
|
32
|
+
checkForDeprecatedOptions,
|
|
32
33
|
} from '../utils';
|
|
33
34
|
import { isObject, isString, keysOf } from '../js-utils';
|
|
34
35
|
import {
|
|
@@ -65,7 +66,6 @@ export type JoinOptions = {
|
|
|
65
66
|
'without-x-tag-groups'?: boolean;
|
|
66
67
|
output?: string;
|
|
67
68
|
config?: string;
|
|
68
|
-
extends?: undefined;
|
|
69
69
|
'lint-config'?: RuleSeverity;
|
|
70
70
|
};
|
|
71
71
|
|
|
@@ -76,6 +76,8 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
|
|
|
76
76
|
return exitWithError(`At least 2 apis should be provided. \n\n`);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
checkForDeprecatedOptions(argv, ['lint'] as Array<keyof JoinOptions>);
|
|
80
|
+
|
|
79
81
|
const fileExtension = getAndValidateFileExtension(argv.output || argv.apis[0]);
|
|
80
82
|
|
|
81
83
|
const {
|
package/src/commands/lint.ts
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Config,
|
|
3
|
-
findConfig,
|
|
4
3
|
formatProblems,
|
|
5
4
|
getMergedConfig,
|
|
6
5
|
getTotals,
|
|
7
6
|
lint,
|
|
8
7
|
lintConfig,
|
|
9
|
-
makeDocumentFromString,
|
|
10
|
-
stringifyYaml,
|
|
11
8
|
} from '@redocly/openapi-core';
|
|
12
9
|
import { ConfigValidationError } from '@redocly/openapi-core/lib/config';
|
|
13
10
|
import {
|
|
@@ -21,11 +18,13 @@ import {
|
|
|
21
18
|
printLintTotals,
|
|
22
19
|
printUnusedWarnings,
|
|
23
20
|
} from '../utils';
|
|
24
|
-
import type { OutputFormat, ProblemSeverity, RawConfig, RuleSeverity } from '@redocly/openapi-core';
|
|
25
|
-
import type { CommandOptions, Skips, Totals } from '../types';
|
|
26
21
|
import { blue, gray } from 'colorette';
|
|
27
22
|
import { performance } from 'perf_hooks';
|
|
28
23
|
|
|
24
|
+
import type { OutputFormat, ProblemSeverity, Document, RuleSeverity } from '@redocly/openapi-core';
|
|
25
|
+
import type { ResolvedRefMap } from '@redocly/openapi-core/lib/resolve';
|
|
26
|
+
import type { CommandOptions, Skips, Totals } from '../types';
|
|
27
|
+
|
|
29
28
|
export type LintOptions = {
|
|
30
29
|
apis?: string[];
|
|
31
30
|
'max-problems': number;
|
|
@@ -129,12 +128,10 @@ export function lintConfigCallback(
|
|
|
129
128
|
return;
|
|
130
129
|
}
|
|
131
130
|
|
|
132
|
-
return async (
|
|
133
|
-
const configPath = findConfig(argv.config) || '';
|
|
134
|
-
const stringYaml = stringifyYaml(config);
|
|
135
|
-
const configContent = makeDocumentFromString(stringYaml, configPath);
|
|
131
|
+
return async (document: Document, resolvedRefMap: ResolvedRefMap) => {
|
|
136
132
|
const problems = await lintConfig({
|
|
137
|
-
document
|
|
133
|
+
document,
|
|
134
|
+
resolvedRefMap,
|
|
138
135
|
severity: (argv['lint-config'] || 'warn') as ProblemSeverity,
|
|
139
136
|
});
|
|
140
137
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Product } from './types';
|
|
2
|
+
|
|
3
|
+
export const PRODUCT_PACKAGES = {
|
|
4
|
+
realm: '@redocly/realm',
|
|
5
|
+
'redoc-revel': '@redocly/redoc-revel',
|
|
6
|
+
'revel-reef': '@redocly/revel-reef',
|
|
7
|
+
'redoc-reef': '@redocly/redoc-reef',
|
|
8
|
+
redoc: '@redocly/redoc',
|
|
9
|
+
revel: '@redocly/revel',
|
|
10
|
+
reef: '@redocly/reef',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const PRODUCT_NAMES: { [key in Product]: string } = {
|
|
14
|
+
redoc: 'Redoc',
|
|
15
|
+
revel: 'Revel',
|
|
16
|
+
reef: 'Reef',
|
|
17
|
+
realm: 'Realm',
|
|
18
|
+
'redoc-revel': 'Redoc + Revel',
|
|
19
|
+
'redoc-reef': 'Redoc + Reef',
|
|
20
|
+
'revel-reef': 'Revel + Reef',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const PRODUCT_PLANS = ['pro', 'enterprise'] as const;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import path = require('path');
|
|
2
|
+
import { existsSync, readFileSync } from 'fs';
|
|
3
|
+
import { spawn } from 'child_process';
|
|
4
|
+
import { PRODUCT_NAMES, PRODUCT_PACKAGES } from './constants';
|
|
5
|
+
|
|
6
|
+
import type { PreviewProjectOptions, Product } from './types';
|
|
7
|
+
|
|
8
|
+
export const previewProject = async (args: PreviewProjectOptions) => {
|
|
9
|
+
const { plan, port } = args;
|
|
10
|
+
const projectDir = args['source-dir'];
|
|
11
|
+
|
|
12
|
+
const product = args.product || tryGetProductFromPackageJson(projectDir);
|
|
13
|
+
|
|
14
|
+
if (!isValidProduct(product)) {
|
|
15
|
+
process.stderr.write(`Invalid product ${product}`);
|
|
16
|
+
throw new Error(`Project preview launch failed`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const productName = PRODUCT_NAMES[product];
|
|
20
|
+
const packageName = PRODUCT_PACKAGES[product];
|
|
21
|
+
|
|
22
|
+
process.stdout.write(`\nLaunching preview of ${productName} ${plan} using NPX\n\n`);
|
|
23
|
+
|
|
24
|
+
spawn('npx', ['-y', packageName, 'develop', `--plan=${plan}`, `--port=${port || 4000}`], {
|
|
25
|
+
stdio: 'inherit',
|
|
26
|
+
cwd: projectDir,
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const isValidProduct = (product: string | undefined): product is Product => {
|
|
31
|
+
if (!product) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return !!PRODUCT_NAMES[product as Product];
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const tryGetProductFromPackageJson = (projectDir: string): Product => {
|
|
39
|
+
const packageJsonPath = path.join(process.cwd(), projectDir, 'package.json');
|
|
40
|
+
|
|
41
|
+
if (existsSync(packageJsonPath)) {
|
|
42
|
+
try {
|
|
43
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
44
|
+
const packageJsonDeps = packageJson.dependencies || {};
|
|
45
|
+
|
|
46
|
+
for (const [product, packageName] of Object.entries(PRODUCT_PACKAGES)) {
|
|
47
|
+
if (packageJsonDeps[packageName]) {
|
|
48
|
+
process.stdout.write(`\n${packageName} detected in project's 'package.json'`);
|
|
49
|
+
return product as Product;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
} catch (error) {
|
|
53
|
+
process.stdout.write(`Invalid 'package.json': ${packageJsonPath}. Using Realm.`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return 'realm';
|
|
58
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { PRODUCT_PACKAGES, PRODUCT_PLANS } from './constants';
|
|
2
|
+
|
|
3
|
+
export type Product = keyof typeof PRODUCT_PACKAGES;
|
|
4
|
+
export type ProductPlan = typeof PRODUCT_PLANS[number];
|
|
5
|
+
|
|
6
|
+
export type PreviewProjectOptions = {
|
|
7
|
+
product?: Product | string;
|
|
8
|
+
plan: ProductPlan | string;
|
|
9
|
+
port?: number;
|
|
10
|
+
'source-dir': string;
|
|
11
|
+
config: string | undefined;
|
|
12
|
+
};
|
|
@@ -97,8 +97,8 @@ function isStartsWithComponents(node: string) {
|
|
|
97
97
|
return node.startsWith(componentsPath);
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
function
|
|
101
|
-
return
|
|
100
|
+
function isSupportedExtension(filename: string) {
|
|
101
|
+
return filename.endsWith('.yaml') || filename.endsWith('.yml') || filename.endsWith('.json');
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
function loadFile(fileName: string) {
|
|
@@ -138,7 +138,7 @@ function traverseDirectoryDeepCallback(
|
|
|
138
138
|
directory: string,
|
|
139
139
|
componentsFiles: object
|
|
140
140
|
) {
|
|
141
|
-
if (
|
|
141
|
+
if (!isSupportedExtension(filename)) return;
|
|
142
142
|
const pathData = readYaml(filename);
|
|
143
143
|
replace$Refs(pathData, directory, componentsFiles);
|
|
144
144
|
writeToFileByExtension(pathData, filename);
|
package/src/index.ts
CHANGED
|
@@ -19,6 +19,8 @@ import { version } from './update-version-notifier';
|
|
|
19
19
|
import type { Arguments } from 'yargs';
|
|
20
20
|
import type { OutputFormat, RuleSeverity } from '@redocly/openapi-core';
|
|
21
21
|
import type { BuildDocsArgv } from './commands/build-docs/types';
|
|
22
|
+
import { previewProject } from './commands/preview-project';
|
|
23
|
+
import { PRODUCT_PLANS } from './commands/preview-project/constants';
|
|
22
24
|
|
|
23
25
|
if (!('replaceAll' in String.prototype)) {
|
|
24
26
|
require('core-js/actual/string/replace-all');
|
|
@@ -92,7 +94,7 @@ yargs
|
|
|
92
94
|
)
|
|
93
95
|
.command(
|
|
94
96
|
'join [apis...]',
|
|
95
|
-
'Join
|
|
97
|
+
'Join multiple API descriptions into one [experimental].',
|
|
96
98
|
(yargs) =>
|
|
97
99
|
yargs
|
|
98
100
|
.positional('apis', {
|
|
@@ -101,7 +103,7 @@ yargs
|
|
|
101
103
|
demandOption: true,
|
|
102
104
|
})
|
|
103
105
|
.option({
|
|
104
|
-
lint: { description: 'Lint
|
|
106
|
+
lint: { description: 'Lint descriptions', type: 'boolean', default: false, hidden: true },
|
|
105
107
|
decorate: { description: 'Run decorators', type: 'boolean', default: false },
|
|
106
108
|
preprocess: { description: 'Run preprocessors', type: 'boolean', default: false },
|
|
107
109
|
'prefix-tags-with-info-prop': {
|
|
@@ -124,7 +126,7 @@ yargs
|
|
|
124
126
|
type: 'boolean',
|
|
125
127
|
},
|
|
126
128
|
output: {
|
|
127
|
-
|
|
129
|
+
description: 'Output file.',
|
|
128
130
|
alias: 'o',
|
|
129
131
|
type: 'string',
|
|
130
132
|
},
|
|
@@ -228,7 +230,7 @@ yargs
|
|
|
228
230
|
)
|
|
229
231
|
.command(
|
|
230
232
|
'lint [apis...]',
|
|
231
|
-
'Lint
|
|
233
|
+
'Lint an API description.',
|
|
232
234
|
(yargs) =>
|
|
233
235
|
yargs.positional('apis', { array: true, type: 'string', demandOption: true }).option({
|
|
234
236
|
format: {
|
|
@@ -287,10 +289,14 @@ yargs
|
|
|
287
289
|
)
|
|
288
290
|
.command(
|
|
289
291
|
'bundle [apis...]',
|
|
290
|
-
'Bundle
|
|
292
|
+
'Bundle a multi-file API description to a single file.',
|
|
291
293
|
(yargs) =>
|
|
292
294
|
yargs.positional('apis', { array: true, type: 'string', demandOption: true }).options({
|
|
293
|
-
output: {
|
|
295
|
+
output: {
|
|
296
|
+
type: 'string',
|
|
297
|
+
description: 'Output file.',
|
|
298
|
+
alias: 'o',
|
|
299
|
+
},
|
|
294
300
|
format: {
|
|
295
301
|
description: 'Use a specific output format.',
|
|
296
302
|
choices: ['stylish', 'codeframe', 'json', 'checkstyle'] as ReadonlyArray<OutputFormat>,
|
|
@@ -413,9 +419,41 @@ yargs
|
|
|
413
419
|
})(argv);
|
|
414
420
|
}
|
|
415
421
|
)
|
|
422
|
+
.command(
|
|
423
|
+
'preview',
|
|
424
|
+
'Preview Redocly project using one of the product NPM packages.',
|
|
425
|
+
(yargs) =>
|
|
426
|
+
yargs.options({
|
|
427
|
+
product: {
|
|
428
|
+
type: 'string',
|
|
429
|
+
choices: ['redoc', 'revel', 'reef', 'realm', 'redoc-revel', 'redoc-reef', 'revel-reef'],
|
|
430
|
+
description:
|
|
431
|
+
"Product used to launch preview. Default is inferred from project's package.json or 'realm' is used.",
|
|
432
|
+
},
|
|
433
|
+
plan: {
|
|
434
|
+
type: 'string',
|
|
435
|
+
choices: PRODUCT_PLANS,
|
|
436
|
+
default: 'enterprise',
|
|
437
|
+
},
|
|
438
|
+
port: {
|
|
439
|
+
type: 'number',
|
|
440
|
+
description: 'Preview port.',
|
|
441
|
+
default: 4000,
|
|
442
|
+
},
|
|
443
|
+
'source-dir': {
|
|
444
|
+
alias: 'd',
|
|
445
|
+
type: 'string',
|
|
446
|
+
description: 'Project directory.',
|
|
447
|
+
default: '.',
|
|
448
|
+
},
|
|
449
|
+
}),
|
|
450
|
+
(argv) => {
|
|
451
|
+
commandWrapper(previewProject)(argv);
|
|
452
|
+
}
|
|
453
|
+
)
|
|
416
454
|
.command(
|
|
417
455
|
'preview-docs [api]',
|
|
418
|
-
'Preview API reference docs for
|
|
456
|
+
'Preview API reference docs for an API description.',
|
|
419
457
|
(yargs) =>
|
|
420
458
|
yargs.positional('api', { type: 'string' }).options({
|
|
421
459
|
port: {
|
|
@@ -519,7 +557,7 @@ yargs
|
|
|
519
557
|
commandWrapper(handlerBuildCommand)(argv as Arguments<BuildDocsArgv>);
|
|
520
558
|
}
|
|
521
559
|
)
|
|
522
|
-
.completion('completion', 'Generate
|
|
560
|
+
.completion('completion', 'Generate autocomplete script for `redocly` command.')
|
|
523
561
|
.demandCommand(1)
|
|
524
562
|
.middleware([notifyUpdateCliVersion])
|
|
525
563
|
.strict().argv;
|
package/src/types.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type { StatsOptions } from './commands/stats';
|
|
|
8
8
|
import type { SplitOptions } from './commands/split';
|
|
9
9
|
import type { PreviewDocsOptions } from './commands/preview-docs';
|
|
10
10
|
import type { BuildDocsArgv } from './commands/build-docs/types';
|
|
11
|
+
import type { PreviewProjectOptions } from './commands/preview-project/types';
|
|
11
12
|
|
|
12
13
|
export type Totals = {
|
|
13
14
|
errors: number;
|
|
@@ -30,7 +31,9 @@ export type CommandOptions =
|
|
|
30
31
|
| BundleOptions
|
|
31
32
|
| LoginOptions
|
|
32
33
|
| PreviewDocsOptions
|
|
33
|
-
| BuildDocsArgv
|
|
34
|
+
| BuildDocsArgv
|
|
35
|
+
| PreviewProjectOptions;
|
|
36
|
+
|
|
34
37
|
export type Skips = {
|
|
35
38
|
'skip-rule'?: string[];
|
|
36
39
|
'skip-decorator'?: string[];
|
package/src/utils.ts
CHANGED
|
@@ -17,14 +17,12 @@ import {
|
|
|
17
17
|
stringifyYaml,
|
|
18
18
|
isAbsoluteUrl,
|
|
19
19
|
loadConfig,
|
|
20
|
-
RawConfig,
|
|
21
20
|
Region,
|
|
22
21
|
Config,
|
|
23
22
|
Oas3Definition,
|
|
24
23
|
Oas2Definition,
|
|
25
24
|
RedoclyClient,
|
|
26
25
|
} from '@redocly/openapi-core';
|
|
27
|
-
import { ConfigValidationError } from '@redocly/openapi-core/lib/config';
|
|
28
26
|
import {
|
|
29
27
|
Totals,
|
|
30
28
|
outputExtensions,
|
|
@@ -37,6 +35,9 @@ import { isEmptyObject } from '@redocly/openapi-core/lib/utils';
|
|
|
37
35
|
import { Arguments } from 'yargs';
|
|
38
36
|
import { version } from './update-version-notifier';
|
|
39
37
|
import { DESTINATION_REGEX } from './commands/push';
|
|
38
|
+
import { ConfigValidationError } from '@redocly/openapi-core/lib/config';
|
|
39
|
+
|
|
40
|
+
import type { RawConfigProcessor } from '@redocly/openapi-core/lib/config';
|
|
40
41
|
|
|
41
42
|
export async function getFallbackApisOrExit(
|
|
42
43
|
argsApis: string[] | undefined,
|
|
@@ -145,6 +146,24 @@ export function langToExt(lang: string) {
|
|
|
145
146
|
javascript: '.js',
|
|
146
147
|
js: '.js',
|
|
147
148
|
python: '.py',
|
|
149
|
+
c: '.c',
|
|
150
|
+
'c++': '.cpp',
|
|
151
|
+
coffeescript: '.litcoffee',
|
|
152
|
+
dart: '.dart',
|
|
153
|
+
elixir: '.ex',
|
|
154
|
+
go: '.go',
|
|
155
|
+
groovy: '.groovy',
|
|
156
|
+
java: '.java',
|
|
157
|
+
kotlin: '.kt',
|
|
158
|
+
'objective-c': '.m',
|
|
159
|
+
perl: '.pl',
|
|
160
|
+
powershell: '.ps1',
|
|
161
|
+
ruby: '.rb',
|
|
162
|
+
rust: '.rs',
|
|
163
|
+
scala: '.sc',
|
|
164
|
+
swift: '.swift',
|
|
165
|
+
typescript: '.ts',
|
|
166
|
+
tsx: '.tsx',
|
|
148
167
|
};
|
|
149
168
|
return langObj[lang.toLowerCase()];
|
|
150
169
|
}
|
|
@@ -425,7 +444,7 @@ export async function loadConfigAndHandleErrors(
|
|
|
425
444
|
options: {
|
|
426
445
|
configPath?: string;
|
|
427
446
|
customExtends?: string[];
|
|
428
|
-
processRawConfig?:
|
|
447
|
+
processRawConfig?: RawConfigProcessor;
|
|
429
448
|
files?: string[];
|
|
430
449
|
region?: Region;
|
|
431
450
|
} = {}
|
|
@@ -626,3 +645,17 @@ export function cleanArgs(args: CommandOptions) {
|
|
|
626
645
|
export function cleanRawInput(argv: string[]) {
|
|
627
646
|
return argv.map((entry) => entry.split('=').map(cleanString).join('=')).join(' ');
|
|
628
647
|
}
|
|
648
|
+
|
|
649
|
+
export function checkForDeprecatedOptions<T>(argv: T, deprecatedOptions: Array<keyof T>) {
|
|
650
|
+
for (const option of deprecatedOptions) {
|
|
651
|
+
if (argv[option]) {
|
|
652
|
+
process.stderr.write(
|
|
653
|
+
yellow(
|
|
654
|
+
`[WARNING] "${String(
|
|
655
|
+
option
|
|
656
|
+
)}" option is deprecated and will be removed in a future release. \n\n`
|
|
657
|
+
)
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|