@redocly/cli 1.0.0-beta.98 ā 1.0.0-rc.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/bin/cli.js +1 -1
- package/lib/__mocks__/@redocly/openapi-core.d.ts +96 -0
- package/lib/__mocks__/@redocly/openapi-core.js +79 -0
- package/lib/__mocks__/documents.d.ts +92 -0
- package/lib/__mocks__/documents.js +63 -0
- package/lib/__mocks__/fs.d.ts +9 -0
- package/lib/__mocks__/fs.js +9 -0
- package/lib/__mocks__/perf_hooks.d.ts +4 -0
- package/lib/__mocks__/perf_hooks.js +6 -0
- package/lib/__mocks__/redoc.d.ts +7 -0
- package/lib/__mocks__/redoc.js +5 -0
- package/lib/__mocks__/utils.d.ts +26 -4
- package/lib/__mocks__/utils.js +8 -3
- package/lib/__tests__/commands/build-docs.test.d.ts +1 -0
- package/lib/__tests__/commands/build-docs.test.js +59 -0
- package/lib/__tests__/commands/bundle.test.js +66 -30
- package/lib/__tests__/commands/join.test.d.ts +1 -0
- package/lib/__tests__/commands/join.test.js +85 -0
- package/lib/__tests__/commands/lint.test.d.ts +1 -0
- package/lib/__tests__/commands/lint.test.js +149 -0
- package/lib/__tests__/commands/push-region.test.js +5 -4
- package/lib/__tests__/commands/push.test.js +254 -39
- package/lib/__tests__/fetch-with-timeout.test.d.ts +1 -0
- package/lib/__tests__/fetch-with-timeout.test.js +38 -0
- package/lib/__tests__/fixtures/config.d.ts +22 -0
- package/lib/__tests__/fixtures/config.js +24 -0
- package/lib/__tests__/utils.test.js +429 -1
- package/lib/__tests__/wrapper.test.d.ts +1 -0
- package/lib/__tests__/wrapper.test.js +57 -0
- package/lib/commands/build-docs/index.d.ts +3 -0
- package/lib/commands/build-docs/index.js +50 -0
- package/{src/commands/preview-docs/preview-server/default.hbs ā lib/commands/build-docs/template.hbs} +2 -3
- package/lib/commands/build-docs/types.d.ts +23 -0
- package/lib/commands/build-docs/types.js +2 -0
- package/lib/commands/build-docs/utils.d.ts +7 -0
- package/lib/commands/build-docs/utils.js +99 -0
- package/lib/commands/bundle.d.ts +11 -12
- package/lib/commands/bundle.js +26 -24
- package/lib/commands/join.d.ts +12 -3
- package/lib/commands/join.js +295 -109
- package/lib/commands/lint.d.ts +11 -8
- package/lib/commands/lint.js +49 -19
- package/lib/commands/login.d.ts +5 -3
- package/lib/commands/login.js +2 -2
- package/lib/commands/preview-docs/index.d.ts +6 -6
- package/lib/commands/preview-docs/index.js +30 -20
- package/lib/commands/preview-docs/preview-server/oauth2-redirect.html +1 -1
- package/lib/commands/preview-docs/preview-server/preview-server.js +5 -4
- package/lib/commands/preview-docs/preview-server/server.d.ts +1 -1
- package/lib/commands/push.d.ts +21 -10
- package/lib/commands/push.js +110 -63
- package/lib/commands/split/__tests__/index.test.js +23 -8
- package/lib/commands/split/index.d.ts +5 -3
- package/lib/commands/split/index.js +15 -24
- package/lib/commands/split/types.d.ts +11 -11
- package/lib/commands/split/types.js +19 -19
- package/lib/commands/stats.d.ts +7 -4
- package/lib/commands/stats.js +13 -12
- package/lib/fetch-with-timeout.d.ts +2 -0
- package/lib/fetch-with-timeout.js +30 -0
- package/lib/index.js +194 -40
- package/lib/js-utils.d.ts +1 -0
- package/lib/js-utils.js +9 -3
- package/lib/types.d.ts +17 -1
- package/lib/update-version-notifier.d.ts +3 -0
- package/lib/update-version-notifier.js +105 -0
- package/lib/utils.d.ts +39 -5
- package/lib/utils.js +273 -37
- package/lib/wrapper.d.ts +4 -0
- package/lib/wrapper.js +52 -0
- package/package.json +18 -8
- package/README.md +0 -39
- package/src/__mocks__/utils.ts +0 -11
- package/src/__tests__/commands/bundle.test.ts +0 -120
- package/src/__tests__/commands/push-region.test.ts +0 -51
- package/src/__tests__/commands/push.test.ts +0 -158
- package/src/__tests__/utils.test.ts +0 -50
- package/src/assert-node-version.ts +0 -8
- package/src/commands/bundle.ts +0 -178
- package/src/commands/join.ts +0 -488
- package/src/commands/lint.ts +0 -110
- package/src/commands/login.ts +0 -21
- package/src/commands/preview-docs/index.ts +0 -188
- 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 -155
- package/src/commands/preview-docs/preview-server/server.ts +0 -91
- package/src/commands/push.ts +0 -357
- 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 -96
- package/src/commands/split/index.ts +0 -349
- package/src/commands/split/types.ts +0 -73
- package/src/commands/stats.ts +0 -115
- package/src/index.ts +0 -316
- package/src/js-utils.ts +0 -12
- package/src/types.ts +0 -13
- package/src/utils.ts +0 -300
- package/tsconfig.json +0 -9
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const fetch_with_timeout_1 = require("../fetch-with-timeout");
|
|
13
|
+
const node_fetch_1 = require("node-fetch");
|
|
14
|
+
jest.mock('node-fetch');
|
|
15
|
+
describe('fetchWithTimeout', () => {
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
jest.clearAllMocks();
|
|
18
|
+
});
|
|
19
|
+
it('should use bare node-fetch if AbortController is not available', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
20
|
+
// @ts-ignore
|
|
21
|
+
global.AbortController = undefined;
|
|
22
|
+
// @ts-ignore
|
|
23
|
+
global.setTimeout = jest.fn();
|
|
24
|
+
yield fetch_with_timeout_1.default('url', { method: 'GET' });
|
|
25
|
+
expect(node_fetch_1.default).toHaveBeenCalledWith('url', { method: 'GET' });
|
|
26
|
+
expect(global.setTimeout).toHaveBeenCalledTimes(0);
|
|
27
|
+
}));
|
|
28
|
+
it('should call node-fetch with signal if AbortController is available', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
29
|
+
global.AbortController = jest.fn().mockImplementation(() => ({ signal: 'something' }));
|
|
30
|
+
// @ts-ignore
|
|
31
|
+
global.setTimeout = jest.fn();
|
|
32
|
+
global.clearTimeout = jest.fn();
|
|
33
|
+
yield fetch_with_timeout_1.default('url');
|
|
34
|
+
expect(global.setTimeout).toHaveBeenCalledTimes(1);
|
|
35
|
+
expect(node_fetch_1.default).toHaveBeenCalledWith('url', { signal: 'something' });
|
|
36
|
+
expect(global.clearTimeout).toHaveBeenCalledTimes(1);
|
|
37
|
+
}));
|
|
38
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/// <reference types="jest" />
|
|
2
|
+
export declare const ConfigFixture: {
|
|
3
|
+
configFile: null;
|
|
4
|
+
styleguide: {
|
|
5
|
+
addIgnore: jest.Mock<any, any>;
|
|
6
|
+
skipRules: jest.Mock<any, any>;
|
|
7
|
+
skipPreprocessors: jest.Mock<any, any>;
|
|
8
|
+
saveIgnore: jest.Mock<any, any>;
|
|
9
|
+
skipDecorators: jest.Mock<any, any>;
|
|
10
|
+
ignore: null;
|
|
11
|
+
decorators: {
|
|
12
|
+
oas2: {};
|
|
13
|
+
oas3_0: {};
|
|
14
|
+
oas3_1: {};
|
|
15
|
+
};
|
|
16
|
+
preprocessors: {
|
|
17
|
+
oas2: {};
|
|
18
|
+
oas3_0: {};
|
|
19
|
+
oas3_1: {};
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ConfigFixture = void 0;
|
|
4
|
+
exports.ConfigFixture = {
|
|
5
|
+
configFile: null,
|
|
6
|
+
styleguide: {
|
|
7
|
+
addIgnore: jest.fn(),
|
|
8
|
+
skipRules: jest.fn(),
|
|
9
|
+
skipPreprocessors: jest.fn(),
|
|
10
|
+
saveIgnore: jest.fn(),
|
|
11
|
+
skipDecorators: jest.fn(),
|
|
12
|
+
ignore: null,
|
|
13
|
+
decorators: {
|
|
14
|
+
oas2: {},
|
|
15
|
+
oas3_0: {},
|
|
16
|
+
oas3_1: {},
|
|
17
|
+
},
|
|
18
|
+
preprocessors: {
|
|
19
|
+
oas2: {},
|
|
20
|
+
oas3_0: {},
|
|
21
|
+
oas3_1: {},
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
};
|
|
@@ -1,7 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
2
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
12
|
const utils_1 = require("../utils");
|
|
4
|
-
|
|
13
|
+
const openapi_core_1 = require("@redocly/openapi-core");
|
|
14
|
+
const colorette_1 = require("colorette");
|
|
15
|
+
const fs_1 = require("fs");
|
|
16
|
+
const path = require("path");
|
|
17
|
+
const process = require("process");
|
|
18
|
+
jest.mock('os');
|
|
19
|
+
jest.mock('colorette');
|
|
20
|
+
jest.mock('fs');
|
|
5
21
|
describe('isSubdir', () => {
|
|
6
22
|
it('can correctly determine if subdir', () => {
|
|
7
23
|
[
|
|
@@ -39,3 +55,415 @@ describe('pathToFilename', () => {
|
|
|
39
55
|
expect(processedPath).toEqual('user_createWithList');
|
|
40
56
|
});
|
|
41
57
|
});
|
|
58
|
+
describe('getFallbackApisOrExit', () => {
|
|
59
|
+
it('should find alias by filename', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
60
|
+
fs_1.existsSync.mockImplementationOnce(() => true);
|
|
61
|
+
const entry = yield utils_1.getFallbackApisOrExit(['./test.yaml'], {
|
|
62
|
+
apis: {
|
|
63
|
+
main: {
|
|
64
|
+
root: 'test.yaml',
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
expect(entry).toEqual([{ path: './test.yaml', alias: 'main' }]);
|
|
69
|
+
}));
|
|
70
|
+
});
|
|
71
|
+
describe('printConfigLintTotals', () => {
|
|
72
|
+
const totalProblemsMock = {
|
|
73
|
+
errors: 1,
|
|
74
|
+
warnings: 0,
|
|
75
|
+
ignored: 0,
|
|
76
|
+
};
|
|
77
|
+
const redColoretteMocks = colorette_1.red;
|
|
78
|
+
const yellowColoretteMocks = colorette_1.yellow;
|
|
79
|
+
beforeEach(() => {
|
|
80
|
+
yellowColoretteMocks.mockImplementation((text) => text);
|
|
81
|
+
redColoretteMocks.mockImplementation((text) => text);
|
|
82
|
+
jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
83
|
+
});
|
|
84
|
+
it('should print errors if such exist', () => {
|
|
85
|
+
utils_1.printConfigLintTotals(totalProblemsMock);
|
|
86
|
+
expect(process.stderr.write).toHaveBeenCalledWith('ā Your config has 1 error.\n');
|
|
87
|
+
expect(redColoretteMocks).toHaveBeenCalledWith('ā Your config has 1 error.\n');
|
|
88
|
+
});
|
|
89
|
+
it('should print warnign and error', () => {
|
|
90
|
+
utils_1.printConfigLintTotals(Object.assign(Object.assign({}, totalProblemsMock), { warnings: 2 }));
|
|
91
|
+
expect(process.stderr.write).toHaveBeenCalledWith('ā Your config has 1 error and 2 warnings.\n');
|
|
92
|
+
expect(redColoretteMocks).toHaveBeenCalledWith('ā Your config has 1 error and 2 warnings.\n');
|
|
93
|
+
});
|
|
94
|
+
it('should print warnign if no error', () => {
|
|
95
|
+
utils_1.printConfigLintTotals(Object.assign(Object.assign({}, totalProblemsMock), { errors: 0, warnings: 2 }));
|
|
96
|
+
expect(process.stderr.write).toHaveBeenCalledWith('You have 2 warnings.\n');
|
|
97
|
+
expect(yellowColoretteMocks).toHaveBeenCalledWith('You have 2 warnings.\n');
|
|
98
|
+
});
|
|
99
|
+
it('should print nothing if no error and no warnings', () => {
|
|
100
|
+
const result = utils_1.printConfigLintTotals(Object.assign(Object.assign({}, totalProblemsMock), { errors: 0 }));
|
|
101
|
+
expect(result).toBeUndefined();
|
|
102
|
+
expect(process.stderr.write).toHaveBeenCalledTimes(0);
|
|
103
|
+
expect(yellowColoretteMocks).toHaveBeenCalledTimes(0);
|
|
104
|
+
expect(redColoretteMocks).toHaveBeenCalledTimes(0);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
describe('getFallbackApisOrExit', () => {
|
|
108
|
+
const redColoretteMocks = colorette_1.red;
|
|
109
|
+
const yellowColoretteMocks = colorette_1.yellow;
|
|
110
|
+
const apis = {
|
|
111
|
+
main: {
|
|
112
|
+
root: 'someFile.yaml',
|
|
113
|
+
styleguide: {},
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
const config = { apis };
|
|
117
|
+
beforeEach(() => {
|
|
118
|
+
yellowColoretteMocks.mockImplementation((text) => text);
|
|
119
|
+
redColoretteMocks.mockImplementation((text) => text);
|
|
120
|
+
jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
121
|
+
jest.spyOn(process, 'exit').mockImplementation();
|
|
122
|
+
});
|
|
123
|
+
afterEach(() => {
|
|
124
|
+
jest.clearAllMocks();
|
|
125
|
+
});
|
|
126
|
+
it('should exit with error because no path provided', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
127
|
+
const apisConfig = {
|
|
128
|
+
apis: {},
|
|
129
|
+
};
|
|
130
|
+
expect.assertions(1);
|
|
131
|
+
try {
|
|
132
|
+
yield utils_1.getFallbackApisOrExit([''], apisConfig);
|
|
133
|
+
}
|
|
134
|
+
catch (e) {
|
|
135
|
+
expect(e.message).toEqual('Path cannot be empty.');
|
|
136
|
+
}
|
|
137
|
+
}));
|
|
138
|
+
it('should error if file from config do not exist', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
139
|
+
fs_1.existsSync.mockImplementationOnce(() => false);
|
|
140
|
+
expect.assertions(3);
|
|
141
|
+
try {
|
|
142
|
+
yield utils_1.getFallbackApisOrExit(undefined, config);
|
|
143
|
+
}
|
|
144
|
+
catch (e) {
|
|
145
|
+
expect(process.stderr.write).toHaveBeenCalledWith('\nsomeFile.yaml does not exist or is invalid.\n\n');
|
|
146
|
+
expect(process.stderr.write).toHaveBeenCalledWith('Please provide a valid path.\n\n');
|
|
147
|
+
expect(e.message).toEqual('Please provide a valid path.');
|
|
148
|
+
}
|
|
149
|
+
}));
|
|
150
|
+
it('should return valid array with results if such file exist', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
151
|
+
fs_1.existsSync.mockImplementationOnce(() => true);
|
|
152
|
+
jest.spyOn(path, 'resolve').mockImplementationOnce((_, path) => path);
|
|
153
|
+
const result = yield utils_1.getFallbackApisOrExit(undefined, config);
|
|
154
|
+
expect(process.stderr.write).toHaveBeenCalledTimes(0);
|
|
155
|
+
expect(process.exit).toHaveBeenCalledTimes(0);
|
|
156
|
+
expect(result).toStrictEqual([
|
|
157
|
+
{
|
|
158
|
+
alias: 'main',
|
|
159
|
+
path: 'someFile.yaml',
|
|
160
|
+
},
|
|
161
|
+
]);
|
|
162
|
+
}));
|
|
163
|
+
it('should exit with error in case if invalid path provided as args', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
164
|
+
const apisConfig = {
|
|
165
|
+
apis: {},
|
|
166
|
+
};
|
|
167
|
+
fs_1.existsSync.mockImplementationOnce(() => false);
|
|
168
|
+
expect.assertions(3);
|
|
169
|
+
try {
|
|
170
|
+
yield utils_1.getFallbackApisOrExit(['someFile.yaml'], apisConfig);
|
|
171
|
+
}
|
|
172
|
+
catch (e) {
|
|
173
|
+
expect(process.stderr.write).toHaveBeenCalledWith('\nsomeFile.yaml does not exist or is invalid.\n\n');
|
|
174
|
+
expect(process.stderr.write).toHaveBeenCalledWith('Please provide a valid path.\n\n');
|
|
175
|
+
expect(e.message).toEqual('Please provide a valid path.');
|
|
176
|
+
}
|
|
177
|
+
}));
|
|
178
|
+
it('should exit with error in case if invalid 2 path provided as args', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
179
|
+
const apisConfig = {
|
|
180
|
+
apis: {},
|
|
181
|
+
};
|
|
182
|
+
fs_1.existsSync.mockImplementationOnce(() => false);
|
|
183
|
+
expect.assertions(3);
|
|
184
|
+
try {
|
|
185
|
+
yield utils_1.getFallbackApisOrExit(['someFile.yaml', 'someFile2.yaml'], apisConfig);
|
|
186
|
+
}
|
|
187
|
+
catch (e) {
|
|
188
|
+
expect(process.stderr.write).toHaveBeenCalledWith('\nsomeFile.yaml does not exist or is invalid.\n\n');
|
|
189
|
+
expect(process.stderr.write).toHaveBeenCalledWith('Please provide a valid path.\n\n');
|
|
190
|
+
expect(e.message).toEqual('Please provide a valid path.');
|
|
191
|
+
}
|
|
192
|
+
}));
|
|
193
|
+
it('should exit with error if only one file exist ', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
194
|
+
const apisStub = Object.assign(Object.assign({}, apis), { notExist: {
|
|
195
|
+
root: 'notExist.yaml',
|
|
196
|
+
styleguide: {},
|
|
197
|
+
} });
|
|
198
|
+
const configStub = { apis: apisStub };
|
|
199
|
+
const existSyncMock = fs_1.existsSync.mockImplementation((path) => path.endsWith('someFile.yaml'));
|
|
200
|
+
expect.assertions(4);
|
|
201
|
+
try {
|
|
202
|
+
yield utils_1.getFallbackApisOrExit(undefined, configStub);
|
|
203
|
+
}
|
|
204
|
+
catch (e) {
|
|
205
|
+
expect(process.stderr.write).toHaveBeenCalledWith('\nnotExist.yaml does not exist or is invalid.\n\n');
|
|
206
|
+
expect(process.stderr.write).toHaveBeenCalledWith('Please provide a valid path.\n\n');
|
|
207
|
+
expect(process.stderr.write).toHaveBeenCalledTimes(2);
|
|
208
|
+
expect(e.message).toEqual('Please provide a valid path.');
|
|
209
|
+
}
|
|
210
|
+
existSyncMock.mockClear();
|
|
211
|
+
}));
|
|
212
|
+
it('should work ok if it is url passed', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
213
|
+
fs_1.existsSync.mockImplementationOnce(() => false);
|
|
214
|
+
openapi_core_1.isAbsoluteUrl.mockImplementation(() => true);
|
|
215
|
+
const apisConfig = {
|
|
216
|
+
apis: {
|
|
217
|
+
main: {
|
|
218
|
+
root: 'https://someLinkt/petstore.yaml?main',
|
|
219
|
+
styleguide: {},
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
const result = yield utils_1.getFallbackApisOrExit(undefined, apisConfig);
|
|
224
|
+
expect(process.stderr.write).toHaveBeenCalledTimes(0);
|
|
225
|
+
expect(result).toStrictEqual([
|
|
226
|
+
{
|
|
227
|
+
alias: 'main',
|
|
228
|
+
path: 'https://someLinkt/petstore.yaml?main',
|
|
229
|
+
},
|
|
230
|
+
]);
|
|
231
|
+
}));
|
|
232
|
+
});
|
|
233
|
+
describe('langToExt', () => {
|
|
234
|
+
it.each([
|
|
235
|
+
['php', '.php'],
|
|
236
|
+
['c#', '.cs'],
|
|
237
|
+
['shell', '.sh'],
|
|
238
|
+
['curl', '.sh'],
|
|
239
|
+
['bash', '.sh'],
|
|
240
|
+
['javascript', '.js'],
|
|
241
|
+
['js', '.js'],
|
|
242
|
+
['python', '.py'],
|
|
243
|
+
])('should infer file extension from lang - %s', (lang, expected) => {
|
|
244
|
+
expect(utils_1.langToExt(lang)).toBe(expected);
|
|
245
|
+
});
|
|
246
|
+
it('should ignore case when inferring file extension', () => {
|
|
247
|
+
expect(utils_1.langToExt('JavaScript')).toBe('.js');
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
describe('sorTopLevelKeysForOas', () => {
|
|
251
|
+
it('should sort oas3 top level keys', () => {
|
|
252
|
+
const openApi = {
|
|
253
|
+
openapi: '3.0.0',
|
|
254
|
+
components: {},
|
|
255
|
+
security: [],
|
|
256
|
+
tags: [],
|
|
257
|
+
servers: [],
|
|
258
|
+
paths: {},
|
|
259
|
+
info: {},
|
|
260
|
+
externalDocs: {},
|
|
261
|
+
webhooks: [],
|
|
262
|
+
'x-webhooks': [],
|
|
263
|
+
jsonSchemaDialect: '',
|
|
264
|
+
};
|
|
265
|
+
const orderedKeys = [
|
|
266
|
+
'openapi',
|
|
267
|
+
'info',
|
|
268
|
+
'jsonSchemaDialect',
|
|
269
|
+
'servers',
|
|
270
|
+
'security',
|
|
271
|
+
'tags',
|
|
272
|
+
'externalDocs',
|
|
273
|
+
'paths',
|
|
274
|
+
'webhooks',
|
|
275
|
+
'x-webhooks',
|
|
276
|
+
'components',
|
|
277
|
+
];
|
|
278
|
+
const result = utils_1.sortTopLevelKeysForOas(openApi);
|
|
279
|
+
Object.keys(result).forEach((key, index) => {
|
|
280
|
+
expect(key).toEqual(orderedKeys[index]);
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
it('should sort oas2 top level keys', () => {
|
|
284
|
+
const openApi = {
|
|
285
|
+
swagger: '2.0.0',
|
|
286
|
+
security: [],
|
|
287
|
+
tags: [],
|
|
288
|
+
paths: {},
|
|
289
|
+
info: {},
|
|
290
|
+
externalDocs: {},
|
|
291
|
+
host: '',
|
|
292
|
+
basePath: '',
|
|
293
|
+
securityDefinitions: [],
|
|
294
|
+
schemes: [],
|
|
295
|
+
consumes: [],
|
|
296
|
+
parameters: [],
|
|
297
|
+
produces: [],
|
|
298
|
+
definitions: [],
|
|
299
|
+
responses: [],
|
|
300
|
+
};
|
|
301
|
+
const orderedKeys = [
|
|
302
|
+
'swagger',
|
|
303
|
+
'info',
|
|
304
|
+
'host',
|
|
305
|
+
'basePath',
|
|
306
|
+
'schemes',
|
|
307
|
+
'consumes',
|
|
308
|
+
'produces',
|
|
309
|
+
'security',
|
|
310
|
+
'tags',
|
|
311
|
+
'externalDocs',
|
|
312
|
+
'paths',
|
|
313
|
+
'definitions',
|
|
314
|
+
'parameters',
|
|
315
|
+
'responses',
|
|
316
|
+
'securityDefinitions',
|
|
317
|
+
];
|
|
318
|
+
const result = utils_1.sortTopLevelKeysForOas(openApi);
|
|
319
|
+
Object.keys(result).forEach((key, index) => {
|
|
320
|
+
expect(key).toEqual(orderedKeys[index]);
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
describe('handleErrors', () => {
|
|
325
|
+
const ref = 'openapi/test.yaml';
|
|
326
|
+
const redColoretteMocks = colorette_1.red;
|
|
327
|
+
const blueColoretteMocks = colorette_1.blue;
|
|
328
|
+
beforeEach(() => {
|
|
329
|
+
jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
330
|
+
jest.spyOn(process, 'exit').mockImplementation((code) => code);
|
|
331
|
+
redColoretteMocks.mockImplementation((text) => text);
|
|
332
|
+
blueColoretteMocks.mockImplementation((text) => text);
|
|
333
|
+
});
|
|
334
|
+
afterEach(() => {
|
|
335
|
+
jest.clearAllMocks();
|
|
336
|
+
});
|
|
337
|
+
it('should handle ResolveError', () => {
|
|
338
|
+
const resolveError = new openapi_core_1.ResolveError(new Error('File not found'));
|
|
339
|
+
expect(() => utils_1.handleError(resolveError, ref)).toThrowError(utils_1.HandledError);
|
|
340
|
+
expect(redColoretteMocks).toHaveBeenCalledTimes(1);
|
|
341
|
+
expect(process.stderr.write).toHaveBeenCalledWith(`Failed to resolve api definition at openapi/test.yaml:\n\n - File not found.\n\n`);
|
|
342
|
+
});
|
|
343
|
+
it('should handle YamlParseError', () => {
|
|
344
|
+
const yamlParseError = new openapi_core_1.YamlParseError(new Error('Invalid yaml'), {});
|
|
345
|
+
expect(() => utils_1.handleError(yamlParseError, ref)).toThrowError(utils_1.HandledError);
|
|
346
|
+
expect(redColoretteMocks).toHaveBeenCalledTimes(1);
|
|
347
|
+
expect(process.stderr.write).toHaveBeenCalledWith(`Failed to parse api definition at openapi/test.yaml:\n\n - Invalid yaml.\n\n`);
|
|
348
|
+
});
|
|
349
|
+
it('should handle CircularJSONNotSupportedError', () => {
|
|
350
|
+
const circularError = new utils_1.CircularJSONNotSupportedError(new Error('Circular json'));
|
|
351
|
+
expect(() => utils_1.handleError(circularError, ref)).toThrowError(utils_1.HandledError);
|
|
352
|
+
expect(process.stderr.write).toHaveBeenCalledWith(`Detected circular reference which can't be converted to JSON.\n` +
|
|
353
|
+
`Try to use ${colorette_1.blue('yaml')} output or remove ${colorette_1.blue('--dereferenced')}.\n\n`);
|
|
354
|
+
});
|
|
355
|
+
it('should handle SyntaxError', () => {
|
|
356
|
+
const testError = new SyntaxError('Unexpected identifier');
|
|
357
|
+
testError.stack = 'test stack';
|
|
358
|
+
expect(() => utils_1.handleError(testError, ref)).toThrowError(utils_1.HandledError);
|
|
359
|
+
expect(process.stderr.write).toHaveBeenCalledWith('Syntax error: Unexpected identifier test stack\n\n');
|
|
360
|
+
});
|
|
361
|
+
it('should throw unknown error', () => {
|
|
362
|
+
const testError = new Error('Test error');
|
|
363
|
+
expect(() => utils_1.handleError(testError, ref)).toThrowError(utils_1.HandledError);
|
|
364
|
+
expect(process.stderr.write).toHaveBeenCalledWith(`Something went wrong when processing openapi/test.yaml:\n\n - Test error.\n\n`);
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
describe('checkIfRulesetExist', () => {
|
|
368
|
+
beforeEach(() => {
|
|
369
|
+
jest.spyOn(process, 'exit').mockImplementation((code) => code);
|
|
370
|
+
});
|
|
371
|
+
afterEach(() => {
|
|
372
|
+
jest.clearAllMocks();
|
|
373
|
+
});
|
|
374
|
+
it('should throw an error if rules are not provided', () => {
|
|
375
|
+
const rules = {
|
|
376
|
+
oas2: {},
|
|
377
|
+
oas3_0: {},
|
|
378
|
+
oas3_1: {},
|
|
379
|
+
};
|
|
380
|
+
expect(() => utils_1.checkIfRulesetExist(rules)).toThrowError('ā ļø No rules were configured. Learn how to configure rules: https://redocly.com/docs/cli/rules/');
|
|
381
|
+
});
|
|
382
|
+
it('should not throw an error if rules are provided', () => {
|
|
383
|
+
const rules = {
|
|
384
|
+
oas2: { 'operation-4xx-response': 'error' },
|
|
385
|
+
oas3_0: {},
|
|
386
|
+
oas3_1: {},
|
|
387
|
+
};
|
|
388
|
+
utils_1.checkIfRulesetExist(rules);
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
describe('cleanColors', () => {
|
|
392
|
+
it('should remove colors from string', () => {
|
|
393
|
+
const stringWithColors = `String for ${colorette_1.red('test')}`;
|
|
394
|
+
const result = utils_1.cleanColors(stringWithColors);
|
|
395
|
+
expect(result).not.toMatch(/\x1b\[\d+m/g);
|
|
396
|
+
});
|
|
397
|
+
});
|
|
398
|
+
describe('cleanArgs', () => {
|
|
399
|
+
beforeEach(() => {
|
|
400
|
+
// @ts-ignore
|
|
401
|
+
openapi_core_1.isAbsoluteUrl = jest.requireActual('@redocly/openapi-core').isAbsoluteUrl;
|
|
402
|
+
// @ts-ignore
|
|
403
|
+
fs_1.existsSync = (value) => jest.requireActual('fs').existsSync(path.resolve(__dirname, value));
|
|
404
|
+
// @ts-ignore
|
|
405
|
+
fs_1.statSync = (value) => jest.requireActual('fs').statSync(path.resolve(__dirname, value));
|
|
406
|
+
});
|
|
407
|
+
afterEach(() => {
|
|
408
|
+
jest.clearAllMocks();
|
|
409
|
+
});
|
|
410
|
+
it('should remove potentially sensitive data from args', () => {
|
|
411
|
+
const testArgs = {
|
|
412
|
+
config: './fixtures/redocly.yaml',
|
|
413
|
+
apis: ['main@v1', 'fixtures/openapi.yaml', 'http://some.url/openapi.yaml'],
|
|
414
|
+
format: 'codeframe',
|
|
415
|
+
};
|
|
416
|
+
expect(utils_1.cleanArgs(testArgs)).toEqual({
|
|
417
|
+
config: 'file-yaml',
|
|
418
|
+
apis: ['api-name@api-version', 'file-yaml', 'http://url'],
|
|
419
|
+
format: 'codeframe',
|
|
420
|
+
});
|
|
421
|
+
});
|
|
422
|
+
it('should remove potentially sensitive data from a push destination', () => {
|
|
423
|
+
const testArgs = {
|
|
424
|
+
destination: '@org/name@version',
|
|
425
|
+
};
|
|
426
|
+
expect(utils_1.cleanArgs(testArgs)).toEqual({
|
|
427
|
+
destination: '@organization/api-name@api-version',
|
|
428
|
+
});
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
describe('cleanRawInput', () => {
|
|
432
|
+
beforeEach(() => {
|
|
433
|
+
// @ts-ignore
|
|
434
|
+
openapi_core_1.isAbsoluteUrl = jest.requireActual('@redocly/openapi-core').isAbsoluteUrl;
|
|
435
|
+
// @ts-ignore
|
|
436
|
+
fs_1.existsSync = (value) => jest.requireActual('fs').existsSync(path.resolve(__dirname, value));
|
|
437
|
+
// @ts-ignore
|
|
438
|
+
fs_1.statSync = (value) => jest.requireActual('fs').statSync(path.resolve(__dirname, value));
|
|
439
|
+
});
|
|
440
|
+
afterEach(() => {
|
|
441
|
+
jest.clearAllMocks();
|
|
442
|
+
});
|
|
443
|
+
it('should remove potentially sensitive data from raw CLI input', () => {
|
|
444
|
+
const rawInput = [
|
|
445
|
+
'redocly',
|
|
446
|
+
'bundle',
|
|
447
|
+
'api-name@api-version',
|
|
448
|
+
'./fixtures/openapi.yaml',
|
|
449
|
+
'http://some.url/openapi.yaml',
|
|
450
|
+
'--config=fixtures/redocly.yaml',
|
|
451
|
+
'--output',
|
|
452
|
+
'fixtures',
|
|
453
|
+
];
|
|
454
|
+
expect(utils_1.cleanRawInput(rawInput)).toEqual('redocly bundle api-name@api-version file-yaml http://url --config=file-yaml --output folder');
|
|
455
|
+
});
|
|
456
|
+
it('should preserve safe data from raw CLI input', () => {
|
|
457
|
+
const rawInput = [
|
|
458
|
+
'redocly',
|
|
459
|
+
'lint',
|
|
460
|
+
'./fixtures/openapi.json',
|
|
461
|
+
'--format',
|
|
462
|
+
'stylish',
|
|
463
|
+
'--extends=minimal',
|
|
464
|
+
'--skip-rule',
|
|
465
|
+
'operation-4xx-response',
|
|
466
|
+
];
|
|
467
|
+
expect(utils_1.cleanRawInput(rawInput)).toEqual('redocly lint file-json --format stylish --extends=minimal --skip-rule operation-4xx-response');
|
|
468
|
+
});
|
|
469
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const utils_1 = require("../utils");
|
|
13
|
+
const process = require("process");
|
|
14
|
+
const wrapper_1 = require("../wrapper");
|
|
15
|
+
const lint_1 = require("../commands/lint");
|
|
16
|
+
const push_1 = require("../commands/push");
|
|
17
|
+
jest.mock('node-fetch');
|
|
18
|
+
jest.mock('../utils', () => ({
|
|
19
|
+
sendTelemetry: jest.fn(),
|
|
20
|
+
loadConfigAndHandleErrors: jest.fn(),
|
|
21
|
+
}));
|
|
22
|
+
jest.mock('../commands/lint', () => ({
|
|
23
|
+
handleLint: jest.fn(),
|
|
24
|
+
lintConfigCallback: jest.fn(),
|
|
25
|
+
}));
|
|
26
|
+
describe('commandWrapper', () => {
|
|
27
|
+
it('should send telemetry if there is "telemetry: on" in the config', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
28
|
+
utils_1.loadConfigAndHandleErrors.mockImplementation(() => {
|
|
29
|
+
return { telemetry: 'on', styleguide: { recommendedFallback: true } };
|
|
30
|
+
});
|
|
31
|
+
process.env.REDOCLY_TELEMETRY = 'on';
|
|
32
|
+
const wrappedHandler = wrapper_1.commandWrapper(lint_1.handleLint);
|
|
33
|
+
yield wrappedHandler({});
|
|
34
|
+
expect(lint_1.handleLint).toHaveBeenCalledTimes(1);
|
|
35
|
+
expect(utils_1.sendTelemetry).toHaveBeenCalledTimes(1);
|
|
36
|
+
expect(utils_1.sendTelemetry).toHaveBeenCalledWith({}, 0, false);
|
|
37
|
+
}));
|
|
38
|
+
it('should NOT send telemetry if there is "telemetry: off" in the config', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
39
|
+
utils_1.loadConfigAndHandleErrors.mockImplementation(() => {
|
|
40
|
+
return { telemetry: 'off', styleguide: { recommendedFallback: true } };
|
|
41
|
+
});
|
|
42
|
+
process.env.REDOCLY_TELEMETRY = 'on';
|
|
43
|
+
const wrappedHandler = wrapper_1.commandWrapper(lint_1.handleLint);
|
|
44
|
+
yield wrappedHandler({});
|
|
45
|
+
expect(lint_1.handleLint).toHaveBeenCalledTimes(1);
|
|
46
|
+
expect(utils_1.sendTelemetry).toHaveBeenCalledTimes(0);
|
|
47
|
+
}));
|
|
48
|
+
it('should pass files from arguments to config', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
49
|
+
const filesToPush = ['test1.yaml', 'test2.yaml'];
|
|
50
|
+
const loadConfigMock = utils_1.loadConfigAndHandleErrors;
|
|
51
|
+
const argv = {
|
|
52
|
+
files: filesToPush,
|
|
53
|
+
};
|
|
54
|
+
yield wrapper_1.commandWrapper(push_1.handlePush)(argv);
|
|
55
|
+
expect(loadConfigMock).toHaveBeenCalledWith(expect.objectContaining({ files: filesToPush }));
|
|
56
|
+
}));
|
|
57
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.handlerBuildCommand = void 0;
|
|
13
|
+
const redoc_1 = require("redoc");
|
|
14
|
+
const path_1 = require("path");
|
|
15
|
+
const fs_1 = require("fs");
|
|
16
|
+
const perf_hooks_1 = require("perf_hooks");
|
|
17
|
+
const utils_1 = require("./utils");
|
|
18
|
+
const openapi_core_1 = require("@redocly/openapi-core");
|
|
19
|
+
const utils_2 = require("../../utils");
|
|
20
|
+
const handlerBuildCommand = (argv, configFromFile) => __awaiter(void 0, void 0, void 0, function* () {
|
|
21
|
+
var _a;
|
|
22
|
+
const startedAt = perf_hooks_1.performance.now();
|
|
23
|
+
const config = openapi_core_1.getMergedConfig(configFromFile, argv.api);
|
|
24
|
+
const apis = yield utils_2.getFallbackApisOrExit(argv.api ? [argv.api] : [], config);
|
|
25
|
+
const { path: pathToApi } = apis[0];
|
|
26
|
+
const options = {
|
|
27
|
+
output: argv.o,
|
|
28
|
+
title: argv.title,
|
|
29
|
+
disableGoogleFont: argv.disableGoogleFont,
|
|
30
|
+
templateFileName: argv.template,
|
|
31
|
+
templateOptions: argv.templateOptions || {},
|
|
32
|
+
redocOptions: utils_1.getObjectOrJSON((_a = argv.theme) === null || _a === void 0 ? void 0 : _a.openapi, config),
|
|
33
|
+
};
|
|
34
|
+
const redocCurrentVersion = require('../../../package.json').dependencies.redoc.substring(1); // remove ~
|
|
35
|
+
try {
|
|
36
|
+
const elapsed = utils_2.getExecutionTime(startedAt);
|
|
37
|
+
const api = yield redoc_1.loadAndBundleSpec(openapi_core_1.isAbsoluteUrl(pathToApi)
|
|
38
|
+
? pathToApi
|
|
39
|
+
: path_1.resolve(argv.config ? path_1.dirname(argv.config) : '', pathToApi));
|
|
40
|
+
const pageHTML = yield utils_1.getPageHTML(api, pathToApi, Object.assign(Object.assign({}, options), { redocCurrentVersion }));
|
|
41
|
+
fs_1.mkdirSync(path_1.dirname(options.output), { recursive: true });
|
|
42
|
+
fs_1.writeFileSync(options.output, pageHTML);
|
|
43
|
+
const sizeInKiB = Math.ceil(Buffer.byteLength(pageHTML) / 1024);
|
|
44
|
+
process.stdout.write(`\nš bundled successfully in: ${options.output} (${sizeInKiB} KiB) [ā± ${elapsed}].\n`);
|
|
45
|
+
}
|
|
46
|
+
catch (e) {
|
|
47
|
+
utils_2.exitWithError(e);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
exports.handlerBuildCommand = handlerBuildCommand;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
<!DOCTYPE html>
|
|
3
2
|
<html>
|
|
4
3
|
|
|
@@ -14,11 +13,11 @@
|
|
|
14
13
|
}
|
|
15
14
|
</style>
|
|
16
15
|
{{{redocHead}}}
|
|
17
|
-
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
|
|
16
|
+
{{#unless disableGoogleFont}}<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">{{/unless}}
|
|
18
17
|
</head>
|
|
19
18
|
|
|
20
19
|
<body>
|
|
21
20
|
{{{redocHTML}}}
|
|
22
21
|
</body>
|
|
23
22
|
|
|
24
|
-
</html>
|
|
23
|
+
</html>
|