mirror of
https://github.com/Redocly/redoc.git
synced 2025-08-08 14:14:56 +03:00
remove cli
This commit is contained in:
parent
13bd59be41
commit
bcae2d5d69
1
cli/.gitignore
vendored
1
cli/.gitignore
vendored
|
@ -1 +0,0 @@
|
||||||
/node_modules
|
|
|
@ -1,4 +0,0 @@
|
||||||
*/
|
|
||||||
!index.js
|
|
||||||
!package.json
|
|
||||||
!README.md
|
|
|
@ -1,23 +0,0 @@
|
||||||
# Package the 'redoc-cli' as a docker image.
|
|
||||||
#
|
|
||||||
# To build:
|
|
||||||
# $ cd <Redoc project directory>
|
|
||||||
# $ docker build -t redoc-cli -f cli/Dockerfile .
|
|
||||||
#
|
|
||||||
# To run:
|
|
||||||
# To display the command line options:
|
|
||||||
# $ docker run --rm -it redoc-cli --help
|
|
||||||
# .. will display the comand line help
|
|
||||||
#
|
|
||||||
# To turn `swagger.yml` file in the current directory, to html documentation 'redoc-static.html'
|
|
||||||
# $ docker run --rm -it -v $PWD:/data redoc-cli bundle swagger.yml
|
|
||||||
|
|
||||||
FROM node:alpine
|
|
||||||
|
|
||||||
RUN npm install -g redoc-cli
|
|
||||||
|
|
||||||
WORKDIR /data
|
|
||||||
EXPOSE 8080
|
|
||||||
|
|
||||||
ENTRYPOINT ["redoc-cli"]
|
|
||||||
CMD []
|
|
|
@ -1,22 +0,0 @@
|
||||||
# redoc-cli
|
|
||||||
|
|
||||||
**[ReDoc](https://github.com/Redocly/redoc)'s Command Line Interface**
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
You can use redoc cli by installing `redoc-cli` globally or using [npx](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b).
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
Two following commands are available:
|
|
||||||
|
|
||||||
- `redoc-cli serve [spec]` - starts the server with `spec` rendered with ReDoc. Supports SSR mode (`--ssr`) and can watch the spec (`--watch`)
|
|
||||||
- `redoc-cli bundle [spec]` - bundles spec and ReDoc into **zero-dependency** HTML file.
|
|
||||||
|
|
||||||
Some examples:
|
|
||||||
|
|
||||||
- Bundle with main color changed to `orange`: <br> `$ redoc-cli bundle [spec] --options.theme.colors.primary.main=orange`
|
|
||||||
- Serve with `nativeScrollbars` option set to true: <br> `$ redoc-cli serve [spec] --options.nativeScrollbars`
|
|
||||||
- Bundle using custom template (check [default template](https://github.com/Redocly/redoc/blob/master/cli/template.hbs) for reference): <br> `$ redoc-cli bundle [spec] -t custom.hbs`
|
|
||||||
- Bundle using custom template and add custom `templateOptions`: <br> `$ redoc-cli bundle [spec] -t custom.hbs --templateOptions.metaDescription "Page meta description"`
|
|
||||||
|
|
||||||
For more details run `redoc-cli --help`.
|
|
265
cli/index.js
265
cli/index.js
|
@ -1,265 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
"use strict";
|
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
||||||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
/* tslint:disable:no-implicit-dependencies */
|
|
||||||
const React = require("react");
|
|
||||||
const server_1 = require("react-dom/server");
|
|
||||||
const styled_components_1 = require("styled-components");
|
|
||||||
const handlebars_1 = require("handlebars");
|
|
||||||
const http_1 = require("http");
|
|
||||||
const path_1 = require("path");
|
|
||||||
const zlib = require("zlib");
|
|
||||||
// @ts-ignore
|
|
||||||
const redoc_1 = require("redoc");
|
|
||||||
const chokidar_1 = require("chokidar");
|
|
||||||
const fs_1 = require("fs");
|
|
||||||
const mkdirp = require("mkdirp");
|
|
||||||
const YargsParser = require("yargs");
|
|
||||||
const BUNDLES_DIR = path_1.dirname(require.resolve('redoc'));
|
|
||||||
/* tslint:disable-next-line */
|
|
||||||
YargsParser.command('serve <spec>', 'start the server', yargs => {
|
|
||||||
yargs.positional('spec', {
|
|
||||||
describe: 'path or URL to your spec',
|
|
||||||
});
|
|
||||||
yargs.option('s', {
|
|
||||||
alias: 'ssr',
|
|
||||||
describe: 'Enable server-side rendering',
|
|
||||||
type: 'boolean',
|
|
||||||
});
|
|
||||||
yargs.option('p', {
|
|
||||||
alias: 'port',
|
|
||||||
type: 'number',
|
|
||||||
default: 8080,
|
|
||||||
});
|
|
||||||
yargs.option('w', {
|
|
||||||
alias: 'watch',
|
|
||||||
type: 'boolean',
|
|
||||||
});
|
|
||||||
yargs.demandOption('spec');
|
|
||||||
return yargs;
|
|
||||||
}, (argv) => __awaiter(this, void 0, void 0, function* () {
|
|
||||||
const config = {
|
|
||||||
ssr: argv.ssr,
|
|
||||||
watch: argv.watch,
|
|
||||||
templateFileName: argv.template,
|
|
||||||
redocOptions: argv.options || {},
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
yield serve(argv.port, argv.spec, config);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
handleError(e);
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
.command('bundle <spec>', 'bundle spec into zero-dependency HTML-file', yargs => {
|
|
||||||
yargs.positional('spec', {
|
|
||||||
describe: 'path or URL to your spec',
|
|
||||||
});
|
|
||||||
yargs.option('o', {
|
|
||||||
describe: 'Output file',
|
|
||||||
alias: 'output',
|
|
||||||
type: 'string',
|
|
||||||
default: 'redoc-static.html',
|
|
||||||
});
|
|
||||||
yargs.options('title', {
|
|
||||||
describe: 'Page Title',
|
|
||||||
type: 'string',
|
|
||||||
default: 'ReDoc documentation',
|
|
||||||
});
|
|
||||||
yargs.option('cdn', {
|
|
||||||
describe: 'Do not include ReDoc source code into html page, use link to CDN instead',
|
|
||||||
type: 'boolean',
|
|
||||||
default: false,
|
|
||||||
});
|
|
||||||
yargs.demandOption('spec');
|
|
||||||
return yargs;
|
|
||||||
}, (argv) => __awaiter(this, void 0, void 0, function* () {
|
|
||||||
const config = {
|
|
||||||
ssr: true,
|
|
||||||
output: argv.o,
|
|
||||||
cdn: argv.cdn,
|
|
||||||
title: argv.title,
|
|
||||||
templateFileName: argv.template,
|
|
||||||
redocOptions: argv.options || {},
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
yield bundle(argv.spec, config);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
handleError(e);
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
.demandCommand()
|
|
||||||
.options('t', {
|
|
||||||
alias: 'template',
|
|
||||||
describe: 'Path to handlebars page template, see https://git.io/vh8fP for the example ',
|
|
||||||
type: 'string',
|
|
||||||
})
|
|
||||||
.options('options', {
|
|
||||||
describe: 'ReDoc options, use dot notation, e.g. options.nativeScrollbars',
|
|
||||||
}).argv;
|
|
||||||
function serve(port, pathToSpec, options = {}) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
let spec = yield redoc_1.loadAndBundleSpec(pathToSpec);
|
|
||||||
let pageHTML = yield getPageHTML(spec, pathToSpec, options);
|
|
||||||
const server = http_1.createServer((request, response) => {
|
|
||||||
console.time('GET ' + request.url);
|
|
||||||
if (request.url === '/redoc.standalone.js') {
|
|
||||||
respondWithGzip(fs_1.createReadStream(path_1.join(BUNDLES_DIR, 'redoc.standalone.js'), 'utf8'), request, response, {
|
|
||||||
'Content-Type': 'application/javascript',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (request.url === '/') {
|
|
||||||
respondWithGzip(pageHTML, request, response);
|
|
||||||
}
|
|
||||||
else if (request.url === '/spec.json') {
|
|
||||||
const specStr = JSON.stringify(spec, null, 2);
|
|
||||||
respondWithGzip(specStr, request, response, {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
response.writeHead(404);
|
|
||||||
response.write('Not found');
|
|
||||||
response.end();
|
|
||||||
}
|
|
||||||
console.timeEnd('GET ' + request.url);
|
|
||||||
});
|
|
||||||
console.log();
|
|
||||||
server.listen(port, () => console.log(`Server started: http://127.0.0.1:${port}`));
|
|
||||||
if (options.watch && fs_1.existsSync(pathToSpec)) {
|
|
||||||
const pathToSpecDirectory = path_1.resolve(path_1.dirname(pathToSpec));
|
|
||||||
const watchOptions = {
|
|
||||||
ignored: /(^|[\/\\])\../,
|
|
||||||
};
|
|
||||||
const watcher = chokidar_1.watch(pathToSpecDirectory, watchOptions);
|
|
||||||
const log = console.log.bind(console);
|
|
||||||
watcher
|
|
||||||
.on('change', (path) => __awaiter(this, void 0, void 0, function* () {
|
|
||||||
log(`${path} changed, updating docs`);
|
|
||||||
try {
|
|
||||||
spec = yield redoc_1.loadAndBundleSpec(pathToSpec);
|
|
||||||
pageHTML = yield getPageHTML(spec, pathToSpec, options);
|
|
||||||
log('Updated successfully');
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
console.error('Error while updating: ', e.message);
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
.on('error', error => console.error(`Watcher error: ${error}`))
|
|
||||||
.on('ready', () => log(`👀 Watching ${pathToSpecDirectory} for changes...`));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function bundle(pathToSpec, options = {}) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
const start = Date.now();
|
|
||||||
const spec = yield redoc_1.loadAndBundleSpec(pathToSpec);
|
|
||||||
const pageHTML = yield getPageHTML(spec, pathToSpec, Object.assign({}, options, { ssr: true }));
|
|
||||||
mkdirp.sync(path_1.dirname(options.output));
|
|
||||||
fs_1.writeFileSync(options.output, pageHTML);
|
|
||||||
const sizeInKiB = Math.ceil(Buffer.byteLength(pageHTML) / 1024);
|
|
||||||
const time = Date.now() - start;
|
|
||||||
console.log(`\n🎉 bundled successfully in: ${options.output} (${sizeInKiB} KiB) [⏱ ${time / 1000}s]`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function getPageHTML(spec, pathToSpec, { ssr, cdn, title, templateFileName, redocOptions = {} }) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
let html;
|
|
||||||
let css;
|
|
||||||
let state;
|
|
||||||
let redocStandaloneSrc;
|
|
||||||
if (ssr) {
|
|
||||||
console.log('Prerendering docs');
|
|
||||||
const specUrl = redocOptions.specUrl || (isURL(pathToSpec) ? pathToSpec : undefined);
|
|
||||||
const store = yield redoc_1.createStore(spec, specUrl, redocOptions);
|
|
||||||
const sheet = new styled_components_1.ServerStyleSheet();
|
|
||||||
html = server_1.renderToString(sheet.collectStyles(React.createElement(redoc_1.Redoc, { store })));
|
|
||||||
css = sheet.getStyleTags();
|
|
||||||
state = yield store.toJS();
|
|
||||||
if (!cdn) {
|
|
||||||
redocStandaloneSrc = fs_1.readFileSync(path_1.join(BUNDLES_DIR, 'redoc.standalone.js'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
templateFileName = templateFileName ? templateFileName : path_1.join(__dirname, './template.hbs');
|
|
||||||
const template = handlebars_1.compile(fs_1.readFileSync(templateFileName).toString());
|
|
||||||
return template({
|
|
||||||
redocHTML: `
|
|
||||||
<div id="redoc">${(ssr && html) || ''}</div>
|
|
||||||
<script>
|
|
||||||
${(ssr && `const __redoc_state = ${sanitizeJSONString(JSON.stringify(state))};`) || ''}
|
|
||||||
|
|
||||||
var container = document.getElementById('redoc');
|
|
||||||
Redoc.${ssr
|
|
||||||
? 'hydrate(__redoc_state, container);'
|
|
||||||
: `init("spec.json", ${JSON.stringify(redocOptions)}, container)`};
|
|
||||||
|
|
||||||
</script>`,
|
|
||||||
redocHead: ssr
|
|
||||||
? (cdn
|
|
||||||
? '<script src="https://unpkg.com/redoc@next/bundles/redoc.standalone.js"></script>'
|
|
||||||
: `<script>${redocStandaloneSrc}</script>`) + css
|
|
||||||
: '<script src="redoc.standalone.js"></script>',
|
|
||||||
title,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// credits: https://stackoverflow.com/a/9238214/1749888
|
|
||||||
function respondWithGzip(contents, request, response, headers = {}) {
|
|
||||||
let compressedStream;
|
|
||||||
const acceptEncoding = request.headers['accept-encoding'] || '';
|
|
||||||
if (acceptEncoding.match(/\bdeflate\b/)) {
|
|
||||||
response.writeHead(200, Object.assign({}, headers, { 'content-encoding': 'deflate' }));
|
|
||||||
compressedStream = zlib.createDeflate();
|
|
||||||
}
|
|
||||||
else if (acceptEncoding.match(/\bgzip\b/)) {
|
|
||||||
response.writeHead(200, Object.assign({}, headers, { 'content-encoding': 'gzip' }));
|
|
||||||
compressedStream = zlib.createGzip();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
response.writeHead(200, headers);
|
|
||||||
if (typeof contents === 'string') {
|
|
||||||
response.write(contents);
|
|
||||||
response.end();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
contents.pipe(response);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (typeof contents === 'string') {
|
|
||||||
compressedStream.write(contents);
|
|
||||||
compressedStream.pipe(response);
|
|
||||||
compressedStream.end();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
contents.pipe(compressedStream).pipe(response);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function isURL(str) {
|
|
||||||
return /^(https?:)\/\//m.test(str);
|
|
||||||
}
|
|
||||||
function sanitizeJSONString(str) {
|
|
||||||
return escapeClosingScriptTag(escapeUnicode(str));
|
|
||||||
}
|
|
||||||
// see http://www.thespanner.co.uk/2011/07/25/the-json-specification-is-now-wrong/
|
|
||||||
function escapeClosingScriptTag(str) {
|
|
||||||
return str.replace(/<\/script>/g, '<\\/script>');
|
|
||||||
}
|
|
||||||
// see http://www.thespanner.co.uk/2011/07/25/the-json-specification-is-now-wrong/
|
|
||||||
function escapeUnicode(str) {
|
|
||||||
return str.replace(/\u2028|\u2029/g, m => '\\u202' + (m === '\u2028' ? '8' : '9'));
|
|
||||||
}
|
|
||||||
function handleError(error) {
|
|
||||||
console.error(error.stack);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
375
cli/index.ts
375
cli/index.ts
|
@ -1,375 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
/* tslint:disable:no-implicit-dependencies */
|
|
||||||
import * as React from 'react';
|
|
||||||
import { renderToString } from 'react-dom/server';
|
|
||||||
import { ServerStyleSheet } from 'styled-components';
|
|
||||||
|
|
||||||
import { compile } from 'handlebars';
|
|
||||||
import { createServer, IncomingMessage, ServerResponse } from 'http';
|
|
||||||
import { dirname, join, resolve } from 'path';
|
|
||||||
|
|
||||||
import * as zlib from 'zlib';
|
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
import { createStore, loadAndBundleSpec, Redoc } from 'redoc';
|
|
||||||
|
|
||||||
import { watch } from 'chokidar';
|
|
||||||
import { createReadStream, existsSync, readFileSync, ReadStream, writeFileSync, lstatSync } from 'fs';
|
|
||||||
import * as mkdirp from 'mkdirp';
|
|
||||||
|
|
||||||
import * as YargsParser from 'yargs';
|
|
||||||
|
|
||||||
interface Options {
|
|
||||||
ssr?: boolean;
|
|
||||||
watch?: boolean;
|
|
||||||
cdn?: boolean;
|
|
||||||
output?: string;
|
|
||||||
title?: string;
|
|
||||||
disableGoogleFont?: boolean;
|
|
||||||
port?: number;
|
|
||||||
templateFileName?: string;
|
|
||||||
templateOptions?: any;
|
|
||||||
redocOptions?: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
const BUNDLES_DIR = dirname(require.resolve('redoc'));
|
|
||||||
|
|
||||||
/* tslint:disable-next-line */
|
|
||||||
YargsParser.command(
|
|
||||||
'serve <spec>',
|
|
||||||
'start the server',
|
|
||||||
yargs => {
|
|
||||||
yargs.positional('spec', {
|
|
||||||
describe: 'path or URL to your spec',
|
|
||||||
});
|
|
||||||
|
|
||||||
yargs.option('s', {
|
|
||||||
alias: 'ssr',
|
|
||||||
describe: 'Enable server-side rendering',
|
|
||||||
type: 'boolean',
|
|
||||||
});
|
|
||||||
|
|
||||||
yargs.option('p', {
|
|
||||||
alias: 'port',
|
|
||||||
type: 'number',
|
|
||||||
default: 8080,
|
|
||||||
});
|
|
||||||
|
|
||||||
yargs.option('w', {
|
|
||||||
alias: 'watch',
|
|
||||||
type: 'boolean',
|
|
||||||
});
|
|
||||||
|
|
||||||
yargs.demandOption('spec');
|
|
||||||
return yargs;
|
|
||||||
},
|
|
||||||
async argv => {
|
|
||||||
const config: Options = {
|
|
||||||
ssr: argv.ssr as boolean,
|
|
||||||
watch: argv.watch as boolean,
|
|
||||||
templateFileName: argv.template as string,
|
|
||||||
templateOptions: argv.templateOptions || {},
|
|
||||||
redocOptions: getObjectOrJSON(argv.options),
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log(config);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await serve(argv.port, argv.spec, config);
|
|
||||||
} catch (e) {
|
|
||||||
handleError(e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.command(
|
|
||||||
'bundle <spec>',
|
|
||||||
'bundle spec into zero-dependency HTML-file',
|
|
||||||
yargs => {
|
|
||||||
yargs.positional('spec', {
|
|
||||||
describe: 'path or URL to your spec',
|
|
||||||
});
|
|
||||||
|
|
||||||
yargs.option('o', {
|
|
||||||
describe: 'Output file',
|
|
||||||
alias: 'output',
|
|
||||||
type: 'string',
|
|
||||||
default: 'redoc-static.html',
|
|
||||||
});
|
|
||||||
|
|
||||||
yargs.options('title', {
|
|
||||||
describe: 'Page Title',
|
|
||||||
type: 'string',
|
|
||||||
default: 'ReDoc documentation',
|
|
||||||
});
|
|
||||||
|
|
||||||
yargs.options('disableGoogleFont', {
|
|
||||||
describe: 'Disable Google Font',
|
|
||||||
type: 'boolean',
|
|
||||||
default: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
yargs.option('cdn', {
|
|
||||||
describe: 'Do not include ReDoc source code into html page, use link to CDN instead',
|
|
||||||
type: 'boolean',
|
|
||||||
default: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
yargs.demandOption('spec');
|
|
||||||
return yargs;
|
|
||||||
},
|
|
||||||
async (argv: any) => {
|
|
||||||
const config = {
|
|
||||||
ssr: true,
|
|
||||||
output: argv.o as string,
|
|
||||||
cdn: argv.cdn as boolean,
|
|
||||||
title: argv.title as string,
|
|
||||||
disableGoogleFont: argv.disableGoogleFont as boolean,
|
|
||||||
templateFileName: argv.template as string,
|
|
||||||
templateOptions: argv.templateOptions || {},
|
|
||||||
redocOptions: getObjectOrJSON(argv.options),
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
await bundle(argv.spec, config);
|
|
||||||
} catch (e) {
|
|
||||||
handleError(e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.demandCommand()
|
|
||||||
.options('t', {
|
|
||||||
alias: 'template',
|
|
||||||
describe: 'Path to handlebars page template, see https://git.io/vh8fP for the example ',
|
|
||||||
type: 'string',
|
|
||||||
})
|
|
||||||
.options('options', {
|
|
||||||
describe: 'ReDoc options, use dot notation, e.g. options.nativeScrollbars',
|
|
||||||
}).argv;
|
|
||||||
|
|
||||||
async function serve(port: any, pathToSpec: any, options: any = {}) {
|
|
||||||
let spec = await loadAndBundleSpec(pathToSpec);
|
|
||||||
let pageHTML = await getPageHTML(spec, pathToSpec, options);
|
|
||||||
|
|
||||||
const server = createServer((request, response) => {
|
|
||||||
console.time('GET ' + request.url);
|
|
||||||
if (request.url === '/redoc.standalone.js') {
|
|
||||||
respondWithGzip(
|
|
||||||
createReadStream(join(BUNDLES_DIR, 'redoc.standalone.js'), 'utf8'),
|
|
||||||
request,
|
|
||||||
response,
|
|
||||||
{
|
|
||||||
'Content-Type': 'application/javascript',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} else if (request.url === '/') {
|
|
||||||
respondWithGzip(pageHTML, request, response, {
|
|
||||||
'Content-Type': 'text/html',
|
|
||||||
});
|
|
||||||
} else if (request.url === '/spec.json') {
|
|
||||||
const specStr = JSON.stringify(spec, null, 2);
|
|
||||||
respondWithGzip(specStr, request, response, {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
response.writeHead(404);
|
|
||||||
response.write('Not found');
|
|
||||||
response.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
console.timeEnd('GET ' + request.url);
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log();
|
|
||||||
|
|
||||||
server.listen(port, () => console.log(`Server started: http://127.0.0.1:${port}`));
|
|
||||||
|
|
||||||
if (options.watch && existsSync(pathToSpec)) {
|
|
||||||
const pathToSpecDirectory = resolve(dirname(pathToSpec));
|
|
||||||
const watchOptions = {
|
|
||||||
ignored: [/(^|[\/\\])\../, /___jb_[a-z]+___$/],
|
|
||||||
ignoreInitial: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const watcher = watch(pathToSpecDirectory, watchOptions);
|
|
||||||
const log = console.log.bind(console);
|
|
||||||
|
|
||||||
const handlePath = async path => {
|
|
||||||
try {
|
|
||||||
spec = await loadAndBundleSpec(pathToSpec);
|
|
||||||
pageHTML = await getPageHTML(spec, pathToSpec, options);
|
|
||||||
log('Updated successfully');
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Error while updating: ', e.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
watcher
|
|
||||||
.on('change', async path => {
|
|
||||||
log(`${path} changed, updating docs`);
|
|
||||||
handlePath(path);
|
|
||||||
})
|
|
||||||
.on('add', async path => {
|
|
||||||
log(`File ${path} added, updating docs`);
|
|
||||||
handlePath(path);
|
|
||||||
})
|
|
||||||
.on('addDir', path => {
|
|
||||||
log(`↗ Directory ${path} added. Files in here will trigger reload.`);
|
|
||||||
})
|
|
||||||
.on('error', error => console.error(`Watcher error: ${error}`))
|
|
||||||
.on('ready', () => log(`👀 Watching ${pathToSpecDirectory} for changes...`));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function bundle(pathToSpec, options: any = {}) {
|
|
||||||
const start = Date.now();
|
|
||||||
const spec = await loadAndBundleSpec(pathToSpec);
|
|
||||||
const pageHTML = await getPageHTML(spec, pathToSpec, { ...options, ssr: true });
|
|
||||||
|
|
||||||
mkdirp.sync(dirname(options.output!));
|
|
||||||
writeFileSync(options.output!, pageHTML);
|
|
||||||
const sizeInKiB = Math.ceil(Buffer.byteLength(pageHTML) / 1024);
|
|
||||||
const time = Date.now() - start;
|
|
||||||
console.log(
|
|
||||||
`\n🎉 bundled successfully in: ${options.output!} (${sizeInKiB} KiB) [⏱ ${time / 1000}s]`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getPageHTML(
|
|
||||||
spec: any,
|
|
||||||
pathToSpec: string,
|
|
||||||
{
|
|
||||||
ssr,
|
|
||||||
cdn,
|
|
||||||
title,
|
|
||||||
disableGoogleFont,
|
|
||||||
templateFileName,
|
|
||||||
templateOptions,
|
|
||||||
redocOptions = {},
|
|
||||||
}: Options,
|
|
||||||
) {
|
|
||||||
let html;
|
|
||||||
let css;
|
|
||||||
let state;
|
|
||||||
let redocStandaloneSrc;
|
|
||||||
if (ssr) {
|
|
||||||
console.log('Prerendering docs');
|
|
||||||
|
|
||||||
const specUrl = redocOptions.specUrl || (isURL(pathToSpec) ? pathToSpec : undefined);
|
|
||||||
const store = await createStore(spec, specUrl, redocOptions);
|
|
||||||
const sheet = new ServerStyleSheet();
|
|
||||||
html = renderToString(sheet.collectStyles(React.createElement(Redoc, { store })));
|
|
||||||
css = sheet.getStyleTags();
|
|
||||||
state = await store.toJS();
|
|
||||||
|
|
||||||
if (!cdn) {
|
|
||||||
redocStandaloneSrc = readFileSync(join(BUNDLES_DIR, 'redoc.standalone.js'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
templateFileName = templateFileName ? templateFileName : join(__dirname, './template.hbs');
|
|
||||||
const template = compile(readFileSync(templateFileName).toString());
|
|
||||||
return template({
|
|
||||||
redocHTML: `
|
|
||||||
<div id="redoc">${(ssr && html) || ''}</div>
|
|
||||||
<script>
|
|
||||||
${(ssr && `const __redoc_state = ${sanitizeJSONString(JSON.stringify(state))};`) || ''}
|
|
||||||
|
|
||||||
var container = document.getElementById('redoc');
|
|
||||||
Redoc.${
|
|
||||||
ssr
|
|
||||||
? 'hydrate(__redoc_state, container);'
|
|
||||||
: `init("spec.json", ${JSON.stringify(redocOptions)}, container)`
|
|
||||||
};
|
|
||||||
|
|
||||||
</script>`,
|
|
||||||
redocHead: ssr
|
|
||||||
? (cdn
|
|
||||||
? '<script src="https://unpkg.com/redoc@next/bundles/redoc.standalone.js"></script>'
|
|
||||||
: `<script>${redocStandaloneSrc}</script>`) + css
|
|
||||||
: '<script src="redoc.standalone.js"></script>',
|
|
||||||
title,
|
|
||||||
disableGoogleFont,
|
|
||||||
templateOptions,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// credits: https://stackoverflow.com/a/9238214/1749888
|
|
||||||
function respondWithGzip(
|
|
||||||
contents: string | ReadStream,
|
|
||||||
request: IncomingMessage,
|
|
||||||
response: ServerResponse,
|
|
||||||
headers = {},
|
|
||||||
) {
|
|
||||||
let compressedStream;
|
|
||||||
const acceptEncoding = (request.headers['accept-encoding'] as string) || '';
|
|
||||||
if (acceptEncoding.match(/\bdeflate\b/)) {
|
|
||||||
response.writeHead(200, { ...headers, 'content-encoding': 'deflate' });
|
|
||||||
compressedStream = zlib.createDeflate();
|
|
||||||
} else if (acceptEncoding.match(/\bgzip\b/)) {
|
|
||||||
response.writeHead(200, { ...headers, 'content-encoding': 'gzip' });
|
|
||||||
compressedStream = zlib.createGzip();
|
|
||||||
} else {
|
|
||||||
response.writeHead(200, headers);
|
|
||||||
if (typeof contents === 'string') {
|
|
||||||
response.write(contents);
|
|
||||||
response.end();
|
|
||||||
} else {
|
|
||||||
contents.pipe(response);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof contents === 'string') {
|
|
||||||
compressedStream.write(contents);
|
|
||||||
compressedStream.pipe(response);
|
|
||||||
compressedStream.end();
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
contents.pipe(compressedStream).pipe(response);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isURL(str: string): boolean {
|
|
||||||
return /^(https?:)\/\//m.test(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sanitizeJSONString(str: string) {
|
|
||||||
return escapeClosingScriptTag(escapeUnicode(str));
|
|
||||||
}
|
|
||||||
|
|
||||||
// see http://www.thespanner.co.uk/2011/07/25/the-json-specification-is-now-wrong/
|
|
||||||
function escapeClosingScriptTag(str) {
|
|
||||||
return str.replace(/<\/script>/g, '<\\/script>');
|
|
||||||
}
|
|
||||||
|
|
||||||
// see http://www.thespanner.co.uk/2011/07/25/the-json-specification-is-now-wrong/
|
|
||||||
function escapeUnicode(str) {
|
|
||||||
return str.replace(/\u2028|\u2029/g, m => '\\u202' + (m === '\u2028' ? '8' : '9'));
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleError(error: Error) {
|
|
||||||
console.error(error.stack);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getObjectOrJSON(options) {
|
|
||||||
switch (typeof options) {
|
|
||||||
case 'object':
|
|
||||||
return options;
|
|
||||||
case 'string':
|
|
||||||
try {
|
|
||||||
if (existsSync(options) && lstatSync(options).isFile()) {
|
|
||||||
return JSON.parse(readFileSync(options, 'utf-8'));
|
|
||||||
} else {
|
|
||||||
return JSON.parse(options);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.log(
|
|
||||||
`Encountered error:\n\n${options}\n\nis neither a file with a valid JSON object neither a stringified JSON object.`
|
|
||||||
);
|
|
||||||
handleError(e);
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
5560
cli/package-lock.json
generated
5560
cli/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -1,35 +0,0 @@
|
||||||
{
|
|
||||||
"name": "redoc-cli",
|
|
||||||
"version": "0.9.2",
|
|
||||||
"description": "ReDoc's Command Line Interface",
|
|
||||||
"main": "index.js",
|
|
||||||
"bin": "index.js",
|
|
||||||
"repository": "https://github.com/Redocly/redoc",
|
|
||||||
"author": "Roman Hotsiy <gotsijroman@gmail.com>",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 8"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"chokidar": "^3.0.2",
|
|
||||||
"handlebars": "^4.1.2",
|
|
||||||
"isarray": "^2.0.5",
|
|
||||||
"mkdirp": "^0.5.1",
|
|
||||||
"mobx": "^4.2.0",
|
|
||||||
"node-libs-browser": "^2.2.1",
|
|
||||||
"react": "^16.8.6",
|
|
||||||
"react-dom": "^16.8.6",
|
|
||||||
"redoc": "github:BusinessDuck/ReDoc#master",
|
|
||||||
"styled-components": "^4.3.2",
|
|
||||||
"tslib": "^1.10.0",
|
|
||||||
"yargs": "^13.3.0"
|
|
||||||
},
|
|
||||||
"publishConfig": {
|
|
||||||
"access": "public"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/chokidar": "^2.1.3",
|
|
||||||
"@types/handlebars": "^4.0.39",
|
|
||||||
"@types/mkdirp": "^0.5.2"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf8" />
|
|
||||||
<title>{{title}}</title>
|
|
||||||
<!-- needed for adaptive design -->
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{{{redocHead}}}
|
|
||||||
{{#unless disableGoogleFont}}<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">{{/unless}}
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
{{{redocHTML}}}
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
1986
cli/yarn.lock
1986
cli/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user