@redocly/cli 1.0.0-beta.125 → 1.0.0-beta.127
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/lib/__mocks__/@redocly/openapi-core.d.ts +20 -2
- package/lib/__mocks__/@redocly/openapi-core.js +17 -1
- package/lib/__mocks__/utils.d.ts +12 -0
- package/lib/__mocks__/utils.js +3 -1
- package/lib/__tests__/commands/join.test.js +21 -0
- package/lib/__tests__/commands/lint.test.js +9 -0
- package/lib/__tests__/commands/push.test.js +16 -0
- package/lib/__tests__/fixtures/config.d.ts +10 -0
- package/lib/__tests__/fixtures/config.js +10 -0
- package/lib/__tests__/utils.test.js +151 -0
- package/lib/commands/build-docs/utils.js +6 -2
- package/lib/commands/bundle.js +3 -2
- package/lib/commands/join.d.ts +2 -0
- package/lib/commands/join.js +17 -1
- package/lib/commands/lint.js +4 -0
- package/lib/commands/preview-docs/index.js +1 -1
- package/lib/commands/push.js +3 -3
- package/lib/index.js +6 -0
- package/lib/update-version-notifier.d.ts +2 -0
- package/lib/update-version-notifier.js +100 -0
- package/lib/utils.d.ts +4 -1
- package/lib/utils.js +82 -13
- package/package.json +5 -3
- package/src/__mocks__/@redocly/openapi-core.ts +0 -66
- package/src/__mocks__/documents.ts +0 -63
- package/src/__mocks__/fs.ts +0 -6
- package/src/__mocks__/perf_hooks.ts +0 -3
- package/src/__mocks__/redoc.ts +0 -2
- package/src/__mocks__/utils.ts +0 -17
- package/src/__tests__/commands/build-docs.test.ts +0 -61
- package/src/__tests__/commands/bundle.test.ts +0 -169
- package/src/__tests__/commands/join.test.ts +0 -83
- package/src/__tests__/commands/lint.test.ts +0 -154
- package/src/__tests__/commands/push-region.test.ts +0 -51
- package/src/__tests__/commands/push.test.ts +0 -342
- package/src/__tests__/fixtures/config.ts +0 -11
- package/src/__tests__/utils.test.ts +0 -263
- package/src/assert-node-version.ts +0 -8
- package/src/commands/build-docs/index.ts +0 -56
- package/src/commands/build-docs/template.hbs +0 -23
- package/src/commands/build-docs/types.ts +0 -26
- package/src/commands/build-docs/utils.ts +0 -108
- package/src/commands/bundle.ts +0 -163
- package/src/commands/join.ts +0 -789
- package/src/commands/lint.ts +0 -154
- package/src/commands/login.ts +0 -21
- package/src/commands/preview-docs/index.ts +0 -182
- package/src/commands/preview-docs/preview-server/default.hbs +0 -24
- package/src/commands/preview-docs/preview-server/hot.js +0 -42
- package/src/commands/preview-docs/preview-server/oauth2-redirect.html +0 -21
- package/src/commands/preview-docs/preview-server/preview-server.ts +0 -156
- package/src/commands/preview-docs/preview-server/server.ts +0 -91
- package/src/commands/push.ts +0 -387
- package/src/commands/split/__tests__/fixtures/samples.json +0 -61
- package/src/commands/split/__tests__/fixtures/spec.json +0 -70
- package/src/commands/split/__tests__/fixtures/webhooks.json +0 -88
- package/src/commands/split/__tests__/index.test.ts +0 -137
- package/src/commands/split/index.ts +0 -378
- package/src/commands/split/types.ts +0 -85
- package/src/commands/stats.ts +0 -117
- package/src/custom.d.ts +0 -1
- package/src/index.ts +0 -429
- package/src/js-utils.ts +0 -17
- package/src/types.ts +0 -28
- package/src/utils.ts +0 -393
- package/tsconfig.json +0 -9
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -1,263 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getFallbackApisOrExit,
|
|
3
|
-
isSubdir,
|
|
4
|
-
pathToFilename,
|
|
5
|
-
printConfigLintTotals,
|
|
6
|
-
langToExt,
|
|
7
|
-
} from '../utils';
|
|
8
|
-
import { ResolvedApi, Totals, isAbsoluteUrl } from '@redocly/openapi-core';
|
|
9
|
-
import { red, yellow } from 'colorette';
|
|
10
|
-
import { existsSync } from 'fs';
|
|
11
|
-
import * as path from 'path';
|
|
12
|
-
|
|
13
|
-
jest.mock('os');
|
|
14
|
-
jest.mock('colorette');
|
|
15
|
-
jest.mock('fs');
|
|
16
|
-
|
|
17
|
-
describe('isSubdir', () => {
|
|
18
|
-
it('can correctly determine if subdir', () => {
|
|
19
|
-
(
|
|
20
|
-
[
|
|
21
|
-
['/foo', '/foo', false],
|
|
22
|
-
['/foo', '/bar', false],
|
|
23
|
-
['/foo', '/foobar', false],
|
|
24
|
-
['/foo', '/foo/bar', true],
|
|
25
|
-
['/foo', '/foo/../bar', false],
|
|
26
|
-
['/foo', '/foo/./bar', true],
|
|
27
|
-
['/bar/../foo', '/foo/bar', true],
|
|
28
|
-
['/foo', './bar', false],
|
|
29
|
-
['/foo', '/foo/..bar', true],
|
|
30
|
-
] as [string, string, boolean][]
|
|
31
|
-
).forEach(([parent, child, expectRes]) => {
|
|
32
|
-
expect(isSubdir(parent, child)).toBe(expectRes);
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('can correctly determine if subdir for windows-based paths', () => {
|
|
37
|
-
const os = require('os');
|
|
38
|
-
os.platform.mockImplementation(() => 'win32');
|
|
39
|
-
|
|
40
|
-
(
|
|
41
|
-
[
|
|
42
|
-
['C:/Foo', 'C:/Foo/Bar', true],
|
|
43
|
-
['C:\\Foo', 'C:\\Bar', false],
|
|
44
|
-
['C:\\Foo', 'D:\\Foo\\Bar', false],
|
|
45
|
-
] as [string, string, boolean][]
|
|
46
|
-
).forEach(([parent, child, expectRes]) => {
|
|
47
|
-
expect(isSubdir(parent, child)).toBe(expectRes);
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
afterEach(() => {
|
|
52
|
-
jest.resetModules();
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
describe('pathToFilename', () => {
|
|
57
|
-
it('should use correct path separator', () => {
|
|
58
|
-
const processedPath = pathToFilename('/user/createWithList', '_');
|
|
59
|
-
expect(processedPath).toEqual('user_createWithList');
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
describe('getFallbackApisOrExit', () => {
|
|
64
|
-
it('should find alias by filename', async () => {
|
|
65
|
-
(existsSync as jest.Mock<any, any>).mockImplementationOnce(() => true);
|
|
66
|
-
const entry = await getFallbackApisOrExit(['./test.yaml'], {
|
|
67
|
-
apis: {
|
|
68
|
-
main: {
|
|
69
|
-
root: 'test.yaml',
|
|
70
|
-
},
|
|
71
|
-
},
|
|
72
|
-
} as any);
|
|
73
|
-
expect(entry).toEqual([{ path: './test.yaml', alias: 'main' }]);
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
describe('printConfigLintTotals', () => {
|
|
78
|
-
const totalProblemsMock: Totals = {
|
|
79
|
-
errors: 1,
|
|
80
|
-
warnings: 0,
|
|
81
|
-
ignored: 0,
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
const redColoretteMocks = red as jest.Mock<any, any>;
|
|
85
|
-
const yellowColoretteMocks = yellow as jest.Mock<any, any>;
|
|
86
|
-
|
|
87
|
-
beforeEach(() => {
|
|
88
|
-
yellowColoretteMocks.mockImplementation((text: string) => text);
|
|
89
|
-
redColoretteMocks.mockImplementation((text: string) => text);
|
|
90
|
-
jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('should print errors if such exist', () => {
|
|
94
|
-
printConfigLintTotals(totalProblemsMock);
|
|
95
|
-
expect(process.stderr.write).toHaveBeenCalledWith('❌ Your config has 1 error.\n');
|
|
96
|
-
expect(redColoretteMocks).toHaveBeenCalledWith('❌ Your config has 1 error.\n');
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('should print warnign and error', () => {
|
|
100
|
-
printConfigLintTotals({ ...totalProblemsMock, warnings: 2 });
|
|
101
|
-
expect(process.stderr.write).toHaveBeenCalledWith(
|
|
102
|
-
'❌ Your config has 1 error and 2 warnings.\n'
|
|
103
|
-
);
|
|
104
|
-
expect(redColoretteMocks).toHaveBeenCalledWith('❌ Your config has 1 error and 2 warnings.\n');
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('should print warnign if no error', () => {
|
|
108
|
-
printConfigLintTotals({ ...totalProblemsMock, errors: 0, warnings: 2 });
|
|
109
|
-
expect(process.stderr.write).toHaveBeenCalledWith('You have 2 warnings.\n');
|
|
110
|
-
expect(yellowColoretteMocks).toHaveBeenCalledWith('You have 2 warnings.\n');
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('should print nothing if no error and no warnings', () => {
|
|
114
|
-
const result = printConfigLintTotals({ ...totalProblemsMock, errors: 0 });
|
|
115
|
-
expect(result).toBeUndefined();
|
|
116
|
-
expect(process.stderr.write).toHaveBeenCalledTimes(0);
|
|
117
|
-
expect(yellowColoretteMocks).toHaveBeenCalledTimes(0);
|
|
118
|
-
expect(redColoretteMocks).toHaveBeenCalledTimes(0);
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
describe('getFallbackApisOrExit', () => {
|
|
123
|
-
const redColoretteMocks = red as jest.Mock<any, any>;
|
|
124
|
-
const yellowColoretteMocks = yellow as jest.Mock<any, any>;
|
|
125
|
-
|
|
126
|
-
const apis: Record<string, ResolvedApi> = {
|
|
127
|
-
main: {
|
|
128
|
-
root: 'someFile.yaml',
|
|
129
|
-
styleguide: {},
|
|
130
|
-
},
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
const config = { apis };
|
|
134
|
-
|
|
135
|
-
beforeEach(() => {
|
|
136
|
-
yellowColoretteMocks.mockImplementation((text: string) => text);
|
|
137
|
-
redColoretteMocks.mockImplementation((text: string) => text);
|
|
138
|
-
jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
139
|
-
jest.spyOn(process, 'exit').mockImplementation();
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it('should exit with error because no path provided', async () => {
|
|
143
|
-
const apisConfig = {
|
|
144
|
-
apis: {},
|
|
145
|
-
};
|
|
146
|
-
await getFallbackApisOrExit([''], apisConfig);
|
|
147
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it('should error if file from config do not exist', async () => {
|
|
151
|
-
(existsSync as jest.Mock<any, any>).mockImplementationOnce(() => false);
|
|
152
|
-
await getFallbackApisOrExit(undefined, config);
|
|
153
|
-
|
|
154
|
-
expect(process.stderr.write).toHaveBeenCalledWith(
|
|
155
|
-
'\n someFile.yaml does not exist or is invalid. Please provide a valid path. \n\n'
|
|
156
|
-
);
|
|
157
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it('should return valid array with results if such file exist', async () => {
|
|
161
|
-
(existsSync as jest.Mock<any, any>).mockImplementationOnce(() => true);
|
|
162
|
-
jest.spyOn(path, 'resolve').mockImplementationOnce((_, path) => path);
|
|
163
|
-
|
|
164
|
-
const result = await getFallbackApisOrExit(undefined, config);
|
|
165
|
-
expect(process.stderr.write).toHaveBeenCalledTimes(0);
|
|
166
|
-
expect(process.exit).toHaveBeenCalledTimes(0);
|
|
167
|
-
expect(result).toStrictEqual([
|
|
168
|
-
{
|
|
169
|
-
alias: 'main',
|
|
170
|
-
path: 'someFile.yaml',
|
|
171
|
-
},
|
|
172
|
-
]);
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
it('should exit with error in case if invalid path provided as args', async () => {
|
|
176
|
-
const apisConfig = {
|
|
177
|
-
apis: {},
|
|
178
|
-
};
|
|
179
|
-
(existsSync as jest.Mock<any, any>).mockImplementationOnce(() => false);
|
|
180
|
-
await getFallbackApisOrExit(['someFile.yaml'], apisConfig);
|
|
181
|
-
|
|
182
|
-
expect(process.stderr.write).toHaveBeenCalledWith(
|
|
183
|
-
'\n someFile.yaml does not exist or is invalid. Please provide a valid path. \n\n'
|
|
184
|
-
);
|
|
185
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
it('should exit with error in case if invalid 2 path provided as args', async () => {
|
|
189
|
-
const apisConfig = {
|
|
190
|
-
apis: {},
|
|
191
|
-
};
|
|
192
|
-
(existsSync as jest.Mock<any, any>).mockImplementationOnce(() => false);
|
|
193
|
-
await getFallbackApisOrExit(['someFile.yaml', 'someFile2.yaml'], apisConfig);
|
|
194
|
-
|
|
195
|
-
expect(process.stderr.write).lastCalledWith(
|
|
196
|
-
'\n someFile2.yaml does not exist or is invalid. Please provide a valid path. \n\n'
|
|
197
|
-
);
|
|
198
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
it('should exit with error if only one file exist ', async () => {
|
|
202
|
-
const apisStub = {
|
|
203
|
-
...apis,
|
|
204
|
-
notExist: {
|
|
205
|
-
root: 'notExist.yaml',
|
|
206
|
-
styleguide: {},
|
|
207
|
-
},
|
|
208
|
-
};
|
|
209
|
-
const configStub = { apis: apisStub };
|
|
210
|
-
|
|
211
|
-
(existsSync as jest.Mock<any, any>).mockImplementationOnce((path) => path === 'someFile.yaml');
|
|
212
|
-
|
|
213
|
-
await getFallbackApisOrExit(undefined, configStub);
|
|
214
|
-
|
|
215
|
-
expect(process.stderr.write).toBeCalledWith(
|
|
216
|
-
'\n notExist.yaml does not exist or is invalid. Please provide a valid path. \n\n'
|
|
217
|
-
);
|
|
218
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
it('should work ok if it is url passed', async () => {
|
|
222
|
-
(existsSync as jest.Mock<any, any>).mockImplementationOnce(() => false);
|
|
223
|
-
(isAbsoluteUrl as jest.Mock<any, any>).mockImplementation(() => true);
|
|
224
|
-
const apisConfig = {
|
|
225
|
-
apis: {
|
|
226
|
-
main: {
|
|
227
|
-
root: 'https://someLinkt/petstore.yaml?main',
|
|
228
|
-
styleguide: {},
|
|
229
|
-
},
|
|
230
|
-
},
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
const result = await getFallbackApisOrExit(undefined, apisConfig);
|
|
234
|
-
|
|
235
|
-
expect(process.stderr.write).toHaveBeenCalledTimes(0);
|
|
236
|
-
expect(process.exit).toHaveBeenCalledTimes(0);
|
|
237
|
-
expect(result).toStrictEqual([
|
|
238
|
-
{
|
|
239
|
-
alias: 'main',
|
|
240
|
-
path: 'https://someLinkt/petstore.yaml?main',
|
|
241
|
-
},
|
|
242
|
-
]);
|
|
243
|
-
});
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
describe('langToExt', () => {
|
|
247
|
-
it.each([
|
|
248
|
-
['php', '.php'],
|
|
249
|
-
['c#', '.cs'],
|
|
250
|
-
['shell', '.sh'],
|
|
251
|
-
['curl', '.sh'],
|
|
252
|
-
['bash', '.sh'],
|
|
253
|
-
['javascript', '.js'],
|
|
254
|
-
['js', '.js'],
|
|
255
|
-
['python', '.py'],
|
|
256
|
-
])('should infer file extension from lang - %s', (lang, expected) => {
|
|
257
|
-
expect(langToExt(lang)).toBe(expected);
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
it('should ignore case when inferring file extension', () => {
|
|
261
|
-
expect(langToExt('JavaScript')).toBe('.js');
|
|
262
|
-
});
|
|
263
|
-
});
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { loadAndBundleSpec } from 'redoc';
|
|
2
|
-
import { dirname, resolve } from 'path';
|
|
3
|
-
import { writeFileSync, mkdirSync } from 'fs';
|
|
4
|
-
import { performance } from 'perf_hooks';
|
|
5
|
-
|
|
6
|
-
import { getObjectOrJSON, getPageHTML } from './utils';
|
|
7
|
-
import type { BuildDocsArgv } from './types';
|
|
8
|
-
import { getMergedConfig, isAbsoluteUrl } from '@redocly/openapi-core';
|
|
9
|
-
import {
|
|
10
|
-
exitWithError,
|
|
11
|
-
getExecutionTime,
|
|
12
|
-
getFallbackApisOrExit,
|
|
13
|
-
loadConfigAndHandleErrors,
|
|
14
|
-
} from '../../utils';
|
|
15
|
-
|
|
16
|
-
export const handlerBuildCommand = async (argv: BuildDocsArgv) => {
|
|
17
|
-
const startedAt = performance.now();
|
|
18
|
-
|
|
19
|
-
const configFromFile = await loadConfigAndHandleErrors({ configPath: argv.config });
|
|
20
|
-
const config = getMergedConfig(configFromFile, argv.api);
|
|
21
|
-
|
|
22
|
-
const apis = await getFallbackApisOrExit(argv.api ? [argv.api] : [], config);
|
|
23
|
-
const { path: pathToApi } = apis[0];
|
|
24
|
-
|
|
25
|
-
const options = {
|
|
26
|
-
output: argv.o,
|
|
27
|
-
cdn: argv.cdn,
|
|
28
|
-
title: argv.title,
|
|
29
|
-
disableGoogleFont: argv.disableGoogleFont,
|
|
30
|
-
templateFileName: argv.template,
|
|
31
|
-
templateOptions: argv.templateOptions || {},
|
|
32
|
-
redocOptions: getObjectOrJSON(argv.theme?.openapi, config),
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const redocCurrentVersion = require('../../../package.json').dependencies.redoc.substring(1); // remove ~
|
|
36
|
-
|
|
37
|
-
try {
|
|
38
|
-
const elapsed = getExecutionTime(startedAt);
|
|
39
|
-
|
|
40
|
-
const api = await loadAndBundleSpec(
|
|
41
|
-
isAbsoluteUrl(pathToApi)
|
|
42
|
-
? pathToApi
|
|
43
|
-
: resolve(argv.config ? dirname(argv.config) : '', pathToApi)
|
|
44
|
-
);
|
|
45
|
-
const pageHTML = await getPageHTML(api, pathToApi, { ...options, redocCurrentVersion });
|
|
46
|
-
|
|
47
|
-
mkdirSync(dirname(options.output), { recursive: true });
|
|
48
|
-
writeFileSync(options.output, pageHTML);
|
|
49
|
-
const sizeInKiB = Math.ceil(Buffer.byteLength(pageHTML) / 1024);
|
|
50
|
-
process.stdout.write(
|
|
51
|
-
`\n🎉 bundled successfully in: ${options.output} (${sizeInKiB} KiB) [⏱ ${elapsed}].\n`
|
|
52
|
-
);
|
|
53
|
-
} catch (e) {
|
|
54
|
-
exitWithError(e);
|
|
55
|
-
}
|
|
56
|
-
};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
3
|
-
|
|
4
|
-
<head>
|
|
5
|
-
<meta charset="utf8" />
|
|
6
|
-
<title>{{title}}</title>
|
|
7
|
-
<!-- needed for adaptive design -->
|
|
8
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
9
|
-
<style>
|
|
10
|
-
body {
|
|
11
|
-
padding: 0;
|
|
12
|
-
margin: 0;
|
|
13
|
-
}
|
|
14
|
-
</style>
|
|
15
|
-
{{{redocHead}}}
|
|
16
|
-
{{#unless disableGoogleFont}}<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">{{/unless}}
|
|
17
|
-
</head>
|
|
18
|
-
|
|
19
|
-
<body>
|
|
20
|
-
{{{redocHTML}}}
|
|
21
|
-
</body>
|
|
22
|
-
|
|
23
|
-
</html>
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
export type BuildDocsOptions = {
|
|
2
|
-
watch?: boolean;
|
|
3
|
-
cdn?: boolean;
|
|
4
|
-
output?: string;
|
|
5
|
-
title?: string;
|
|
6
|
-
disableGoogleFont?: boolean;
|
|
7
|
-
port?: number;
|
|
8
|
-
templateFileName?: string;
|
|
9
|
-
templateOptions?: any;
|
|
10
|
-
redocOptions?: any;
|
|
11
|
-
redocCurrentVersion: string;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export type BuildDocsArgv = {
|
|
15
|
-
api: string;
|
|
16
|
-
o: string;
|
|
17
|
-
cdn: boolean;
|
|
18
|
-
title?: string;
|
|
19
|
-
disableGoogleFont?: boolean;
|
|
20
|
-
template?: string;
|
|
21
|
-
templateOptions: Record<string, any>;
|
|
22
|
-
theme: {
|
|
23
|
-
openapi: string | Record<string, unknown>;
|
|
24
|
-
};
|
|
25
|
-
config?: string;
|
|
26
|
-
};
|
|
@@ -1,108 +0,0 @@
|
|
|
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 { join } 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`);
|
|
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
|
-
cdn,
|
|
55
|
-
title,
|
|
56
|
-
disableGoogleFont,
|
|
57
|
-
templateFileName,
|
|
58
|
-
templateOptions,
|
|
59
|
-
redocOptions = {},
|
|
60
|
-
redocCurrentVersion,
|
|
61
|
-
}: BuildDocsOptions
|
|
62
|
-
) {
|
|
63
|
-
process.stderr.write('Prerendering docs');
|
|
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 ? templateFileName : join(__dirname, './template.hbs');
|
|
74
|
-
const template = compile(readFileSync(templateFileName).toString());
|
|
75
|
-
return template({
|
|
76
|
-
redocHTML: `
|
|
77
|
-
<div id="redoc">${html || ''}</div>
|
|
78
|
-
<script>
|
|
79
|
-
${`const __redoc_state = ${sanitizeJSONString(JSON.stringify(state))};` || ''}
|
|
80
|
-
|
|
81
|
-
var container = document.getElementById('redoc');
|
|
82
|
-
Redoc.${'hydrate(__redoc_state, container)'};
|
|
83
|
-
|
|
84
|
-
</script>`,
|
|
85
|
-
redocHead:
|
|
86
|
-
(cdn
|
|
87
|
-
? '<script src="https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js"></script>'
|
|
88
|
-
: `<script src="https://cdn.redoc.ly/redoc/v${redocCurrentVersion}/bundles/redoc.standalone.js"></script>`) +
|
|
89
|
-
css,
|
|
90
|
-
title: title || api.info.title || 'ReDoc documentation',
|
|
91
|
-
disableGoogleFont,
|
|
92
|
-
templateOptions,
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export function sanitizeJSONString(str: string): string {
|
|
97
|
-
return escapeClosingScriptTag(escapeUnicode(str));
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// see http://www.thespanner.co.uk/2011/07/25/the-json-specification-is-now-wrong/
|
|
101
|
-
export function escapeClosingScriptTag(str: string): string {
|
|
102
|
-
return str.replace(/<\/script>/g, '<\\/script>');
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// see http://www.thespanner.co.uk/2011/07/25/the-json-specification-is-now-wrong/
|
|
106
|
-
export function escapeUnicode(str: string): string {
|
|
107
|
-
return str.replace(/\u2028|\u2029/g, (m) => '\\u202' + (m === '\u2028' ? '8' : '9'));
|
|
108
|
-
}
|
package/src/commands/bundle.ts
DELETED
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
import { formatProblems, getTotals, getMergedConfig, lint, bundle } from '@redocly/openapi-core';
|
|
2
|
-
import {
|
|
3
|
-
dumpBundle,
|
|
4
|
-
getExecutionTime,
|
|
5
|
-
getFallbackApisOrExit,
|
|
6
|
-
getOutputFileName,
|
|
7
|
-
handleError,
|
|
8
|
-
printUnusedWarnings,
|
|
9
|
-
saveBundle,
|
|
10
|
-
printLintTotals,
|
|
11
|
-
loadConfigAndHandleErrors,
|
|
12
|
-
} from '../utils';
|
|
13
|
-
import type { CommonOptions, OutputExtensions, Skips, Totals } from '../types';
|
|
14
|
-
import { performance } from 'perf_hooks';
|
|
15
|
-
import { blue, gray, green, yellow } from 'colorette';
|
|
16
|
-
import { writeFileSync } from 'fs';
|
|
17
|
-
|
|
18
|
-
export type BundleOptions = CommonOptions &
|
|
19
|
-
Skips & {
|
|
20
|
-
output?: string;
|
|
21
|
-
ext: OutputExtensions;
|
|
22
|
-
dereferenced?: boolean;
|
|
23
|
-
force?: boolean;
|
|
24
|
-
lint?: boolean;
|
|
25
|
-
metafile?: string;
|
|
26
|
-
'remove-unused-components'?: boolean;
|
|
27
|
-
'keep-url-references'?: boolean;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export async function handleBundle(argv: BundleOptions, version: string) {
|
|
31
|
-
const config = await loadConfigAndHandleErrors({
|
|
32
|
-
configPath: argv.config,
|
|
33
|
-
customExtends: argv.extends,
|
|
34
|
-
});
|
|
35
|
-
const removeUnusedComponents =
|
|
36
|
-
argv['remove-unused-components'] ||
|
|
37
|
-
config.rawConfig?.styleguide?.decorators?.hasOwnProperty('remove-unused-components');
|
|
38
|
-
const apis = await getFallbackApisOrExit(argv.apis, config);
|
|
39
|
-
const totals: Totals = { errors: 0, warnings: 0, ignored: 0 };
|
|
40
|
-
const maxProblems = argv['max-problems'];
|
|
41
|
-
|
|
42
|
-
for (const { path, alias } of apis) {
|
|
43
|
-
try {
|
|
44
|
-
const startedAt = performance.now();
|
|
45
|
-
const resolvedConfig = getMergedConfig(config, alias);
|
|
46
|
-
const { styleguide } = resolvedConfig;
|
|
47
|
-
|
|
48
|
-
styleguide.skipRules(argv['skip-rule']);
|
|
49
|
-
styleguide.skipPreprocessors(argv['skip-preprocessor']);
|
|
50
|
-
styleguide.skipDecorators(argv['skip-decorator']);
|
|
51
|
-
|
|
52
|
-
if (argv.lint) {
|
|
53
|
-
if (config.styleguide.recommendedFallback) {
|
|
54
|
-
process.stderr.write(
|
|
55
|
-
`No configurations were provided -- using built in ${blue(
|
|
56
|
-
'recommended'
|
|
57
|
-
)} configuration by default.\n\n`
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
const results = await lint({
|
|
61
|
-
ref: path,
|
|
62
|
-
config: resolvedConfig,
|
|
63
|
-
});
|
|
64
|
-
const fileLintTotals = getTotals(results);
|
|
65
|
-
|
|
66
|
-
totals.errors += fileLintTotals.errors;
|
|
67
|
-
totals.warnings += fileLintTotals.warnings;
|
|
68
|
-
totals.ignored += fileLintTotals.ignored;
|
|
69
|
-
|
|
70
|
-
formatProblems(results, {
|
|
71
|
-
format: argv.format || 'codeframe',
|
|
72
|
-
totals: fileLintTotals,
|
|
73
|
-
version,
|
|
74
|
-
maxProblems,
|
|
75
|
-
});
|
|
76
|
-
printLintTotals(fileLintTotals, 2);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
process.stderr.write(gray(`bundling ${path}...\n`));
|
|
80
|
-
|
|
81
|
-
const {
|
|
82
|
-
bundle: result,
|
|
83
|
-
problems,
|
|
84
|
-
...meta
|
|
85
|
-
} = await bundle({
|
|
86
|
-
config: resolvedConfig,
|
|
87
|
-
ref: path,
|
|
88
|
-
dereference: argv.dereferenced,
|
|
89
|
-
removeUnusedComponents,
|
|
90
|
-
keepUrlRefs: argv['keep-url-references'],
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
const fileTotals = getTotals(problems);
|
|
94
|
-
const { outputFile, ext } = getOutputFileName(path, apis.length, argv.output, argv.ext);
|
|
95
|
-
|
|
96
|
-
if (fileTotals.errors === 0 || argv.force) {
|
|
97
|
-
if (!argv.output) {
|
|
98
|
-
const output = dumpBundle(result.parsed, argv.ext || 'yaml', argv.dereferenced);
|
|
99
|
-
process.stdout.write(output);
|
|
100
|
-
} else {
|
|
101
|
-
const output = dumpBundle(result.parsed, ext, argv.dereferenced);
|
|
102
|
-
saveBundle(outputFile, output);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
totals.errors += fileTotals.errors;
|
|
107
|
-
totals.warnings += fileTotals.warnings;
|
|
108
|
-
totals.ignored += fileTotals.ignored;
|
|
109
|
-
|
|
110
|
-
formatProblems(problems, {
|
|
111
|
-
format: argv.format,
|
|
112
|
-
maxProblems,
|
|
113
|
-
totals: fileTotals,
|
|
114
|
-
version,
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
if (argv.metafile) {
|
|
118
|
-
if (apis.length > 1) {
|
|
119
|
-
process.stderr.write(
|
|
120
|
-
yellow(`[WARNING] "--metafile" cannot be used with multiple apis. Skipping...`)
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
{
|
|
124
|
-
writeFileSync(argv.metafile, JSON.stringify(meta), 'utf-8');
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const elapsed = getExecutionTime(startedAt);
|
|
129
|
-
if (fileTotals.errors > 0) {
|
|
130
|
-
if (argv.force) {
|
|
131
|
-
process.stderr.write(
|
|
132
|
-
`❓ Created a bundle for ${blue(path)} at ${blue(outputFile)} with errors ${green(
|
|
133
|
-
elapsed
|
|
134
|
-
)}.\n${yellow('Errors ignored because of --force')}.\n`
|
|
135
|
-
);
|
|
136
|
-
} else {
|
|
137
|
-
process.stderr.write(
|
|
138
|
-
`❌ Errors encountered while bundling ${blue(
|
|
139
|
-
path
|
|
140
|
-
)}: bundle not created (use --force to ignore errors).\n`
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
} else {
|
|
144
|
-
process.stderr.write(
|
|
145
|
-
`📦 Created a bundle for ${blue(path)} at ${blue(outputFile)} ${green(elapsed)}.\n`
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const removedCount = meta.visitorsData?.['remove-unused-components']?.removedCount;
|
|
150
|
-
if (removedCount) {
|
|
151
|
-
process.stderr.write(gray(`🧹 Removed ${removedCount} unused components.\n`));
|
|
152
|
-
}
|
|
153
|
-
} catch (e) {
|
|
154
|
-
handleError(e, path);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
printUnusedWarnings(config.styleguide);
|
|
159
|
-
|
|
160
|
-
// defer process exit to allow STDOUT pipe to flush
|
|
161
|
-
// see https://github.com/nodejs/node-v0.x-archive/issues/3737#issuecomment-19156072
|
|
162
|
-
process.once('exit', () => process.exit(totals.errors === 0 || argv.force ? 0 : 1));
|
|
163
|
-
}
|