Skip to content

Commit

Permalink
Add support for global and npx install modes (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
LucaScorpion authored Aug 11, 2023
1 parent 939df25 commit 99a1efb
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 16 deletions.
22 changes: 22 additions & 0 deletions site/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,35 @@ <h2>Examples</h2>
<img src="/npm/@11ty/eleventy" alt="npm badge for Eleventy" />
</div>
</div>
<div class="examples">
<div>
<a href="/npm/@nestjs/cli?mode=global">
https://npmbadge.com/npm/@nestjs/cli?mode=global
</a>
<img
src="/npm/@nestjs/cli?mode=global"
alt="npm badge for Nest CLI"
/>
</div>
<div>
<a href="/npm/serve?mode=npx">
https://npmbadge.com/npm/serve?mode=npx
</a>
<img src="/npm/serve?mode=npx" alt="npm badge for Serve" />
</div>
</div>

<hr />

<h2>Make Me One!</h2>
<div class="make-badge">
<div class="input-row">
<input id="make-badge-input" type="text" placeholder="package-name" />
<select id="make-badge-mode">
<option value="install">install</option>
<option value="global">install -g</option>
<option value="npx">npx</option>
</select>
<span id="make-badge-info" class="info"></span>
</div>
<img id="make-badge-img" src="/npm/express" alt="" />
Expand Down
40 changes: 31 additions & 9 deletions site/make-badge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ const DEBOUNCE_TIME = 300;

document.addEventListener('DOMContentLoaded', () => {
const input = document.getElementById('make-badge-input') as HTMLInputElement;
const modeSelect = document.getElementById(
'make-badge-mode'
) as HTMLSelectElement;
const imgOutput = document.getElementById(
'make-badge-img'
) as HTMLImageElement;
Expand All @@ -14,26 +17,43 @@ document.addEventListener('DOMContentLoaded', () => {
'make-badge-embed-html'
) as HTMLTextAreaElement;

input.addEventListener(
'input',
debounce(() => setBadgeSrc(input, imgOutput))
);
input.addEventListener('input', () =>
setStatusAndEmbedCodes(input.value, info, embedMd, embedHtml)
const setBadgeSrcDebounced = debounce(() =>
setBadgeSrc(input, modeSelect, imgOutput)
);
const setStatusInstant = () =>
setStatusAndEmbedCodes(
input.value,
modeSelect.value,
info,
embedMd,
embedHtml
);

input.addEventListener('input', setBadgeSrcDebounced);
input.addEventListener('input', setStatusInstant);
modeSelect.addEventListener('change', setBadgeSrcDebounced);
modeSelect.addEventListener('change', setStatusInstant);

imgOutput.addEventListener('error', () => handleImgError(info));
imgOutput.addEventListener('load', () => handleImgLoad(info));

setStatusAndEmbedCodes('', info, embedMd, embedHtml);
setStatusAndEmbedCodes('', 'install', info, embedMd, embedHtml);
});

function setBadgeSrc(
input: HTMLInputElement,
modeSelect: HTMLSelectElement,
imgOutput: HTMLImageElement
): void {
const pkg = input.value;
const mode = modeSelect.value;

if (pkg) {
imgOutput.src = `${NPM_BADGE_URL}${pkg}`;
let src = `${NPM_BADGE_URL}${pkg}`;
if (mode !== 'install') {
src = `${src}?mode=${mode}`;
}
imgOutput.src = src;
}
}

Expand All @@ -47,6 +67,7 @@ function handleImgLoad(info: HTMLSpanElement): void {

function setStatusAndEmbedCodes(
name: string,
mode: string,
info: HTMLSpanElement,
embedMd: HTMLTextAreaElement,
embedHtml: HTMLTextAreaElement
Expand All @@ -57,8 +78,9 @@ function setStatusAndEmbedCodes(
} else {
info.textContent = 'Loading...';
}
const modeQuery = mode === 'install' ? '' : `?mode=${mode}`;

const badgeLink = `${NPM_BADGE_URL}${name}`;
const badgeLink = `${NPM_BADGE_URL}${name}${modeQuery}`;
const npmLink = `https://www.npmjs.com/package/${name}`;

embedMd.value = `[![npm](${badgeLink})](${npmLink})`;
Expand Down
7 changes: 5 additions & 2 deletions site/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ a {
}

input,
textarea {
textarea,
select {
font-size: inherit;
color: inherit;
padding: 8px;
Expand All @@ -63,6 +64,7 @@ main {
display: flex;
justify-content: space-around;
gap: 32px;
margin-bottom: 32px;

@media screen and (max-width: 1024px) {
flex-direction: column;
Expand All @@ -89,7 +91,8 @@ main {
.input-row {
margin-bottom: 16px;

input {
input,
select {
margin-right: 8px;
}

Expand Down
18 changes: 16 additions & 2 deletions src/badge/badgeElements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { formatNumber } from '../utils/formatNumber';
import { COLORS } from './colors';
import { timeAgo } from '../utils/timeAgo';
import { FONTS } from './fonts';
import { InstallMode } from '../installMode';

export interface BadgeElements {
npmLogo: TextElement;
Expand All @@ -19,8 +20,11 @@ export interface TextElement {
color: string;
}

export function getBadgeElements(pkg: PackageInfo): BadgeElements {
return {
export function getBadgeElements(
pkg: PackageInfo,
installMode?: InstallMode
): BadgeElements {
const result: BadgeElements = {
npmLogo: {
text: 'npm',
font: FONTS.npm,
Expand Down Expand Up @@ -56,4 +60,14 @@ export function getBadgeElements(pkg: PackageInfo): BadgeElements {
color: COLORS.darkGrey,
},
};

if (installMode === 'global') {
result.installCommand.text = `npm install -g ${pkg.name}`;
}

if (installMode === 'npx') {
result.installCommand.text = `npx ${pkg.name}`;
}

return result;
}
8 changes: 6 additions & 2 deletions src/badge/drawBadge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { initFonts } from './fonts';
import { getBadgeElements } from './badgeElements';
import { drawText, getTextSizes } from './text';
import { COLORS } from './colors';
import { InstallMode } from '../installMode';

const BORDER_WIDTH = 2;
const PADDING = 9;
Expand All @@ -23,12 +24,15 @@ initFonts();
| M 1,000 weekly downloads updated 5 months ago |
+---------------------------------------------------+
*/
export function drawBadge(pkg: PackageInfo): PNGStream {
export function drawBadge(
pkg: PackageInfo,
installMode?: InstallMode
): PNGStream {
const canvas = new Canvas(0, 0);
const ctx = canvas.getContext('2d');
ctx.antialias = 'subpixel';

const elems = getBadgeElements(pkg);
const elems = getBadgeElements(pkg, installMode);
const sizes = getTextSizes(ctx, elems);

const pkgInfoX = NPM_LOGO_X + sizes.npmLogo.width + LOGO_SPACING_RIGHT;
Expand Down
4 changes: 3 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@ import express from 'express';
import { getPackageInfo } from './packageInfo';
import { drawBadge } from './badge/drawBadge';
import { packageNameMiddleware } from './packageNameMiddleware';
import { parseInstallMode } from './installMode';

const app = express();
app.use(express.static('_site'));

app.get('/npm/*', packageNameMiddleware, async function (req, res, next) {
getPackageInfo(req.packageName)
.then((pkg) => {
const { mode } = req.query;
res.type('image/png');
drawBadge(pkg).pipe(res);
drawBadge(pkg, parseInstallMode(mode)).pipe(res);
})
.catch(next);
});
Expand Down
9 changes: 9 additions & 0 deletions src/installMode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type InstallMode = 'install' | 'global' | 'npx';

const validModes: InstallMode[] = ['install', 'global', 'npx'];

export function parseInstallMode(mode?: unknown): InstallMode | undefined {
if (typeof mode === 'string' && validModes.includes(mode as InstallMode)) {
return mode as InstallMode;
}
}

0 comments on commit 99a1efb

Please sign in to comment.