1
0
Fork 0
mirror of https://github.com/codex-team/codex.docs.git synced 2025-07-31 19:19:41 +02:00

implement cli and some refactoring

This commit is contained in:
Nikita Melnikov 2022-10-08 22:42:58 +08:00
parent 1ec4d11883
commit d883fb7961
8 changed files with 379 additions and 249 deletions

2
.gitignore vendored
View file

@ -83,3 +83,5 @@ db/
/public/dist/* /public/dist/*
*.local.yaml *.local.yaml
static-build

View file

@ -1,10 +1,10 @@
{ {
"name": "codex.docs", "name": "codex.docs",
"license": "Apache-2.0", "license": "Apache-2.0",
"version": "0.0.1-alpha.5", "version": "0.0.1-alpha.6",
"type": "module", "type": "module",
"bin": { "bin": {
"codex.docs": "dist/backend/server.js" "codex.docs": "dist/backend/app.js"
}, },
"browserslist": [ "browserslist": [
"last 2 versions", "last 2 versions",
@ -14,8 +14,8 @@
"start": "concurrently \"yarn start-backend\" \"yarn build-frontend\"", "start": "concurrently \"yarn start-backend\" \"yarn build-frontend\"",
"dev": "concurrently \"yarn start-backend\" \"yarn build-frontend:dev\"", "dev": "concurrently \"yarn start-backend\" \"yarn build-frontend:dev\"",
"build-all": "yarn build-frontend && yarn build-backend", "build-all": "yarn build-frontend && yarn build-backend",
"build-static": "ts-node src/backend/build-static.ts", "build-static": "ts-node src/backend/app.ts build-static -c docs-config.yaml -c docs-config.local.yaml",
"start-backend": "cross-env NODE_ENV=development npx nodemon --config nodemon.json src/backend/server.ts -c docs-config.yaml -c docs-config.local.yaml", "start-backend": "cross-env NODE_ENV=development npx nodemon --config nodemon.json src/backend/app.ts -c docs-config.yaml -c docs-config.local.yaml",
"build-backend": "tsc && copyfiles -u 3 ./src/**/*.twig ./dist/backend/views && copyfiles -u 1 ./src/**/*.svg ./dist/", "build-backend": "tsc && copyfiles -u 3 ./src/**/*.twig ./dist/backend/views && copyfiles -u 1 ./src/**/*.svg ./dist/",
"build-frontend": "webpack --mode=production", "build-frontend": "webpack --mode=production",
"build-frontend:dev": "webpack --mode=development --watch", "build-frontend:dev": "webpack --mode=development --watch",
@ -31,6 +31,7 @@
"@hawk.so/javascript": "^3.0.1", "@hawk.so/javascript": "^3.0.1",
"@hawk.so/nodejs": "^3.1.4", "@hawk.so/nodejs": "^3.1.4",
"@types/multer-s3": "^3.0.0", "@types/multer-s3": "^3.0.0",
"@types/yargs": "^17.0.13",
"arg": "^5.0.2", "arg": "^5.0.2",
"config": "^3.3.6", "config": "^3.3.6",
"cookie-parser": "^1.4.5", "cookie-parser": "^1.4.5",
@ -39,6 +40,7 @@
"dotenv": "^16.0.0", "dotenv": "^16.0.0",
"express": "^4.17.1", "express": "^4.17.1",
"file-type": "^16.5.4", "file-type": "^16.5.4",
"fs-extra": "^10.1.0",
"http-errors": "^2.0.0", "http-errors": "^2.0.0",
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",
"mime": "^3.0.0", "mime": "^3.0.0",
@ -53,6 +55,7 @@
"open-graph-scraper": "^4.9.0", "open-graph-scraper": "^4.9.0",
"twig": "^1.15.4", "twig": "^1.15.4",
"uuid4": "^2.0.2", "uuid4": "^2.0.2",
"yargs": "^17.6.0",
"zod": "^3.19.1" "zod": "^3.19.1"
}, },
"devDependencies": { "devDependencies": {
@ -84,6 +87,7 @@
"@types/debug": "^4.1.7", "@types/debug": "^4.1.7",
"@types/express": "^4.17.13", "@types/express": "^4.17.13",
"@types/file-type": "^10.9.1", "@types/file-type": "^10.9.1",
"@types/fs-extra": "^9.0.13",
"@types/jsonwebtoken": "^8.5.4", "@types/jsonwebtoken": "^8.5.4",
"@types/mime": "^2.0.3", "@types/mime": "^2.0.3",
"@types/mkdirp": "^1.0.2", "@types/mkdirp": "^1.0.2",

View file

@ -1,99 +1,20 @@
import express, { NextFunction, Request, Response } from 'express'; import yargs from 'yargs';
import path from 'path'; import { hideBin } from 'yargs/helpers';
import { fileURLToPath } from 'url'; import runHttpServer from './server.js';
import cookieParser from 'cookie-parser'; import buildStatic from './build-static.js';
import morgan from 'morgan';
import routes from './routes/index.js';
import HttpException from './exceptions/httpException.js';
import * as dotenv from 'dotenv';
import HawkCatcher from '@hawk.so/nodejs';
import os from 'os';
import { downloadFavicon, FaviconData } from './utils/downloadFavicon.js';
import appConfig from './utils/appConfig.js';
/** yargs(hideBin(process.argv))
* The __dirname CommonJS variables are not available in ES modules. .option('config', {
* https://nodejs.org/api/esm.html#no-__filename-or-__dirname alias: 'c',
*/ type: 'string',
// eslint-disable-next-line @typescript-eslint/naming-convention default: './docs-config.yaml',
const __dirname = path.dirname(fileURLToPath(import.meta.url)); description: 'Config files paths',
dotenv.config();
const app = express();
const localConfig = appConfig.frontend;
// Initialize the backend error tracking catcher.
if (appConfig.hawk?.backendToken) {
HawkCatcher.init(appConfig.hawk.backendToken);
}
// Get url to upload favicon from config
const favicon = appConfig.favicon;
app.locals.config = localConfig;
// Set client error tracking token as app local.
if (appConfig.hawk?.frontendToken) {
app.locals.config.hawkClientToken = appConfig.hawk.frontendToken;
}
// view engine setup
app.set('views', path.join(__dirname, './', 'views'));
app.set('view engine', 'twig');
import('./utils/twig.js');
const downloadedFaviconFolder = os.tmpdir();
// Check if favicon is not empty
if (favicon) {
// Upload favicon by url, it's path on server is '/temp/favicon.{format}'
downloadFavicon(favicon, downloadedFaviconFolder).then((res) => {
app.locals.favicon = res;
console.log('Favicon successfully uploaded');
}) })
.catch( (err) => { .help('h')
console.log(err); .alias('h', 'help')
console.log('Favicon has not uploaded'); .command('$0', 'start the server', () => {/* empty */}, runHttpServer)
}); .command('build-static', 'build files from database', () => {/* empty */}, async () => {
} else { await buildStatic();
console.log('Favicon is empty, using default path'); process.exit(0);
app.locals.favicon = { })
destination: '/favicon.png', .parse();
type: 'image/png',
} as FaviconData;
}
app.use(morgan('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, '../../public')));
if (appConfig.uploads.driver === 'local') {
app.use('/uploads', express.static(appConfig.uploads.local.path));
}
app.use('/favicon', express.static(downloadedFaviconFolder));
app.use('/', routes);
// global error handler
app.use(function (err: unknown, req: Request, res: Response, next: NextFunction) {
// send any type of error to hawk server.
if (appConfig.hawk?.backendToken && err instanceof Error) {
HawkCatcher.send(err);
}
// only send Http based exception to client.
if (err instanceof HttpException) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
}
next(err);
});
export default app;

View file

@ -1,7 +1,6 @@
import twig from 'twig'; import twig from 'twig';
import Page from './models/page.js'; import Page from './models/page.js';
import PagesFlatArray from './models/pagesFlatArray.js'; import PagesFlatArray from './models/pagesFlatArray.js';
import appConfig from './utils/appConfig.js';
import path from 'path'; import path from 'path';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import('./utils/twig.js'); import('./utils/twig.js');
@ -10,85 +9,113 @@ import mkdirp from 'mkdirp';
import { createMenuTree } from './utils/menu.js'; import { createMenuTree } from './utils/menu.js';
import { EntityId } from './database/types.js'; import { EntityId } from './database/types.js';
import PagesOrder from './controllers/pagesOrder.js'; import PagesOrder from './controllers/pagesOrder.js';
import fse from 'fs-extra';
const dirname = path.dirname(fileURLToPath(import.meta.url)); import appConfig from './utils/appConfig.js';
const cwd = process.cwd(); import Aliases from './controllers/aliases.js';
const distPath = path.resolve(cwd, 'dist'); import Pages from './controllers/pages.js';
const pagesOrder = await PagesOrder.getAll();
const allPages = await Page.getAll();
await mkdirp(distPath);
/** /**
* Render template with twig by path * Build static pages from database
*
* @param filePath - path to template
* @param data - data to render template
*/ */
function renderTemplate(filePath: string, data: Record<string, unknown>): Promise<string> { export default async function buildStatic(): Promise<void> {
return new Promise((resolve, reject) => { const config = appConfig.staticBuild;
twig.renderFile(path.resolve(dirname, filePath), data, (err, html) => {
if (err) {
reject(err);
}
resolve(html);
});
});
}
/** if (!config) {
* Renders single page throw new Error('Static build config not found');
*
* @param page - page to render
*/
async function renderPage(page: Page): Promise<void> {
console.log(`Rendering page ${page.uri}`);
const pageParent = await page.getParent();
const pageId = page._id;
if (!pageId) {
throw new Error('Page id is not defined');
} }
const parentIdOfRootPages = '0' as EntityId;
const previousPage = await PagesFlatArray.getPageBefore(pageId);
const nextPage = await PagesFlatArray.getPageAfter(pageId);
const menu = createMenuTree(parentIdOfRootPages, allPages, pagesOrder, 2);
const result = await renderTemplate('./views/pages/page.twig', {
page,
pageParent,
previousPage,
nextPage,
menu,
config: appConfig.frontend,
});
// console.log(result); const dirname = path.dirname(fileURLToPath(import.meta.url));
const filename = page.uri === '' ? 'index.html' : `${page.uri}.html`; const cwd = process.cwd();
const distPath = path.resolve(cwd, config.outputDir);
await fs.writeFile(path.resolve(distPath, filename), result); /**
console.log(`Page ${page.uri} rendered`); * Render template with twig by path
*
* @param filePath - path to template
* @param data - data to render template
*/
function renderTemplate(filePath: string, data: Record<string, unknown>): Promise<string> {
return new Promise((resolve, reject) => {
twig.renderFile(path.resolve(dirname, filePath), data, (err, html) => {
if (err) {
reject(err);
}
resolve(html);
});
});
}
console.log('Remove old static files');
await fse.remove(distPath);
console.log('Building static files');
const pagesOrder = await PagesOrder.getAll();
const allPages = await Page.getAll();
await mkdirp(distPath);
/**
* Renders single page
*
* @param page - page to render
* @param isIndex - is this page index page
*/
async function renderPage(page: Page, isIndex?: boolean): Promise<void> {
console.log(`Rendering page ${page.uri}`);
const pageParent = await page.getParent();
const pageId = page._id;
if (!pageId) {
throw new Error('Page id is not defined');
}
const parentIdOfRootPages = '0' as EntityId;
const previousPage = await PagesFlatArray.getPageBefore(pageId);
const nextPage = await PagesFlatArray.getPageAfter(pageId);
const menu = createMenuTree(parentIdOfRootPages, allPages, pagesOrder, 2);
const result = await renderTemplate('./views/pages/page.twig', {
page,
pageParent,
previousPage,
nextPage,
menu,
config: appConfig.frontend,
});
const filename = (isIndex || page.uri === '') ? 'index.html' : `${page.uri}.html`;
await fs.writeFile(path.resolve(distPath, filename), result);
console.log(`Page ${page.uri} rendered`);
}
/**
* Render index page
*
* @param indexPageUri - uri of index page
*/
async function renderIndexPage(indexPageUri: string): Promise<void> {
const alias = await Aliases.get(indexPageUri);
if (!alias.id) {
throw new Error(`Alias ${indexPageUri} not found`);
}
const page = await Pages.get(alias.id);
console.log(page);
await renderPage(page, true);
}
/**
* Render all pages
*/
for (const page of allPages) {
await renderPage(page);
}
await renderIndexPage(config.indexPageUri);
await fse.copy(path.resolve(dirname, '../../public'), distPath);
console.log('Static files built');
} }
/**
* Render all pages
*/
for (const page of allPages) {
await renderPage(page);
}
/**
* Render index page
*/
async function renderIndexPage(): Promise<void> {
console.log('Rendering index page');
const result = await renderTemplate('./views/pages/index.twig', {
config: appConfig.frontend,
});
const filename = 'index.html';
await fs.writeFile(path.resolve(distPath, filename), result);
console.log('Index page rendered');
}
await renderIndexPage();

View file

@ -2,10 +2,20 @@
/** /**
* Module dependencies. * Module dependencies.
*/ */
import app from './app.js';
import http from 'http'; import http from 'http';
import Debug from 'debug'; import Debug from 'debug';
import appConfig from './utils/appConfig.js'; import appConfig from './utils/appConfig.js';
import { drawBanner } from './utils/banner.js';
import express, { NextFunction, Request, Response } from 'express';
import path from 'path';
import { fileURLToPath } from 'url';
import HawkCatcher from '@hawk.so/nodejs';
import os from 'os';
import { downloadFavicon, FaviconData } from './utils/downloadFavicon.js';
import morgan from 'morgan';
import cookieParser from 'cookie-parser';
import routes from './routes/index.js';
import HttpException from './exceptions/httpException.js';
const debug = Debug.debug('codex.editor.docs:server'); const debug = Debug.debug('codex.editor.docs:server');
@ -14,19 +24,140 @@ const debug = Debug.debug('codex.editor.docs:server');
*/ */
const port = normalizePort(appConfig.port.toString() || '3000'); const port = normalizePort(appConfig.port.toString() || '3000');
app.set('port', port); /**
* Create Express server
*/
function createApp(): express.Express {
/**
* The __dirname CommonJS variables are not available in ES modules.
* https://nodejs.org/api/esm.html#no-__filename-or-__dirname
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const app = express();
const localConfig = appConfig.frontend;
// Initialize the backend error tracking catcher.
if (appConfig.hawk?.backendToken) {
HawkCatcher.init(appConfig.hawk.backendToken);
}
// Get url to upload favicon from config
const favicon = appConfig.favicon;
app.locals.config = localConfig;
// Set client error tracking token as app local.
if (appConfig.hawk?.frontendToken) {
app.locals.config.hawkClientToken = appConfig.hawk.frontendToken;
}
// view engine setup
app.set('views', path.join(__dirname, './', 'views'));
app.set('view engine', 'twig');
import('./utils/twig.js');
const downloadedFaviconFolder = os.tmpdir();
// Check if favicon is not empty
if (favicon) {
// Upload favicon by url, it's path on server is '/temp/favicon.{format}'
downloadFavicon(favicon, downloadedFaviconFolder).then((res) => {
app.locals.favicon = res;
console.log('Favicon successfully uploaded');
})
.catch((err) => {
console.log(err);
console.log('Favicon has not uploaded');
});
} else {
console.log('Favicon is empty, using default path');
app.locals.favicon = {
destination: '/favicon.png',
type: 'image/png',
} as FaviconData;
}
app.use(morgan('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, '../../public')));
if (appConfig.uploads.driver === 'local') {
app.use('/uploads', express.static(appConfig.uploads.local.path));
}
app.use('/favicon', express.static(downloadedFaviconFolder));
app.use('/', routes);
// global error handler
app.use(function (err: unknown, req: Request, res: Response, next: NextFunction) {
// send any type of error to hawk server.
if (appConfig.hawk?.backendToken && err instanceof Error) {
HawkCatcher.send(err);
}
// only send Http based exception to client.
if (err instanceof HttpException) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
}
next(err);
});
return app;
}
/** /**
* Create HTTP server. * Create and run HTTP server.
*/ */
const server = http.createServer(app); export default function runHttpServer(): void {
const app = createApp();
/** app.set('port', port);
* Listen on provided port, on all network interfaces.
*/ /**
server.listen(port); * Create HTTP server.
server.on('error', onError); */
server.on('listening', onListening); const server = http.createServer(app);
/**
* Event listener for HTTP server 'listening' event.
*/
function onListening(): void {
const addr = server.address();
if (addr === null) {
debug('Address not found');
process.exit(1);
}
const bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
drawBanner([
`CodeX Docs server is running`,
``,
`Main page: http://localhost:${port}`,
]);
}
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
}
/** /**
* Normalize a port into a number, string, or false. * Normalize a port into a number, string, or false.
@ -77,66 +208,3 @@ function onError(error: NodeJS.ErrnoException): void {
throw error; throw error;
} }
} }
/**
* Event listener for HTTP server 'listening' event.
*/
function onListening(): void {
const addr = server.address();
if (addr === null) {
debug('Address not found');
process.exit(1);
}
const bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
drawBanner([
`CodeX Docs server is running`,
``,
`Main page: http://localhost:${port}`,
]);
}
/**
* Draw banner in console with given text lines
*
* @param {string[]} lines
*/
function drawBanner(lines: string[]): void {
/** Define banner parts */
const PARTS = {
TOP_LEFT: '┌',
TOP_RIGHT: '┐',
BOTTOM_LEFT: '└',
BOTTOM_RIGHT: '┘',
HORIZONTAL: '─',
VERTICAL: '│',
SPACE: ' ',
};
/** Calculate max line length */
const maxLength = lines.reduce((max, line) => Math.max(max, line.length), 0);
/** Prepare top line */
const top = PARTS.TOP_LEFT + PARTS.HORIZONTAL.repeat(maxLength + 2) + PARTS.TOP_RIGHT;
/** Compose middle lines */
const middle = lines.map(line => PARTS.VERTICAL + ' ' + line + PARTS.SPACE.repeat(maxLength - line.length) + ' ' + PARTS.VERTICAL);
/** Prepare bottom line */
const bottom = PARTS.BOTTOM_LEFT + PARTS.HORIZONTAL.repeat(maxLength + 2) + PARTS.BOTTOM_RIGHT;
console.log(top);
console.log(middle.join('\n'));
console.log(bottom);
}
export default {
server,
app,
};

View file

@ -84,6 +84,16 @@ const FrontendConfig = z.object({
uri: z.string() })])), // Menu for pages uri: z.string() })])), // Menu for pages
}); });
/**
* Static build configuration
*/
const StaticBuildConfig = z.object({
outputDir: z.string(), // Output directory for static build
indexPageUri: z.string(), // URI for index page to render
});
export type StaticBuildConfig = z.infer<typeof StaticBuildConfig>;
/** /**
* Application configuration * Application configuration
*/ */
@ -97,6 +107,7 @@ const AppConfig = z.object({
frontend: FrontendConfig, // Frontend configuration frontend: FrontendConfig, // Frontend configuration
auth: AuthConfig, // Auth configuration auth: AuthConfig, // Auth configuration
database: z.union([LocalDatabaseConfig, MongoDatabaseConfig]), // Database configuration database: z.union([LocalDatabaseConfig, MongoDatabaseConfig]), // Database configuration
staticBuild: StaticBuildConfig.optional(), // Static build configuration
}); });
export type AppConfig = z.infer<typeof AppConfig>; export type AppConfig = z.infer<typeof AppConfig>;

View file

@ -0,0 +1,33 @@
/**
* Draw banner in console with given text lines
*
* @param lines - data to draw
*/
export function drawBanner(lines: string[]): void {
/** Define banner parts */
const PARTS = {
TOP_LEFT: '┌',
TOP_RIGHT: '┐',
BOTTOM_LEFT: '└',
BOTTOM_RIGHT: '┘',
HORIZONTAL: '─',
VERTICAL: '│',
SPACE: ' ',
};
/** Calculate max line length */
const maxLength = lines.reduce((max, line) => Math.max(max, line.length), 0);
/** Prepare top line */
const top = PARTS.TOP_LEFT + PARTS.HORIZONTAL.repeat(maxLength + 2) + PARTS.TOP_RIGHT;
/** Compose middle lines */
const middle = lines.map(line => PARTS.VERTICAL + ' ' + line + PARTS.SPACE.repeat(maxLength - line.length) + ' ' + PARTS.VERTICAL);
/** Prepare bottom line */
const bottom = PARTS.BOTTOM_LEFT + PARTS.HORIZONTAL.repeat(maxLength + 2) + PARTS.BOTTOM_RIGHT;
console.log(top);
console.log(middle.join('\n'));
console.log(bottom);
}

View file

@ -2216,6 +2216,13 @@
dependencies: dependencies:
file-type "*" file-type "*"
"@types/fs-extra@^9.0.13":
version "9.0.13"
resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.13.tgz#7594fbae04fe7f1918ce8b3d213f74ff44ac1f45"
integrity sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==
dependencies:
"@types/node" "*"
"@types/glob@*": "@types/glob@*":
version "7.2.0" version "7.2.0"
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb"
@ -2397,6 +2404,18 @@
"@types/node" "*" "@types/node" "*"
"@types/webidl-conversions" "*" "@types/webidl-conversions" "*"
"@types/yargs-parser@*":
version "21.0.0"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b"
integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==
"@types/yargs@^17.0.13":
version "17.0.13"
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.13.tgz#34cced675ca1b1d51fcf4d34c3c6f0fa142a5c76"
integrity sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg==
dependencies:
"@types/yargs-parser" "*"
"@typescript-eslint/eslint-plugin@^5.38.0": "@typescript-eslint/eslint-plugin@^5.38.0":
version "5.38.1" version "5.38.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.38.1.tgz#9f05d42fa8fb9f62304cc2f5c2805e03c01c2620" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.38.1.tgz#9f05d42fa8fb9f62304cc2f5c2805e03c01c2620"
@ -3152,6 +3171,15 @@ cliui@^7.0.2:
strip-ansi "^6.0.0" strip-ansi "^6.0.0"
wrap-ansi "^7.0.0" wrap-ansi "^7.0.0"
cliui@^8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa"
integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==
dependencies:
string-width "^4.2.0"
strip-ansi "^6.0.1"
wrap-ansi "^7.0.0"
clone-deep@^4.0.1: clone-deep@^4.0.1:
version "4.0.1" version "4.0.1"
resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387"
@ -4315,6 +4343,15 @@ fresh@0.5.2:
version "0.5.2" version "0.5.2"
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
fs-extra@^10.1.0:
version "10.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf"
integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==
dependencies:
graceful-fs "^4.2.0"
jsonfile "^6.0.1"
universalify "^2.0.0"
fs.realpath@^1.0.0: fs.realpath@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@ -4467,7 +4504,7 @@ got@^11.8.5:
p-cancelable "^2.0.0" p-cancelable "^2.0.0"
responselike "^2.0.0" responselike "^2.0.0"
graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.2.4, graceful-fs@^4.2.9: graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9:
version "4.2.10" version "4.2.10"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
@ -4936,6 +4973,15 @@ json5@^2.1.1, json5@^2.1.2, json5@^2.2.1:
version "2.2.1" version "2.2.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c"
jsonfile@^6.0.1:
version "6.1.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==
dependencies:
universalify "^2.0.0"
optionalDependencies:
graceful-fs "^4.1.6"
jsonwebtoken@^8.5.1: jsonwebtoken@^8.5.1:
version "8.5.1" version "8.5.1"
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d"
@ -7135,6 +7181,11 @@ uniq@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
universalify@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
unpipe@1.0.0, unpipe@~1.0.0: unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
@ -7441,6 +7492,19 @@ yargs@^17.3.1:
y18n "^5.0.5" y18n "^5.0.5"
yargs-parser "^21.0.0" yargs-parser "^21.0.0"
yargs@^17.6.0:
version "17.6.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.0.tgz#e134900fc1f218bc230192bdec06a0a5f973e46c"
integrity sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==
dependencies:
cliui "^8.0.1"
escalade "^3.1.1"
get-caller-file "^2.0.5"
require-directory "^2.1.1"
string-width "^4.2.3"
y18n "^5.0.5"
yargs-parser "^21.0.0"
yn@3.1.1: yn@3.1.1:
version "3.1.1" version "3.1.1"
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"