@redocly/cli 1.0.0 → 1.0.1
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 +8 -0
- package/lib/commands/build-docs/index.js +2 -4
- package/lib/commands/build-docs/utils.d.ts +1 -1
- package/lib/commands/build-docs/utils.js +3 -3
- package/package.json +2 -2
- package/src/__mocks__/@redocly/openapi-core.ts +80 -0
- package/src/__mocks__/documents.ts +63 -0
- package/src/__mocks__/fs.ts +6 -0
- package/src/__mocks__/perf_hooks.ts +3 -0
- package/src/__mocks__/redoc.ts +2 -0
- package/src/__mocks__/utils.ts +19 -0
- package/src/__tests__/commands/build-docs.test.ts +62 -0
- package/src/__tests__/commands/bundle.test.ts +150 -0
- package/src/__tests__/commands/join.test.ts +122 -0
- package/src/__tests__/commands/lint.test.ts +190 -0
- package/src/__tests__/commands/push-region.test.ts +58 -0
- package/src/__tests__/commands/push.test.ts +492 -0
- package/src/__tests__/fetch-with-timeout.test.ts +35 -0
- package/src/__tests__/fixtures/config.ts +21 -0
- package/src/__tests__/fixtures/openapi.json +0 -0
- package/src/__tests__/fixtures/openapi.yaml +0 -0
- package/src/__tests__/fixtures/redocly.yaml +0 -0
- package/src/__tests__/utils.test.ts +564 -0
- package/src/__tests__/wrapper.test.ts +57 -0
- package/src/assert-node-version.ts +8 -0
- package/src/commands/build-docs/index.ts +50 -0
- package/src/commands/build-docs/template.hbs +23 -0
- package/src/commands/build-docs/types.ts +24 -0
- package/src/commands/build-docs/utils.ts +110 -0
- package/src/commands/bundle.ts +177 -0
- package/src/commands/join.ts +811 -0
- package/src/commands/lint.ts +151 -0
- package/src/commands/login.ts +27 -0
- package/src/commands/preview-docs/index.ts +190 -0
- package/src/commands/preview-docs/preview-server/default.hbs +24 -0
- package/src/commands/preview-docs/preview-server/hot.js +42 -0
- package/src/commands/preview-docs/preview-server/oauth2-redirect.html +21 -0
- package/src/commands/preview-docs/preview-server/preview-server.ts +156 -0
- package/src/commands/preview-docs/preview-server/server.ts +91 -0
- package/src/commands/push.ts +441 -0
- package/src/commands/split/__tests__/fixtures/samples.json +61 -0
- package/src/commands/split/__tests__/fixtures/spec.json +70 -0
- package/src/commands/split/__tests__/fixtures/webhooks.json +85 -0
- package/src/commands/split/__tests__/index.test.ts +137 -0
- package/src/commands/split/index.ts +385 -0
- package/src/commands/split/types.ts +85 -0
- package/src/commands/stats.ts +119 -0
- package/src/custom.d.ts +1 -0
- package/src/fetch-with-timeout.ts +21 -0
- package/src/index.ts +484 -0
- package/src/js-utils.ts +17 -0
- package/src/types.ts +40 -0
- package/src/update-version-notifier.ts +106 -0
- package/src/utils.ts +590 -0
- package/src/wrapper.ts +42 -0
- package/tsconfig.json +9 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type BuildDocsOptions = {
|
|
2
|
+
watch?: boolean;
|
|
3
|
+
output?: string;
|
|
4
|
+
title?: string;
|
|
5
|
+
disableGoogleFont?: boolean;
|
|
6
|
+
port?: number;
|
|
7
|
+
templateFileName?: string;
|
|
8
|
+
templateOptions?: any;
|
|
9
|
+
redocOptions?: any;
|
|
10
|
+
redocCurrentVersion: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type BuildDocsArgv = {
|
|
14
|
+
api: string;
|
|
15
|
+
o: string;
|
|
16
|
+
title?: string;
|
|
17
|
+
disableGoogleFont?: boolean;
|
|
18
|
+
template?: string;
|
|
19
|
+
templateOptions: Record<string, any>;
|
|
20
|
+
theme: {
|
|
21
|
+
openapi: string | Record<string, unknown>;
|
|
22
|
+
};
|
|
23
|
+
config?: string;
|
|
24
|
+
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { createElement } from 'react';
|
|
2
|
+
import { createStore, Redoc } from 'redoc';
|
|
3
|
+
import { Config, isAbsoluteUrl } from '@redocly/openapi-core';
|
|
4
|
+
|
|
5
|
+
import { renderToString } from 'react-dom/server';
|
|
6
|
+
import { ServerStyleSheet } from 'styled-components';
|
|
7
|
+
import { compile } from 'handlebars';
|
|
8
|
+
import { dirname, join, resolve } from 'path';
|
|
9
|
+
import { existsSync, lstatSync, readFileSync } from 'fs';
|
|
10
|
+
|
|
11
|
+
import type { BuildDocsOptions } from './types';
|
|
12
|
+
import { red } from 'colorette';
|
|
13
|
+
import { exitWithError } from '../../utils';
|
|
14
|
+
|
|
15
|
+
export function getObjectOrJSON(
|
|
16
|
+
openapiOptions: string | Record<string, unknown>,
|
|
17
|
+
config: Config
|
|
18
|
+
): JSON | Record<string, unknown> | Config {
|
|
19
|
+
switch (typeof openapiOptions) {
|
|
20
|
+
case 'object':
|
|
21
|
+
return openapiOptions;
|
|
22
|
+
case 'string':
|
|
23
|
+
try {
|
|
24
|
+
if (existsSync(openapiOptions) && lstatSync(openapiOptions).isFile()) {
|
|
25
|
+
return JSON.parse(readFileSync(openapiOptions, 'utf-8'));
|
|
26
|
+
} else {
|
|
27
|
+
return JSON.parse(openapiOptions);
|
|
28
|
+
}
|
|
29
|
+
} catch (e) {
|
|
30
|
+
process.stderr.write(
|
|
31
|
+
red(
|
|
32
|
+
`Encountered error:\n\n${openapiOptions}\n\nis neither a file with a valid JSON object neither a stringified JSON object.`
|
|
33
|
+
)
|
|
34
|
+
);
|
|
35
|
+
exitWithError(e);
|
|
36
|
+
}
|
|
37
|
+
break;
|
|
38
|
+
default: {
|
|
39
|
+
if (config) {
|
|
40
|
+
process.stderr.write(`Found ${config.configFile} and using theme.openapi options\n`);
|
|
41
|
+
|
|
42
|
+
return config.theme.openapi ? config.theme.openapi : {};
|
|
43
|
+
}
|
|
44
|
+
return {};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return {};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function getPageHTML(
|
|
51
|
+
api: any,
|
|
52
|
+
pathToApi: string,
|
|
53
|
+
{
|
|
54
|
+
title,
|
|
55
|
+
disableGoogleFont,
|
|
56
|
+
templateFileName,
|
|
57
|
+
templateOptions,
|
|
58
|
+
redocOptions = {},
|
|
59
|
+
redocCurrentVersion,
|
|
60
|
+
}: BuildDocsOptions,
|
|
61
|
+
configPath?: string
|
|
62
|
+
) {
|
|
63
|
+
process.stderr.write('Prerendering docs\n');
|
|
64
|
+
|
|
65
|
+
const apiUrl = redocOptions.specUrl || (isAbsoluteUrl(pathToApi) ? pathToApi : undefined);
|
|
66
|
+
const store = await createStore(api, apiUrl, redocOptions);
|
|
67
|
+
const sheet = new ServerStyleSheet();
|
|
68
|
+
|
|
69
|
+
const html = renderToString(sheet.collectStyles(createElement(Redoc, { store })));
|
|
70
|
+
const state = await store.toJS();
|
|
71
|
+
const css = sheet.getStyleTags();
|
|
72
|
+
|
|
73
|
+
templateFileName = templateFileName
|
|
74
|
+
? templateFileName
|
|
75
|
+
: redocOptions?.htmlTemplate
|
|
76
|
+
? resolve(configPath ? dirname(configPath) : '', redocOptions.htmlTemplate)
|
|
77
|
+
: join(__dirname, './template.hbs');
|
|
78
|
+
const template = compile(readFileSync(templateFileName).toString());
|
|
79
|
+
return template({
|
|
80
|
+
redocHTML: `
|
|
81
|
+
<div id="redoc">${html || ''}</div>
|
|
82
|
+
<script>
|
|
83
|
+
${`const __redoc_state = ${sanitizeJSONString(JSON.stringify(state))};` || ''}
|
|
84
|
+
|
|
85
|
+
var container = document.getElementById('redoc');
|
|
86
|
+
Redoc.${'hydrate(__redoc_state, container)'};
|
|
87
|
+
|
|
88
|
+
</script>`,
|
|
89
|
+
redocHead:
|
|
90
|
+
`<script src="https://cdn.redoc.ly/redoc/v${redocCurrentVersion}/bundles/redoc.standalone.js"></script>` +
|
|
91
|
+
css,
|
|
92
|
+
title: title || api.info.title || 'ReDoc documentation',
|
|
93
|
+
disableGoogleFont,
|
|
94
|
+
templateOptions,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function sanitizeJSONString(str: string): string {
|
|
99
|
+
return escapeClosingScriptTag(escapeUnicode(str));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// see http://www.thespanner.co.uk/2011/07/25/the-json-specification-is-now-wrong/
|
|
103
|
+
export function escapeClosingScriptTag(str: string): string {
|
|
104
|
+
return str.replace(/<\/script>/g, '<\\/script>');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// see http://www.thespanner.co.uk/2011/07/25/the-json-specification-is-now-wrong/
|
|
108
|
+
export function escapeUnicode(str: string): string {
|
|
109
|
+
return str.replace(/\u2028|\u2029/g, (m) => '\\u202' + (m === '\u2028' ? '8' : '9'));
|
|
110
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import {
|
|
2
|
+
formatProblems,
|
|
3
|
+
getTotals,
|
|
4
|
+
getMergedConfig,
|
|
5
|
+
lint,
|
|
6
|
+
bundle,
|
|
7
|
+
Config,
|
|
8
|
+
OutputFormat,
|
|
9
|
+
} from '@redocly/openapi-core';
|
|
10
|
+
import {
|
|
11
|
+
dumpBundle,
|
|
12
|
+
getExecutionTime,
|
|
13
|
+
getFallbackApisOrExit,
|
|
14
|
+
getOutputFileName,
|
|
15
|
+
handleError,
|
|
16
|
+
printUnusedWarnings,
|
|
17
|
+
saveBundle,
|
|
18
|
+
printLintTotals,
|
|
19
|
+
checkIfRulesetExist,
|
|
20
|
+
sortTopLevelKeysForOas,
|
|
21
|
+
} from '../utils';
|
|
22
|
+
import type { OutputExtensions, Skips, Totals } from '../types';
|
|
23
|
+
import { performance } from 'perf_hooks';
|
|
24
|
+
import { blue, gray, green, yellow } from 'colorette';
|
|
25
|
+
import { writeFileSync } from 'fs';
|
|
26
|
+
|
|
27
|
+
export type BundleOptions = {
|
|
28
|
+
apis?: string[];
|
|
29
|
+
'max-problems': number;
|
|
30
|
+
extends?: string[];
|
|
31
|
+
config?: string;
|
|
32
|
+
format: OutputFormat;
|
|
33
|
+
output?: string;
|
|
34
|
+
ext: OutputExtensions;
|
|
35
|
+
dereferenced?: boolean;
|
|
36
|
+
force?: boolean;
|
|
37
|
+
lint?: boolean;
|
|
38
|
+
metafile?: string;
|
|
39
|
+
'remove-unused-components'?: boolean;
|
|
40
|
+
'keep-url-references'?: boolean;
|
|
41
|
+
} & Skips;
|
|
42
|
+
|
|
43
|
+
export async function handleBundle(argv: BundleOptions, config: Config, version: string) {
|
|
44
|
+
const removeUnusedComponents =
|
|
45
|
+
argv['remove-unused-components'] ||
|
|
46
|
+
config.rawConfig?.styleguide?.decorators?.hasOwnProperty('remove-unused-components');
|
|
47
|
+
const apis = await getFallbackApisOrExit(argv.apis, config);
|
|
48
|
+
const totals: Totals = { errors: 0, warnings: 0, ignored: 0 };
|
|
49
|
+
const maxProblems = argv['max-problems'];
|
|
50
|
+
|
|
51
|
+
for (const { path, alias } of apis) {
|
|
52
|
+
try {
|
|
53
|
+
const startedAt = performance.now();
|
|
54
|
+
const resolvedConfig = getMergedConfig(config, alias);
|
|
55
|
+
const { styleguide } = resolvedConfig;
|
|
56
|
+
|
|
57
|
+
styleguide.skipRules(argv['skip-rule']);
|
|
58
|
+
styleguide.skipPreprocessors(argv['skip-preprocessor']);
|
|
59
|
+
styleguide.skipDecorators(argv['skip-decorator']);
|
|
60
|
+
|
|
61
|
+
if (argv.lint) {
|
|
62
|
+
checkIfRulesetExist(styleguide.rules);
|
|
63
|
+
if (config.styleguide.recommendedFallback) {
|
|
64
|
+
process.stderr.write(
|
|
65
|
+
`No configurations were provided -- using built in ${blue(
|
|
66
|
+
'recommended'
|
|
67
|
+
)} configuration by default.\n\n`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
const results = await lint({
|
|
71
|
+
ref: path,
|
|
72
|
+
config: resolvedConfig,
|
|
73
|
+
});
|
|
74
|
+
const fileLintTotals = getTotals(results);
|
|
75
|
+
|
|
76
|
+
totals.errors += fileLintTotals.errors;
|
|
77
|
+
totals.warnings += fileLintTotals.warnings;
|
|
78
|
+
totals.ignored += fileLintTotals.ignored;
|
|
79
|
+
|
|
80
|
+
formatProblems(results, {
|
|
81
|
+
format: argv.format || 'codeframe',
|
|
82
|
+
totals: fileLintTotals,
|
|
83
|
+
version,
|
|
84
|
+
maxProblems,
|
|
85
|
+
});
|
|
86
|
+
printLintTotals(fileLintTotals, 2);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
process.stderr.write(gray(`bundling ${path}...\n`));
|
|
90
|
+
|
|
91
|
+
const {
|
|
92
|
+
bundle: result,
|
|
93
|
+
problems,
|
|
94
|
+
...meta
|
|
95
|
+
} = await bundle({
|
|
96
|
+
config: resolvedConfig,
|
|
97
|
+
ref: path,
|
|
98
|
+
dereference: argv.dereferenced,
|
|
99
|
+
removeUnusedComponents,
|
|
100
|
+
keepUrlRefs: argv['keep-url-references'],
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const fileTotals = getTotals(problems);
|
|
104
|
+
const { outputFile, ext } = getOutputFileName(path, apis.length, argv.output, argv.ext);
|
|
105
|
+
|
|
106
|
+
if (fileTotals.errors === 0 || argv.force) {
|
|
107
|
+
if (!argv.output) {
|
|
108
|
+
const output = dumpBundle(
|
|
109
|
+
sortTopLevelKeysForOas(result.parsed),
|
|
110
|
+
argv.ext || 'yaml',
|
|
111
|
+
argv.dereferenced
|
|
112
|
+
);
|
|
113
|
+
process.stdout.write(output);
|
|
114
|
+
} else {
|
|
115
|
+
const output = dumpBundle(sortTopLevelKeysForOas(result.parsed), ext, argv.dereferenced);
|
|
116
|
+
saveBundle(outputFile, output);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
totals.errors += fileTotals.errors;
|
|
121
|
+
totals.warnings += fileTotals.warnings;
|
|
122
|
+
totals.ignored += fileTotals.ignored;
|
|
123
|
+
|
|
124
|
+
formatProblems(problems, {
|
|
125
|
+
format: argv.format,
|
|
126
|
+
maxProblems,
|
|
127
|
+
totals: fileTotals,
|
|
128
|
+
version,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
if (argv.metafile) {
|
|
132
|
+
if (apis.length > 1) {
|
|
133
|
+
process.stderr.write(
|
|
134
|
+
yellow(`[WARNING] "--metafile" cannot be used with multiple apis. Skipping...`)
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
{
|
|
138
|
+
writeFileSync(argv.metafile, JSON.stringify(meta), 'utf-8');
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const elapsed = getExecutionTime(startedAt);
|
|
143
|
+
if (fileTotals.errors > 0) {
|
|
144
|
+
if (argv.force) {
|
|
145
|
+
process.stderr.write(
|
|
146
|
+
`❓ Created a bundle for ${blue(path)} at ${blue(outputFile)} with errors ${green(
|
|
147
|
+
elapsed
|
|
148
|
+
)}.\n${yellow('Errors ignored because of --force')}.\n`
|
|
149
|
+
);
|
|
150
|
+
} else {
|
|
151
|
+
process.stderr.write(
|
|
152
|
+
`❌ Errors encountered while bundling ${blue(
|
|
153
|
+
path
|
|
154
|
+
)}: bundle not created (use --force to ignore errors).\n`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
process.stderr.write(
|
|
159
|
+
`📦 Created a bundle for ${blue(path)} at ${blue(outputFile)} ${green(elapsed)}.\n`
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const removedCount = meta.visitorsData?.['remove-unused-components']?.removedCount;
|
|
164
|
+
if (removedCount) {
|
|
165
|
+
process.stderr.write(gray(`🧹 Removed ${removedCount} unused components.\n`));
|
|
166
|
+
}
|
|
167
|
+
} catch (e) {
|
|
168
|
+
handleError(e, path);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
printUnusedWarnings(config.styleguide);
|
|
173
|
+
|
|
174
|
+
if (!(totals.errors === 0 || argv.force)) {
|
|
175
|
+
throw new Error('Bundle failed.');
|
|
176
|
+
}
|
|
177
|
+
}
|