├── .gitattributes ├── .gitignore ├── .prettierrc ├── .editorconfig ├── api ├── text │ └── index.js ├── crate │ └── index.js ├── npm │ └── index.js ├── measure-width.js ├── github │ └── index.js ├── home │ └── index.js └── utils.js ├── package.json ├── LICENSE ├── vercel.json └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | *.log 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "bracketSpacing": true 5 | } 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /api/text/index.js: -------------------------------------------------------------------------------- 1 | const { parse } = require('url') 2 | const { sendSVG, handleError, escape } = require('../utils') 3 | 4 | module.exports = handleError(async (req, res) => { 5 | const { 6 | query: { text = '' } 7 | } = parse(req.url, true) 8 | sendSVG(res, escape(text)) 9 | }) 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "version": "0.3.0", 4 | "name": "version-badge", 5 | "license": "MIT", 6 | "dependencies": { 7 | "axios": "^0.18.0", 8 | "express": "^4.15.3", 9 | "lru-cache": "^4.1.1", 10 | "marked": "^0.5.2", 11 | "pdfkit": "^0.8.3" 12 | }, 13 | "devDependencies": { 14 | "semantic-release": "^15.13.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /api/crate/index.js: -------------------------------------------------------------------------------- 1 | const { parse } = require('url') 2 | const axios = require('axios') 3 | const { sendSVG, handleError } = require('../utils') 4 | 5 | module.exports = handleError(async (req, res) => { 6 | const { 7 | query: { name } 8 | } = parse(req.url, true) 9 | const { data } = await axios.get(`https://crates.io/api/v1/crates/${name}`) 10 | sendSVG(res, `v${data.crate.max_version}`) 11 | }) 12 | -------------------------------------------------------------------------------- /api/npm/index.js: -------------------------------------------------------------------------------- 1 | const { parse } = require('url') 2 | const axios = require('axios') 3 | const { sendSVG, handleError } = require('../utils') 4 | 5 | module.exports = handleError(async (req, res) => { 6 | const { 7 | query: { name, tag = 'latest' } 8 | } = parse(req.url, true) 9 | const { data } = await axios.get(`https://registry.npmjs.org/${name}`) 10 | sendSVG(res, `v${data['dist-tags'][tag]}`) 11 | }) 12 | -------------------------------------------------------------------------------- /api/measure-width.js: -------------------------------------------------------------------------------- 1 | const PDFDocument = require('pdfkit') 2 | const cache = require('lru-cache')({ 3 | max: 128 * 1024 * 1024, 4 | length: src => src.length 5 | }) 6 | 7 | const doc = new PDFDocument({ size: 'A4', layout: 'landscape' }) 8 | doc.font('Helvetica') 9 | doc.fontSize(12) 10 | 11 | module.exports = str => { 12 | let width = cache.get(str) 13 | if (width) { 14 | return width 15 | } 16 | 17 | width = doc.widthOfString(str) 18 | cache.set(str, width) 19 | return width 20 | } 21 | -------------------------------------------------------------------------------- /api/github/index.js: -------------------------------------------------------------------------------- 1 | const { parse } = require('url') 2 | const axios = require('axios') 3 | const { sendSVG, handleError } = require('../utils') 4 | 5 | module.exports = handleError(async (req, res) => { 6 | const { 7 | query: { 8 | owner, 9 | repo, 10 | file = 'package.json', 11 | branch = 'master', 12 | field = 'version' 13 | } 14 | } = parse(req.url, true) 15 | const { data } = await axios.get( 16 | `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${file}` 17 | ) 18 | sendSVG(res, `v${data[field]}`) 19 | }) 20 | -------------------------------------------------------------------------------- /api/home/index.js: -------------------------------------------------------------------------------- 1 | const marked = require('marked') 2 | const axios = require('axios') 3 | const { handleError } = require('../utils') 4 | 5 | module.exports = handleError(async (req, res) => { 6 | const { data } = await axios.get( 7 | 'https://raw.githubusercontent.com/egoist/version-badge/master/README.md' 8 | ) 9 | res.setHeader('content-type', 'text/html') 10 | res.end(` 11 | 12 | Version Badge 13 | 14 | 20 |
${marked(data)}
21 | `) 22 | }) 23 | -------------------------------------------------------------------------------- /api/utils.js: -------------------------------------------------------------------------------- 1 | const measureWidth = require('./measure-width') 2 | 3 | exports.sendSVG = (res, text) => { 4 | res.setHeader('content-type', 'image/svg+xml') 5 | res.end(`${text} 8 | `) 9 | } 10 | 11 | exports.handleError = fn => { 12 | return async (req, res) => { 13 | try { 14 | await fn(req, res) 15 | } catch (err) { 16 | console.error(err) 17 | if (err.response) { 18 | res.statusCode = err.response.status 19 | res.end(err.response.data) 20 | } else { 21 | res.statusCode = 500 22 | res.end(err.response.data) 23 | } 24 | } 25 | } 26 | } 27 | 28 | exports.escape = input => input 29 | .replace(/&/g, '&') 30 | .replace(/"/g, '"') 31 | .replace(/'/g, ''') 32 | .replace(//g, '>') -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) EGOIST <0x142857@gmail.com> (egoist.sh) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "alias": ["version-badge.now.sh", "version-badge.egoist.sh"], 3 | "builds": [ 4 | { 5 | "src": "api/*/index.js", 6 | "use": "@now/node" 7 | } 8 | ], 9 | "routes": [ 10 | { 11 | "src": "/npm/(.+)\\.svg$", 12 | "dest": "/api/npm?name=$1" 13 | }, 14 | { 15 | "src": "/npm/(.+)", 16 | "dest": "/api/npm?name=$1" 17 | }, 18 | { 19 | "src": "/github/([^/]+)/([^/]+)\\.svg$", 20 | "dest": "/api/github?owner=$1&repo=$2" 21 | }, 22 | { 23 | "src": "/gh/([^/]+)/([^/]+)\\.svg$", 24 | "dest": "/api/github?owner=$1&repo=$2" 25 | }, 26 | { 27 | "src": "/github/([^/]+)/([^/]+)", 28 | "dest": "/api/github?owner=$1&repo=$2" 29 | }, 30 | { 31 | "src": "/gh/([^/]+)/([^/]+)", 32 | "dest": "/api/github?owner=$1&repo=$2" 33 | }, 34 | { 35 | "src": "/crate/(.+)", 36 | "dest": "/api/crate?name=$1" 37 | }, 38 | { 39 | "src": "/text", 40 | "dest": "/api/text" 41 | }, 42 | { 43 | "src": "/", 44 | "dest": "/api/home" 45 | }, 46 | { 47 | "src": "/favicon.ico", 48 | "dest": "/api/404" 49 | } 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # version-badge ![version badge](https://version-badge.egoist.sh/github/egoist/version-badge) 2 | 3 | > Display a version badge. 4 | 5 | ## What? 6 | 7 | Designed to be put right next to your project title: 8 | 9 | ```markdown 10 | # chalk ![version badge](https://version-badge.egoist.sh/npm/chalk) 11 | ``` 12 | 13 | See the title of this README to get the idea :) 14 | 15 | Of course you can link the svg to anywhere like the npm page: 16 | 17 | ```markdown 18 | # chalk [![version badge](version-badge.egoist.sh/npm/chalk)](https://npmjs.com/package/chalk) 19 | ``` 20 | 21 | > **Note:** To make this service keep working, you can [donate](https://github.com/egoist/donate) to support my work. 22 | 23 | ## Usage 24 | 25 | ### npm 26 | 27 | GET `/npm/:name` 28 | 29 | Query: 30 | 31 | - `tag`: Default `latest`. Release tag, eg: `beta` 32 | 33 | Examples: 34 | 35 | - https://version-badge.egoist.sh/npm/vue 36 | - https://version-badge.egoist.sh/npm/webpack 37 | - https://version-badge.egoist.sh/npm/poi?tag=next 38 | 39 | ### github 40 | 41 | Directly get the version from a file that's hosted on GitHub: 42 | 43 | GET `/github/:owner/:repo`
44 | Alias `/gh/:owner/:repo` 45 | 46 | Query: 47 | 48 | - `file`: Default `package.json` 49 | - `field`: Default `version` 50 | - `branch`: Default `master` 51 | 52 | Examples: 53 | 54 | - https://version-badge.egoist.sh/github/zeit/next.js 55 | - https://version-badge.egoist.sh/github/facebookincubator/create-react-app?file=packages/create-react-app/package.json 56 | 57 | ### rust crate 58 | 59 | GET `/crate/:name` 60 | 61 | Examples: 62 | 63 | - https://version-badge.egoist.sh/crate/rand 64 | - https://version-badge.egoist.sh/crate/clap 65 | 66 | ### text 67 | 68 | GET `/text?text=random_text` 69 | 70 | Examples: 71 | 72 | - https://version-badge.egoist.sh/text?text=beta 73 | - https://version-badge.egoist.sh/text?text=coming%20soon 74 | 75 | ## Prior Art 76 | 77 | I made this service because following one does not support npm (directly) and mono repos. 78 | 79 | - [npm-version-badge](https://github.com/teelaunch/npm-version-badge) 80 | 81 | ## License 82 | 83 | MIT © [EGOIST](github.com/EGOIST) 84 | --------------------------------------------------------------------------------