@redocly/cli 1.0.0-beta.109 → 1.0.0-beta.110

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.
Files changed (40) hide show
  1. package/lib/__mocks__/fs.d.ts +2 -0
  2. package/lib/__mocks__/fs.js +3 -1
  3. package/lib/__mocks__/redoc.d.ts +7 -0
  4. package/lib/__mocks__/redoc.js +5 -0
  5. package/lib/__tests__/commands/build-docs.test.d.ts +1 -0
  6. package/lib/__tests__/commands/build-docs.test.js +58 -0
  7. package/lib/__tests__/commands/lint.test.js +10 -2
  8. package/lib/__tests__/commands/push.test.js +28 -0
  9. package/lib/commands/build-docs/index.d.ts +2 -0
  10. package/lib/commands/build-docs/index.js +45 -0
  11. package/lib/commands/build-docs/types.d.ts +22 -0
  12. package/lib/commands/build-docs/types.js +2 -0
  13. package/lib/commands/build-docs/utils.d.ts +8 -0
  14. package/lib/commands/build-docs/utils.js +108 -0
  15. package/lib/commands/bundle.js +1 -1
  16. package/lib/commands/lint.js +5 -1
  17. package/lib/commands/preview-docs/index.d.ts +1 -1
  18. package/lib/commands/preview-docs/index.js +1 -1
  19. package/lib/commands/push.d.ts +1 -0
  20. package/lib/commands/push.js +15 -3
  21. package/lib/commands/stats.js +1 -1
  22. package/lib/index.js +47 -0
  23. package/package.json +12 -5
  24. package/src/__mocks__/fs.ts +2 -0
  25. package/src/__mocks__/redoc.ts +2 -0
  26. package/src/__tests__/commands/build-docs.test.ts +58 -0
  27. package/src/__tests__/commands/lint.test.ts +10 -2
  28. package/src/__tests__/commands/push.test.ts +33 -1
  29. package/src/commands/build-docs/index.ts +39 -0
  30. package/src/commands/build-docs/template.hbs +23 -0
  31. package/src/commands/build-docs/types.ts +23 -0
  32. package/src/commands/build-docs/utils.ts +116 -0
  33. package/src/commands/bundle.ts +1 -1
  34. package/src/commands/lint.ts +5 -5
  35. package/src/commands/preview-docs/index.ts +2 -2
  36. package/src/commands/push.ts +17 -3
  37. package/src/commands/stats.ts +1 -1
  38. package/src/custom.d.ts +1 -0
  39. package/src/index.ts +62 -0
  40. package/tsconfig.tsbuildinfo +1 -1
@@ -5,3 +5,5 @@ export declare const statSync: jest.Mock<{
5
5
  size: number;
6
6
  }, []>;
7
7
  export declare const createReadStream: jest.Mock<any, any>;
8
+ export declare const writeFileSync: jest.Mock<any, any>;
9
+ export declare const mkdirSync: jest.Mock<any, any>;
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createReadStream = exports.statSync = exports.readFileSync = exports.existsSync = void 0;
3
+ exports.mkdirSync = exports.writeFileSync = exports.createReadStream = exports.statSync = exports.readFileSync = exports.existsSync = void 0;
4
4
  exports.existsSync = jest.fn();
5
5
  exports.readFileSync = jest.fn(() => '');
6
6
  exports.statSync = jest.fn(() => ({ size: 0 }));
7
7
  exports.createReadStream = jest.fn();
8
+ exports.writeFileSync = jest.fn();
9
+ exports.mkdirSync = jest.fn();
@@ -0,0 +1,7 @@
1
+ /// <reference types="jest" />
2
+ export declare const loadAndBundleSpec: jest.Mock<Promise<{
3
+ openapi: string;
4
+ }>, []>;
5
+ export declare const createStore: jest.Mock<Promise<{
6
+ toJS: jest.Mock<string, []>;
7
+ }>, []>;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createStore = exports.loadAndBundleSpec = void 0;
4
+ exports.loadAndBundleSpec = jest.fn(() => Promise.resolve({ openapi: '3.0.0' }));
5
+ exports.createStore = jest.fn(() => Promise.resolve({ toJS: jest.fn(() => '{}') }));
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,58 @@
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 redoc_1 = require("redoc");
13
+ const server_1 = require("react-dom/server");
14
+ const build_docs_1 = require("../../commands/build-docs");
15
+ const utils_1 = require("../../commands/build-docs/utils");
16
+ jest.mock('redoc');
17
+ jest.mock('fs');
18
+ const config = {
19
+ output: '',
20
+ cdn: false,
21
+ title: 'Test',
22
+ disableGoogleFont: false,
23
+ templateFileName: '',
24
+ templateOptions: {},
25
+ redocOptions: {},
26
+ };
27
+ jest.mock('react-dom/server', () => ({
28
+ renderToString: jest.fn(),
29
+ }));
30
+ jest.mock('handlebars', () => ({
31
+ compile: jest.fn(() => jest.fn(() => '<html></html>')),
32
+ }));
33
+ jest.mock('mkdirp', () => ({
34
+ sync: jest.fn(),
35
+ }));
36
+ describe('build-docs', () => {
37
+ it('should return correct html and call function for ssr', () => __awaiter(void 0, void 0, void 0, function* () {
38
+ const result = yield utils_1.getPageHTML({}, '../some-path/openapi.yaml', Object.assign(Object.assign({}, config), { redocCurrentVersion: '2.0.0' }));
39
+ expect(server_1.renderToString).toBeCalledTimes(1);
40
+ expect(redoc_1.createStore).toBeCalledTimes(1);
41
+ expect(result).toBe('<html></html>');
42
+ }));
43
+ it('should work correctly when calling handlerBuildCommand', () => __awaiter(void 0, void 0, void 0, function* () {
44
+ const processExitMock = jest.spyOn(process, 'exit').mockImplementation();
45
+ yield build_docs_1.handlerBuildCommand({
46
+ o: '',
47
+ cdn: false,
48
+ title: 'test',
49
+ disableGoogleFont: false,
50
+ template: '',
51
+ templateOptions: {},
52
+ options: {},
53
+ api: '../some-path/openapi.yaml',
54
+ });
55
+ expect(redoc_1.loadAndBundleSpec).toBeCalledTimes(1);
56
+ expect(processExitMock).toBeCalledTimes(0);
57
+ }));
58
+ });
@@ -49,12 +49,20 @@ describe('handleLint', () => {
49
49
  }));
50
50
  it('should call loadConfig and getFallbackApisOrExit', () => __awaiter(void 0, void 0, void 0, function* () {
51
51
  yield lint_1.handleLint(argvMock, versionMock);
52
- expect(openapi_core_1.loadConfig).toHaveBeenCalledWith(undefined, undefined, undefined);
52
+ expect(openapi_core_1.loadConfig).toHaveBeenCalledWith({
53
+ configPath: undefined,
54
+ customExtends: undefined,
55
+ processRawConfig: undefined,
56
+ });
53
57
  expect(utils_1.getFallbackApisOrExit).toHaveBeenCalled();
54
58
  }));
55
59
  it('should call loadConfig with args if such exist', () => __awaiter(void 0, void 0, void 0, function* () {
56
60
  yield lint_1.handleLint(Object.assign(Object.assign({}, argvMock), { config: 'redocly.yaml', extends: ['some/path'] }), versionMock);
57
- expect(openapi_core_1.loadConfig).toHaveBeenCalledWith('redocly.yaml', ['some/path'], undefined);
61
+ expect(openapi_core_1.loadConfig).toHaveBeenCalledWith({
62
+ configPath: 'redocly.yaml',
63
+ customExtends: ['some/path'],
64
+ processRawConfig: undefined,
65
+ });
58
66
  }));
59
67
  it('should call mergedConfig with clear ignore if `generate-ignore-file` argv', () => __awaiter(void 0, void 0, void 0, function* () {
60
68
  yield lint_1.handleLint(Object.assign(Object.assign({}, argvMock), { 'generate-ignore-file': true }), versionMock);
@@ -9,9 +9,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
+ const fs = require("fs");
12
13
  const openapi_core_1 = require("@redocly/openapi-core");
13
14
  const utils_1 = require("../../utils");
14
15
  const push_1 = require("../../commands/push");
16
+ const config_1 = require("../fixtures/config");
15
17
  jest.mock('fs');
16
18
  jest.mock('node-fetch', () => ({
17
19
  default: jest.fn(() => ({
@@ -76,6 +78,32 @@ describe('push', () => {
76
78
  });
77
79
  expect(utils_1.exitWithError).toBeCalledTimes(1);
78
80
  }));
81
+ it('push with --files', () => __awaiter(void 0, void 0, void 0, function* () {
82
+ openapi_core_1.loadConfig.mockImplementation(({ files }) => {
83
+ return Object.assign(Object.assign({}, config_1.ConfigFixture), { files });
84
+ });
85
+ //@ts-ignore
86
+ fs.statSync.mockImplementation(() => {
87
+ return { isDirectory: () => false, size: 10 };
88
+ });
89
+ yield push_1.handlePush({
90
+ upsert: true,
91
+ api: 'spec.json',
92
+ destination: '@org/my-api@1.0.0',
93
+ public: true,
94
+ files: ['./resouces/1.md', './resouces/2.md'],
95
+ });
96
+ expect(redoclyClient.registryApi.pushApi).toHaveBeenLastCalledWith({
97
+ filePaths: ['filePath', 'filePath', 'filePath'],
98
+ isUpsert: true,
99
+ isPublic: true,
100
+ name: 'my-api',
101
+ organizationId: 'org',
102
+ rootFilePath: 'filePath',
103
+ version: '1.0.0',
104
+ });
105
+ expect(redoclyClient.registryApi.prepareFileUpload).toBeCalledTimes(3);
106
+ }));
79
107
  });
80
108
  describe('transformPush', () => {
81
109
  it('should adapt the existing syntax', () => {
@@ -0,0 +1,2 @@
1
+ import type { BuildDocsArgv } from './types';
2
+ export declare const handlerBuildCommand: (argv: BuildDocsArgv) => Promise<void>;
@@ -0,0 +1,45 @@
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 utils_2 = require("../../utils");
19
+ const handlerBuildCommand = (argv) => __awaiter(void 0, void 0, void 0, function* () {
20
+ const startedAt = perf_hooks_1.performance.now();
21
+ const config = {
22
+ output: argv.o,
23
+ cdn: argv.cdn,
24
+ title: argv.title,
25
+ disableGoogleFont: argv.disableGoogleFont,
26
+ templateFileName: argv.template,
27
+ templateOptions: argv.templateOptions || {},
28
+ redocOptions: utils_1.getObjectOrJSON(argv.options),
29
+ };
30
+ const redocCurrentVersion = require('../../../package.json').dependencies.redoc.substring(1); // remove ~
31
+ const pathToApi = argv.api;
32
+ try {
33
+ const elapsed = utils_2.getExecutionTime(startedAt);
34
+ const api = yield redoc_1.loadAndBundleSpec(utils_1.isURL(pathToApi) ? pathToApi : path_1.resolve(pathToApi));
35
+ const pageHTML = yield utils_1.getPageHTML(api, pathToApi, Object.assign(Object.assign({}, config), { redocCurrentVersion }));
36
+ fs_1.mkdirSync(path_1.dirname(config.output), { recursive: true });
37
+ fs_1.writeFileSync(config.output, pageHTML);
38
+ const sizeInKiB = Math.ceil(Buffer.byteLength(pageHTML) / 1024);
39
+ process.stderr.write(`\n🎉 bundled successfully in: ${config.output} (${sizeInKiB} KiB) [⏱ ${elapsed}].\n`);
40
+ }
41
+ catch (e) {
42
+ utils_2.exitWithError(e);
43
+ }
44
+ });
45
+ exports.handlerBuildCommand = handlerBuildCommand;
@@ -0,0 +1,22 @@
1
+ export declare 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
+ export declare type BuildDocsArgv = {
14
+ api: string;
15
+ o: string;
16
+ cdn: boolean;
17
+ title?: string;
18
+ disableGoogleFont?: boolean;
19
+ template?: string;
20
+ templateOptions: Record<string, any>;
21
+ options: string | Record<string, unknown>;
22
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,8 @@
1
+ import { Config } from '@redocly/openapi-core';
2
+ import type { BuildDocsOptions } from './types';
3
+ export declare function getObjectOrJSON(options: string | Record<string, unknown>): JSON | Record<string, unknown> | Config;
4
+ export declare function getPageHTML(api: any, pathToApi: string, { cdn, title, disableGoogleFont, templateFileName, templateOptions, redocOptions, redocCurrentVersion, }: BuildDocsOptions): Promise<string>;
5
+ export declare function isURL(str: string): boolean;
6
+ export declare function sanitizeJSONString(str: string): string;
7
+ export declare function escapeClosingScriptTag(str: string): string;
8
+ export declare function escapeUnicode(str: string): string;
@@ -0,0 +1,108 @@
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.escapeUnicode = exports.escapeClosingScriptTag = exports.sanitizeJSONString = exports.isURL = exports.getPageHTML = exports.getObjectOrJSON = void 0;
13
+ const React = require("react");
14
+ const redoc_1 = require("redoc");
15
+ const openapi_core_1 = require("@redocly/openapi-core");
16
+ const server_1 = require("react-dom/server");
17
+ const styled_components_1 = require("styled-components");
18
+ const handlebars_1 = require("handlebars");
19
+ const path_1 = require("path");
20
+ const fs_1 = require("fs");
21
+ const colorette_1 = require("colorette");
22
+ const utils_1 = require("../../utils");
23
+ function getObjectOrJSON(options) {
24
+ switch (typeof options) {
25
+ case 'object':
26
+ return options;
27
+ case 'string':
28
+ try {
29
+ if (fs_1.existsSync(options) && fs_1.lstatSync(options).isFile()) {
30
+ return JSON.parse(fs_1.readFileSync(options, 'utf-8'));
31
+ }
32
+ else {
33
+ return JSON.parse(options);
34
+ }
35
+ }
36
+ catch (e) {
37
+ process.stderr.write(colorette_1.red(`Encountered error:\n\n${options}\n\nis neither a file with a valid JSON object neither a stringified JSON object.`));
38
+ utils_1.exitWithError(e);
39
+ }
40
+ break;
41
+ default: {
42
+ const configFile = openapi_core_1.findConfig();
43
+ if (configFile) {
44
+ process.stderr.write(`Found ${configFile} and using features.openapi options`);
45
+ try {
46
+ const config = openapi_core_1.parseYaml(fs_1.readFileSync(configFile, 'utf-8'));
47
+ return config['features.openapi'];
48
+ }
49
+ catch (e) {
50
+ process.stderr.write(colorette_1.yellow(`Found ${configFile} but failed to parse: ${e.message}`));
51
+ }
52
+ }
53
+ return {};
54
+ }
55
+ }
56
+ return {};
57
+ }
58
+ exports.getObjectOrJSON = getObjectOrJSON;
59
+ function getPageHTML(api, pathToApi, { cdn, title, disableGoogleFont, templateFileName, templateOptions, redocOptions = {}, redocCurrentVersion, }) {
60
+ return __awaiter(this, void 0, void 0, function* () {
61
+ process.stderr.write('Prerendering docs');
62
+ const apiUrl = redocOptions.specUrl || (isURL(pathToApi) ? pathToApi : undefined);
63
+ const store = yield redoc_1.createStore(api, apiUrl, redocOptions);
64
+ const sheet = new styled_components_1.ServerStyleSheet();
65
+ const html = server_1.renderToString(sheet.collectStyles(React.createElement(redoc_1.Redoc, { store })));
66
+ const state = yield store.toJS();
67
+ const css = sheet.getStyleTags();
68
+ templateFileName = templateFileName ? templateFileName : path_1.join(__dirname, './template.hbs');
69
+ const template = handlebars_1.compile(fs_1.readFileSync(templateFileName).toString());
70
+ return template({
71
+ redocHTML: `
72
+ <div id="redoc">${html || ''}</div>
73
+ <script>
74
+ ${`const __redoc_state = ${sanitizeJSONString(JSON.stringify(state))};` || ''}
75
+
76
+ var container = document.getElementById('redoc');
77
+ Redoc.${'hydrate(__redoc_state, container)'};
78
+
79
+ </script>`,
80
+ redocHead: (cdn
81
+ ? '<script src="https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js"></script>'
82
+ : `<script src="https://cdn.redoc.ly/redoc/v${redocCurrentVersion}/bundles/redoc.standalone.js"></script>`) +
83
+ css,
84
+ title: title || api.info.title || 'ReDoc documentation',
85
+ disableGoogleFont,
86
+ templateOptions,
87
+ });
88
+ });
89
+ }
90
+ exports.getPageHTML = getPageHTML;
91
+ function isURL(str) {
92
+ return /^(https?:)\/\//m.test(str);
93
+ }
94
+ exports.isURL = isURL;
95
+ function sanitizeJSONString(str) {
96
+ return escapeClosingScriptTag(escapeUnicode(str));
97
+ }
98
+ exports.sanitizeJSONString = sanitizeJSONString;
99
+ // see http://www.thespanner.co.uk/2011/07/25/the-json-specification-is-now-wrong/
100
+ function escapeClosingScriptTag(str) {
101
+ return str.replace(/<\/script>/g, '<\\/script>');
102
+ }
103
+ exports.escapeClosingScriptTag = escapeClosingScriptTag;
104
+ // see http://www.thespanner.co.uk/2011/07/25/the-json-specification-is-now-wrong/
105
+ function escapeUnicode(str) {
106
+ return str.replace(/\u2028|\u2029/g, (m) => '\\u202' + (m === '\u2028' ? '8' : '9'));
107
+ }
108
+ exports.escapeUnicode = escapeUnicode;
@@ -29,7 +29,7 @@ const fs_1 = require("fs");
29
29
  function handleBundle(argv, version) {
30
30
  var _a, _b, _c, _d;
31
31
  return __awaiter(this, void 0, void 0, function* () {
32
- const config = yield openapi_core_1.loadConfig(argv.config, argv.extends);
32
+ const config = yield openapi_core_1.loadConfig({ configPath: argv.config, customExtends: argv.extends });
33
33
  const removeUnusedComponents = argv['remove-unused-components'] &&
34
34
  !((_b = (_a = config.rawConfig.styleguide) === null || _a === void 0 ? void 0 : _a.decorators) === null || _b === void 0 ? void 0 : _b.hasOwnProperty('remove-unused-components'));
35
35
  const apis = yield utils_1.getFallbackApisOrExit(argv.apis, config);
@@ -19,7 +19,11 @@ function handleLint(argv, version) {
19
19
  if (argv.config && !openapi_core_1.doesYamlFileExist(argv.config)) {
20
20
  return utils_1.exitWithError('Please, provide valid path to the configuration file');
21
21
  }
22
- const config = yield openapi_core_1.loadConfig(argv.config, argv.extends, lintConfigCallback(argv, version));
22
+ const config = yield openapi_core_1.loadConfig({
23
+ configPath: argv.config,
24
+ customExtends: argv.extends,
25
+ processRawConfig: lintConfigCallback(argv, version),
26
+ });
23
27
  const apis = yield utils_1.getFallbackApisOrExit(argv.apis, config);
24
28
  if (argv['generate-ignore-file']) {
25
29
  config.styleguide.ignore = {}; // clear ignore
@@ -1,4 +1,4 @@
1
- import type { Skips } from 'cli/src/types';
1
+ import type { Skips } from '../../types';
2
2
  export declare function previewDocs(argv: {
3
3
  port: number;
4
4
  host: string;
@@ -94,7 +94,7 @@ function previewDocs(argv) {
94
94
  });
95
95
  function reloadConfig() {
96
96
  return __awaiter(this, void 0, void 0, function* () {
97
- const config = yield openapi_core_1.loadConfig(argv.config);
97
+ const config = yield openapi_core_1.loadConfig({ configPath: argv.config });
98
98
  const redoclyClient = new openapi_core_1.RedoclyClient();
99
99
  isAuthorizedWithRedocly = yield redoclyClient.isAuthorizedWithRedocly();
100
100
  const resolvedConfig = openapi_core_1.getMergedConfig(config, argv.api);
@@ -9,6 +9,7 @@ declare type PushArgs = {
9
9
  region?: Region;
10
10
  'skip-decorator'?: string[];
11
11
  public?: boolean;
12
+ files?: string[];
12
13
  };
13
14
  export declare function handlePush(argv: PushArgs): Promise<void>;
14
15
  export declare function getDestinationProps(destination: string | undefined, organization: string | undefined): (string | undefined)[];
@@ -33,9 +33,8 @@ const login_1 = require("./login");
33
33
  const DEFAULT_VERSION = 'latest';
34
34
  function handlePush(argv) {
35
35
  return __awaiter(this, void 0, void 0, function* () {
36
- const config = yield openapi_core_1.loadConfig();
37
- const region = argv.region || config.region;
38
- const client = new openapi_core_1.RedoclyClient(region);
36
+ const config = yield openapi_core_1.loadConfig({ region: argv.region, files: argv.files });
37
+ const client = new openapi_core_1.RedoclyClient(config.region);
39
38
  const isAuthorized = yield client.isAuthorizedWithRedoclyByRegion();
40
39
  if (!isAuthorized) {
41
40
  const clientToken = yield login_1.promptClientToken(client.domain);
@@ -182,6 +181,19 @@ function collectFilesToUpload(api, config) {
182
181
  }
183
182
  files.push(...filterPluginFilesByExt(Array.from(pluginFiles)).map((f) => getFileEntry(f)));
184
183
  }
184
+ if (config.files) {
185
+ const otherFiles = new Set();
186
+ for (const file of config.files) {
187
+ if (fs.statSync(file).isDirectory()) {
188
+ const fileList = getFilesList(file, []);
189
+ fileList.forEach((f) => otherFiles.add(f));
190
+ }
191
+ else {
192
+ otherFiles.add(file);
193
+ }
194
+ }
195
+ files.push(...Array.from(otherFiles).map((f) => getFileEntry(f)));
196
+ }
185
197
  return {
186
198
  files,
187
199
  root: path.resolve(apiPath),
@@ -54,7 +54,7 @@ function printStats(statsAccumulator, api, format) {
54
54
  }
55
55
  function handleStats(argv) {
56
56
  return __awaiter(this, void 0, void 0, function* () {
57
- const config = yield openapi_core_1.loadConfig(argv.config);
57
+ const config = yield openapi_core_1.loadConfig({ configPath: argv.config });
58
58
  const [{ path }] = yield utils_1.getFallbackApisOrExit(argv.api ? [argv.api] : [], config);
59
59
  const externalRefResolver = new openapi_core_1.BaseResolver(config.resolve);
60
60
  const { bundle: document } = yield openapi_core_1.bundle({ config, ref: path });
package/lib/index.js CHANGED
@@ -22,6 +22,7 @@ const push_1 = require("./commands/push");
22
22
  const lint_1 = require("./commands/lint");
23
23
  const bundle_1 = require("./commands/bundle");
24
24
  const login_1 = require("./commands/login");
25
+ const build_docs_1 = require("./commands/build-docs");
25
26
  const version = require('../package.json').version;
26
27
  yargs
27
28
  .version('version', 'Show version number.', version)
@@ -117,6 +118,11 @@ yargs
117
118
  description: 'Make API registry available to the public',
118
119
  type: 'boolean',
119
120
  },
121
+ files: {
122
+ description: 'List of other folders and files to upload',
123
+ array: true,
124
+ type: 'string',
125
+ },
120
126
  })
121
127
  .implies('batch-id', 'batch-size')
122
128
  .implies('batch-size', 'batch-id'), (argv) => {
@@ -309,6 +315,47 @@ yargs
309
315
  process.env.REDOCLY_CLI_COMMAND = 'preview-docs';
310
316
  preview_docs_1.previewDocs(argv);
311
317
  })
318
+ .command('build-docs [api]', 'build definition into zero-dependency HTML-file', (yargs) => {
319
+ yargs.positional('api', {
320
+ describe: 'path or URL to your api',
321
+ });
322
+ yargs.option('o', {
323
+ describe: 'Output file',
324
+ alias: 'output',
325
+ type: 'string',
326
+ default: 'redoc-static.html',
327
+ });
328
+ yargs.options('title', {
329
+ describe: 'Page Title',
330
+ type: 'string',
331
+ });
332
+ yargs.options('disableGoogleFont', {
333
+ describe: 'Disable Google Font',
334
+ type: 'boolean',
335
+ default: false,
336
+ });
337
+ yargs.option('cdn', {
338
+ describe: 'Do not include ReDoc source code into html page, use link to CDN instead',
339
+ type: 'boolean',
340
+ default: false,
341
+ });
342
+ yargs.options('t', {
343
+ alias: 'template',
344
+ describe: 'Path to handlebars page template, see https://git.io/vh8fP for the example ',
345
+ type: 'string',
346
+ });
347
+ yargs.options('templateOptions', {
348
+ describe: 'Additional options that you want pass to template. Use dot notation, e.g. templateOptions.metaDescription',
349
+ });
350
+ yargs.options('options', {
351
+ describe: 'ReDoc options, use dot notation, e.g. options.nativeScrollbars',
352
+ });
353
+ yargs.demandOption('api');
354
+ return yargs;
355
+ }, (argv) => __awaiter(void 0, void 0, void 0, function* () {
356
+ process.env.REDOCLY_CLI_COMMAND = 'build-docs';
357
+ build_docs_1.handlerBuildCommand(argv);
358
+ }))
312
359
  .completion('completion', 'Generate completion script.')
313
360
  .demandCommand(1)
314
361
  .strict().argv;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/cli",
3
- "version": "1.0.0-beta.109",
3
+ "version": "1.0.0-beta.110",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -30,23 +30,30 @@
30
30
  "oas"
31
31
  ],
32
32
  "contributors": [
33
- "Sergey Dubovyk <serhii@redoc.ly> (https://redoc.ly/)",
34
- "Roman Hotsiy <roman@redoc.ly> (https://redoc.ly/)",
35
- "Andriy Leliv <andriy@redoc.ly> (https://redoc.ly/)"
33
+ "Roman Hotsiy <roman@redoc.ly> (https://redoc.ly/)"
36
34
  ],
37
35
  "dependencies": {
38
- "@redocly/openapi-core": "1.0.0-beta.109",
36
+ "@redocly/openapi-core": "1.0.0-beta.110",
39
37
  "assert-node-version": "^1.0.3",
40
38
  "chokidar": "^3.5.1",
41
39
  "colorette": "^1.2.0",
42
40
  "glob": "^7.1.6",
43
41
  "glob-promise": "^3.4.0",
44
42
  "handlebars": "^4.7.6",
43
+ "mobx": "^6.3.2",
45
44
  "portfinder": "^1.0.26",
45
+ "react": "^17.0.1",
46
+ "react-dom": "^17.0.1",
47
+ "redoc": "~2.0.0",
46
48
  "simple-websocket": "^9.0.0",
49
+ "styled-components": "^5.3.0",
47
50
  "yargs": "17.0.1"
48
51
  },
49
52
  "devDependencies": {
53
+ "@types/configstore": "^5.0.1",
54
+ "@types/react": "^17.0.8",
55
+ "@types/react-dom": "^17.0.5",
56
+ "@types/styled-components": "^5.1.1",
50
57
  "@types/yargs": "16.0.2",
51
58
  "typescript": "^4.0.3"
52
59
  }
@@ -2,3 +2,5 @@ export const existsSync = jest.fn();
2
2
  export const readFileSync = jest.fn(() => '');
3
3
  export const statSync = jest.fn(() => ({ size: 0 }));
4
4
  export const createReadStream = jest.fn();
5
+ export const writeFileSync = jest.fn();
6
+ export const mkdirSync = jest.fn();
@@ -0,0 +1,2 @@
1
+ export const loadAndBundleSpec = jest.fn(() => Promise.resolve({ openapi: '3.0.0' }));
2
+ export const createStore = jest.fn(() => Promise.resolve({ toJS: jest.fn(() => '{}') }));
@@ -0,0 +1,58 @@
1
+ import { createStore, loadAndBundleSpec } from 'redoc';
2
+ import { renderToString } from 'react-dom/server';
3
+ import { handlerBuildCommand } from '../../commands/build-docs';
4
+ import { BuildDocsArgv } from '../../commands/build-docs/types';
5
+ import { getPageHTML } from '../../commands/build-docs/utils';
6
+
7
+ jest.mock('redoc');
8
+ jest.mock('fs');
9
+
10
+ const config = {
11
+ output: '',
12
+ cdn: false,
13
+ title: 'Test',
14
+ disableGoogleFont: false,
15
+ templateFileName: '',
16
+ templateOptions: {},
17
+ redocOptions: {},
18
+ };
19
+
20
+ jest.mock('react-dom/server', () => ({
21
+ renderToString: jest.fn(),
22
+ }));
23
+
24
+ jest.mock('handlebars', () => ({
25
+ compile: jest.fn(() => jest.fn(() => '<html></html>')),
26
+ }));
27
+
28
+ jest.mock('mkdirp', () => ({
29
+ sync: jest.fn(),
30
+ }));
31
+
32
+ describe('build-docs', () => {
33
+ it('should return correct html and call function for ssr', async () => {
34
+ const result = await getPageHTML({}, '../some-path/openapi.yaml', {
35
+ ...config,
36
+ redocCurrentVersion: '2.0.0',
37
+ });
38
+ expect(renderToString).toBeCalledTimes(1);
39
+ expect(createStore).toBeCalledTimes(1);
40
+ expect(result).toBe('<html></html>');
41
+ });
42
+
43
+ it('should work correctly when calling handlerBuildCommand', async () => {
44
+ const processExitMock = jest.spyOn(process, 'exit').mockImplementation();
45
+ await handlerBuildCommand({
46
+ o: '',
47
+ cdn: false,
48
+ title: 'test',
49
+ disableGoogleFont: false,
50
+ template: '',
51
+ templateOptions: {},
52
+ options: {},
53
+ api: '../some-path/openapi.yaml',
54
+ } as BuildDocsArgv);
55
+ expect(loadAndBundleSpec).toBeCalledTimes(1);
56
+ expect(processExitMock).toBeCalledTimes(0);
57
+ });
58
+ });