@redocly/cli 0.0.0-snapshot.1737554067
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/README.md +114 -0
- package/bin/cli.js +3 -0
- package/lib/__mocks__/@redocly/openapi-core.d.ts +99 -0
- package/lib/__mocks__/@redocly/openapi-core.js +84 -0
- package/lib/__mocks__/documents.d.ts +150 -0
- package/lib/__mocks__/documents.js +123 -0
- package/lib/__mocks__/fs.d.ts +8 -0
- package/lib/__mocks__/fs.js +9 -0
- package/lib/__mocks__/perf_hooks.d.ts +3 -0
- package/lib/__mocks__/perf_hooks.js +6 -0
- package/lib/__mocks__/redoc.d.ts +6 -0
- package/lib/__mocks__/redoc.js +5 -0
- package/lib/__tests__/commands/build-docs.test.d.ts +1 -0
- package/lib/__tests__/commands/build-docs.test.js +54 -0
- package/lib/__tests__/commands/bundle.test.d.ts +1 -0
- package/lib/__tests__/commands/bundle.test.js +235 -0
- package/lib/__tests__/commands/join.test.d.ts +1 -0
- package/lib/__tests__/commands/join.test.js +274 -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.d.ts +1 -0
- package/lib/__tests__/commands/push-region.test.js +55 -0
- package/lib/__tests__/commands/push.test.d.ts +1 -0
- package/lib/__tests__/commands/push.test.js +463 -0
- package/lib/__tests__/fetch-with-timeout.test.d.ts +1 -0
- package/lib/__tests__/fetch-with-timeout.test.js +44 -0
- package/lib/__tests__/fixtures/config.d.ts +21 -0
- package/lib/__tests__/fixtures/config.js +24 -0
- package/lib/__tests__/spinner.test.d.ts +1 -0
- package/lib/__tests__/spinner.test.js +43 -0
- package/lib/__tests__/utils.test.d.ts +1 -0
- package/lib/__tests__/utils.test.js +593 -0
- package/lib/__tests__/wrapper.test.d.ts +1 -0
- package/lib/__tests__/wrapper.test.js +68 -0
- package/lib/cms/api/__tests__/api-keys.test.d.ts +1 -0
- package/lib/cms/api/__tests__/api-keys.test.js +26 -0
- package/lib/cms/api/__tests__/api.client.test.d.ts +1 -0
- package/lib/cms/api/__tests__/api.client.test.js +333 -0
- package/lib/cms/api/__tests__/domains.test.d.ts +1 -0
- package/lib/cms/api/__tests__/domains.test.js +13 -0
- package/lib/cms/api/api-client.d.ts +75 -0
- package/lib/cms/api/api-client.js +225 -0
- package/lib/cms/api/api-keys.d.ts +1 -0
- package/lib/cms/api/api-keys.js +23 -0
- package/lib/cms/api/domains.d.ts +1 -0
- package/lib/cms/api/domains.js +11 -0
- package/lib/cms/api/index.d.ts +3 -0
- package/lib/cms/api/index.js +19 -0
- package/lib/cms/api/types.d.ts +102 -0
- package/lib/cms/api/types.js +2 -0
- package/lib/cms/commands/__tests__/push-status.test.d.ts +1 -0
- package/lib/cms/commands/__tests__/push-status.test.js +563 -0
- package/lib/cms/commands/__tests__/push.test.d.ts +1 -0
- package/lib/cms/commands/__tests__/push.test.js +315 -0
- package/lib/cms/commands/__tests__/utils.test.d.ts +1 -0
- package/lib/cms/commands/__tests__/utils.test.js +51 -0
- package/lib/cms/commands/push-status.d.ts +23 -0
- package/lib/cms/commands/push-status.js +206 -0
- package/lib/cms/commands/push.d.ts +28 -0
- package/lib/cms/commands/push.js +142 -0
- package/lib/cms/commands/utils.d.ts +25 -0
- package/lib/cms/commands/utils.js +46 -0
- package/lib/cms/utils.d.ts +2 -0
- package/lib/cms/utils.js +6 -0
- package/lib/commands/build-docs/index.d.ts +3 -0
- package/lib/commands/build-docs/index.js +39 -0
- package/lib/commands/build-docs/template.hbs +23 -0
- 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 +87 -0
- package/lib/commands/bundle.d.ts +14 -0
- package/lib/commands/bundle.js +91 -0
- package/lib/commands/eject.d.ts +9 -0
- package/lib/commands/eject.js +28 -0
- package/lib/commands/join.d.ts +11 -0
- package/lib/commands/join.js +565 -0
- package/lib/commands/lint.d.ts +13 -0
- package/lib/commands/lint.js +108 -0
- package/lib/commands/login.d.ts +9 -0
- package/lib/commands/login.js +23 -0
- package/lib/commands/preview-docs/index.d.ts +12 -0
- package/lib/commands/preview-docs/index.js +127 -0
- package/lib/commands/preview-docs/preview-server/default.hbs +24 -0
- package/lib/commands/preview-docs/preview-server/hot.js +59 -0
- package/lib/commands/preview-docs/preview-server/oauth2-redirect.html +21 -0
- package/lib/commands/preview-docs/preview-server/preview-server.d.ts +5 -0
- package/lib/commands/preview-docs/preview-server/preview-server.js +113 -0
- package/lib/commands/preview-docs/preview-server/server.d.ts +22 -0
- package/lib/commands/preview-docs/preview-server/server.js +85 -0
- package/lib/commands/preview-project/constants.d.ts +14 -0
- package/lib/commands/preview-project/constants.js +22 -0
- package/lib/commands/preview-project/index.d.ts +3 -0
- package/lib/commands/preview-project/index.js +56 -0
- package/lib/commands/preview-project/types.d.ts +10 -0
- package/lib/commands/preview-project/types.js +2 -0
- package/lib/commands/push.d.ts +44 -0
- package/lib/commands/push.js +295 -0
- package/lib/commands/split/__tests__/index.test.d.ts +1 -0
- package/lib/commands/split/__tests__/index.test.js +91 -0
- package/lib/commands/split/index.d.ts +13 -0
- package/lib/commands/split/index.js +259 -0
- package/lib/commands/split/types.d.ts +36 -0
- package/lib/commands/split/types.js +51 -0
- package/lib/commands/stats.d.ts +8 -0
- package/lib/commands/stats.js +96 -0
- package/lib/commands/translations.d.ts +7 -0
- package/lib/commands/translations.js +20 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +733 -0
- package/lib/types.d.ts +43 -0
- package/lib/types.js +5 -0
- package/lib/utils/__mocks__/miscellaneous.d.ts +43 -0
- package/lib/utils/__mocks__/miscellaneous.js +24 -0
- package/lib/utils/assert-node-version.d.ts +1 -0
- package/lib/utils/assert-node-version.js +16 -0
- package/lib/utils/fetch-with-timeout.d.ts +7 -0
- package/lib/utils/fetch-with-timeout.js +26 -0
- package/lib/utils/getCommandNameFromArgs.d.ts +2 -0
- package/lib/utils/getCommandNameFromArgs.js +6 -0
- package/lib/utils/js-utils.d.ts +5 -0
- package/lib/utils/js-utils.js +28 -0
- package/lib/utils/miscellaneous.d.ts +81 -0
- package/lib/utils/miscellaneous.js +551 -0
- package/lib/utils/platform.d.ts +16 -0
- package/lib/utils/platform.js +34 -0
- package/lib/utils/spinner.d.ts +10 -0
- package/lib/utils/spinner.js +42 -0
- package/lib/utils/update-version-notifier.d.ts +3 -0
- package/lib/utils/update-version-notifier.js +102 -0
- package/lib/wrapper.d.ts +11 -0
- package/lib/wrapper.js +65 -0
- package/package.json +69 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CommandArgs } from '../wrapper';
|
|
2
|
+
import type { Region } from '@redocly/openapi-core';
|
|
3
|
+
export declare function promptClientToken(domain: string): Promise<string>;
|
|
4
|
+
export type LoginOptions = {
|
|
5
|
+
verbose?: boolean;
|
|
6
|
+
region?: Region;
|
|
7
|
+
config?: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function handleLogin({ argv, config }: CommandArgs<LoginOptions>): Promise<void>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.promptClientToken = promptClientToken;
|
|
4
|
+
exports.handleLogin = handleLogin;
|
|
5
|
+
const colorette_1 = require("colorette");
|
|
6
|
+
const openapi_core_1 = require("@redocly/openapi-core");
|
|
7
|
+
const miscellaneous_1 = require("../utils/miscellaneous");
|
|
8
|
+
function promptClientToken(domain) {
|
|
9
|
+
return (0, miscellaneous_1.promptUser)((0, colorette_1.green)(`\n 🔑 Copy your API key from ${(0, colorette_1.blue)(`https://app.${domain}/profile`)} and paste it below`), true);
|
|
10
|
+
}
|
|
11
|
+
async function handleLogin({ argv, config }) {
|
|
12
|
+
try {
|
|
13
|
+
const region = argv.region || config.region;
|
|
14
|
+
const client = new openapi_core_1.RedoclyClient(region);
|
|
15
|
+
const clientToken = await promptClientToken(client.domain);
|
|
16
|
+
process.stdout.write((0, colorette_1.gray)('\n Logging in...\n'));
|
|
17
|
+
await client.login(clientToken, argv.verbose);
|
|
18
|
+
process.stdout.write((0, colorette_1.green)(' Authorization confirmed. ✅\n\n'));
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
(0, miscellaneous_1.exitWithError)(' ' + err?.message);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Skips, VerifyConfigOptions } from '../../types';
|
|
2
|
+
import type { CommandArgs } from '../../wrapper';
|
|
3
|
+
export type PreviewDocsOptions = {
|
|
4
|
+
port: number;
|
|
5
|
+
host: string;
|
|
6
|
+
'use-community-edition'?: boolean;
|
|
7
|
+
config?: string;
|
|
8
|
+
api?: string;
|
|
9
|
+
force?: boolean;
|
|
10
|
+
} & Omit<Skips, 'skip-rule'> & VerifyConfigOptions;
|
|
11
|
+
export declare function previewDocs({ argv, config: configFromFile, }: CommandArgs<PreviewDocsOptions>): Promise<void>;
|
|
12
|
+
export declare function debounce(func: Function, wait: number, immediate?: boolean): (...args: any[]) => void;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.previewDocs = previewDocs;
|
|
4
|
+
exports.debounce = debounce;
|
|
5
|
+
const colorette = require("colorette");
|
|
6
|
+
const chockidar = require("chokidar");
|
|
7
|
+
const openapi_core_1 = require("@redocly/openapi-core");
|
|
8
|
+
const miscellaneous_1 = require("../../utils/miscellaneous");
|
|
9
|
+
const preview_server_1 = require("./preview-server/preview-server");
|
|
10
|
+
async function previewDocs({ argv, config: configFromFile, }) {
|
|
11
|
+
let isAuthorizedWithRedocly = false;
|
|
12
|
+
let redocOptions = {};
|
|
13
|
+
let config = await reloadConfig(configFromFile);
|
|
14
|
+
const apis = await (0, miscellaneous_1.getFallbackApisOrExit)(argv.api ? [argv.api] : [], config);
|
|
15
|
+
const api = apis[0];
|
|
16
|
+
let cachedBundle;
|
|
17
|
+
const deps = new Set();
|
|
18
|
+
async function getBundle() {
|
|
19
|
+
return cachedBundle;
|
|
20
|
+
}
|
|
21
|
+
async function updateBundle() {
|
|
22
|
+
process.stdout.write('\nBundling...\n\n');
|
|
23
|
+
try {
|
|
24
|
+
const { bundle: openapiBundle, problems, fileDependencies, } = await (0, openapi_core_1.bundle)({
|
|
25
|
+
ref: api.path,
|
|
26
|
+
config,
|
|
27
|
+
});
|
|
28
|
+
const removed = [...deps].filter((x) => !fileDependencies.has(x));
|
|
29
|
+
watcher.unwatch(removed);
|
|
30
|
+
watcher.add([...fileDependencies]);
|
|
31
|
+
deps.clear();
|
|
32
|
+
fileDependencies.forEach(deps.add, deps);
|
|
33
|
+
const fileTotals = (0, openapi_core_1.getTotals)(problems);
|
|
34
|
+
if (fileTotals.errors === 0) {
|
|
35
|
+
process.stdout.write(fileTotals.errors === 0
|
|
36
|
+
? `Created a bundle for ${api.alias || api.path} ${fileTotals.warnings > 0 ? 'with warnings' : 'successfully'}\n`
|
|
37
|
+
: colorette.yellow(`Created a bundle for ${api.alias || api.path} with errors. Docs may be broken or not accurate\n`));
|
|
38
|
+
}
|
|
39
|
+
return openapiBundle.parsed;
|
|
40
|
+
}
|
|
41
|
+
catch (e) {
|
|
42
|
+
(0, miscellaneous_1.handleError)(e, api.path);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
setImmediate(() => {
|
|
46
|
+
cachedBundle = updateBundle();
|
|
47
|
+
}); // initial cache
|
|
48
|
+
const isAuthorized = isAuthorizedWithRedocly || redocOptions.licenseKey;
|
|
49
|
+
if (!isAuthorized) {
|
|
50
|
+
process.stderr.write(`Using Redoc community edition.\nLogin with redocly ${colorette.blue('login')} or use an enterprise license key to preview with the premium docs.\n\n`);
|
|
51
|
+
}
|
|
52
|
+
const hotClients = await (0, preview_server_1.default)(argv.port, argv.host, {
|
|
53
|
+
getBundle,
|
|
54
|
+
getOptions: () => redocOptions,
|
|
55
|
+
useRedocPro: isAuthorized && !redocOptions.useCommunityEdition,
|
|
56
|
+
});
|
|
57
|
+
const watchPaths = [api.path, config.configFile].filter((e) => !!e);
|
|
58
|
+
const watcher = chockidar.watch(watchPaths, {
|
|
59
|
+
disableGlobbing: true,
|
|
60
|
+
ignoreInitial: true,
|
|
61
|
+
});
|
|
62
|
+
const debouncedUpdatedBundle = debounce(async () => {
|
|
63
|
+
cachedBundle = updateBundle();
|
|
64
|
+
await cachedBundle;
|
|
65
|
+
hotClients.broadcast('{"type": "reload", "bundle": true}');
|
|
66
|
+
}, 2000);
|
|
67
|
+
const changeHandler = async (type, file) => {
|
|
68
|
+
process.stdout.write(`${colorette.green('watch')} ${type} ${colorette.blue(file)}\n`);
|
|
69
|
+
if (file === config.configFile) {
|
|
70
|
+
config = await reloadConfig();
|
|
71
|
+
hotClients.broadcast(JSON.stringify({ type: 'reload' }));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
debouncedUpdatedBundle();
|
|
75
|
+
};
|
|
76
|
+
watcher.on('change', changeHandler.bind(undefined, 'changed'));
|
|
77
|
+
watcher.on('add', changeHandler.bind(undefined, 'added'));
|
|
78
|
+
watcher.on('unlink', changeHandler.bind(undefined, 'removed'));
|
|
79
|
+
watcher.on('ready', () => {
|
|
80
|
+
process.stdout.write(`\n 👀 Watching ${colorette.blue(api.path)} and all related resources for changes\n\n`);
|
|
81
|
+
});
|
|
82
|
+
async function reloadConfig(config) {
|
|
83
|
+
if (!config) {
|
|
84
|
+
try {
|
|
85
|
+
config = (await (0, miscellaneous_1.loadConfigAndHandleErrors)({ configPath: argv.config }));
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
config = new openapi_core_1.Config({ apis: {}, styleguide: {} });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const redoclyClient = new openapi_core_1.RedoclyClient();
|
|
92
|
+
isAuthorizedWithRedocly = await redoclyClient.isAuthorizedWithRedocly();
|
|
93
|
+
const resolvedConfig = (0, openapi_core_1.getMergedConfig)(config, argv.api);
|
|
94
|
+
const { styleguide } = resolvedConfig;
|
|
95
|
+
styleguide.skipPreprocessors(argv['skip-preprocessor']);
|
|
96
|
+
styleguide.skipDecorators(argv['skip-decorator']);
|
|
97
|
+
const referenceDocs = resolvedConfig.theme?.openapi;
|
|
98
|
+
redocOptions = {
|
|
99
|
+
...referenceDocs,
|
|
100
|
+
useCommunityEdition: argv['use-community-edition'] || referenceDocs?.useCommunityEdition,
|
|
101
|
+
licenseKey: process.env.REDOCLY_LICENSE_KEY || referenceDocs?.licenseKey,
|
|
102
|
+
whiteLabel: true,
|
|
103
|
+
};
|
|
104
|
+
return resolvedConfig;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
108
|
+
function debounce(func, wait, immediate) {
|
|
109
|
+
let timeout;
|
|
110
|
+
return function executedFunction(...args) {
|
|
111
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
112
|
+
// @ts-ignore
|
|
113
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
114
|
+
const context = this;
|
|
115
|
+
const later = () => {
|
|
116
|
+
timeout = null;
|
|
117
|
+
if (!immediate)
|
|
118
|
+
func.apply(context, args);
|
|
119
|
+
};
|
|
120
|
+
const callNow = immediate && !timeout;
|
|
121
|
+
if (timeout)
|
|
122
|
+
clearTimeout(timeout);
|
|
123
|
+
timeout = setTimeout(later, wait);
|
|
124
|
+
if (callNow)
|
|
125
|
+
func.apply(context, args);
|
|
126
|
+
};
|
|
127
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
|
|
2
|
+
<!DOCTYPE html>
|
|
3
|
+
<html>
|
|
4
|
+
|
|
5
|
+
<head>
|
|
6
|
+
<meta charset="utf8" />
|
|
7
|
+
<title>{{title}}</title>
|
|
8
|
+
<!-- needed for adaptive design -->
|
|
9
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
10
|
+
<style>
|
|
11
|
+
body {
|
|
12
|
+
padding: 0;
|
|
13
|
+
margin: 0;
|
|
14
|
+
}
|
|
15
|
+
</style>
|
|
16
|
+
{{{redocHead}}}
|
|
17
|
+
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
|
|
18
|
+
</head>
|
|
19
|
+
|
|
20
|
+
<body>
|
|
21
|
+
{{{redocHTML}}}
|
|
22
|
+
</body>
|
|
23
|
+
|
|
24
|
+
</html>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
(function run() {
|
|
2
|
+
const Socket = window.SimpleWebsocket;
|
|
3
|
+
const port = window.__OPENAPI_CLI_WS_PORT;
|
|
4
|
+
const host = window.__OPENAPI_CLI_WS_HOST;
|
|
5
|
+
|
|
6
|
+
let socket;
|
|
7
|
+
|
|
8
|
+
reconnect();
|
|
9
|
+
|
|
10
|
+
function getFormattedHost() {
|
|
11
|
+
// Use localhost when bound to all interfaces
|
|
12
|
+
if (host === '::' || host === '0.0.0.0') {
|
|
13
|
+
return 'localhost';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Other IPv6 addresses must be wrapped in brackets
|
|
17
|
+
if (host.includes('::')) {
|
|
18
|
+
return `[${host}]`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Otherwise return as-is
|
|
22
|
+
return host;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function reconnect() {
|
|
26
|
+
socket = new Socket(`ws://${getFormattedHost()}:${port}`);
|
|
27
|
+
socket.on('connect', () => {
|
|
28
|
+
socket.send('{"type": "ping"}');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
socket.on('data', (data) => {
|
|
32
|
+
const message = JSON.parse(data);
|
|
33
|
+
switch (message.type) {
|
|
34
|
+
case 'pong':
|
|
35
|
+
console.log('[hot] hot reloading connected');
|
|
36
|
+
break;
|
|
37
|
+
case 'reload':
|
|
38
|
+
console.log('[hot] full page reload');
|
|
39
|
+
window.location.reload();
|
|
40
|
+
break;
|
|
41
|
+
default:
|
|
42
|
+
console.log(`[hot] ${message.type} received`);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
socket.on('close', () => {
|
|
47
|
+
socket.destroy();
|
|
48
|
+
console.log('[hot] Connection lost, trying to reconnect in 4s');
|
|
49
|
+
setTimeout(() => {
|
|
50
|
+
reconnect();
|
|
51
|
+
}, 4000);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
socket.on('error', () => {
|
|
55
|
+
console.log('[hot] Error connecting to hot reloading server');
|
|
56
|
+
socket.destroy();
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
})();
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no" />
|
|
6
|
+
<title>Redocly Reference Docs: OAuth2 Redirect</title>
|
|
7
|
+
<style>
|
|
8
|
+
.user-info {
|
|
9
|
+
margin: 150px auto auto;
|
|
10
|
+
width: 50%;
|
|
11
|
+
background-color: whitesmoke;
|
|
12
|
+
padding: 20px;
|
|
13
|
+
text-align: center;
|
|
14
|
+
}
|
|
15
|
+
</style>
|
|
16
|
+
</head>
|
|
17
|
+
<body>
|
|
18
|
+
<script src="https://cdn.redocly.com/reference-docs/latest/oauth2-redirect.js"></script>
|
|
19
|
+
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
20
|
+
</body>
|
|
21
|
+
</html>
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = startPreviewServer;
|
|
4
|
+
const handlebars_1 = require("handlebars");
|
|
5
|
+
const colorette = require("colorette");
|
|
6
|
+
const get_port_please_1 = require("get-port-please");
|
|
7
|
+
const fs_1 = require("fs");
|
|
8
|
+
const path = require("path");
|
|
9
|
+
const server_1 = require("./server");
|
|
10
|
+
const miscellaneous_1 = require("../../../utils/miscellaneous");
|
|
11
|
+
function getPageHTML(htmlTemplate, redocOptions = {}, useRedocPro, wsPort, host) {
|
|
12
|
+
let templateSrc = (0, fs_1.readFileSync)(htmlTemplate, 'utf-8');
|
|
13
|
+
// fix template for backward compatibility
|
|
14
|
+
templateSrc = templateSrc
|
|
15
|
+
.replace(/{?{{redocHead}}}?/, '{{{redocHead}}}')
|
|
16
|
+
.replace('{{redocBody}}', '{{{redocHTML}}}');
|
|
17
|
+
const template = (0, handlebars_1.compile)(templateSrc);
|
|
18
|
+
return template({
|
|
19
|
+
redocHead: `
|
|
20
|
+
<script>
|
|
21
|
+
window.__REDOC_EXPORT = '${useRedocPro ? 'RedoclyReferenceDocs' : 'Redoc'}';
|
|
22
|
+
window.__OPENAPI_CLI_WS_PORT = ${wsPort};
|
|
23
|
+
window.__OPENAPI_CLI_WS_HOST = "${host}";
|
|
24
|
+
</script>
|
|
25
|
+
<script src="/simplewebsocket.min.js"></script>
|
|
26
|
+
<script src="/hot.js"></script>
|
|
27
|
+
<script src="${useRedocPro
|
|
28
|
+
? 'https://cdn.redocly.com/reference-docs/latest/redocly-reference-docs.min.js'
|
|
29
|
+
: 'https://cdn.redocly.com/redoc/latest/bundles/redoc.standalone.js'}"></script>
|
|
30
|
+
`,
|
|
31
|
+
redocHTML: `
|
|
32
|
+
<div id="redoc"></div>
|
|
33
|
+
<script>
|
|
34
|
+
var container = document.getElementById('redoc');
|
|
35
|
+
${useRedocPro
|
|
36
|
+
? "window[window.__REDOC_EXPORT].setPublicPath('https://cdn.redocly.com/reference-docs/latest/');"
|
|
37
|
+
: ''}
|
|
38
|
+
window[window.__REDOC_EXPORT].init("/openapi.json", ${JSON.stringify(redocOptions)}, container)
|
|
39
|
+
</script>`,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
async function startPreviewServer(port, host, { getBundle, getOptions, useRedocPro, }) {
|
|
43
|
+
const defaultTemplate = path.join(__dirname, 'default.hbs');
|
|
44
|
+
const handler = async (request, response) => {
|
|
45
|
+
console.time(colorette.dim(`GET ${request.url}`));
|
|
46
|
+
const { htmlTemplate } = getOptions() || {};
|
|
47
|
+
if (request.url?.endsWith('/') || path.extname(request.url) === '') {
|
|
48
|
+
(0, server_1.respondWithGzip)(getPageHTML(htmlTemplate || defaultTemplate, getOptions(), useRedocPro, wsPort, host), request, response, {
|
|
49
|
+
'Content-Type': 'text/html',
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
else if (request.url === '/openapi.json') {
|
|
53
|
+
const bundle = await getBundle();
|
|
54
|
+
if (bundle === undefined) {
|
|
55
|
+
(0, server_1.respondWithGzip)(JSON.stringify({
|
|
56
|
+
openapi: '3.0.0',
|
|
57
|
+
info: {
|
|
58
|
+
description: '<code> Failed to generate bundle: check out console output for more details </code>',
|
|
59
|
+
},
|
|
60
|
+
paths: {},
|
|
61
|
+
}), request, response, {
|
|
62
|
+
'Content-Type': 'application/json',
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
(0, server_1.respondWithGzip)(JSON.stringify(bundle), request, response, {
|
|
67
|
+
'Content-Type': 'application/json',
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
let filePath =
|
|
73
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
74
|
+
// @ts-ignore
|
|
75
|
+
{
|
|
76
|
+
'/hot.js': path.join(__dirname, 'hot.js'),
|
|
77
|
+
'/oauth2-redirect.html': path.join(__dirname, 'oauth2-redirect.html'),
|
|
78
|
+
'/simplewebsocket.min.js': require.resolve('simple-websocket/simplewebsocket.min.js'),
|
|
79
|
+
}[request.url || ''];
|
|
80
|
+
if (!filePath) {
|
|
81
|
+
const basePath = htmlTemplate ? path.dirname(htmlTemplate) : process.cwd();
|
|
82
|
+
filePath = path.resolve(basePath, `.${request.url}`);
|
|
83
|
+
if (!(0, miscellaneous_1.isSubdir)(basePath, filePath)) {
|
|
84
|
+
(0, server_1.respondWithGzip)('404 Not Found', request, response, { 'Content-Type': 'text/html' }, 404);
|
|
85
|
+
console.timeEnd(colorette.dim(`GET ${request.url}`));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const extname = String(path.extname(filePath)).toLowerCase();
|
|
90
|
+
const contentType = server_1.mimeTypes[extname] || 'application/octet-stream';
|
|
91
|
+
try {
|
|
92
|
+
(0, server_1.respondWithGzip)(await fs_1.promises.readFile(filePath), request, response, {
|
|
93
|
+
'Content-Type': contentType,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
catch (e) {
|
|
97
|
+
if (e.code === 'ENOENT') {
|
|
98
|
+
(0, server_1.respondWithGzip)('404 Not Found', request, response, { 'Content-Type': 'text/html' }, 404);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
(0, server_1.respondWithGzip)(`Something went wrong: ${e.code || e.message}...\n`, request, response, {}, 500);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
console.timeEnd(colorette.dim(`GET ${request.url}`));
|
|
106
|
+
};
|
|
107
|
+
const wsPort = await (0, get_port_please_1.getPort)({ port: 32201, portRange: [32201, 32301], host });
|
|
108
|
+
const server = (0, server_1.startHttpServer)(port, host, handler);
|
|
109
|
+
server.on('listening', () => {
|
|
110
|
+
process.stdout.write(`\n 🔎 Preview server running at ${colorette.blue(`http://${host}:${port}\n`)}`);
|
|
111
|
+
});
|
|
112
|
+
return (0, server_1.startWsServer)(wsPort, host);
|
|
113
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as http from 'http';
|
|
2
|
+
import type { ReadStream } from 'fs';
|
|
3
|
+
export declare const mimeTypes: {
|
|
4
|
+
'.html': string;
|
|
5
|
+
'.js': string;
|
|
6
|
+
'.css': string;
|
|
7
|
+
'.json': string;
|
|
8
|
+
'.png': string;
|
|
9
|
+
'.jpg': string;
|
|
10
|
+
'.gif': string;
|
|
11
|
+
'.svg': string;
|
|
12
|
+
'.wav': string;
|
|
13
|
+
'.mp4': string;
|
|
14
|
+
'.woff': string;
|
|
15
|
+
'.ttf': string;
|
|
16
|
+
'.eot': string;
|
|
17
|
+
'.otf': string;
|
|
18
|
+
'.wasm': string;
|
|
19
|
+
};
|
|
20
|
+
export declare function respondWithGzip(contents: string | Buffer | ReadStream, request: http.IncomingMessage, response: http.ServerResponse, headers?: {}, code?: number): void;
|
|
21
|
+
export declare function startHttpServer(port: number, host: string, handler: http.RequestListener): http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
|
|
22
|
+
export declare function startWsServer(port: number, host: string): any;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mimeTypes = void 0;
|
|
4
|
+
exports.respondWithGzip = respondWithGzip;
|
|
5
|
+
exports.startHttpServer = startHttpServer;
|
|
6
|
+
exports.startWsServer = startWsServer;
|
|
7
|
+
const http = require("http");
|
|
8
|
+
const zlib = require("zlib");
|
|
9
|
+
const SocketServer = require('simple-websocket/server.js');
|
|
10
|
+
exports.mimeTypes = {
|
|
11
|
+
'.html': 'text/html',
|
|
12
|
+
'.js': 'text/javascript',
|
|
13
|
+
'.css': 'text/css',
|
|
14
|
+
'.json': 'application/json',
|
|
15
|
+
'.png': 'image/png',
|
|
16
|
+
'.jpg': 'image/jpg',
|
|
17
|
+
'.gif': 'image/gif',
|
|
18
|
+
'.svg': 'image/svg+xml',
|
|
19
|
+
'.wav': 'audio/wav',
|
|
20
|
+
'.mp4': 'video/mp4',
|
|
21
|
+
'.woff': 'application/font-woff',
|
|
22
|
+
'.ttf': 'application/font-ttf',
|
|
23
|
+
'.eot': 'application/vnd.ms-fontobject',
|
|
24
|
+
'.otf': 'application/font-otf',
|
|
25
|
+
'.wasm': 'application/wasm',
|
|
26
|
+
};
|
|
27
|
+
// credits: https://stackoverflow.com/a/9238214/1749888
|
|
28
|
+
function respondWithGzip(contents, request, response, headers = {}, code = 200) {
|
|
29
|
+
let compressedStream;
|
|
30
|
+
const acceptEncoding = request.headers['accept-encoding'] || '';
|
|
31
|
+
if (acceptEncoding.match(/\bdeflate\b/)) {
|
|
32
|
+
response.writeHead(code, { ...headers, 'content-encoding': 'deflate' });
|
|
33
|
+
compressedStream = zlib.createDeflate();
|
|
34
|
+
}
|
|
35
|
+
else if (acceptEncoding.match(/\bgzip\b/)) {
|
|
36
|
+
response.writeHead(code, { ...headers, 'content-encoding': 'gzip' });
|
|
37
|
+
compressedStream = zlib.createGzip();
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
response.writeHead(code, headers);
|
|
41
|
+
if (typeof contents === 'string' || Buffer.isBuffer(contents)) {
|
|
42
|
+
response.write(contents);
|
|
43
|
+
response.end();
|
|
44
|
+
}
|
|
45
|
+
else if (response !== undefined) {
|
|
46
|
+
contents.pipe(response);
|
|
47
|
+
}
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (typeof contents === 'string' || Buffer.isBuffer(contents)) {
|
|
51
|
+
compressedStream.write(contents);
|
|
52
|
+
compressedStream.pipe(response);
|
|
53
|
+
compressedStream.end();
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
contents.pipe(compressedStream).pipe(response);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function startHttpServer(port, host, handler) {
|
|
60
|
+
return http.createServer(handler).listen(port, host);
|
|
61
|
+
}
|
|
62
|
+
function startWsServer(port, host) {
|
|
63
|
+
const socketServer = new SocketServer({ port, host, clientTracking: true });
|
|
64
|
+
socketServer.on('connection', (socket) => {
|
|
65
|
+
socket.on('data', (data) => {
|
|
66
|
+
const message = JSON.parse(data);
|
|
67
|
+
switch (message.type) {
|
|
68
|
+
case 'ping':
|
|
69
|
+
socket.send('{"type": "pong"}');
|
|
70
|
+
break;
|
|
71
|
+
default:
|
|
72
|
+
// nope
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
socketServer.broadcast = (message) => {
|
|
77
|
+
socketServer._server.clients.forEach((client) => {
|
|
78
|
+
if (client.readyState === 1) {
|
|
79
|
+
// OPEN
|
|
80
|
+
client.send(message);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
return socketServer;
|
|
85
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Product } from './types';
|
|
2
|
+
export declare const PRODUCT_PACKAGES: {
|
|
3
|
+
realm: string;
|
|
4
|
+
'redoc-revel': string;
|
|
5
|
+
'revel-reef': string;
|
|
6
|
+
'redoc-reef': string;
|
|
7
|
+
redoc: string;
|
|
8
|
+
revel: string;
|
|
9
|
+
reef: string;
|
|
10
|
+
};
|
|
11
|
+
export declare const PRODUCT_NAMES: {
|
|
12
|
+
[key in Product]: string;
|
|
13
|
+
};
|
|
14
|
+
export declare const PRODUCT_PLANS: readonly ["pro", "enterprise"];
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PRODUCT_PLANS = exports.PRODUCT_NAMES = exports.PRODUCT_PACKAGES = void 0;
|
|
4
|
+
exports.PRODUCT_PACKAGES = {
|
|
5
|
+
realm: '@redocly/realm',
|
|
6
|
+
'redoc-revel': '@redocly/redoc-revel',
|
|
7
|
+
'revel-reef': '@redocly/revel-reef',
|
|
8
|
+
'redoc-reef': '@redocly/redoc-reef',
|
|
9
|
+
redoc: '@redocly/redoc',
|
|
10
|
+
revel: '@redocly/revel',
|
|
11
|
+
reef: '@redocly/reef',
|
|
12
|
+
};
|
|
13
|
+
exports.PRODUCT_NAMES = {
|
|
14
|
+
redoc: 'Redoc',
|
|
15
|
+
revel: 'Revel',
|
|
16
|
+
reef: 'Reef',
|
|
17
|
+
realm: 'Realm',
|
|
18
|
+
'redoc-revel': 'Redoc + Revel',
|
|
19
|
+
'redoc-reef': 'Redoc + Reef',
|
|
20
|
+
'revel-reef': 'Revel + Reef',
|
|
21
|
+
};
|
|
22
|
+
exports.PRODUCT_PLANS = ['pro', 'enterprise'];
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.previewProject = void 0;
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
const child_process_1 = require("child_process");
|
|
7
|
+
const constants_1 = require("./constants");
|
|
8
|
+
const platform_1 = require("../../utils/platform");
|
|
9
|
+
const previewProject = async ({ argv }) => {
|
|
10
|
+
const { plan, port } = argv;
|
|
11
|
+
const projectDir = argv['project-dir'];
|
|
12
|
+
const product = argv.product || tryGetProductFromPackageJson(projectDir);
|
|
13
|
+
if (!isValidProduct(product)) {
|
|
14
|
+
process.stderr.write(`Invalid product ${product}.`);
|
|
15
|
+
throw new Error(`Project preview launch failed.`);
|
|
16
|
+
}
|
|
17
|
+
const productName = constants_1.PRODUCT_NAMES[product];
|
|
18
|
+
const packageName = constants_1.PRODUCT_PACKAGES[product];
|
|
19
|
+
process.stdout.write(`\nLaunching preview of ${productName} ${plan} using NPX.\n\n`);
|
|
20
|
+
const { npxExecutableName, shell } = (0, platform_1.getPlatformSpawnArgs)();
|
|
21
|
+
const child = (0, child_process_1.spawn)(npxExecutableName, ['-y', packageName, 'preview', `--plan=${plan}`, `--port=${port || 4000}`], {
|
|
22
|
+
stdio: 'inherit',
|
|
23
|
+
cwd: projectDir,
|
|
24
|
+
shell,
|
|
25
|
+
});
|
|
26
|
+
child.on('error', (error) => {
|
|
27
|
+
process.stderr.write(`Project preview launch failed: ${error.message}`);
|
|
28
|
+
throw new Error(`Project preview launch failed.`);
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
exports.previewProject = previewProject;
|
|
32
|
+
const isValidProduct = (product) => {
|
|
33
|
+
if (!product) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
return !!constants_1.PRODUCT_NAMES[product];
|
|
37
|
+
};
|
|
38
|
+
const tryGetProductFromPackageJson = (projectDir) => {
|
|
39
|
+
const packageJsonPath = path.join(process.cwd(), projectDir, 'package.json');
|
|
40
|
+
if ((0, fs_1.existsSync)(packageJsonPath)) {
|
|
41
|
+
try {
|
|
42
|
+
const packageJson = JSON.parse((0, fs_1.readFileSync)(packageJsonPath, 'utf-8'));
|
|
43
|
+
const packageJsonDeps = packageJson.dependencies || {};
|
|
44
|
+
for (const [product, packageName] of Object.entries(constants_1.PRODUCT_PACKAGES)) {
|
|
45
|
+
if (packageJsonDeps[packageName]) {
|
|
46
|
+
process.stdout.write(`\n${packageName} detected in project's 'package.json'`);
|
|
47
|
+
return product;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
process.stdout.write(`Invalid 'package.json': ${packageJsonPath}. Using Realm.`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return 'realm';
|
|
56
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { VerifyConfigOptions } from '../../types';
|
|
2
|
+
import type { PRODUCT_PACKAGES, PRODUCT_PLANS } from './constants';
|
|
3
|
+
export type Product = keyof typeof PRODUCT_PACKAGES;
|
|
4
|
+
export type ProductPlan = typeof PRODUCT_PLANS[number];
|
|
5
|
+
export type PreviewProjectOptions = {
|
|
6
|
+
product?: Product | string;
|
|
7
|
+
plan: ProductPlan | string;
|
|
8
|
+
port?: number;
|
|
9
|
+
'project-dir': string;
|
|
10
|
+
} & VerifyConfigOptions;
|