@redocly/cli 1.0.0-beta.105 → 1.0.0-beta.108
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/bin/cli.js +1 -1
- package/lib/__mocks__/@redocly/openapi-core.d.ts +1 -1
- package/lib/__mocks__/perf_hooks.js +1 -1
- package/lib/__mocks__/utils.d.ts +1 -1
- package/lib/__mocks__/utils.js +2 -2
- package/lib/__tests__/commands/bundle.test.js +52 -17
- package/lib/__tests__/commands/join.test.js +4 -4
- package/lib/__tests__/commands/lint.test.js +6 -6
- package/lib/__tests__/commands/push-region.test.js +2 -2
- package/lib/__tests__/commands/push.test.js +18 -18
- package/lib/__tests__/fixtures/config.d.ts +1 -1
- package/lib/__tests__/fixtures/config.js +1 -1
- package/lib/commands/bundle.d.ts +4 -12
- package/lib/commands/bundle.js +12 -11
- package/lib/commands/join.d.ts +1 -1
- package/lib/commands/join.js +112 -68
- package/lib/commands/lint.d.ts +3 -9
- package/lib/commands/lint.js +12 -12
- package/lib/commands/preview-docs/index.d.ts +3 -5
- package/lib/commands/preview-docs/index.js +17 -15
- package/lib/commands/preview-docs/preview-server/preview-server.js +2 -1
- package/lib/commands/push.d.ts +6 -6
- package/lib/commands/push.js +32 -32
- package/lib/commands/split/__tests__/index.test.js +8 -8
- package/lib/commands/split/index.d.ts +1 -1
- package/lib/commands/split/index.js +15 -11
- package/lib/commands/split/types.d.ts +2 -2
- package/lib/commands/split/types.js +2 -2
- package/lib/commands/stats.d.ts +1 -1
- package/lib/commands/stats.js +9 -7
- package/lib/index.js +35 -20
- package/lib/js-utils.js +2 -2
- package/lib/types.d.ts +13 -1
- package/lib/utils.d.ts +4 -4
- package/lib/utils.js +14 -17
- package/package.json +2 -2
- package/src/__mocks__/perf_hooks.ts +2 -2
- package/src/__mocks__/utils.ts +3 -1
- package/src/__tests__/commands/bundle.test.ts +71 -22
- package/src/__tests__/commands/join.test.ts +8 -8
- package/src/__tests__/commands/lint.test.ts +10 -8
- package/src/__tests__/commands/push-region.test.ts +2 -2
- package/src/__tests__/commands/push.test.ts +19 -24
- package/src/__tests__/fixtures/config.ts +1 -1
- package/src/__tests__/utils.test.ts +5 -8
- package/src/commands/bundle.ts +28 -40
- package/src/commands/join.ts +216 -131
- package/src/commands/lint.ts +26 -33
- package/src/commands/login.ts +2 -2
- package/src/commands/preview-docs/index.ts +37 -42
- package/src/commands/preview-docs/preview-server/preview-server.ts +8 -7
- package/src/commands/preview-docs/preview-server/server.ts +1 -1
- package/src/commands/push.ts +50 -59
- package/src/commands/split/__tests__/index.test.ts +47 -30
- package/src/commands/split/index.ts +91 -49
- package/src/commands/split/types.ts +19 -7
- package/src/commands/stats.ts +27 -24
- package/src/index.ts +40 -25
- package/src/js-utils.ts +2 -2
- package/src/types.ts +14 -1
- package/src/utils.ts +52 -53
- package/tsconfig.tsbuildinfo +1 -1
package/src/commands/lint.ts
CHANGED
|
@@ -6,43 +6,35 @@ import {
|
|
|
6
6
|
lintConfig,
|
|
7
7
|
findConfig,
|
|
8
8
|
getMergedConfig,
|
|
9
|
-
OutputFormat,
|
|
10
9
|
makeDocumentFromString,
|
|
11
10
|
loadConfig,
|
|
12
11
|
stringifyYaml,
|
|
13
12
|
RawConfig,
|
|
14
13
|
RuleSeverity,
|
|
15
14
|
ProblemSeverity,
|
|
16
|
-
doesYamlFileExist
|
|
15
|
+
doesYamlFileExist,
|
|
17
16
|
} from '@redocly/openapi-core';
|
|
18
17
|
import {
|
|
19
18
|
getExecutionTime,
|
|
20
|
-
|
|
19
|
+
getFallbackApisOrExit,
|
|
21
20
|
handleError,
|
|
22
21
|
pluralize,
|
|
23
22
|
printLintTotals,
|
|
24
23
|
printConfigLintTotals,
|
|
25
24
|
printUnusedWarnings,
|
|
26
|
-
exitWithError
|
|
25
|
+
exitWithError,
|
|
27
26
|
} from '../utils';
|
|
28
|
-
import { Totals } from '../types';
|
|
27
|
+
import type { CommonOptions, Skips, Totals } from '../types';
|
|
29
28
|
import { blue, gray, red } from 'colorette';
|
|
30
29
|
import { performance } from 'perf_hooks';
|
|
31
30
|
|
|
32
|
-
export type LintOptions =
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
'skip-preprocessor'?: string[];
|
|
38
|
-
'lint-config': RuleSeverity;
|
|
39
|
-
extends?: string[];
|
|
40
|
-
config?: string;
|
|
41
|
-
format: OutputFormat;
|
|
42
|
-
};
|
|
31
|
+
export type LintOptions = CommonOptions &
|
|
32
|
+
Omit<Skips, 'skip-decorator'> & {
|
|
33
|
+
'generate-ignore-file'?: boolean;
|
|
34
|
+
'lint-config': RuleSeverity;
|
|
35
|
+
};
|
|
43
36
|
|
|
44
37
|
export async function handleLint(argv: LintOptions, version: string) {
|
|
45
|
-
|
|
46
38
|
if (argv.config && !doesYamlFileExist(argv.config)) {
|
|
47
39
|
return exitWithError('Please, provide valid path to the configuration file');
|
|
48
40
|
}
|
|
@@ -53,29 +45,31 @@ export async function handleLint(argv: LintOptions, version: string) {
|
|
|
53
45
|
lintConfigCallback(argv, version)
|
|
54
46
|
);
|
|
55
47
|
|
|
56
|
-
const
|
|
48
|
+
const apis = await getFallbackApisOrExit(argv.apis, config);
|
|
57
49
|
|
|
58
50
|
if (argv['generate-ignore-file']) {
|
|
59
|
-
config.
|
|
51
|
+
config.styleguide.ignore = {}; // clear ignore
|
|
60
52
|
}
|
|
61
53
|
const totals: Totals = { errors: 0, warnings: 0, ignored: 0 };
|
|
62
54
|
let totalIgnored = 0;
|
|
63
55
|
|
|
64
56
|
// TODO: use shared externalRef resolver, blocked by preprocessors now as they can mutate documents
|
|
65
|
-
for (const { path, alias } of
|
|
57
|
+
for (const { path, alias } of apis) {
|
|
66
58
|
try {
|
|
67
59
|
const startedAt = performance.now();
|
|
68
60
|
const resolvedConfig = getMergedConfig(config, alias);
|
|
69
|
-
resolvedConfig
|
|
70
|
-
|
|
61
|
+
const { styleguide } = resolvedConfig;
|
|
62
|
+
|
|
63
|
+
styleguide.skipRules(argv['skip-rule']);
|
|
64
|
+
styleguide.skipPreprocessors(argv['skip-preprocessor']);
|
|
71
65
|
|
|
72
|
-
if (
|
|
66
|
+
if (styleguide.recommendedFallback) {
|
|
73
67
|
process.stderr.write(
|
|
74
68
|
`No configurations were defined in extends -- using built in ${blue(
|
|
75
|
-
'recommended'
|
|
69
|
+
'recommended'
|
|
76
70
|
)} configuration by default.\n${red(
|
|
77
|
-
'Warning! This default behavior is going to be deprecated soon.'
|
|
78
|
-
)}\n\n
|
|
71
|
+
'Warning! This default behavior is going to be deprecated soon.'
|
|
72
|
+
)}\n\n`
|
|
79
73
|
);
|
|
80
74
|
}
|
|
81
75
|
process.stderr.write(gray(`validating ${path.replace(process.cwd(), '')}...\n`));
|
|
@@ -90,8 +84,8 @@ export async function handleLint(argv: LintOptions, version: string) {
|
|
|
90
84
|
totals.ignored += fileTotals.ignored;
|
|
91
85
|
|
|
92
86
|
if (argv['generate-ignore-file']) {
|
|
93
|
-
for (
|
|
94
|
-
config.
|
|
87
|
+
for (const m of results) {
|
|
88
|
+
config.styleguide.addIgnore(m);
|
|
95
89
|
totalIgnored++;
|
|
96
90
|
}
|
|
97
91
|
} else {
|
|
@@ -106,26 +100,25 @@ export async function handleLint(argv: LintOptions, version: string) {
|
|
|
106
100
|
const elapsed = getExecutionTime(startedAt);
|
|
107
101
|
process.stderr.write(gray(`${path.replace(process.cwd(), '')}: validated in ${elapsed}\n\n`));
|
|
108
102
|
} catch (e) {
|
|
109
|
-
totals.errors++;
|
|
110
103
|
handleError(e, path);
|
|
111
104
|
}
|
|
112
105
|
}
|
|
113
106
|
|
|
114
107
|
if (argv['generate-ignore-file']) {
|
|
115
|
-
config.
|
|
108
|
+
config.styleguide.saveIgnore();
|
|
116
109
|
process.stderr.write(
|
|
117
110
|
`Generated ignore file with ${totalIgnored} ${pluralize('problem', totalIgnored)}.\n\n`
|
|
118
111
|
);
|
|
119
112
|
} else {
|
|
120
|
-
printLintTotals(totals,
|
|
113
|
+
printLintTotals(totals, apis.length);
|
|
121
114
|
}
|
|
122
115
|
|
|
123
|
-
printUnusedWarnings(config.
|
|
116
|
+
printUnusedWarnings(config.styleguide);
|
|
124
117
|
|
|
125
118
|
// defer process exit to allow STDOUT pipe to flush
|
|
126
119
|
// see https://github.com/nodejs/node-v0.x-archive/issues/3737#issuecomment-19156072
|
|
127
120
|
process.once('exit', () =>
|
|
128
|
-
process.exit(totals.errors === 0 || argv['generate-ignore-file'] ? 0 : 1)
|
|
121
|
+
process.exit(totals.errors === 0 || argv['generate-ignore-file'] ? 0 : 1)
|
|
129
122
|
);
|
|
130
123
|
}
|
|
131
124
|
|
package/src/commands/login.ts
CHANGED
|
@@ -5,9 +5,9 @@ import { promptUser } from '../utils';
|
|
|
5
5
|
export function promptClientToken(domain: string) {
|
|
6
6
|
return promptUser(
|
|
7
7
|
green(
|
|
8
|
-
`\n 🔑 Copy your API key from ${blue(`https://app.${domain}/profile`)} and paste it below
|
|
8
|
+
`\n 🔑 Copy your API key from ${blue(`https://app.${domain}/profile`)} and paste it below`
|
|
9
9
|
),
|
|
10
|
-
true
|
|
10
|
+
true
|
|
11
11
|
);
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -9,29 +9,26 @@ import {
|
|
|
9
9
|
getTotals,
|
|
10
10
|
getMergedConfig,
|
|
11
11
|
} from '@redocly/openapi-core';
|
|
12
|
-
import {
|
|
12
|
+
import { getFallbackApisOrExit } from '../../utils';
|
|
13
13
|
import startPreviewServer from './preview-server/preview-server';
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
let isAuthorizedWithRedocly
|
|
14
|
+
import type { Skips } from 'cli/src/types';
|
|
15
|
+
|
|
16
|
+
export async function previewDocs(
|
|
17
|
+
argv: {
|
|
18
|
+
port: number;
|
|
19
|
+
host: string;
|
|
20
|
+
'use-community-edition'?: boolean;
|
|
21
|
+
config?: string;
|
|
22
|
+
api?: string;
|
|
23
|
+
force?: boolean;
|
|
24
|
+
} & Omit<Skips, 'skip-rule'>
|
|
25
|
+
) {
|
|
26
|
+
let isAuthorizedWithRedocly = false;
|
|
27
27
|
let redocOptions: any = {};
|
|
28
28
|
let config = await reloadConfig();
|
|
29
29
|
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
config,
|
|
33
|
-
);
|
|
34
|
-
const entrypoint = entrypoints[0];
|
|
30
|
+
const apis = await getFallbackApisOrExit(argv.api ? [argv.api] : [], config);
|
|
31
|
+
const api = apis[0];
|
|
35
32
|
|
|
36
33
|
let cachedBundle: any;
|
|
37
34
|
const deps = new Set<string>();
|
|
@@ -48,7 +45,7 @@ export async function previewDocs(argv: {
|
|
|
48
45
|
problems,
|
|
49
46
|
fileDependencies,
|
|
50
47
|
} = await bundle({
|
|
51
|
-
ref:
|
|
48
|
+
ref: api.path,
|
|
52
49
|
config,
|
|
53
50
|
});
|
|
54
51
|
const removed = [...deps].filter((x) => !fileDependencies.has(x));
|
|
@@ -62,20 +59,20 @@ export async function previewDocs(argv: {
|
|
|
62
59
|
if (fileTotals.errors === 0) {
|
|
63
60
|
process.stdout.write(
|
|
64
61
|
fileTotals.errors === 0
|
|
65
|
-
? `Created a bundle for ${
|
|
62
|
+
? `Created a bundle for ${api.alias || api.path} ${
|
|
66
63
|
fileTotals.warnings > 0 ? 'with warnings' : 'successfully'
|
|
67
64
|
}\n`
|
|
68
65
|
: colorette.yellow(
|
|
69
66
|
`Created a bundle for ${
|
|
70
|
-
|
|
71
|
-
} with errors. Docs may be broken or not accurate\n
|
|
72
|
-
)
|
|
67
|
+
api.alias || api.path
|
|
68
|
+
} with errors. Docs may be broken or not accurate\n`
|
|
69
|
+
)
|
|
73
70
|
);
|
|
74
71
|
}
|
|
75
72
|
|
|
76
73
|
return openapiBundle.parsed;
|
|
77
74
|
} catch (e) {
|
|
78
|
-
handleError(e,
|
|
75
|
+
handleError(e, api.path);
|
|
79
76
|
}
|
|
80
77
|
}
|
|
81
78
|
|
|
@@ -87,8 +84,8 @@ export async function previewDocs(argv: {
|
|
|
87
84
|
if (!isAuthorized) {
|
|
88
85
|
process.stderr.write(
|
|
89
86
|
`Using Redoc community edition.\nLogin with redocly ${colorette.blue(
|
|
90
|
-
'login'
|
|
91
|
-
)} or use an enterprise license key to preview with the premium docs.\n\n
|
|
87
|
+
'login'
|
|
88
|
+
)} or use an enterprise license key to preview with the premium docs.\n\n`
|
|
92
89
|
);
|
|
93
90
|
}
|
|
94
91
|
|
|
@@ -98,7 +95,7 @@ export async function previewDocs(argv: {
|
|
|
98
95
|
useRedocPro: isAuthorized && !redocOptions.useCommunityEdition,
|
|
99
96
|
});
|
|
100
97
|
|
|
101
|
-
const watchPaths = [
|
|
98
|
+
const watchPaths = [api.path, config.configFile!].filter((e) => !!e);
|
|
102
99
|
const watcher = chockidar.watch(watchPaths, {
|
|
103
100
|
disableGlobbing: true,
|
|
104
101
|
ignoreInitial: true,
|
|
@@ -127,20 +124,20 @@ export async function previewDocs(argv: {
|
|
|
127
124
|
|
|
128
125
|
watcher.on('ready', () => {
|
|
129
126
|
process.stdout.write(
|
|
130
|
-
`\n 👀 Watching ${colorette.blue(
|
|
131
|
-
entrypoint.path,
|
|
132
|
-
)} and all related resources for changes\n\n`,
|
|
127
|
+
`\n 👀 Watching ${colorette.blue(api.path)} and all related resources for changes\n\n`
|
|
133
128
|
);
|
|
134
129
|
});
|
|
135
130
|
|
|
136
131
|
async function reloadConfig() {
|
|
137
|
-
|
|
132
|
+
const config = await loadConfig(argv.config);
|
|
138
133
|
const redoclyClient = new RedoclyClient();
|
|
139
134
|
isAuthorizedWithRedocly = await redoclyClient.isAuthorizedWithRedocly();
|
|
140
|
-
const resolvedConfig = getMergedConfig(config, argv.
|
|
141
|
-
resolvedConfig
|
|
142
|
-
|
|
143
|
-
|
|
135
|
+
const resolvedConfig = getMergedConfig(config, argv.api);
|
|
136
|
+
const { styleguide } = resolvedConfig;
|
|
137
|
+
|
|
138
|
+
styleguide.skipPreprocessors(argv['skip-preprocessor']);
|
|
139
|
+
styleguide.skipDecorators(argv['skip-decorator']);
|
|
140
|
+
|
|
144
141
|
const referenceDocs = resolvedConfig['features.openapi'];
|
|
145
142
|
redocOptions = {
|
|
146
143
|
...referenceDocs,
|
|
@@ -155,7 +152,9 @@ export function debounce(func: Function, wait: number, immediate?: boolean) {
|
|
|
155
152
|
let timeout: NodeJS.Timeout | null;
|
|
156
153
|
|
|
157
154
|
return function executedFunction(...args: any[]) {
|
|
155
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
158
156
|
// @ts-ignore
|
|
157
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
159
158
|
const context = this;
|
|
160
159
|
|
|
161
160
|
const later = () => {
|
|
@@ -175,13 +174,9 @@ export function debounce(func: Function, wait: number, immediate?: boolean) {
|
|
|
175
174
|
|
|
176
175
|
function handleError(e: Error, ref: string) {
|
|
177
176
|
if (e instanceof ResolveError) {
|
|
178
|
-
process.stderr.write(
|
|
179
|
-
`Failed to resolve entrypoint definition at ${ref}:\n\n - ${e.message}.\n\n`,
|
|
180
|
-
);
|
|
177
|
+
process.stderr.write(`Failed to resolve api definition at ${ref}:\n\n - ${e.message}.\n\n`);
|
|
181
178
|
} else if (e instanceof YamlParseError) {
|
|
182
|
-
process.stderr.write(
|
|
183
|
-
`Failed to parse entrypoint definition at ${ref}:\n\n - ${e.message}.\n\n`,
|
|
184
|
-
);
|
|
179
|
+
process.stderr.write(`Failed to parse api definition at ${ref}:\n\n - ${e.message}.\n\n`);
|
|
185
180
|
} else {
|
|
186
181
|
process.stderr.write(`Something went wrong when processing ${ref}:\n\n - ${e.message}.\n\n`);
|
|
187
182
|
}
|
|
@@ -12,7 +12,7 @@ function getPageHTML(
|
|
|
12
12
|
htmlTemplate: string,
|
|
13
13
|
redocOptions: object = {},
|
|
14
14
|
useRedocPro: boolean,
|
|
15
|
-
wsPort: number
|
|
15
|
+
wsPort: number
|
|
16
16
|
) {
|
|
17
17
|
let templateSrc = readFileSync(htmlTemplate, 'utf-8');
|
|
18
18
|
|
|
@@ -58,7 +58,7 @@ export default async function startPreviewServer(
|
|
|
58
58
|
getBundle,
|
|
59
59
|
getOptions,
|
|
60
60
|
useRedocPro,
|
|
61
|
-
}: { getBundle: Function; getOptions: Function; useRedocPro: boolean }
|
|
61
|
+
}: { getBundle: Function; getOptions: Function; useRedocPro: boolean }
|
|
62
62
|
) {
|
|
63
63
|
const defaultTemplate = path.join(__dirname, 'default.hbs');
|
|
64
64
|
const handler = async (request: IncomingMessage, response: any) => {
|
|
@@ -72,7 +72,7 @@ export default async function startPreviewServer(
|
|
|
72
72
|
response,
|
|
73
73
|
{
|
|
74
74
|
'Content-Type': 'text/html',
|
|
75
|
-
}
|
|
75
|
+
}
|
|
76
76
|
);
|
|
77
77
|
} else if (request.url === '/openapi.json') {
|
|
78
78
|
const bundle = await getBundle();
|
|
@@ -90,7 +90,7 @@ export default async function startPreviewServer(
|
|
|
90
90
|
response,
|
|
91
91
|
{
|
|
92
92
|
'Content-Type': 'application/json',
|
|
93
|
-
}
|
|
93
|
+
}
|
|
94
94
|
);
|
|
95
95
|
} else {
|
|
96
96
|
respondWithGzip(JSON.stringify(bundle), request, response, {
|
|
@@ -99,6 +99,7 @@ export default async function startPreviewServer(
|
|
|
99
99
|
}
|
|
100
100
|
} else {
|
|
101
101
|
let filePath =
|
|
102
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
102
103
|
// @ts-ignore
|
|
103
104
|
{
|
|
104
105
|
'/hot.js': path.join(__dirname, 'hot.js'),
|
|
@@ -134,7 +135,7 @@ export default async function startPreviewServer(
|
|
|
134
135
|
request,
|
|
135
136
|
response,
|
|
136
137
|
{},
|
|
137
|
-
500
|
|
138
|
+
500
|
|
138
139
|
);
|
|
139
140
|
}
|
|
140
141
|
}
|
|
@@ -142,12 +143,12 @@ export default async function startPreviewServer(
|
|
|
142
143
|
console.timeEnd(colorette.dim(`GET ${request.url}`));
|
|
143
144
|
};
|
|
144
145
|
|
|
145
|
-
|
|
146
|
+
const wsPort = await portfinder.getPortPromise({ port: 32201 });
|
|
146
147
|
|
|
147
148
|
const server = startHttpServer(port, host, handler);
|
|
148
149
|
server.on('listening', () => {
|
|
149
150
|
process.stdout.write(
|
|
150
|
-
`\n 🔎 Preview server running at ${colorette.blue(`http://${host}:${port}\n`)}
|
|
151
|
+
`\n 🔎 Preview server running at ${colorette.blue(`http://${host}:${port}\n`)}`
|
|
151
152
|
);
|
|
152
153
|
});
|
|
153
154
|
|
package/src/commands/push.ts
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
import {
|
|
20
20
|
exitWithError,
|
|
21
21
|
printExecutionTime,
|
|
22
|
-
|
|
22
|
+
getFallbackApisOrExit,
|
|
23
23
|
pluralize,
|
|
24
24
|
dumpBundle,
|
|
25
25
|
} from '../utils';
|
|
@@ -28,7 +28,7 @@ import { promptClientToken } from './login';
|
|
|
28
28
|
const DEFAULT_VERSION = 'latest';
|
|
29
29
|
|
|
30
30
|
type PushArgs = {
|
|
31
|
-
|
|
31
|
+
api?: string;
|
|
32
32
|
destination?: string;
|
|
33
33
|
branchName?: string;
|
|
34
34
|
upsert?: boolean;
|
|
@@ -36,7 +36,7 @@ type PushArgs = {
|
|
|
36
36
|
'batch-size'?: number;
|
|
37
37
|
region?: Region;
|
|
38
38
|
'skip-decorator'?: string[];
|
|
39
|
-
|
|
39
|
+
public?: boolean;
|
|
40
40
|
};
|
|
41
41
|
|
|
42
42
|
export async function handlePush(argv: PushArgs): Promise<void> {
|
|
@@ -61,8 +61,8 @@ export async function handlePush(argv: PushArgs): Promise<void> {
|
|
|
61
61
|
) {
|
|
62
62
|
exitWithError(
|
|
63
63
|
`Destination argument value is not valid, please use the right format: ${yellow(
|
|
64
|
-
'<@organization-id/api-name@api-version>'
|
|
65
|
-
)}
|
|
64
|
+
'<@organization-id/api-name@api-version>'
|
|
65
|
+
)}`
|
|
66
66
|
);
|
|
67
67
|
}
|
|
68
68
|
|
|
@@ -71,18 +71,17 @@ export async function handlePush(argv: PushArgs): Promise<void> {
|
|
|
71
71
|
if (!organizationId) {
|
|
72
72
|
return exitWithError(
|
|
73
73
|
`No organization provided, please use the right format: ${yellow(
|
|
74
|
-
'<@organization-id/api-name@api-version>'
|
|
75
|
-
)} or specify the 'organization' field in the config file
|
|
74
|
+
'<@organization-id/api-name@api-version>'
|
|
75
|
+
)} or specify the 'organization' field in the config file.`
|
|
76
76
|
);
|
|
77
77
|
}
|
|
78
|
-
const
|
|
79
|
-
argv.entrypoint || (name && version && getApiEntrypoint({ name, version, config }));
|
|
78
|
+
const api = argv.api || (name && version && getApiRoot({ name, version, config }));
|
|
80
79
|
|
|
81
|
-
if (name && version && !
|
|
80
|
+
if (name && version && !api) {
|
|
82
81
|
exitWithError(
|
|
83
|
-
`No
|
|
84
|
-
`${name}@${version}
|
|
85
|
-
)}. Please make sure you have provided the correct data in the config file
|
|
82
|
+
`No api found that matches ${blue(
|
|
83
|
+
`${name}@${version}`
|
|
84
|
+
)}. Please make sure you have provided the correct data in the config file.`
|
|
86
85
|
);
|
|
87
86
|
}
|
|
88
87
|
|
|
@@ -98,29 +97,29 @@ export async function handlePush(argv: PushArgs): Promise<void> {
|
|
|
98
97
|
);
|
|
99
98
|
}
|
|
100
99
|
|
|
101
|
-
const apis =
|
|
100
|
+
const apis = api ? { [`${name}@${version}`]: { root: api } } : config.apis;
|
|
102
101
|
|
|
103
|
-
for (const [apiNameAndVersion, { root:
|
|
102
|
+
for (const [apiNameAndVersion, { root: api }] of Object.entries(apis)) {
|
|
104
103
|
const resolvedConfig = getMergedConfig(config, apiNameAndVersion);
|
|
105
|
-
resolvedConfig.
|
|
104
|
+
resolvedConfig.styleguide.skipDecorators(argv['skip-decorator']);
|
|
106
105
|
|
|
107
106
|
const [name, version = DEFAULT_VERSION] = apiNameAndVersion.split('@');
|
|
108
107
|
try {
|
|
109
108
|
let rootFilePath = '';
|
|
110
109
|
const filePaths: string[] = [];
|
|
111
|
-
const filesToUpload = await collectFilesToUpload(
|
|
110
|
+
const filesToUpload = await collectFilesToUpload(api, resolvedConfig);
|
|
112
111
|
const filesHash = hashFiles(filesToUpload.files);
|
|
113
112
|
|
|
114
113
|
process.stdout.write(
|
|
115
114
|
`Uploading ${filesToUpload.files.length} ${pluralize(
|
|
116
115
|
'file',
|
|
117
|
-
filesToUpload.files.length
|
|
118
|
-
)}:\n
|
|
116
|
+
filesToUpload.files.length
|
|
117
|
+
)}:\n`
|
|
119
118
|
);
|
|
120
119
|
|
|
121
120
|
let uploaded = 0;
|
|
122
121
|
|
|
123
|
-
for (
|
|
122
|
+
for (const file of filesToUpload.files) {
|
|
124
123
|
const { signedUploadUrl, filePath } = await client.registryApi.prepareFileUpload({
|
|
125
124
|
organizationId,
|
|
126
125
|
name,
|
|
@@ -137,12 +136,12 @@ export async function handlePush(argv: PushArgs): Promise<void> {
|
|
|
137
136
|
filePaths.push(filePath);
|
|
138
137
|
|
|
139
138
|
process.stdout.write(
|
|
140
|
-
`Uploading ${file.contents ? 'bundle for ' : ''}${blue(file.filePath)}
|
|
139
|
+
`Uploading ${file.contents ? 'bundle for ' : ''}${blue(file.filePath)}...`
|
|
141
140
|
);
|
|
142
141
|
|
|
143
142
|
const uploadResponse = await uploadFileToS3(
|
|
144
143
|
signedUploadUrl,
|
|
145
|
-
file.contents || file.filePath
|
|
144
|
+
file.contents || file.filePath
|
|
146
145
|
);
|
|
147
146
|
|
|
148
147
|
const fileCounter = `(${++uploaded}/${filesToUpload.files.length})`;
|
|
@@ -175,9 +174,9 @@ export async function handlePush(argv: PushArgs): Promise<void> {
|
|
|
175
174
|
|
|
176
175
|
if (error.message === 'API_VERSION_NOT_FOUND') {
|
|
177
176
|
exitWithError(`The definition version ${blue(name)}/${blue(
|
|
178
|
-
version
|
|
177
|
+
version
|
|
179
178
|
)} does not exist in organization ${blue(organizationId)}!\n${yellow(
|
|
180
|
-
'Suggestion:'
|
|
179
|
+
'Suggestion:'
|
|
181
180
|
)} please use ${blue('-u')} or ${blue('--upsert')} to create definition.
|
|
182
181
|
`);
|
|
183
182
|
}
|
|
@@ -186,10 +185,10 @@ export async function handlePush(argv: PushArgs): Promise<void> {
|
|
|
186
185
|
}
|
|
187
186
|
|
|
188
187
|
process.stdout.write(
|
|
189
|
-
`Definition: ${blue(
|
|
188
|
+
`Definition: ${blue(api!)} is successfully pushed to Redocly API Registry \n`
|
|
190
189
|
);
|
|
191
190
|
}
|
|
192
|
-
printExecutionTime('push', startedAt,
|
|
191
|
+
printExecutionTime('push', startedAt, api || `apis in organization ${organizationId}`);
|
|
193
192
|
}
|
|
194
193
|
|
|
195
194
|
function getFilesList(dir: string, files?: any): string[] {
|
|
@@ -206,15 +205,15 @@ function getFilesList(dir: string, files?: any): string[] {
|
|
|
206
205
|
return files;
|
|
207
206
|
}
|
|
208
207
|
|
|
209
|
-
async function collectFilesToUpload(
|
|
210
|
-
|
|
211
|
-
const [{ path:
|
|
208
|
+
async function collectFilesToUpload(api: string, config: Config) {
|
|
209
|
+
const files: { filePath: string; keyOnS3: string; contents?: Buffer }[] = [];
|
|
210
|
+
const [{ path: apiPath }] = await getFallbackApisOrExit([api], config);
|
|
212
211
|
|
|
213
212
|
process.stdout.write('Bundling definition\n');
|
|
214
213
|
|
|
215
214
|
const { bundle: openapiBundle, problems } = await bundle({
|
|
216
215
|
config,
|
|
217
|
-
ref:
|
|
216
|
+
ref: apiPath,
|
|
218
217
|
skipRedoclyRegistryRefs: true,
|
|
219
218
|
});
|
|
220
219
|
|
|
@@ -222,17 +221,15 @@ async function collectFilesToUpload(entrypoint: string, config: Config) {
|
|
|
222
221
|
|
|
223
222
|
if (fileTotals.errors === 0) {
|
|
224
223
|
process.stdout.write(
|
|
225
|
-
`Created a bundle for ${blue(
|
|
226
|
-
fileTotals.warnings > 0 ? 'with warnings' : ''
|
|
227
|
-
}\n`,
|
|
224
|
+
`Created a bundle for ${blue(api)} ${fileTotals.warnings > 0 ? 'with warnings' : ''}\n`
|
|
228
225
|
);
|
|
229
226
|
} else {
|
|
230
|
-
exitWithError(`Failed to create a bundle for ${blue(
|
|
227
|
+
exitWithError(`Failed to create a bundle for ${blue(api)}\n`);
|
|
231
228
|
}
|
|
232
229
|
|
|
233
|
-
const fileExt = path.extname(
|
|
230
|
+
const fileExt = path.extname(apiPath).split('.').pop();
|
|
234
231
|
files.push(
|
|
235
|
-
getFileEntry(
|
|
232
|
+
getFileEntry(apiPath, dumpBundle(openapiBundle.parsed, fileExt as BundleOutputFormat))
|
|
236
233
|
);
|
|
237
234
|
|
|
238
235
|
if (fs.existsSync('package.json')) {
|
|
@@ -243,14 +240,14 @@ async function collectFilesToUpload(entrypoint: string, config: Config) {
|
|
|
243
240
|
}
|
|
244
241
|
if (config.configFile) {
|
|
245
242
|
// All config file paths including the root one
|
|
246
|
-
files.push(...[...new Set(config.
|
|
243
|
+
files.push(...[...new Set(config.styleguide.extendPaths)].map((f) => getFileEntry(f)));
|
|
247
244
|
if (config['features.openapi'].htmlTemplate) {
|
|
248
245
|
const dir = getFolder(config['features.openapi'].htmlTemplate);
|
|
249
246
|
const fileList = getFilesList(dir, []);
|
|
250
247
|
files.push(...fileList.map((f) => getFileEntry(f)));
|
|
251
248
|
}
|
|
252
|
-
|
|
253
|
-
for (const plugin of config.
|
|
249
|
+
const pluginFiles = new Set<string>();
|
|
250
|
+
for (const plugin of config.styleguide.pluginPaths) {
|
|
254
251
|
if (typeof plugin !== 'string') continue;
|
|
255
252
|
const fileList = getFilesList(getFolder(plugin), []);
|
|
256
253
|
fileList.forEach((f) => pluginFiles.add(f));
|
|
@@ -259,7 +256,7 @@ async function collectFilesToUpload(entrypoint: string, config: Config) {
|
|
|
259
256
|
}
|
|
260
257
|
return {
|
|
261
258
|
files,
|
|
262
|
-
root: path.resolve(
|
|
259
|
+
root: path.resolve(apiPath),
|
|
263
260
|
};
|
|
264
261
|
|
|
265
262
|
function filterPluginFilesByExt(files: string[]) {
|
|
@@ -285,7 +282,7 @@ function getFolder(filePath: string) {
|
|
|
285
282
|
}
|
|
286
283
|
|
|
287
284
|
function hashFiles(filePaths: { filePath: string }[]) {
|
|
288
|
-
|
|
285
|
+
const sum = createHash('sha256');
|
|
289
286
|
filePaths.forEach((file) => sum.update(fs.readFileSync(file.filePath)));
|
|
290
287
|
return sum.digest('hex');
|
|
291
288
|
}
|
|
@@ -302,7 +299,7 @@ function validateDestinationWithoutOrganization(destination: string) {
|
|
|
302
299
|
|
|
303
300
|
export function getDestinationProps(
|
|
304
301
|
destination: string | undefined,
|
|
305
|
-
organization: string | undefined
|
|
302
|
+
organization: string | undefined
|
|
306
303
|
) {
|
|
307
304
|
return destination && validateDestination(destination)
|
|
308
305
|
? destination.substring(1).split(/[@\/]/)
|
|
@@ -311,8 +308,8 @@ export function getDestinationProps(
|
|
|
311
308
|
: [organization];
|
|
312
309
|
}
|
|
313
310
|
|
|
314
|
-
type BarePushArgs = Omit<PushArgs, '
|
|
315
|
-
|
|
311
|
+
type BarePushArgs = Omit<PushArgs, 'api' | 'destination' | 'branchName'> & {
|
|
312
|
+
maybeApiOrDestination?: string;
|
|
316
313
|
maybeDestination?: string;
|
|
317
314
|
maybeBranchName?: string;
|
|
318
315
|
branch?: string;
|
|
@@ -320,31 +317,25 @@ type BarePushArgs = Omit<PushArgs, 'entrypoint' | 'destination' | 'branchName'>
|
|
|
320
317
|
|
|
321
318
|
export const transformPush =
|
|
322
319
|
(callback: typeof handlePush) =>
|
|
323
|
-
({
|
|
324
|
-
|
|
325
|
-
maybeDestination,
|
|
326
|
-
maybeBranchName,
|
|
327
|
-
branch,
|
|
328
|
-
...rest
|
|
329
|
-
}: BarePushArgs) => {
|
|
330
|
-
if (!!maybeBranchName) {
|
|
320
|
+
({ maybeApiOrDestination, maybeDestination, maybeBranchName, branch, ...rest }: BarePushArgs) => {
|
|
321
|
+
if (maybeBranchName) {
|
|
331
322
|
process.stderr.write(
|
|
332
323
|
yellow(
|
|
333
|
-
'Deprecation warning: Do not use the third parameter as a branch name. Please use a separate --branch option instead.'
|
|
334
|
-
)
|
|
324
|
+
'Deprecation warning: Do not use the third parameter as a branch name. Please use a separate --branch option instead.'
|
|
325
|
+
)
|
|
335
326
|
);
|
|
336
327
|
}
|
|
337
|
-
const
|
|
338
|
-
const destination = maybeDestination ||
|
|
328
|
+
const api = maybeDestination ? maybeApiOrDestination : undefined;
|
|
329
|
+
const destination = maybeDestination || maybeApiOrDestination;
|
|
339
330
|
return callback({
|
|
340
331
|
...rest,
|
|
341
332
|
destination,
|
|
342
|
-
|
|
333
|
+
api,
|
|
343
334
|
branchName: branch ?? maybeBranchName,
|
|
344
335
|
});
|
|
345
336
|
};
|
|
346
337
|
|
|
347
|
-
export function
|
|
338
|
+
export function getApiRoot({
|
|
348
339
|
name,
|
|
349
340
|
version,
|
|
350
341
|
config: { apis },
|
|
@@ -362,7 +353,7 @@ function uploadFileToS3(url: string, filePathOrBuffer: string | Buffer) {
|
|
|
362
353
|
typeof filePathOrBuffer === 'string'
|
|
363
354
|
? fs.statSync(filePathOrBuffer).size
|
|
364
355
|
: filePathOrBuffer.byteLength;
|
|
365
|
-
|
|
356
|
+
const readStream =
|
|
366
357
|
typeof filePathOrBuffer === 'string' ? fs.createReadStream(filePathOrBuffer) : filePathOrBuffer;
|
|
367
358
|
|
|
368
359
|
return fetch(url, {
|