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
Define a specific public url for your server, it overrules the `Host` and `X-Forwarded-Proto` header if a reverse proxy is
being used.
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.
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;
}
function wrapPrefix(prefix: string): string {
if (prefix === '' || _.isNil(prefix)) {
export function wrapPrefix(prefix: string | void): string {
if (prefix === '' || typeof prefix === 'undefined' || prefix === null) {
return '';
} else if (!prefix.startsWith('/') && prefix.endsWith('/')) {
return `/${prefix}`;
@ -72,9 +72,10 @@ export function combineBaseUrl(protocol: string, host: string, prefix: string =
debug('combined host %o', host);
const newPrefix = wrapPrefix(prefix);
debug('combined prefix %o', newPrefix);
const result = new URL(wrapPrefix(prefix), `${protocol}://${host}`);
debug('combined url %o', result.href);
return result.href;
const groupedURI = new URL(wrapPrefix(prefix), `${protocol}://${host}`);
const result = groupedURI.href;
debug('combined url %o', result);
return result;
}
export function validateURL(publicUrl: string | void) {
@ -92,7 +93,7 @@ export function validateURL(publicUrl: string | void) {
export function getPublicUrl(url_prefix: string = '', req): 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);
return envURL;
} else if (req.get('host')) {
@ -100,7 +101,8 @@ export function getPublicUrl(url_prefix: string = '', req): string {
if (!isHost(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);
debug('public url by request %o', combinedUrl);
return combinedUrl;

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

@ -1,10 +1,8 @@
import { inspect } from 'util';
import { white, red, green } from 'kleur';
import padLeft from 'pad-left';
import { calculateLevel, LevelCode, levelsColors, subSystemLevels } from './levels';
import { formatLoggingDate, isObject, pad } from './utils';
import { formatLoggingDate, isObject, padLeft, padRight } from './utils';
import { PrettyOptionsExtended } from './types';
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) {
const finalMessage = fillInMsgTemplate(msg, templateObjects, hasColors);
const subSystemType = subSystemLevels.color[sub ?? 'default'];
if (hasColors) {
const logString = `${levelsColors[debugLevel](pad(debugLevel, LEVEL_VALUE_MAX))}${white(
const logString = `${levelsColors[debugLevel](padRight(debugLevel, LEVEL_VALUE_MAX))}${white(
`${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(

@ -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';
@ -6,18 +6,20 @@ export type LevelCode = number;
export function calculateLevel(levelCode: LevelCode): LogLevel {
switch (true) {
case levelCode < 15:
case levelCode === 10:
return 'trace';
case levelCode < 25:
case levelCode === 20:
return 'debug';
case levelCode < 35:
return 'info';
case levelCode == 35:
case levelCode === 25:
return 'http';
case levelCode < 45:
case levelCode === 30:
return 'info';
case levelCode === 40:
return 'warn';
case levelCode < 55:
case levelCode === 50:
return 'error';
case levelCode === 60:
return 'fatal';
default:
return 'fatal';
}

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

@ -1,18 +1,18 @@
// 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 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 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"`;

@ -4,18 +4,13 @@ import buildDebug from 'debug';
const debug = buildDebug('verdaccio:logger');
const DEFAULT_LOG_FORMAT = 'pretty';
export let logger;
function isProd() {
return process.env.NODE_ENV === 'production';
}
function getPrettifier() {
// TODO: this module can be loaded dynamically and allow custom formatting
return require('@verdaccio/logger-prettify');
}
const DEFAULT_LOG_FORMAT = isProd() ? 'json' : 'pretty';
export type LogPlugin = {
dest: string;
@ -26,7 +21,7 @@ export type LogType = 'file' | 'stdout';
export type LogFormat = 'json' | 'pretty-timestamped' | 'pretty';
export function createLogger(
options = {},
options = { level: 'http' },
destination = pino.destination(1),
format: LogFormat = DEFAULT_LOG_FORMAT,
prettyPrintOptions = {
@ -40,10 +35,11 @@ export function createLogger(
}
let pinoConfig = {
...options,
customLevels: {
http: 35,
http: 25,
},
...options,
level: options.level,
serializers: {
err: pino.stdSerializers.err,
req: pino.stdSerializers.req,
@ -52,26 +48,33 @@ export function createLogger(
};
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) {
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
prettyPrint: {
levelFirst: true,
prettyStamp: format === 'pretty-timestamped',
...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() {
if (_.isNil(logger)) {
console.warn('logger is not defined');
process.emitWarning('logger is not defined');
return;
}
@ -96,40 +99,58 @@ export type LoggerConfig = LoggerConfigItem[];
export function setup(options: LoggerConfig | LoggerConfigItem = [DEFAULT_LOGGER_CONF]) {
debug('setup logger');
const isLegacyConf = _.isArray(options);
// verdaccio 4 allows array configuration
// backward compatible, pick only the first option
let loggerConfig = isLegacyConf ? options[0] : options;
if (!loggerConfig?.level) {
loggerConfig = Object.assign({}, loggerConfig, {
level: 'http',
});
const isLegacyConf = Array.isArray(options);
if (isLegacyConf) {
const deprecateMessage =
'deprecate: multiple logger configuration is deprecated, please check the migration guide.';
process.emitWarning(deprecateMessage);
}
// 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 };
if (loggerConfig.type === 'file') {
debug('logging file enabled');
logger = createLogger(pinoConfig, pino.destination(loggerConfig.path), loggerConfig.format);
} 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 {
debug('logging stdout enabled');
logger = createLogger(pinoConfig, pino.destination(1), loggerConfig.format);
}
if (isProd()) {
process.on(
'uncaughtException',
pino.final(logger, (err, finalLogger) => {
finalLogger.fatal(err, 'uncaughtException');
process.exit(1);
})
);
// why only on prod? https://github.com/pinojs/pino/issues/920#issuecomment-710807667
const finalHandler = pino.final(logger, (err, finalLogger, event) => {
finalLogger.info(`${event} caught`);
if (err) {
finalLogger.error(err, 'error caused exit');
}
process.exit(err ? 1 : 0);
});
process.on(
'unhandledRejection',
pino.final(logger, (err, finalLogger) => {
finalLogger.fatal(err, 'uncaughtException');
process.exit(1);
})
);
process.on('uncaughtException', (err) => finalHandler(err, 'uncaughtException'));
process.on('unhandledRejection', (err) => finalHandler(err as Error, 'unhandledRejection'));
process.on('beforeExit', () => finalHandler(null, 'beforeExit'));
process.on('exit', () => finalHandler(null, 'exit'));
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.log.warn(
req.log.http(
{
request: {
method: req.method,
url: req.url,
},
level: 35, // http
user: (req.remote_user && req.remote_user.name) || null,
remoteIP,
status: res.statusCode,

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

@ -8,7 +8,7 @@ import 'mutationobserver-shim';
// @ts-ignore : Property '__APP_VERSION__' does not exist on type 'Global'.
global.__APP_VERSION__ = '1.0.0';
// @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'.
global.VERDACCIO_API_URL = 'https://verdaccio.tld';

@ -26,9 +26,9 @@ describe('utils', () => {
test('getRegistryURL() - should change when UI options change', () => {
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');
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 {
return window?.__VERDACCIO_BASENAME_UI_OPTIONS?.basePath;
return window?.__VERDACCIO_BASENAME_UI_OPTIONS?.base;
}
export function extractFileName(url: string): string {

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

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

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

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

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

@ -6,21 +6,20 @@ exports[`template custom body after 1`] = `
<html lang=\\"en-us\\">
<head>
<meta charset=\\"utf-8\\">
<base href=\\"undefined\\">
<base href=\\"http://domain.com\\">
<title></title>
<link rel=\\"shortcut icon\\" href=\\"/-/static/favicon.ico\\"/>
<link rel=\\"icon\\" type=\\"image/png\\" href=\\"/static/foo.ico\\" />
<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=undefined
window.__VERDACCIO_BASENAME_UI_OPTIONS={\\"base\\":\\"http://domain.com\\"}
</script>
</head>
<body class=\\"body\\">
<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>
</html>
"
@ -32,20 +31,19 @@ exports[`template custom body before 1`] = `
<html lang=\\"en-us\\">
<head>
<meta charset=\\"utf-8\\">
<base href=\\"undefined\\">
<base href=\\"http://domain.com\\">
<title></title>
<link rel=\\"shortcut icon\\" href=\\"/-/static/favicon.ico\\"/>
<link rel=\\"icon\\" type=\\"image/png\\" href=\\"/static/foo.ico\\" />
<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=undefined
window.__VERDACCIO_BASENAME_UI_OPTIONS={\\"base\\":\\"http://domain.com\\"}
</script>
</head>
<body class=\\"body\\">
<script src=\\"fooBefore\\"/>,<script src=\\"barBefore\\"/>
<script src=\\"fooBefore\\"/><script src=\\"barBefore\\"/>
<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>
@ -58,20 +56,19 @@ exports[`template custom render 1`] = `
<html lang=\\"en-us\\">
<head>
<meta charset=\\"utf-8\\">
<base href=\\"undefined\\">
<base href=\\"http://domain.com\\">
<title></title>
<link rel=\\"shortcut icon\\" href=\\"/-/static/favicon.ico\\"/>
<link rel=\\"icon\\" type=\\"image/png\\" href=\\"/static/foo.ico\\" />
<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={}
window.__VERDACCIO_BASENAME_UI_OPTIONS={\\"base\\":\\"http://domain.com\\"}
</script>
</head>
<body class=\\"body\\">
<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>
@ -84,20 +81,69 @@ exports[`template custom title 1`] = `
<html lang=\\"en-us\\">
<head>
<meta charset=\\"utf-8\\">
<base href=\\"undefined\\">
<base href=\\"http://domain.com\\">
<title>foo title</title>
<link rel=\\"shortcut icon\\" href=\\"/-/static/favicon.ico\\"/>
<link rel=\\"icon\\" type=\\"image/png\\" href=\\"/static/foo.ico\\" />
<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={\\"title\\":\\"foo title\\"}
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=\\"/-/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>
</html>

@ -10,22 +10,19 @@ const mockManifest = jest.fn();
jest.mock('@verdaccio/ui-theme', () => mockManifest());
describe('test web server', () => {
beforeAll(() => {
mockManifest.mockReturnValue({
staticPath: path.join(__dirname, 'static'),
manifest: require('./partials/manifest/manifest.json'),
});
});
afterEach(() => {
jest.clearAllMocks();
mockManifest.mockClear();
});
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'),
});
}));
const response = await supertest(await initializeServer('default-test.yaml'))
.get('/-/verdaccio/packages')
.set('Accept', HEADERS.JSON_CHARSET)

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

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

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

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

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

20
pnpm-lock.yaml generated

@ -520,8 +520,6 @@ importers:
fast-safe-stringify: 2.0.7
kleur: 3.0.3
lodash: 4.17.20
pad-left: 2.1.0
pad-right: 0.2.2
prettier-bytes: 1.0.4
pretty-ms: 5.1.0
devDependencies:
@ -534,8 +532,6 @@ importers:
fast-safe-stringify: 2.0.7
kleur: 3.0.3
lodash: 4.17.20
pad-left: 2.1.0
pad-right: 0.2.2
pino: 6.2.1
prettier-bytes: 1.0.4
pretty-ms: 5.1.0
@ -21345,22 +21341,6 @@ packages:
node: '>=8'
resolution:
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:
dev: false
resolution: