mirror of
https://github.com/verdaccio/verdaccio.git
synced 2024-12-24 21:15:51 +01:00
refactor: improve versions and dist-tag filters (#2650)
* refactor: improve versions and dist-tag filters * chore: restore this later * improve documentation of dis-tag normalizer * chore: add changeset
This commit is contained in:
parent
d8cd1ca887
commit
b13a3fefd3
7
.changeset/three-moles-drop.md
Normal file
7
.changeset/three-moles-drop.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
'@verdaccio/api': minor
|
||||||
|
'@verdaccio/store': minor
|
||||||
|
'@verdaccio/utils': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
refactor: improve versions and dist-tag filters
|
@ -112,7 +112,7 @@
|
|||||||
"docker": "docker build -t verdaccio/verdaccio:local . --no-cache",
|
"docker": "docker build -t verdaccio/verdaccio:local . --no-cache",
|
||||||
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,yml,yaml,md}\"",
|
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,yml,yaml,md}\"",
|
||||||
"format:check": "prettier --check \"**/*.{js,jsx,ts,tsx,json,yml,yaml,md}\"",
|
"format:check": "prettier --check \"**/*.{js,jsx,ts,tsx,json,yml,yaml,md}\"",
|
||||||
"lint": "eslint --max-warnings 46 \"**/*.{js,jsx,ts,tsx}\"",
|
"lint": "eslint --max-warnings 47 \"**/*.{js,jsx,ts,tsx}\"",
|
||||||
"test": "pnpm recursive test --filter ./packages",
|
"test": "pnpm recursive test --filter ./packages",
|
||||||
"test:e2e:cli": "pnpm test --filter ...@verdaccio/e2e-cli",
|
"test:e2e:cli": "pnpm test --filter ...@verdaccio/e2e-cli",
|
||||||
"test:e2e:ui": "pnpm test --filter ...@verdaccio/e2e-ui",
|
"test:e2e:ui": "pnpm test --filter ...@verdaccio/e2e-ui",
|
||||||
|
@ -44,6 +44,13 @@ export default function (route: Router, auth: IAuth, storage: Storage, config: C
|
|||||||
function (req: $RequestExtend, _res: $ResponseExtend, next: $NextFunctionVer): void {
|
function (req: $RequestExtend, _res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||||
debug('init package by version');
|
debug('init package by version');
|
||||||
const name = req.params.package;
|
const name = req.params.package;
|
||||||
|
let queryVersion = req.params.version;
|
||||||
|
const requestOptions = {
|
||||||
|
protocol: req.protocol,
|
||||||
|
headers: req.headers as any,
|
||||||
|
// FIXME: if we migrate to req.hostname, the port is not longer included.
|
||||||
|
host: req.host,
|
||||||
|
};
|
||||||
const getPackageMetaCallback = function (err, metadata: Package): void {
|
const getPackageMetaCallback = function (err, metadata: Package): void {
|
||||||
if (err) {
|
if (err) {
|
||||||
debug('error on fetch metadata for %o with error %o', name, err.message);
|
debug('error on fetch metadata for %o with error %o', name, err.message);
|
||||||
@ -52,18 +59,17 @@ export default function (route: Router, auth: IAuth, storage: Storage, config: C
|
|||||||
debug('convert dist remote to local with prefix %o', config?.url_prefix);
|
debug('convert dist remote to local with prefix %o', config?.url_prefix);
|
||||||
metadata = convertDistRemoteToLocalTarballUrls(
|
metadata = convertDistRemoteToLocalTarballUrls(
|
||||||
metadata,
|
metadata,
|
||||||
{ protocol: req.protocol, headers: req.headers as any, host: req.host },
|
requestOptions,
|
||||||
config?.url_prefix
|
config?.url_prefix
|
||||||
);
|
);
|
||||||
|
|
||||||
let queryVersion = req.params.version;
|
|
||||||
debug('query by param version: %o', queryVersion);
|
debug('query by param version: %o', queryVersion);
|
||||||
if (_.isNil(queryVersion)) {
|
if (_.isNil(queryVersion)) {
|
||||||
debug('param %o version found', queryVersion);
|
debug('param %o version found', queryVersion);
|
||||||
return next(metadata);
|
return next(metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
let version = getVersion(metadata, queryVersion);
|
let version = getVersion(metadata.versions, queryVersion);
|
||||||
debug('query by latest version %o and result %o', queryVersion, version);
|
debug('query by latest version %o and result %o', queryVersion, version);
|
||||||
if (_.isNil(version) === false) {
|
if (_.isNil(version) === false) {
|
||||||
debug('latest version found %o', version);
|
debug('latest version found %o', version);
|
||||||
@ -74,7 +80,7 @@ export default function (route: Router, auth: IAuth, storage: Storage, config: C
|
|||||||
if (_.isNil(metadata[DIST_TAGS][queryVersion]) === false) {
|
if (_.isNil(metadata[DIST_TAGS][queryVersion]) === false) {
|
||||||
queryVersion = metadata[DIST_TAGS][queryVersion];
|
queryVersion = metadata[DIST_TAGS][queryVersion];
|
||||||
debug('dist-tag version found %o', queryVersion);
|
debug('dist-tag version found %o', queryVersion);
|
||||||
version = getVersion(metadata, queryVersion);
|
version = getVersion(metadata.versions, queryVersion);
|
||||||
if (_.isNil(version) === false) {
|
if (_.isNil(version) === false) {
|
||||||
debug('dist-tag found %o', version);
|
debug('dist-tag found %o', version);
|
||||||
return next(version);
|
return next(version);
|
||||||
|
@ -55,9 +55,7 @@ export function normalizePackage(pkg: Package): Package {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// normalize dist-tags
|
// normalize dist-tags
|
||||||
normalizeDistTags(pkg);
|
return normalizeDistTags(pkg);
|
||||||
|
|
||||||
return pkg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateRevision(rev: string): string {
|
export function generateRevision(rev: string): string {
|
||||||
|
@ -378,7 +378,7 @@ class Storage {
|
|||||||
return options.callback(err);
|
return options.callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
normalizeDistTags(cleanUpLinksRef(result, options?.keepUpLinkData));
|
result = normalizeDistTags(cleanUpLinksRef(result, options?.keepUpLinkData));
|
||||||
|
|
||||||
// npm can throw if this field doesn't exist
|
// npm can throw if this field doesn't exist
|
||||||
result._attachments = {};
|
result._attachments = {};
|
||||||
|
@ -3,3 +3,4 @@ export * from './utils';
|
|||||||
export * from './crypto-utils';
|
export * from './crypto-utils';
|
||||||
export * from './replace-lodash';
|
export * from './replace-lodash';
|
||||||
export * from './matcher';
|
export * from './matcher';
|
||||||
|
export * from './versions';
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import semver from 'semver';
|
|
||||||
|
|
||||||
import { DEFAULT_USER, DIST_TAGS } from '@verdaccio/core';
|
import { DEFAULT_USER, DIST_TAGS } from '@verdaccio/core';
|
||||||
import { Author, Package, Version } from '@verdaccio/types';
|
import { Author, Package } from '@verdaccio/types';
|
||||||
|
|
||||||
import { stringToMD5 } from './crypto-utils';
|
import { stringToMD5 } from './crypto-utils';
|
||||||
|
|
||||||
@ -97,87 +96,6 @@ export function validateMetadata(object: Package, name: string): Package {
|
|||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets version from a package object taking into account semver weirdness.
|
|
||||||
* @return {String} return the semantic version of a package
|
|
||||||
*/
|
|
||||||
export function getVersion(pkg: Package, version: any): Version | void {
|
|
||||||
// this condition must allow cast
|
|
||||||
if (_.isNil(pkg.versions[version]) === false) {
|
|
||||||
return pkg.versions[version];
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
version = semver.parse(version, true);
|
|
||||||
for (const versionItem in pkg.versions) {
|
|
||||||
if (version.compare(semver.parse(versionItem, true)) === 0) {
|
|
||||||
return pkg.versions[versionItem];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function filters out bad semver versions and sorts the array.
|
|
||||||
* @return {Array} sorted Array
|
|
||||||
*/
|
|
||||||
export function semverSort(listVersions: string[] /* logger */): string[] {
|
|
||||||
return (
|
|
||||||
listVersions
|
|
||||||
.filter(function (x): boolean {
|
|
||||||
if (!semver.parse(x, true)) {
|
|
||||||
// FIXME: logger is always undefined
|
|
||||||
// logger.warn({ ver: x }, 'ignoring bad version @{ver}');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
// FIXME: it seems the @types/semver do not handle a legitimate method named 'compareLoose'
|
|
||||||
// @ts-ignore
|
|
||||||
.sort(semver.compareLoose)
|
|
||||||
.map(String)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flatten arrays of tags.
|
|
||||||
* @param {*} data
|
|
||||||
*/
|
|
||||||
export function normalizeDistTags(pkg: Package): void {
|
|
||||||
let sorted;
|
|
||||||
if (!pkg[DIST_TAGS].latest) {
|
|
||||||
// overwrite latest with highest known version based on semver sort
|
|
||||||
sorted = semverSort(Object.keys(pkg.versions));
|
|
||||||
if (sorted?.length) {
|
|
||||||
pkg[DIST_TAGS].latest = sorted.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const tag in pkg[DIST_TAGS]) {
|
|
||||||
if (_.isArray(pkg[DIST_TAGS][tag])) {
|
|
||||||
if (pkg[DIST_TAGS][tag].length) {
|
|
||||||
// sort array
|
|
||||||
// FIXME: this is clearly wrong, we need to research why this is like this.
|
|
||||||
// @ts-ignore
|
|
||||||
sorted = semverSort(pkg[DIST_TAGS][tag]);
|
|
||||||
if (sorted.length) {
|
|
||||||
// use highest version based on semver sort
|
|
||||||
pkg[DIST_TAGS][tag] = sorted.pop();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
delete pkg[DIST_TAGS][tag];
|
|
||||||
}
|
|
||||||
} else if (_.isString(pkg[DIST_TAGS][tag])) {
|
|
||||||
if (!semver.parse(pkg[DIST_TAGS][tag], true)) {
|
|
||||||
// if the version is invalid, delete the dist-tag entry
|
|
||||||
delete pkg[DIST_TAGS][tag];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getLatestVersion(pkgInfo: Package): string {
|
export function getLatestVersion(pkgInfo: Package): string {
|
||||||
return pkgInfo[DIST_TAGS].latest;
|
return pkgInfo[DIST_TAGS].latest;
|
||||||
}
|
}
|
||||||
|
108
packages/utils/src/versions.ts
Normal file
108
packages/utils/src/versions.ts
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
import semver, { SemVer } from 'semver';
|
||||||
|
|
||||||
|
import { DIST_TAGS } from '@verdaccio/core';
|
||||||
|
import { Package, Version, Versions } from '@verdaccio/types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets version from a package object taking into account semver weirdness.
|
||||||
|
* @return {String} return the semantic version of a package
|
||||||
|
*/
|
||||||
|
export function getVersion(versions: Versions, version: any): Version | void {
|
||||||
|
if (!versions) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this condition must allow cast
|
||||||
|
if (_.isNil(versions[version]) === false) {
|
||||||
|
return versions[version];
|
||||||
|
}
|
||||||
|
|
||||||
|
const versionSemver: SemVer | null = semver.parse(version, true);
|
||||||
|
if (versionSemver === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const versionItem in versions) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(versions, versionItem)) {
|
||||||
|
// @ts-ignore
|
||||||
|
if (versionSemver.compare(semver.parse(versionItem, true)) === 0) {
|
||||||
|
return versions[versionItem];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function filters out bad semver versions and sorts the array.
|
||||||
|
* @return {Array} sorted Array
|
||||||
|
*/
|
||||||
|
export function sortVersionsAndFilterInvalid(listVersions: string[] /* logger */): string[] {
|
||||||
|
return (
|
||||||
|
listVersions
|
||||||
|
.filter(function (version): boolean {
|
||||||
|
if (!semver.parse(version, true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
// FIXME: it seems the @types/semver do not handle a legitimate method named 'compareLoose'
|
||||||
|
// @ts-ignore
|
||||||
|
.sort(semver.compareLoose)
|
||||||
|
.map(String)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize dist-tags.
|
||||||
|
*
|
||||||
|
* There is a legacy behaviour where the dist-tags could be an array, in such
|
||||||
|
* case, the array is orderded and the highest version becames the
|
||||||
|
* latest.
|
||||||
|
*
|
||||||
|
* The dist-tag tags must be plain strigs, if the latest is empty (for whatever reason) is
|
||||||
|
* normalized to be the highest version available.
|
||||||
|
*
|
||||||
|
* This function cleans up every invalid version on dist-tags, but does not remove
|
||||||
|
* invalid versions from the manifest.
|
||||||
|
*
|
||||||
|
* @param {*} data
|
||||||
|
*/
|
||||||
|
export function normalizeDistTags(manifest: Package): Package {
|
||||||
|
let sorted;
|
||||||
|
// handle missing latest dist-tag
|
||||||
|
if (!manifest[DIST_TAGS].latest) {
|
||||||
|
// if there is no latest tag, set the highest known version based on semver sort
|
||||||
|
sorted = sortVersionsAndFilterInvalid(Object.keys(manifest.versions));
|
||||||
|
if (sorted?.length) {
|
||||||
|
// get the highest published version
|
||||||
|
manifest[DIST_TAGS].latest = sorted.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const tag in manifest[DIST_TAGS]) {
|
||||||
|
// deprecated (will be removed un future majors)
|
||||||
|
// this should not happen, tags should be plain strings, legacy fallback
|
||||||
|
if (_.isArray(manifest[DIST_TAGS][tag])) {
|
||||||
|
if (manifest[DIST_TAGS][tag].length) {
|
||||||
|
// sort array
|
||||||
|
// FIXME: this is clearly wrong, we need to research why this is like this.
|
||||||
|
// @ts-ignore
|
||||||
|
sorted = sortVersionsAndFilterInvalid(manifest[DIST_TAGS][tag]);
|
||||||
|
if (sorted.length) {
|
||||||
|
// use highest version based on semver sort
|
||||||
|
manifest[DIST_TAGS][tag] = sorted.pop();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
delete manifest[DIST_TAGS][tag];
|
||||||
|
}
|
||||||
|
} else if (_.isString(manifest[DIST_TAGS][tag])) {
|
||||||
|
if (!semver.parse(manifest[DIST_TAGS][tag], true)) {
|
||||||
|
// if the version is invalid, delete the dist-tag entry
|
||||||
|
delete manifest[DIST_TAGS][tag];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return manifest;
|
||||||
|
}
|
@ -74,22 +74,6 @@ describe('Utilities', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getVersion', () => {
|
|
||||||
test('should get the right version', () => {
|
|
||||||
expect(getVersion(cloneMetadata(), '1.0.0')).toEqual(metadata.versions['1.0.0']);
|
|
||||||
expect(getVersion(cloneMetadata(), 'v1.0.0')).toEqual(metadata.versions['1.0.0']);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should return nothing on get non existing version', () => {
|
|
||||||
expect(getVersion(cloneMetadata(), '0')).toBeUndefined();
|
|
||||||
expect(getVersion(cloneMetadata(), '2.0.0')).toBeUndefined();
|
|
||||||
expect(getVersion(cloneMetadata(), 'v2.0.0')).toBeUndefined();
|
|
||||||
expect(getVersion(cloneMetadata(), undefined)).toBeUndefined();
|
|
||||||
expect(getVersion(cloneMetadata(), null)).toBeUndefined();
|
|
||||||
expect(getVersion(cloneMetadata(), 2)).toBeUndefined();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('validateMetadata', () => {
|
describe('validateMetadata', () => {
|
||||||
test('should fills an empty metadata object', () => {
|
test('should fills an empty metadata object', () => {
|
||||||
// intended to fail with flow, do not remove
|
// intended to fail with flow, do not remove
|
||||||
|
125
packages/utils/test/versions.spec.ts
Normal file
125
packages/utils/test/versions.spec.ts
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
import { DIST_TAGS } from '@verdaccio/core';
|
||||||
|
import { Package } from '@verdaccio/types';
|
||||||
|
|
||||||
|
import { getVersion, normalizeDistTags, sortVersionsAndFilterInvalid } from '../src/index';
|
||||||
|
|
||||||
|
describe('Utilities', () => {
|
||||||
|
const dist = (version) => ({
|
||||||
|
tarball: `http://registry.org/npm_test/-/npm_test-${version}.tgz`,
|
||||||
|
shasum: `sha1-${version}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getVersion', () => {
|
||||||
|
const metadata = {
|
||||||
|
'1.0.0': { dist: dist('1.0.0') },
|
||||||
|
'1.0.1': { dist: dist('1.0.1') },
|
||||||
|
'0.2.1-1': { dist: dist('0.2.1-1') },
|
||||||
|
'0.2.1-alpha': { dist: dist('0.2.1-alpha') },
|
||||||
|
'0.2.1-alpha.0': { dist: dist('0.2.1-alpha.0') },
|
||||||
|
};
|
||||||
|
|
||||||
|
test('should get the right version', () => {
|
||||||
|
expect(getVersion({ ...metadata } as any, '1.0.0')).toEqual({ dist: dist('1.0.0') });
|
||||||
|
expect(getVersion({ ...metadata } as any, 'v1.0.0')).toEqual({ dist: dist('1.0.0') });
|
||||||
|
expect(getVersion({ ...metadata } as any, 'v0.2.1-1')).toEqual({ dist: dist('0.2.1-1') });
|
||||||
|
expect(getVersion({ ...metadata } as any, '0.2.1-alpha')).toEqual({
|
||||||
|
dist: dist('0.2.1-alpha'),
|
||||||
|
});
|
||||||
|
expect(getVersion({ ...metadata } as any, '0.2.1-alpha.0')).toEqual({
|
||||||
|
dist: dist('0.2.1-alpha.0'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return nothing on get non existing version', () => {
|
||||||
|
expect(getVersion({ ...metadata } as any, '0')).toBeUndefined();
|
||||||
|
expect(getVersion({ ...metadata } as any, '2.0.0')).toBeUndefined();
|
||||||
|
expect(getVersion({ ...metadata } as any, 'v2.0.0')).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return nothing on get invalid versions', () => {
|
||||||
|
expect(getVersion({ ...metadata } as any, undefined)).toBeUndefined();
|
||||||
|
expect(getVersion({ ...metadata } as any, null)).toBeUndefined();
|
||||||
|
expect(getVersion({ ...metadata } as any, 8)).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle no versions', () => {
|
||||||
|
expect(getVersion(undefined, undefined)).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('semverSort', () => {
|
||||||
|
test('should sort versions', () => {
|
||||||
|
expect(sortVersionsAndFilterInvalid(['1.0.0', '5.0.0', '2.0.0'])).toEqual([
|
||||||
|
'1.0.0',
|
||||||
|
'2.0.0',
|
||||||
|
'5.0.0',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
test('should sort versions and filter out invalid', () => {
|
||||||
|
expect(sortVersionsAndFilterInvalid(['1.0.0', '5.0.0', '2.0.0', '', null])).toEqual([
|
||||||
|
'1.0.0',
|
||||||
|
'2.0.0',
|
||||||
|
'5.0.0',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('normalizeDistTags', () => {
|
||||||
|
const metadata = {
|
||||||
|
name: 'npm_test',
|
||||||
|
versions: {
|
||||||
|
'1.0.0': { dist: dist('1.0.0') },
|
||||||
|
'1.0.1': { dist: dist('1.0.1') },
|
||||||
|
'0.2.1-1': { dist: dist('0.2.1-1') },
|
||||||
|
'0.2.1-alpha': { dist: dist('0.2.1-alpha') },
|
||||||
|
'0.2.1-alpha.0': { dist: dist('0.2.1-alpha.0') },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const cloneMetadata: Package | any = (pkg = metadata) => Object.assign({}, pkg);
|
||||||
|
|
||||||
|
describe('tag as arrays [deprecated]', () => {
|
||||||
|
test('should convert any array of dist-tags to a plain string', () => {
|
||||||
|
const pkg = cloneMetadata();
|
||||||
|
pkg[DIST_TAGS] = {
|
||||||
|
latest: ['1.0.1'],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(normalizeDistTags(pkg)[DIST_TAGS]).toEqual({ latest: '1.0.1' });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should convert any empty array to empty list of dist-tags', () => {
|
||||||
|
const pkg = cloneMetadata();
|
||||||
|
pkg[DIST_TAGS] = {
|
||||||
|
latest: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(normalizeDistTags(pkg)[DIST_TAGS]).toEqual({});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should clean up a invalid latest version', () => {
|
||||||
|
const pkg = cloneMetadata();
|
||||||
|
pkg[DIST_TAGS] = {
|
||||||
|
latest: '20000',
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(Object.keys(normalizeDistTags(pkg)[DIST_TAGS])).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle empty dis-tags and define last published version as latest', () => {
|
||||||
|
const pkg = cloneMetadata();
|
||||||
|
pkg[DIST_TAGS] = {};
|
||||||
|
|
||||||
|
expect(normalizeDistTags(pkg)[DIST_TAGS]).toEqual({ latest: '1.0.1' });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should define last published version as latest with a custom dist-tag', () => {
|
||||||
|
const pkg = cloneMetadata();
|
||||||
|
pkg[DIST_TAGS] = {
|
||||||
|
beta: '1.0.1',
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(normalizeDistTags(pkg)[DIST_TAGS]).toEqual({ beta: '1.0.1', latest: '1.0.1' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user