Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[npm node] support for custom npm registry #1277

Merged
merged 10 commits into from
Nov 21, 2017
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