1
0
mirror of https://github.com/verdaccio/verdaccio.git synced 2024-11-08 23:25:51 +01:00

chore: update with master (#2158)

This commit is contained in:
Juan Picado 2021-04-02 15:59:47 +02:00
parent 5ccb2bad16
commit 4a3e11d072
29 changed files with 302 additions and 323 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -14,7 +14,33 @@ By default, the token stores in the database, but using this variable allows to
#### VERDACCIO_PUBLIC_URL #### VERDACCIO_PUBLIC_URL
Define a specific public url for your server, it overrules the `Host` and `X-Forwarded-Proto` header if a reverse proxy is Define a specific public url for your server, it overrules the `Host` and `X-Forwarded-Proto` header if a reverse proxy is being used, it takes in account the `url_prefix` if is defined.
being used.
This is handy in such situations where a dynamic url is required. This is handy in such situations where a dynamic url is required.
eg:
```
VERDACCIO_PUBLIC_URL='https://somedomain.org';
url_prefix: '/my_prefix'
// url -> https://somedomain.org/my_prefix/
VERDACCIO_PUBLIC_URL='https://somedomain.org';
url_prefix: '/'
// url -> https://somedomain.org/
VERDACCIO_PUBLIC_URL='https://somedomain.org/first_prefix';
url_prefix: '/second_prefix'
// url -> https://somedomain.org/second_prefix/'
```
#### VERDACCIO_FORWARDED_PROTO
The default header to identify the protocol is `X-Forwarded-Proto`, but there are some environments which [uses something different](https://github.com/verdaccio/verdaccio/issues/990), to change it use the variable `VERDACCIO_FORWARDED_PROTO`
```
$ VERDACCIO_FORWARDED_PROTO=CloudFront-Forwarded-Proto verdaccio --listen 5000
```

@ -49,8 +49,8 @@ export function getWebProtocol(headerProtocol: string | void, protocol: string):
return validProtocols.includes(returnProtocol) ? returnProtocol : defaultProtocol; return validProtocols.includes(returnProtocol) ? returnProtocol : defaultProtocol;
} }
function wrapPrefix(prefix: string): string { export function wrapPrefix(prefix: string | void): string {
if (prefix === '' || _.isNil(prefix)) { if (prefix === '' || typeof prefix === 'undefined' || prefix === null) {
return ''; return '';
} else if (!prefix.startsWith('/') && prefix.endsWith('/')) { } else if (!prefix.startsWith('/') && prefix.endsWith('/')) {
return `/${prefix}`; return `/${prefix}`;
@ -72,9 +72,10 @@ export function combineBaseUrl(protocol: string, host: string, prefix: string =
debug('combined host %o', host); debug('combined host %o', host);
const newPrefix = wrapPrefix(prefix); const newPrefix = wrapPrefix(prefix);
debug('combined prefix %o', newPrefix); debug('combined prefix %o', newPrefix);
const result = new URL(wrapPrefix(prefix), `${protocol}://${host}`); const groupedURI = new URL(wrapPrefix(prefix), `${protocol}://${host}`);
debug('combined url %o', result.href); const result = groupedURI.href;
return result.href; debug('combined url %o', result);
return result;
} }
export function validateURL(publicUrl: string | void) { export function validateURL(publicUrl: string | void) {
@ -92,7 +93,7 @@ export function validateURL(publicUrl: string | void) {
export function getPublicUrl(url_prefix: string = '', req): string { export function getPublicUrl(url_prefix: string = '', req): string {
if (validateURL(process.env.VERDACCIO_PUBLIC_URL as string)) { if (validateURL(process.env.VERDACCIO_PUBLIC_URL as string)) {
const envURL = new URL(process.env.VERDACCIO_PUBLIC_URL as string).href; const envURL = new URL(wrapPrefix(url_prefix), process.env.VERDACCIO_PUBLIC_URL as string).href;
debug('public url by env %o', envURL); debug('public url by env %o', envURL);
return envURL; return envURL;
} else if (req.get('host')) { } else if (req.get('host')) {
@ -100,7 +101,8 @@ export function getPublicUrl(url_prefix: string = '', req): string {
if (!isHost(host)) { if (!isHost(host)) {
throw new Error('invalid host'); throw new Error('invalid host');
} }
const protocol = getWebProtocol(req.get(HEADERS.FORWARDED_PROTO), req.protocol); const protoHeader = process.env.VERDACCIO_FORWARDED_PROTO ?? HEADERS.FORWARDED_PROTO;
const protocol = getWebProtocol(req.get(protoHeader), req.protocol);
const combinedUrl = combineBaseUrl(protocol, host, url_prefix); const combinedUrl = combineBaseUrl(protocol, host, url_prefix);
debug('public url by request %o', combinedUrl); debug('public url by request %o', combinedUrl);
return combinedUrl; return combinedUrl;

@ -44,8 +44,6 @@
"fast-safe-stringify": "2.0.7", "fast-safe-stringify": "2.0.7",
"kleur": "3.0.3", "kleur": "3.0.3",
"lodash": "4.17.20", "lodash": "4.17.20",
"pad-left": "2.1.0",
"pad-right": "0.2.2",
"prettier-bytes": "1.0.4", "prettier-bytes": "1.0.4",
"pretty-ms": "5.1.0" "pretty-ms": "5.1.0"
}, },

@ -1,10 +1,8 @@
import { inspect } from 'util'; import { inspect } from 'util';
import { white, red, green } from 'kleur'; import { white, red, green } from 'kleur';
import padLeft from 'pad-left';
import { calculateLevel, LevelCode, levelsColors, subSystemLevels } from './levels'; import { calculateLevel, LevelCode, levelsColors, subSystemLevels } from './levels';
import { formatLoggingDate, isObject, pad } from './utils'; import { formatLoggingDate, isObject, padLeft, padRight } from './utils';
import { PrettyOptionsExtended } from './types'; import { PrettyOptionsExtended } from './types';
let LEVEL_VALUE_MAX = 0; let LEVEL_VALUE_MAX = 0;
@ -55,22 +53,20 @@ export function fillInMsgTemplate(msg, templateOptions: ObjectTemplate, colors):
}); });
} }
const CUSTOM_PAD_LENGTH = 1;
function getMessage(debugLevel, msg, sub, templateObjects, hasColors) { function getMessage(debugLevel, msg, sub, templateObjects, hasColors) {
const finalMessage = fillInMsgTemplate(msg, templateObjects, hasColors); const finalMessage = fillInMsgTemplate(msg, templateObjects, hasColors);
const subSystemType = subSystemLevels.color[sub ?? 'default']; const subSystemType = subSystemLevels.color[sub ?? 'default'];
if (hasColors) { if (hasColors) {
const logString = `${levelsColors[debugLevel](pad(debugLevel, LEVEL_VALUE_MAX))}${white( const logString = `${levelsColors[debugLevel](padRight(debugLevel, LEVEL_VALUE_MAX))}${white(
`${subSystemType} ${finalMessage}` `${subSystemType} ${finalMessage}`
)}`; )}`;
return padLeft(logString, logString.length + CUSTOM_PAD_LENGTH, ' '); return padLeft(logString);
} }
const logString = `${pad(debugLevel, LEVEL_VALUE_MAX)}${subSystemType} ${finalMessage}`; const logString = `${padRight(debugLevel, LEVEL_VALUE_MAX)}${subSystemType} ${finalMessage}`;
return padLeft(logString, logString.length + CUSTOM_PAD_LENGTH, ' '); return padRight(logString);
} }
export function printMessage( export function printMessage(

@ -1,4 +1,4 @@
import { yellow, green, black, blue, red, magenta, cyan, white } from 'kleur'; import { yellow, green, red, magenta, black, blue, cyan, white } from 'kleur';
export type LogLevel = 'trace' | 'debug' | 'info' | 'http' | 'warn' | 'error' | 'fatal'; export type LogLevel = 'trace' | 'debug' | 'info' | 'http' | 'warn' | 'error' | 'fatal';
@ -6,18 +6,20 @@ export type LevelCode = number;
export function calculateLevel(levelCode: LevelCode): LogLevel { export function calculateLevel(levelCode: LevelCode): LogLevel {
switch (true) { switch (true) {
case levelCode < 15: case levelCode === 10:
return 'trace'; return 'trace';
case levelCode < 25: case levelCode === 20:
return 'debug'; return 'debug';
case levelCode < 35: case levelCode === 25:
return 'info';
case levelCode == 35:
return 'http'; return 'http';
case levelCode < 45: case levelCode === 30:
return 'info';
case levelCode === 40:
return 'warn'; return 'warn';
case levelCode < 55: case levelCode === 50:
return 'error'; return 'error';
case levelCode === 60:
return 'fatal';
default: default:
return 'fatal'; return 'fatal';
} }

@ -1,15 +1,19 @@
import _ from 'lodash'; import _ from 'lodash';
import padRight from 'pad-right';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
export const FORMAT_DATE = 'YYYY-MM-DD HH:mm:ss'; export const FORMAT_DATE = 'YYYY-MM-DD HH:mm:ss';
export const CUSTOM_PAD_LENGTH = 1;
export function isObject(obj: unknown): boolean { export function isObject(obj: unknown): boolean {
return _.isObject(obj) && _.isNull(obj) === false && _.isArray(obj) === false; return _.isObject(obj) && _.isNull(obj) === false && _.isArray(obj) === false;
} }
export function pad(str: string, max: number): string { export function padLeft(message: string) {
return padRight(str, max, ' '); return message.padStart(message.length + CUSTOM_PAD_LENGTH, ' ');
}
export function padRight(message: string, max = message.length + CUSTOM_PAD_LENGTH) {
return message.padEnd(max, ' ');
} }
export function formatLoggingDate(time: number, message): string { export function formatLoggingDate(time: number, message): string {

@ -1,18 +1,18 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`formatter printMessage should display a bytes request 1`] = `" http <-- 200, user: null(127.0.0.1), req: 'GET /verdaccio', bytes: 0/150186"`; exports[`formatter printMessage should display a bytes request 1`] = `" fatal<-- 200, user: null(127.0.0.1), req: 'GET /verdaccio', bytes: 0/150186"`;
exports[`formatter printMessage should display a resource request 1`] = `" info <-- 127.0.0.1 requested 'GET /verdaccio'"`; exports[`formatter printMessage should display a resource request 1`] = `"info <-- 127.0.0.1 requested 'GET /verdaccio' "`;
exports[`formatter printMessage should display a streaming request 1`] = `" http --> 304, req: 'GET https://registry.npmjs.org/verdaccio' (streaming)"`; exports[`formatter printMessage should display a streaming request 1`] = `" fatal--> 304, req: 'GET https://registry.npmjs.org/verdaccio' (streaming)"`;
exports[`formatter printMessage should display an error request 1`] = `" error--> ERR, req: 'GET https://registry.fake.org/aaa', error: getaddrinfo ENOTFOUND registry.fake.org"`; exports[`formatter printMessage should display an error request 1`] = `" fatal--> ERR, req: 'GET https://registry.fake.org/aaa', error: getaddrinfo ENOTFOUND registry.fake.org"`;
exports[`formatter printMessage should display an fatal request 1`] = `" fatal--> ERR, req: 'GET https://registry.fake.org/aaa', error: fatal error"`; exports[`formatter printMessage should display an fatal request 1`] = `" fatal--> ERR, req: 'GET https://registry.fake.org/aaa', error: fatal error"`;
exports[`formatter printMessage should display config file 1`] = `" warn --- config file - /Users/user/.config/verdaccio/config/config.yaml"`; exports[`formatter printMessage should display config file 1`] = `" warn --- config file - /Users/user/.config/verdaccio/config/config.yaml"`;
exports[`formatter printMessage should display custom log message 1`] = `" debug--- custom - foo - undefined"`; exports[`formatter printMessage should display custom log message 1`] = `" fatal--- custom - foo - undefined"`;
exports[`formatter printMessage should display trace level 1`] = `" trace--- [trace] - foo"`; exports[`formatter printMessage should display trace level 1`] = `" trace--- [trace] - foo"`;

@ -4,18 +4,13 @@ import buildDebug from 'debug';
const debug = buildDebug('verdaccio:logger'); const debug = buildDebug('verdaccio:logger');
const DEFAULT_LOG_FORMAT = 'pretty';
export let logger; export let logger;
function isProd() { function isProd() {
return process.env.NODE_ENV === 'production'; return process.env.NODE_ENV === 'production';
} }
function getPrettifier() { const DEFAULT_LOG_FORMAT = isProd() ? 'json' : 'pretty';
// TODO: this module can be loaded dynamically and allow custom formatting
return require('@verdaccio/logger-prettify');
}
export type LogPlugin = { export type LogPlugin = {
dest: string; dest: string;
@ -26,7 +21,7 @@ export type LogType = 'file' | 'stdout';
export type LogFormat = 'json' | 'pretty-timestamped' | 'pretty'; export type LogFormat = 'json' | 'pretty-timestamped' | 'pretty';
export function createLogger( export function createLogger(
options = {}, options = { level: 'http' },
destination = pino.destination(1), destination = pino.destination(1),
format: LogFormat = DEFAULT_LOG_FORMAT, format: LogFormat = DEFAULT_LOG_FORMAT,
prettyPrintOptions = { prettyPrintOptions = {
@ -40,10 +35,11 @@ export function createLogger(
} }
let pinoConfig = { let pinoConfig = {
...options,
customLevels: { customLevels: {
http: 35, http: 25,
}, },
...options,
level: options.level,
serializers: { serializers: {
err: pino.stdSerializers.err, err: pino.stdSerializers.err,
req: pino.stdSerializers.req, req: pino.stdSerializers.req,
@ -52,26 +48,33 @@ export function createLogger(
}; };
debug('has prettifier? %o', !isProd()); debug('has prettifier? %o', !isProd());
// pretty logs are not allowed in production for performance reason // pretty logs are not allowed in production for performance reasons
if ((format === DEFAULT_LOG_FORMAT || format !== 'json') && isProd() === false) { if ((format === DEFAULT_LOG_FORMAT || format !== 'json') && isProd() === false) {
pinoConfig = Object.assign({}, pinoConfig, { pinoConfig = Object.assign({}, pinoConfig, {
// FIXME: this property cannot be used in combination with pino.final // more info
// https://github.com/pinojs/pino-pretty/issues/37 // https://github.com/pinojs/pino-pretty/issues/37
prettyPrint: { prettyPrint: {
levelFirst: true, levelFirst: true,
prettyStamp: format === 'pretty-timestamped', prettyStamp: format === 'pretty-timestamped',
...prettyPrintOptions, ...prettyPrintOptions,
}, },
prettifier: getPrettifier(), prettifier: require('@verdaccio/logger-prettify'),
});
}
const logger = pino(pinoConfig, destination);
if (process.env.DEBUG) {
logger.on('level-change', (lvl, val, prevLvl, prevVal) => {
debug('%s (%d) was changed to %s (%d)', lvl, val, prevLvl, prevVal);
}); });
} }
return pino(pinoConfig, destination); return logger;
} }
export function getLogger() { export function getLogger() {
if (_.isNil(logger)) { if (_.isNil(logger)) {
console.warn('logger is not defined'); process.emitWarning('logger is not defined');
return; return;
} }
@ -96,40 +99,58 @@ export type LoggerConfig = LoggerConfigItem[];
export function setup(options: LoggerConfig | LoggerConfigItem = [DEFAULT_LOGGER_CONF]) { export function setup(options: LoggerConfig | LoggerConfigItem = [DEFAULT_LOGGER_CONF]) {
debug('setup logger'); debug('setup logger');
const isLegacyConf = _.isArray(options); const isLegacyConf = Array.isArray(options);
// verdaccio 4 allows array configuration if (isLegacyConf) {
// backward compatible, pick only the first option const deprecateMessage =
let loggerConfig = isLegacyConf ? options[0] : options; 'deprecate: multiple logger configuration is deprecated, please check the migration guide.';
if (!loggerConfig?.level) { process.emitWarning(deprecateMessage);
loggerConfig = Object.assign({}, loggerConfig, {
level: 'http',
});
} }
// verdaccio 5 does not allow multiple logger configuration
// backward compatible, pick only the first option
// next major will thrown an error
let loggerConfig = isLegacyConf ? options[0] : options;
if (!loggerConfig?.level) {
loggerConfig = Object.assign(
{},
{
level: 'http',
},
loggerConfig
);
}
const pinoConfig = { level: loggerConfig.level }; const pinoConfig = { level: loggerConfig.level };
if (loggerConfig.type === 'file') { if (loggerConfig.type === 'file') {
debug('logging file enabled');
logger = createLogger(pinoConfig, pino.destination(loggerConfig.path), loggerConfig.format); logger = createLogger(pinoConfig, pino.destination(loggerConfig.path), loggerConfig.format);
} else if (loggerConfig.type === 'rotating-file') { } else if (loggerConfig.type === 'rotating-file') {
throw new Error('rotating-file type is not longer supported, consider use [logrotate] instead'); process.emitWarning(
'rotating-file type is not longer supported, consider use [logrotate] instead'
);
debug('logging stdout enabled');
logger = createLogger(pinoConfig, pino.destination(1), loggerConfig.format);
} else { } else {
debug('logging stdout enabled');
logger = createLogger(pinoConfig, pino.destination(1), loggerConfig.format); logger = createLogger(pinoConfig, pino.destination(1), loggerConfig.format);
} }
if (isProd()) { if (isProd()) {
process.on( // why only on prod? https://github.com/pinojs/pino/issues/920#issuecomment-710807667
'uncaughtException', const finalHandler = pino.final(logger, (err, finalLogger, event) => {
pino.final(logger, (err, finalLogger) => { finalLogger.info(`${event} caught`);
finalLogger.fatal(err, 'uncaughtException'); if (err) {
process.exit(1); finalLogger.error(err, 'error caused exit');
}) }
); process.exit(err ? 1 : 0);
});
process.on( process.on('uncaughtException', (err) => finalHandler(err, 'uncaughtException'));
'unhandledRejection', process.on('unhandledRejection', (err) => finalHandler(err as Error, 'unhandledRejection'));
pino.final(logger, (err, finalLogger) => { process.on('beforeExit', () => finalHandler(null, 'beforeExit'));
finalLogger.fatal(err, 'uncaughtException'); process.on('exit', () => finalHandler(null, 'exit'));
process.exit(1); process.on('uncaughtException', (err) => finalHandler(err, 'uncaughtException'));
}) process.on('SIGINT', () => finalHandler(null, 'SIGINT'));
); process.on('SIGQUIT', () => finalHandler(null, 'SIGQUIT'));
process.on('SIGTERM', () => finalHandler(null, 'SIGTERM'));
} }
} }

@ -314,13 +314,12 @@ export function log(req: $RequestExtend, res: $ResponseExtend, next: $NextFuncti
} }
req.url = req.originalUrl; req.url = req.originalUrl;
req.log.warn( req.log.http(
{ {
request: { request: {
method: req.method, method: req.method,
url: req.url, url: req.url,
}, },
level: 35, // http
user: (req.remote_user && req.remote_user.name) || null, user: (req.remote_user && req.remote_user.name) || null,
remoteIP, remoteIP,
status: res.statusCode, status: res.statusCode,

@ -1,9 +1,11 @@
const path = require('path'); const path = require('path');
exports.staticPath = path.join(__dirname, 'static'); module.exports = () => {
exports.manifest = require('./static/manifest.json'); return {
exports.manifestFiles = { staticPath: path.join(__dirname, 'static'),
js: ['runtime.js', 'vendors.js', 'main.js'], manifest: require('./static/manifest.json'),
css: [], manifestFiles: {
ico: 'favicon.ico', js: ['runtime.js', 'vendors.js', 'main.js'],
},
};
}; };

@ -8,7 +8,7 @@ import 'mutationobserver-shim';
// @ts-ignore : Property '__APP_VERSION__' does not exist on type 'Global'. // @ts-ignore : Property '__APP_VERSION__' does not exist on type 'Global'.
global.__APP_VERSION__ = '1.0.0'; global.__APP_VERSION__ = '1.0.0';
// @ts-ignore : Property '__VERDACCIO_BASENAME_UI_OPTIONS' does not exist on type 'Global'. // @ts-ignore : Property '__VERDACCIO_BASENAME_UI_OPTIONS' does not exist on type 'Global'.
global.__VERDACCIO_BASENAME_UI_OPTIONS = { basePath: 'http://localhost' }; global.__VERDACCIO_BASENAME_UI_OPTIONS = { base: 'http://localhost' };
// @ts-ignore : Property 'VERDACCIO_API_URL' does not exist on type 'Global'. // @ts-ignore : Property 'VERDACCIO_API_URL' does not exist on type 'Global'.
global.VERDACCIO_API_URL = 'https://verdaccio.tld'; global.VERDACCIO_API_URL = 'https://verdaccio.tld';

@ -26,9 +26,9 @@ describe('utils', () => {
test('getRegistryURL() - should change when UI options change', () => { test('getRegistryURL() - should change when UI options change', () => {
expect(getRegistryURL()).toBe('http://localhost'); expect(getRegistryURL()).toBe('http://localhost');
window.__VERDACCIO_BASENAME_UI_OPTIONS.basePath = 'http://localhost/test'; window.__VERDACCIO_BASENAME_UI_OPTIONS.base = 'http://localhost/test';
expect(getRegistryURL()).toBe('http://localhost/test'); expect(getRegistryURL()).toBe('http://localhost/test');
window.__VERDACCIO_BASENAME_UI_OPTIONS.basePath = 'http://localhost'; window.__VERDACCIO_BASENAME_UI_OPTIONS.base = 'http://localhost';
}); });
}); });

@ -15,7 +15,7 @@ export function isEmail(email: string): boolean {
} }
export function getRegistryURL(): string { export function getRegistryURL(): string {
return window?.__VERDACCIO_BASENAME_UI_OPTIONS?.basePath; return window?.__VERDACCIO_BASENAME_UI_OPTIONS?.base;
} }
export function extractFileName(url: string): string { export function extractFileName(url: string): string {

@ -230,12 +230,11 @@ class ProxyStorage implements IProxy {
let message = "@{!status}, req: '@{request.method} @{request.url}'"; let message = "@{!status}, req: '@{request.method} @{request.url}'";
// FIXME: use LOG_VERDACCIO_BYTES // FIXME: use LOG_VERDACCIO_BYTES
message += error ? ', error: @{!error}' : ', bytes: @{bytes.in}/@{bytes.out}'; message += error ? ', error: @{!error}' : ', bytes: @{bytes.in}/@{bytes.out}';
self.logger.warn( self.logger.http(
{ {
// if error is null/false change this to undefined so it wont log // if error is null/false change this to undefined so it wont log
err: err || undefined, err: err || undefined,
request: { method: method, url: uri }, request: { method: method, url: uri },
level: 35, // http
status: res != null ? res.statusCode : 'ERR', status: res != null ? res.statusCode : 'ERR',
error: error, error: error,
bytes: { bytes: {
@ -282,13 +281,12 @@ class ProxyStorage implements IProxy {
if (_.isNil(requestCallback) === false) { if (_.isNil(requestCallback) === false) {
(function do_log(): void { (function do_log(): void {
const message = "@{!status}, req: '@{request.method} @{request.url}' (streaming)"; const message = "@{!status}, req: '@{request.method} @{request.url}' (streaming)";
self.logger.warn( self.logger.http(
{ {
request: { request: {
method: method, method: method,
url: uri, url: uri,
}, },
level: 35, // http
status: _.isNull(res) === false ? res.statusCode : 'ERR', status: _.isNull(res) === false ? res.statusCode : 'ERR',
}, },
message message

@ -1,6 +1,6 @@
const path = require('path'); const path = require('path');
const fse = require('fs-extra'); const fse = require('fs-extra');
const uiTheme = require('@verdaccio/ui-theme'); const { staticPath, manifest, manifestFiles } = require('@verdaccio/ui-theme')();
fse.copySync(uiTheme.staticPath, path.join(__dirname, '../dist/static')); fse.copySync(staticPath, path.join(__dirname, '../dist/static'));
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log('theme files copied'); console.log('theme files copied');

@ -40,8 +40,7 @@ const sendFileCallback = (next) => (err) => {
}; };
export function renderWebMiddleware(config, auth, storage): any { export function renderWebMiddleware(config, auth, storage): any {
const pluginTheme = require('@verdaccio/ui-theme'); const { staticPath, manifest, manifestFiles } = require('@verdaccio/ui-theme')();
const { staticPath } = pluginTheme;
debug('static path %o', staticPath); debug('static path %o', staticPath);
SearchInstance.configureStorage(storage); SearchInstance.configureStorage(storage);
@ -73,12 +72,12 @@ export function renderWebMiddleware(config, auth, storage): any {
}); });
router.get('/-/web/:section/*', function (req, res) { router.get('/-/web/:section/*', function (req, res) {
renderHTML(config, req, res); renderHTML(config, manifest, manifestFiles, req, res);
debug('render html section'); debug('render html section');
}); });
router.get('/', function (req, res) { router.get('/', function (req, res) {
renderHTML(config, req, res); renderHTML(config, manifest, manifestFiles, req, res);
debug('render root'); debug('render root');
}); });

@ -19,11 +19,10 @@ const defaultManifestFiles = {
ico: 'favicon.ico', ico: 'favicon.ico',
}; };
export default function renderHTML(config, req, res) { export default function renderHTML(config, manifest, manifestFiles, req, res) {
const { manifest, manifestFiles } = require('@verdaccio/ui-theme');
const { url_prefix } = config; const { url_prefix } = config;
const basePath = getPublicUrl(config?.url_prefix, req); const base = getPublicUrl(config?.url_prefix, req);
const basename = new URL(basePath).pathname; const basename = new URL(base).pathname;
const language = config?.i18n?.web ?? DEFAULT_LANGUAGE; const language = config?.i18n?.web ?? DEFAULT_LANGUAGE;
const darkMode = config?.web?.darkMode ?? false; const darkMode = config?.web?.darkMode ?? false;
const title = config?.web?.title ?? WEB_TITLE; const title = config?.web?.title ?? WEB_TITLE;
@ -32,12 +31,20 @@ export default function renderHTML(config, req, res) {
let logoURI = config?.web?.logo ?? ''; let logoURI = config?.web?.logo ?? '';
const version = pkgJSON.version; const version = pkgJSON.version;
const primaryColor = validatePrimaryColor(config?.web?.primary_color) ?? '#4b5e40'; const primaryColor = validatePrimaryColor(config?.web?.primary_color) ?? '#4b5e40';
const { scriptsBodyAfter, metaScripts, bodyBefore } = config?.web; const { scriptsBodyAfter, metaScripts, scriptsbodyBefore } = Object.assign(
{},
{
scriptsBodyAfter: [],
bodyBefore: [],
metaScripts: [],
},
config?.web
);
const options = { const options = {
darkMode, darkMode,
url_prefix, url_prefix,
basename, basename,
basePath, base,
primaryColor, primaryColor,
version, version,
logoURI, logoURI,
@ -60,7 +67,7 @@ export default function renderHTML(config, req, res) {
options, options,
scriptsBodyAfter, scriptsBodyAfter,
metaScripts, metaScripts,
bodyBefore, scriptsbodyBefore,
}, },
manifest manifest
); );

@ -10,8 +10,7 @@ export type TemplateUIOptions = {
protocol?: string; protocol?: string;
host?: string; host?: string;
url_prefix?: string; url_prefix?: string;
base?: string; base: string;
basePath: string;
primaryColor?: string; primaryColor?: string;
version?: string; version?: string;
logoURI?: string; logoURI?: string;
@ -22,9 +21,9 @@ export type TemplateUIOptions = {
export type Template = { export type Template = {
manifest: Manifest; manifest: Manifest;
options: TemplateUIOptions; options: TemplateUIOptions;
scriptsBodyAfter?: string[];
metaScripts?: string[]; metaScripts?: string[];
bodyBefore?: string[]; scriptsBodyAfter?: string[];
scriptsbodyBefore?: string[];
}; };
// the outcome of the Webpack Manifest Plugin // the outcome of the Webpack Manifest Plugin
@ -35,28 +34,28 @@ export interface WebpackManifest {
export default function renderTemplate(template: Template, manifest: WebpackManifest) { export default function renderTemplate(template: Template, manifest: WebpackManifest) {
debug('template %o', template); debug('template %o', template);
debug('manifest %o', manifest); debug('manifest %o', manifest);
return ` return `
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en-us"> <html lang="en-us">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<base href="${template?.options?.basePath}"> <base href="${template?.options.base}">
<title>${template?.options?.title ?? ''}</title> <title>${template?.options?.title ?? ''}</title>
<link rel="shortcut icon" href="/-/static/favicon.ico"/> <link rel="icon" href="${template?.options.base}-/static/favicon.ico"/>
<link rel="icon" type="image/png" href="${template.manifest.ico}" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<script> <script>
window.__VERDACCIO_BASENAME_UI_OPTIONS=${JSON.stringify(template.options)} window.__VERDACCIO_BASENAME_UI_OPTIONS=${JSON.stringify(template.options)}
</script> </script>
${template.metaScripts ? template.metaScripts.map((item) => item) : ''} ${template?.metaScripts ? template.metaScripts.join('') : ''}
</head> </head>
<body class="body"> <body class="body">
${template.bodyBefore ? template.bodyBefore.map((item) => item) : ''} ${template?.scriptsbodyBefore ? template.scriptsbodyBefore.join('') : ''}
<div id="root"></div> <div id="root"></div>
${getManifestValue(template.manifest.js, manifest, template?.options?.basePath).map( ${getManifestValue(template.manifest.js, manifest, template?.options.base)
(item) => `<script defer="defer" src="${item}"></script>` .map((item) => `<script defer="defer" src="${item}"></script>`)
)} .join('')}
${template.scriptsBodyAfter ? template.scriptsBodyAfter.map((item) => item) : ''} ${template?.scriptsBodyAfter ? template.scriptsBodyAfter.join('') : ''}
</body> </body>
</html> </html>
`; `;

@ -6,21 +6,20 @@ exports[`template custom body after 1`] = `
<html lang=\\"en-us\\"> <html lang=\\"en-us\\">
<head> <head>
<meta charset=\\"utf-8\\"> <meta charset=\\"utf-8\\">
<base href=\\"undefined\\"> <base href=\\"http://domain.com\\">
<title></title> <title></title>
<link rel=\\"shortcut icon\\" href=\\"/-/static/favicon.ico\\"/> <link rel=\\"icon\\" href=\\"http://domain.com-/static/favicon.ico\\"/>
<link rel=\\"icon\\" type=\\"image/png\\" href=\\"/static/foo.ico\\" />
<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\" /> <meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\" />
<script> <script>
window.__VERDACCIO_BASENAME_UI_OPTIONS=undefined window.__VERDACCIO_BASENAME_UI_OPTIONS={\\"base\\":\\"http://domain.com\\"}
</script> </script>
</head> </head>
<body class=\\"body\\"> <body class=\\"body\\">
<div id=\\"root\\"></div> <div id=\\"root\\"></div>
<script defer=\\"defer\\" src=\\"/-/static/runtime.6126058572f989c948b1.js\\"></script>,<script defer=\\"defer\\" src=\\"/-/static/main.6126058572f989c948b1.js\\"></script> <script defer=\\"defer\\" src=\\"http://domain.com/-/static/runtime.6126058572f989c948b1.js\\"></script><script defer=\\"defer\\" src=\\"http://domain.com/-/static/main.6126058572f989c948b1.js\\"></script>
<script src=\\"foo\\"/>
</body> </body>
</html> </html>
" "
@ -32,20 +31,19 @@ exports[`template custom body before 1`] = `
<html lang=\\"en-us\\"> <html lang=\\"en-us\\">
<head> <head>
<meta charset=\\"utf-8\\"> <meta charset=\\"utf-8\\">
<base href=\\"undefined\\"> <base href=\\"http://domain.com\\">
<title></title> <title></title>
<link rel=\\"shortcut icon\\" href=\\"/-/static/favicon.ico\\"/> <link rel=\\"icon\\" href=\\"http://domain.com-/static/favicon.ico\\"/>
<link rel=\\"icon\\" type=\\"image/png\\" href=\\"/static/foo.ico\\" />
<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\" /> <meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\" />
<script> <script>
window.__VERDACCIO_BASENAME_UI_OPTIONS=undefined window.__VERDACCIO_BASENAME_UI_OPTIONS={\\"base\\":\\"http://domain.com\\"}
</script> </script>
</head> </head>
<body class=\\"body\\"> <body class=\\"body\\">
<script src=\\"fooBefore\\"/>,<script src=\\"barBefore\\"/> <script src=\\"fooBefore\\"/><script src=\\"barBefore\\"/>
<div id=\\"root\\"></div> <div id=\\"root\\"></div>
<script defer=\\"defer\\" src=\\"/-/static/runtime.6126058572f989c948b1.js\\"></script>,<script defer=\\"defer\\" src=\\"/-/static/main.6126058572f989c948b1.js\\"></script> <script defer=\\"defer\\" src=\\"http://domain.com/-/static/runtime.6126058572f989c948b1.js\\"></script><script defer=\\"defer\\" src=\\"http://domain.com/-/static/main.6126058572f989c948b1.js\\"></script>
</body> </body>
</html> </html>
@ -58,20 +56,19 @@ exports[`template custom render 1`] = `
<html lang=\\"en-us\\"> <html lang=\\"en-us\\">
<head> <head>
<meta charset=\\"utf-8\\"> <meta charset=\\"utf-8\\">
<base href=\\"undefined\\"> <base href=\\"http://domain.com\\">
<title></title> <title></title>
<link rel=\\"shortcut icon\\" href=\\"/-/static/favicon.ico\\"/> <link rel=\\"icon\\" href=\\"http://domain.com-/static/favicon.ico\\"/>
<link rel=\\"icon\\" type=\\"image/png\\" href=\\"/static/foo.ico\\" />
<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\" /> <meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\" />
<script> <script>
window.__VERDACCIO_BASENAME_UI_OPTIONS={} window.__VERDACCIO_BASENAME_UI_OPTIONS={\\"base\\":\\"http://domain.com\\"}
</script> </script>
</head> </head>
<body class=\\"body\\"> <body class=\\"body\\">
<div id=\\"root\\"></div> <div id=\\"root\\"></div>
<script defer=\\"defer\\" src=\\"/-/static/runtime.6126058572f989c948b1.js\\"></script>,<script defer=\\"defer\\" src=\\"/-/static/main.6126058572f989c948b1.js\\"></script> <script defer=\\"defer\\" src=\\"http://domain.com/-/static/runtime.6126058572f989c948b1.js\\"></script><script defer=\\"defer\\" src=\\"http://domain.com/-/static/main.6126058572f989c948b1.js\\"></script>
</body> </body>
</html> </html>
@ -84,20 +81,69 @@ exports[`template custom title 1`] = `
<html lang=\\"en-us\\"> <html lang=\\"en-us\\">
<head> <head>
<meta charset=\\"utf-8\\"> <meta charset=\\"utf-8\\">
<base href=\\"undefined\\"> <base href=\\"http://domain.com\\">
<title>foo title</title> <title>foo title</title>
<link rel=\\"shortcut icon\\" href=\\"/-/static/favicon.ico\\"/> <link rel=\\"icon\\" href=\\"http://domain.com-/static/favicon.ico\\"/>
<link rel=\\"icon\\" type=\\"image/png\\" href=\\"/static/foo.ico\\" />
<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\" /> <meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\" />
<script> <script>
window.__VERDACCIO_BASENAME_UI_OPTIONS={\\"title\\":\\"foo title\\"} window.__VERDACCIO_BASENAME_UI_OPTIONS={\\"base\\":\\"http://domain.com\\",\\"title\\":\\"foo title\\"}
</script> </script>
</head> </head>
<body class=\\"body\\"> <body class=\\"body\\">
<div id=\\"root\\"></div> <div id=\\"root\\"></div>
<script defer=\\"defer\\" src=\\"/-/static/runtime.6126058572f989c948b1.js\\"></script>,<script defer=\\"defer\\" src=\\"/-/static/main.6126058572f989c948b1.js\\"></script> <script defer=\\"defer\\" src=\\"http://domain.com/-/static/runtime.6126058572f989c948b1.js\\"></script><script defer=\\"defer\\" src=\\"http://domain.com/-/static/main.6126058572f989c948b1.js\\"></script>
</body>
</html>
"
`;
exports[`template custom title 2`] = `
"
<!DOCTYPE html>
<html lang=\\"en-us\\">
<head>
<meta charset=\\"utf-8\\">
<base href=\\"http://domain.com\\">
<title>foo title</title>
<link rel=\\"icon\\" href=\\"http://domain.com-/static/favicon.ico\\"/>
<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\" />
<script>
window.__VERDACCIO_BASENAME_UI_OPTIONS={\\"base\\":\\"http://domain.com\\",\\"title\\":\\"foo title\\"}
</script>
</head>
<body class=\\"body\\">
<div id=\\"root\\"></div>
<script defer=\\"defer\\" src=\\"http://domain.com/-/static/runtime.6126058572f989c948b1.js\\"></script><script defer=\\"defer\\" src=\\"http://domain.com/-/static/main.6126058572f989c948b1.js\\"></script>
</body>
</html>
"
`;
exports[`template meta scripts 1`] = `
"
<!DOCTYPE html>
<html lang=\\"en-us\\">
<head>
<meta charset=\\"utf-8\\">
<base href=\\"http://domain.com\\">
<title></title>
<link rel=\\"icon\\" href=\\"http://domain.com-/static/favicon.ico\\"/>
<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\" />
<script>
window.__VERDACCIO_BASENAME_UI_OPTIONS={\\"base\\":\\"http://domain.com\\"}
</script>
<style>.someclass{font-size:10px;}</style>
</head>
<body class=\\"body\\">
<div id=\\"root\\"></div>
<script defer=\\"defer\\" src=\\"http://domain.com/-/static/runtime.6126058572f989c948b1.js\\"></script><script defer=\\"defer\\" src=\\"http://domain.com/-/static/main.6126058572f989c948b1.js\\"></script>
</body> </body>
</html> </html>

@ -10,22 +10,19 @@ const mockManifest = jest.fn();
jest.mock('@verdaccio/ui-theme', () => mockManifest()); jest.mock('@verdaccio/ui-theme', () => mockManifest());
describe('test web server', () => { describe('test web server', () => {
beforeAll(() => {
mockManifest.mockReturnValue({
staticPath: path.join(__dirname, 'static'),
manifest: require('./partials/manifest/manifest.json'),
});
});
afterEach(() => { afterEach(() => {
jest.clearAllMocks(); jest.clearAllMocks();
mockManifest.mockClear(); mockManifest.mockClear();
}); });
test('should OK to package api', async () => { test('should OK to package api', async () => {
mockManifest.mockReturnValue({ mockManifest.mockReturnValue(() => ({
staticPath: path.join(__dirname, 'static'),
manifestFiles: {
js: ['runtime.js', 'vendors.js', 'main.js'],
},
manifest: require('./partials/manifest/manifest.json'), manifest: require('./partials/manifest/manifest.json'),
}); }));
const response = await supertest(await initializeServer('default-test.yaml')) const response = await supertest(await initializeServer('default-test.yaml'))
.get('/-/verdaccio/packages') .get('/-/verdaccio/packages')
.set('Accept', HEADERS.JSON_CHARSET) .set('Accept', HEADERS.JSON_CHARSET)

@ -38,10 +38,13 @@ jest.mock('@verdaccio/store', () => ({
describe('readme api', () => { describe('readme api', () => {
beforeAll(() => { beforeAll(() => {
mockManifest.mockReturnValue({ mockManifest.mockReturnValue(() => ({
staticPath: path.join(__dirname, 'static'), staticPath: path.join(__dirname, 'static'),
manifestFiles: {
js: ['runtime.js', 'vendors.js', 'main.js'],
},
manifest: require('./partials/manifest/manifest.json'), manifest: require('./partials/manifest/manifest.json'),
}); }));
}); });
afterEach(() => { afterEach(() => {
@ -50,9 +53,6 @@ describe('readme api', () => {
}); });
test('should fetch readme scoped package', async () => { test('should fetch readme scoped package', async () => {
mockManifest.mockReturnValue({
manifest: require('./partials/manifest/manifest.json'),
});
const response = await supertest(await initializeServer('default-test.yaml')) const response = await supertest(await initializeServer('default-test.yaml'))
.get('/-/verdaccio/package/readme/@scope/pk1-test') .get('/-/verdaccio/package/readme/@scope/pk1-test')
.set('Accept', HEADERS.TEXT_PLAIN) .set('Accept', HEADERS.TEXT_PLAIN)
@ -62,9 +62,6 @@ describe('readme api', () => {
}); });
test('should fetch readme a package', async () => { test('should fetch readme a package', async () => {
mockManifest.mockReturnValue({
manifest: require('./partials/manifest/manifest.json'),
});
const response = await supertest(await initializeServer('default-test.yaml')) const response = await supertest(await initializeServer('default-test.yaml'))
.get('/-/verdaccio/package/readme/pk1-test') .get('/-/verdaccio/package/readme/pk1-test')
.set('Accept', HEADERS.TEXT_PLAIN) .set('Accept', HEADERS.TEXT_PLAIN)

@ -39,10 +39,13 @@ jest.mock('@verdaccio/store', () => ({
describe('test web server', () => { describe('test web server', () => {
beforeAll(() => { beforeAll(() => {
mockManifest.mockReturnValue({ mockManifest.mockReturnValue(() => ({
staticPath: path.join(__dirname, 'static'), staticPath: path.join(__dirname, 'static'),
manifestFiles: {
js: ['runtime.js', 'vendors.js', 'main.js'],
},
manifest: require('./partials/manifest/manifest.json'), manifest: require('./partials/manifest/manifest.json'),
}); }));
}); });
afterEach(() => { afterEach(() => {
@ -51,9 +54,6 @@ describe('test web server', () => {
}); });
test('should OK to search api', async () => { test('should OK to search api', async () => {
mockManifest.mockReturnValue({
manifest: require('./partials/manifest/manifest.json'),
});
const response = await supertest(await initializeServer('default-test.yaml')) const response = await supertest(await initializeServer('default-test.yaml'))
.get('/-/verdaccio/search/keyword') .get('/-/verdaccio/search/keyword')
.set('Accept', HEADERS.JSON_CHARSET) .set('Accept', HEADERS.JSON_CHARSET)
@ -63,9 +63,6 @@ describe('test web server', () => {
}); });
test('should 404 to search api', async () => { test('should 404 to search api', async () => {
mockManifest.mockReturnValue({
manifest: require('./partials/manifest/manifest.json'),
});
mockQuery.mockReturnValue([]); mockQuery.mockReturnValue([]);
const response = await supertest(await initializeServer('default-test.yaml')) const response = await supertest(await initializeServer('default-test.yaml'))
.get('/-/verdaccio/search/notFound') .get('/-/verdaccio/search/notFound')
@ -76,9 +73,6 @@ describe('test web server', () => {
}); });
test('should fail search api', async () => { test('should fail search api', async () => {
mockManifest.mockReturnValue({
manifest: require('./partials/manifest/manifest.json'),
});
mockQuery.mockImplementation(() => { mockQuery.mockImplementation(() => {
return ['aa', 'bb', 'cc']; return ['aa', 'bb', 'cc'];
}); });

@ -11,10 +11,13 @@ jest.mock('@verdaccio/ui-theme', () => mockManifest());
describe('test web server', () => { describe('test web server', () => {
beforeAll(() => { beforeAll(() => {
mockManifest.mockReturnValue({ mockManifest.mockReturnValue(() => ({
staticPath: path.join(__dirname, '../static'), staticPath: path.join(__dirname, 'static'),
manifestFiles: {
js: ['runtime.js', 'vendors.js', 'main.js'],
},
manifest: require('./partials/manifest/manifest.json'), manifest: require('./partials/manifest/manifest.json'),
}); }));
}); });
afterEach(() => { afterEach(() => {
@ -23,9 +26,6 @@ describe('test web server', () => {
}); });
test('should get 401', async () => { test('should get 401', async () => {
mockManifest.mockReturnValue({
manifest: require('./partials/manifest/manifest.json'),
});
return supertest(await initializeServer('default-test.yaml')) return supertest(await initializeServer('default-test.yaml'))
.post('/-/verdaccio/login') .post('/-/verdaccio/login')
.send( .send(
@ -43,9 +43,6 @@ describe('test web server', () => {
}); });
test('should log in', async () => { test('should log in', async () => {
mockManifest.mockReturnValue({
manifest: require('./partials/manifest/manifest.json'),
});
return supertest(await initializeServer('default-test.yaml')) return supertest(await initializeServer('default-test.yaml'))
.post('/-/verdaccio/login') .post('/-/verdaccio/login')
.send( .send(

@ -2,7 +2,6 @@ import path from 'path';
import supertest from 'supertest'; import supertest from 'supertest';
import { setup } from '@verdaccio/logger'; import { setup } from '@verdaccio/logger';
import { HEADER_TYPE, HEADERS, HTTP_STATUS } from '@verdaccio/commons-api'; import { HEADER_TYPE, HEADERS, HTTP_STATUS } from '@verdaccio/commons-api';
import { combineBaseUrl } from '../src/renderHTML';
import { initializeServer } from './helper'; import { initializeServer } from './helper';
setup([]); setup([]);
@ -12,10 +11,13 @@ jest.mock('@verdaccio/ui-theme', () => mockManifest());
describe('test web server', () => { describe('test web server', () => {
beforeAll(() => { beforeAll(() => {
mockManifest.mockReturnValue({ mockManifest.mockReturnValue(() => ({
manifestFiles: {
js: ['runtime.js', 'vendors.js', 'main.js'],
},
staticPath: path.join(__dirname, 'static'), staticPath: path.join(__dirname, 'static'),
manifest: require('./partials/manifest/manifest.json'), manifest: require('./partials/manifest/manifest.json'),
}); }));
}); });
afterEach(() => { afterEach(() => {

@ -9,18 +9,52 @@ const exampleManifest = {
describe('template', () => { describe('template', () => {
test('custom render', () => { test('custom render', () => {
expect(template({ options: {}, manifest: exampleManifest }, manifest)).toMatchSnapshot(); expect(
template({ options: { base: 'http://domain.com' }, manifest: exampleManifest }, manifest)
).toMatchSnapshot();
}); });
test('custom title', () => { test('custom title', () => {
expect( expect(
template({ options: { title: 'foo title' }, manifest: exampleManifest }, manifest) template(
{ options: { base: 'http://domain.com', title: 'foo title' }, manifest: exampleManifest },
manifest
)
).toMatchSnapshot();
});
test('custom title', () => {
expect(
template(
{ options: { base: 'http://domain.com', title: 'foo title' }, manifest: exampleManifest },
manifest
)
).toMatchSnapshot();
});
test('meta scripts', () => {
expect(
template(
{
options: { base: 'http://domain.com' },
metaScripts: [`<style>.someclass{font-size:10px;}</style>`],
manifest: exampleManifest,
},
manifest
)
).toMatchSnapshot(); ).toMatchSnapshot();
}); });
test('custom body after', () => { test('custom body after', () => {
expect( expect(
template({ bodyAfter: [`<script src="foo"/>`], manifest: exampleManifest }, manifest) template(
{
options: { base: 'http://domain.com' },
scriptsBodyAfter: [`<script src="foo"/>`],
manifest: exampleManifest,
},
manifest
)
).toMatchSnapshot(); ).toMatchSnapshot();
}); });
@ -28,7 +62,8 @@ describe('template', () => {
expect( expect(
template( template(
{ {
bodyBefore: [`<script src="fooBefore"/>`, `<script src="barBefore"/>`], options: { base: 'http://domain.com' },
scriptsbodyBefore: [`<script src="fooBefore"/>`, `<script src="barBefore"/>`],
manifest: exampleManifest, manifest: exampleManifest,
}, },
manifest manifest

20
pnpm-lock.yaml generated

@ -520,8 +520,6 @@ importers:
fast-safe-stringify: 2.0.7 fast-safe-stringify: 2.0.7
kleur: 3.0.3 kleur: 3.0.3
lodash: 4.17.20 lodash: 4.17.20
pad-left: 2.1.0
pad-right: 0.2.2
prettier-bytes: 1.0.4 prettier-bytes: 1.0.4
pretty-ms: 5.1.0 pretty-ms: 5.1.0
devDependencies: devDependencies:
@ -534,8 +532,6 @@ importers:
fast-safe-stringify: 2.0.7 fast-safe-stringify: 2.0.7
kleur: 3.0.3 kleur: 3.0.3
lodash: 4.17.20 lodash: 4.17.20
pad-left: 2.1.0
pad-right: 0.2.2
pino: 6.2.1 pino: 6.2.1
prettier-bytes: 1.0.4 prettier-bytes: 1.0.4
pretty-ms: 5.1.0 pretty-ms: 5.1.0
@ -21345,22 +21341,6 @@ packages:
node: '>=8' node: '>=8'
resolution: resolution:
integrity: sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== integrity: sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==
/pad-left/2.1.0:
dependencies:
repeat-string: 1.6.1
dev: false
engines:
node: '>=0.10.0'
resolution:
integrity: sha1-FuajstRKjhOMsIOMx8tAOk/J6ZQ=
/pad-right/0.2.2:
dependencies:
repeat-string: 1.6.1
dev: false
engines:
node: '>=0.10.0'
resolution:
integrity: sha1-b7ySQEXSRPKiokRQMGDTv8YAl3Q=
/pako/1.0.11: /pako/1.0.11:
dev: false dev: false
resolution: resolution: