Skip to content

Commit

Permalink
Support custom npm registry URLs (#1277)
Browse files Browse the repository at this point in the history
  • Loading branch information
iamstarkov authored and paulmelnikow committed Nov 21, 2017
1 parent ea061e5 commit e588bef
Show file tree
Hide file tree
Showing 5 changed files with 212 additions and 141 deletions.
28 changes: 28 additions & 0 deletions lib/all-badge-examples.js
Original file line number Diff line number Diff line change
Expand Up @@ -640,13 +640,27 @@ const allBadgeExamples = [
'node'
]
},
{
title: 'npm (custom registry)',
previewUri: '/npm/v/npm.svg?registry_uri=https://registry.npmjs.com',
keywords: [
'node'
]
},
{
title: 'npm (scoped with tag)',
previewUri: '/npm/v/@cycle/core/canary.svg',
keywords: [
'node'
]
},
{
title: 'npm (scoped with tag, custom registry)',
previewUri: '/npm/v/@cycle/core/canary.svg?registry_uri=https://registry.npmjs.com',
keywords: [
'node'
]
},
{
title: 'node',
previewUri: '/node/v/passport.svg',
Expand Down Expand Up @@ -675,6 +689,13 @@ const allBadgeExamples = [
'node'
]
},
{
title: 'node (scoped with tag, custom registry)',
previewUri: '/node/v/@stdlib/stdlib/latest.svg?registry_uri=https://registry.npmjs.com',
keywords: [
'node'
]
},
{
title: 'PyPI',
previewUri: '/pypi/v/nine.svg',
Expand Down Expand Up @@ -1116,6 +1137,13 @@ const allBadgeExamples = [
'node'
]
},
{
title: 'npm (custom registry)',
previewUri: '/npm/l/express.svg?registry_uri=https://registry.npmjs.com',
keywords: [
'node'
]
},
{
title: 'apm',
previewUri: '/apm/l/vim-mode.svg',
Expand Down
7 changes: 7 additions & 0 deletions lib/npm-badge-helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

const defaultNpmRegistryUri = 'https://registry.npmjs.org';

module.exports = {
defaultNpmRegistryUri,
};
297 changes: 156 additions & 141 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ const {
const {
mapNpmDownloads
} = require('./lib/npm-provider');
const {
defaultNpmRegistryUri
} = require('./lib/npm-badge-helpers');
const {
teamcityBadge
} = require('./lib/teamcity-badge-helpers');
Expand Down Expand Up @@ -1580,163 +1583,175 @@ cache(function (data, match, sendBadge, request) {

// npm version integration.
camp.route(/^\/npm\/v\/(?:@([^/]+))?\/?([^/]*)\/?([^/]*)\.(svg|png|gif|jpg|json)$/,
cache(function(data, match, sendBadge, request) {
// e.g. cycle, core, next, svg
const [, scope, packageName, tag, format] = match;
const pkg = encodeURIComponent(scope ? `@${scope}/${packageName}` : packageName);
const apiUrl = `https://registry.npmjs.org/-/package/${pkg}/dist-tags`;
const name = tag ? `npm@${tag}` : 'npm';
const badgeData = getBadgeData(name, data);
// Using the Accept header because of this bug:
// <https://github.com/npm/npmjs.org/issues/163>
request(apiUrl, { headers: { 'Accept': '*/*' } }, (err, res, buffer) => {
if (err != null) {
badgeData.text[1] = 'inaccessible';
sendBadge(format, badgeData);
return;
}
try {
const data = JSON.parse(buffer);
const version = data[tag || 'latest'];
badgeData.text[1] = versionText(version);
badgeData.colorscheme = versionColor(version);
sendBadge(format, badgeData);
} catch(e) {
badgeData.text[1] = 'invalid';
sendBadge(format, badgeData);
}
});
}));

// npm license integration.
camp.route(/^\/npm\/l\/(?:@([^/]+)\/)?([^/]+)\.(svg|png|gif|jpg|json)$/,
cache(function(data, match, sendBadge, request) {
const scope = match[1]; // "user" (when a scope "@user" is supplied)
const packageName = match[2]; // "express"
const format = match[3]; // "svg"
let apiUrl;
if (scope === undefined) {
// e.g. https://registry.npmjs.org/express/latest
// Use this endpoint as an optimization. It covers the vast majority of
// these badges, and the response is smaller.
apiUrl = `https://registry.npmjs.org/${packageName}/latest`;
} else {
// e.g. https://registry.npmjs.org/@cedx%2Fgulp-david
// because https://registry.npmjs.org/@cedx%2Fgulp-david/latest does not work
const path = encodeURIComponent(`${scope}/${packageName}`);
apiUrl = `https://registry.npmjs.org/@${path}`;
}
const badgeData = getBadgeData('license', data);
request(apiUrl, { headers: { 'Accept': '*/*' } }, function(err, res, buffer) {
if (err != null) {
badgeData.text[1] = 'inaccessible';
sendBadge(format, badgeData);
return;
}
try {
const data = JSON.parse(buffer);
let license;
if (scope === undefined) {
license = data.license;
} else {
const latestVersion = data['dist-tags'].latest;
license = data.versions[latestVersion].license;
cache({
queryParams: ['registry_uri'],
handler: function(queryParams, match, sendBadge, request) {
// e.g. cycle, core, next, svg
const [, scope, packageName, tag, format] = match;
const registryUri = queryParams.registry_uri || defaultNpmRegistryUri;
const pkg = encodeURIComponent(scope ? `@${scope}/${packageName}` : packageName);
const apiUrl = `${registryUri}/-/package/${pkg}/dist-tags`;

const name = tag ? `npm@${tag}` : 'npm';
const badgeData = getBadgeData(name, queryParams);
// Using the Accept header because of this bug:
// <https://github.com/npm/npmjs.org/issues/163>
request(apiUrl, { headers: { 'Accept': '*/*' } }, (err, res, buffer) => {
if (err != null) {
badgeData.text[1] = 'inaccessible';
sendBadge(format, badgeData);
return;
}
if (Array.isArray(license)) {
license = license.join(', ');
} else if (typeof license == 'object') {
license = license.type;
try {
const data = JSON.parse(buffer);
const version = data[tag || 'latest'];
badgeData.text[1] = versionText(version);
badgeData.colorscheme = versionColor(version);
sendBadge(format, badgeData);
} catch(e) {
badgeData.text[1] = 'invalid';
sendBadge(format, badgeData);
}
badgeData.text[1] = license;
badgeData.colorscheme = 'blue';
sendBadge(format, badgeData);
} catch(e) {
badgeData.text[1] = 'invalid';
sendBadge(format, badgeData);
}
});
});
}
}));

// npm node version integration.
camp.route(/^\/node\/v\/(?:@([^/]+))?\/?([^/]*)\/?([^/]*)\.(svg|png|gif|jpg|json)$/,
cache(function(data, match, sendBadge, request) {
// e.g. @stdlib, stdlib, next, svg
const [, scope, packageName, tag, format] = match;
const registryTag = tag || 'latest';
let apiUrl;
if (scope === undefined) {
// npm license integration.
camp.route(/^\/npm\/l\/(?:@([^/]+)\/)?([^/]+)\.(svg|png|gif|jpg|json)$/,
cache({
queryParams: ['registry_uri'],
handler: function(queryParams, match, sendBadge, request) {
// e.g. cycle, core, svg
const [, scope, packageName, format ] = match;
const registryUri = queryParams.registry_uri || defaultNpmRegistryUri;
let apiUrl;
if (scope === undefined) {
// e.g. https://registry.npmjs.org/express/latest
// Use this endpoint as an optimization. It covers the vast majority of
// these badges, and the response is smaller.
apiUrl = `https://registry.npmjs.org/${packageName}/${registryTag}`;
} else {
// e.g. https://registry.npmjs.org/@cedx%2Fgulp-david
// because https://registry.npmjs.org/@cedx%2Fgulp-david/latest does not work
const path = encodeURIComponent(`${scope}/${packageName}`);
apiUrl = `https://registry.npmjs.org/@${path}`;
}
const name = tag ? `node@${tag}` : 'node';
const badgeData = getBadgeData(name, data);
// Using the Accept header because of this bug:
// <https://github.com/npm/npmjs.org/issues/163>
request(apiUrl, { headers: { 'Accept': '*/*' } }, (err, res, buffer) => {
if (err != null) {
badgeData.text[1] = 'inaccessible';
sendBadge(format, badgeData);
return;
apiUrl = `${registryUri}/${packageName}/latest`;
} else {
// e.g. https://registry.npmjs.org/@cedx%2Fgulp-david
// because https://registry.npmjs.org/@cedx%2Fgulp-david/latest does not work
const path = encodeURIComponent(`${scope}/${packageName}`);
apiUrl = `${registryUri}/@${path}`;
}
try {
const data = JSON.parse(buffer);
if (data.error === 'not_found') {
badgeData.text[1] = 'package not found';
const badgeData = getBadgeData('license', queryParams);
request(apiUrl, { headers: { 'Accept': '*/*' } }, function(err, res, buffer) {
if (err != null) {
badgeData.text[1] = 'inaccessible';
sendBadge(format, badgeData);
return;
}
let releaseData;
if (scope === undefined) {
releaseData = data;
} else {
const version = data['dist-tags'][registryTag];
releaseData = data.versions[version];
try {
const data = JSON.parse(buffer);
let license;
if (scope === undefined) {
license = data.license;
} else {
const latestVersion = data['dist-tags'].latest;
license = data.versions[latestVersion].license;
}
if (Array.isArray(license)) {
license = license.join(', ');
} else if (typeof license == 'object') {
license = license.type;
}
badgeData.text[1] = license;
badgeData.colorscheme = 'blue';
sendBadge(format, badgeData);
} catch(e) {
badgeData.text[1] = 'invalid';
sendBadge(format, badgeData);
}
const versionRange = (releaseData.engines || {}).node;
if (! versionRange) {
badgeData.text[1] = 'not specified';
});
}
}));

// npm node version integration.
camp.route(/^\/node\/v\/(?:@([^/]+))?\/?([^/]*)\/?([^/]*)\.(svg|png|gif|jpg|json)$/,
cache({
queryParams: ['registry_uri'],
handler: function(queryParams, match, sendBadge, request) {
// e.g. @stdlib, stdlib, next, svg
const [, scope, packageName, tag, format] = match;
const registryUri = queryParams.registry_uri || defaultNpmRegistryUri;
const registryTag = tag || 'latest';
let apiUrl;
if (scope === undefined) {
// e.g. https://registry.npmjs.org/express/latest
// Use this endpoint as an optimization. It covers the vast majority of
// these badges, and the response is smaller.
apiUrl = `${registryUri}/${packageName}/${registryTag}`;
} else {
// e.g. https://registry.npmjs.org/@cedx%2Fgulp-david
// because https://registry.npmjs.org/@cedx%2Fgulp-david/latest does not work
const path = encodeURIComponent(`${scope}/${packageName}`);
apiUrl = `${registryUri}/@${path}`;
}
const name = tag ? `node@${tag}` : 'node';
const badgeData = getBadgeData(name, queryParams);
// Using the Accept header because of this bug:
// <https://github.com/npm/npmjs.org/issues/163>
request(apiUrl, { headers: { 'Accept': '*/*' } }, (err, res, buffer) => {
if (err != null) {
badgeData.text[1] = 'inaccessible';
sendBadge(format, badgeData);
return;
}
badgeData.text[1] = versionRange;
regularUpdate('http://nodejs.org/dist/latest/SHASUMS256.txt',
(24 * 3600 * 1000),
shasums => {
// tarball index start, tarball index end
const taris = shasums.indexOf('node-v');
const tarie = shasums.indexOf('\n', taris);
const tarball = shasums.slice(taris, tarie);
const version = tarball.split('-')[1];
return version;
}, (err, version) => {
if (err != null) {
badgeData.text[1] = 'invalid';
sendBadge(format, badgeData);
return;
}
try {
if (semver.satisfies(version, versionRange)) {
badgeData.colorscheme = 'brightgreen';
} else if (semver.gtr(version, versionRange)) {
badgeData.colorscheme = 'yellow';
} else {
badgeData.colorscheme = 'orange';
}
} catch(e) { }
try {
const data = JSON.parse(buffer);
if (data.error === 'not_found') {
badgeData.text[1] = 'package not found';
sendBadge(format, badgeData);
});
} catch(e) {
badgeData.text[1] = 'invalid';
sendBadge(format, badgeData);
}
});
return;
}
let releaseData;
if (scope === undefined) {
releaseData = data;
} else {
const version = data['dist-tags'][registryTag];
releaseData = data.versions[version];
}
const versionRange = (releaseData.engines || {}).node;
if (! versionRange) {
badgeData.text[1] = 'not specified';
sendBadge(format, badgeData);
return;
}
badgeData.text[1] = versionRange;
regularUpdate('http://nodejs.org/dist/latest/SHASUMS256.txt',
(24 * 3600 * 1000),
shasums => {
// tarball index start, tarball index end
const taris = shasums.indexOf('node-v');
const tarie = shasums.indexOf('\n', taris);
const tarball = shasums.slice(taris, tarie);
const version = tarball.split('-')[1];
return version;
}, (err, version) => {
if (err != null) {
badgeData.text[1] = 'invalid';
sendBadge(format, badgeData);
return;
}
try {
if (semver.satisfies(version, versionRange)) {
badgeData.colorscheme = 'brightgreen';
} else if (semver.gtr(version, versionRange)) {
badgeData.colorscheme = 'yellow';
} else {
badgeData.colorscheme = 'orange';
}
} catch(e) { }
sendBadge(format, badgeData);
});
} catch(e) {
badgeData.text[1] = 'invalid';
sendBadge(format, badgeData);
}
});
}
}));

// Anaconda Cloud / conda package manager integration
Expand Down
Loading

0 comments on commit e588bef

Please sign in to comment.