├── .gitignore ├── icons.js ├── vercel.json ├── .github └── dependabot.yml ├── package.json ├── public └── index.css ├── utils.js ├── README.md ├── LICENSE ├── home.js ├── main.js └── api └── index.ts /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vercel 3 | public/index.html 4 | -------------------------------------------------------------------------------- /icons.js: -------------------------------------------------------------------------------- 1 | const simpleIcons = require('simple-icons') 2 | const { titleToFilename } = require('./utils.js') 3 | 4 | // format title to filename 5 | module.exports = Object.keys(simpleIcons).reduce((accu, curr) => { 6 | accu[titleToFilename(curr)] = simpleIcons[curr] 7 | return accu 8 | }, {}) 9 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://openapi.vercel.sh/vercel.json", 3 | "rewrites": [ 4 | { "source": "/", "destination": "api/index.ts" } 5 | ], 6 | "redirects": [ 7 | { "source": "/:name/:color", "destination": "https://cdn.simpleicons.org/:name/:color" } 8 | ], 9 | "functions": { 10 | "api/*.ts": { 11 | "memory": 128 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | ignore: 9 | - dependency-name: simple-icons 10 | versions: 11 | - 4.10.0 12 | - 4.11.0 13 | - 4.13.0 14 | - 4.14.0 15 | - 4.15.0 16 | - 4.17.0 17 | - 4.20.0 18 | - 4.9.0 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "build": "node build.mjs", 5 | "start": "micro main.js" 6 | }, 7 | "dependencies": { 8 | "micro": "^10.0.1", 9 | "micro-fork": "^3.0", 10 | "my-way": "^2.0.0", 11 | "serve-marked": "^5.0.0", 12 | "simple-icons": "^15.14.0" 13 | }, 14 | "engines": { 15 | "node": "22.x" 16 | }, 17 | "devDependencies": { 18 | "@types/node": "^24.6.2", 19 | "hyper-marked": "^1.1.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /public/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | max-width: 960px; 3 | } 4 | 5 | .markdown-content h1 + p { 6 | text-align: center; 7 | margin: -43px 0 4rem 0; 8 | line-height: 20px; 9 | height: 20px; 10 | z-index: 1; 11 | } 12 | 13 | #icons { margin: -4rem 2rem 2rem 2rem } 14 | #icons a { width: 80px; height: 120px; margin-right: 1rem; vertical-align: top; display: inline-block; word-wrap: break-word } 15 | #icons a { text-align: center; color: #777; font-size: 12px; font-family: consolas, monospace } 16 | #icons img { width: 32px; height: 32px; display: block; margin: 1em auto } 17 | -------------------------------------------------------------------------------- /utils.js: -------------------------------------------------------------------------------- 1 | // https://github.com/simple-icons/simple-icons/blob/master/scripts/utils.js 2 | 3 | module.exports = { 4 | /** 5 | * Converts a brand title into a filename (not a full path) 6 | * @param {String} title The title to convert 7 | */ 8 | titleToFilename: title => ( 9 | title.toLowerCase() 10 | .replace(/^si/, "") 11 | .replace(/\+/g, "plus") 12 | .replace(/^\./, "dot-") 13 | .replace(/\.$/, "-dot") 14 | .replace(/\./g, "-dot-") 15 | .replace(/^&/, "and-") 16 | .replace(/&$/, "-and") 17 | .replace(/&/g, "-and-") 18 | .replace(/[ !’]/g, "") 19 | .replace(/à|á|â|ã|ä/, "a") 20 | .replace(/ç/, "c") 21 | .replace(/è|é|ê|ë/, "e") 22 | .replace(/ì|í|î|ï/, "i") 23 | .replace(/ñ/, "n") 24 | .replace(/ò|ó|ô|õ|ö/, "o") 25 | .replace(/ù|ú|û|ü/, "u") 26 | .replace(/ý|ÿ/, "y") 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # simpleicons.vercel.app 2 | 3 | [![license-src]][license-href] [![github-src]][github-href] 4 | 5 | Serve colorful [simpleicons](https://simpleicons.org/) on Vercel CDN. 6 | 7 | 8 | > NOTE: 9 | > simple-icons have an official [CDN service](https://github.com/simple-icons/simple-icons?tab=readme-ov-file#cdn-with-colors) now. 10 | > this (simpleicons.vercel.app) is no longer needed, cheers! 11 | 12 | 13 | ## Scheme 14 | 15 | ``` 16 | https://simpleicons.vercel.app/:name/:color 17 | ``` 18 | 19 | or even shorter 20 | 21 | ``` 22 | https://ico.vercel.app/:name/:color 23 | ``` 24 | 25 | ## Example 26 | 27 | - [`https://simpleicons.vercel.app/github/aaa`](https://simpleicons.vercel.app/github/aaa) 28 | - [`https://ico.vercel.app/apple/black`](https://ico.vercel.app/apple/black) 29 | 30 | [license-src]: https://badgen.net/badge/license/MIT/blue 31 | [license-href]: https://github.com/simpleicons/simpleicons.vercel.app/blob/master/LICENSE 32 | [github-src]: https://badgen.net/badge/github/amio%2Fsimpleicons.vercel.app?icon&label 33 | [github-href]: https://github.com/simpleicons/simpleicons.vercel.app 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /home.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | const { serveMarked } = require('serve-marked') 4 | 5 | const icons = require('./icons.js') 6 | const readmeMarkdown = fs.readFileSync(path.join(__dirname, 'README.md'), 'utf8') 7 | 8 | const serveHome = serveMarked( 9 | readmeMarkdown, 10 | { 11 | title: 'Simple Icons', 12 | inlineCSS: ` 13 | .markdown-body { max-width: 960px } 14 | .markdown-body h1 + p { text-align: center; margin: -40px 0 4em 0; } 15 | #icons a { display: inline-block; text-align: center; margin-right: 7px } 16 | #icons a { width: 80px; height: 120px; font-size: 12px; vertical-align: top } 17 | #icons a { color: #777; font-family: consolas, monospace } 18 | #icons img { width: 32px; height: 32px; display: block; margin: 1em auto } 19 | `, 20 | beforeHeadEnd: '', 21 | beforeBodyEnd: ` 22 |
23 |

Icons

${genIconsHtml()}
24 |
25 | ` 26 | } 27 | ) 28 | 29 | module.exports = function (req, res) { 30 | serveHome(req, res) 31 | } 32 | 33 | function genIconsHtml () { 34 | return Object.keys(icons).map(k => { 35 | const url = `/${k}/000` 36 | return `${k}${k}` 37 | }).join('') 38 | } 39 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | const { send } = require('micro') 2 | const matchRoute = require('my-way') 3 | 4 | const icons = require('./icons.js') 5 | const serveHomepage = require('./home.js') 6 | 7 | const serveIcon = (req, res, params) => { 8 | const { name, color } = params 9 | 10 | if (icons[name]) { 11 | res.setHeader('Content-Type', 'image/svg+xml') 12 | res.setHeader('Cache-Control', 'public, max-age=86400, s-maxage=31536000, stale-while-revalidate=604800') 13 | return send(res, 200, transformSVG(icons[name].svg, { fill: color })) 14 | } 15 | 16 | send(res, 404) 17 | } 18 | 19 | const serveRedirect = (req, res, params) => { 20 | const name = params.name.split('.')[0] 21 | 22 | if (icons[name]) { 23 | const { color } = require('url').parse(req.url, true).query 24 | res.setHeader('Location', `/${name}/${color}`) 25 | return send(res, 301) 26 | } 27 | 28 | send(res, 404) 29 | } 30 | 31 | const transformSVG = (svgString, { fill }) => { 32 | if (fill) { 33 | svgString = svgString.replace(/^ { 39 | if (req.method !== 'GET') { 40 | return send(res, 403) 41 | } 42 | 43 | if (req.url === '/') { 44 | res.setHeader('Cache-Control', 'public, max-age=3600, s-maxage=2592000') 45 | return serveHomepage(req, res) 46 | } 47 | 48 | const redirectParams = matchRoute('/icons/:name', req.url) 49 | 50 | if (redirectParams) { 51 | return serveRedirect(req, res, redirectParams) 52 | } 53 | 54 | const iconParams = matchRoute('/:name/:color?', req.url) 55 | if (iconParams) { 56 | return serveIcon(req, res, iconParams) 57 | } 58 | 59 | send(res, 404) 60 | } 61 | -------------------------------------------------------------------------------- /api/index.ts: -------------------------------------------------------------------------------- 1 | // import main from '../main' 2 | 3 | import { join } from 'path' 4 | import { readFileSync } from 'fs' 5 | 6 | const readmeMarkdown = readFileSync(join(__dirname, '../README.md'), 'utf-8') 7 | 8 | import { IncomingMessage, ServerResponse } from 'http'; 9 | 10 | // Vercel runtime augments ServerResponse with helper methods 11 | interface VercelServerResponse extends ServerResponse { 12 | status(code: number): this; 13 | send(body: any): this; 14 | json(data: any): this; 15 | } 16 | 17 | export default function handler(request: IncomingMessage, response: VercelServerResponse) { 18 | if (request.method === 'GET' && request.url === '/') { 19 | // Serve the home page with README content 20 | response.setHeader('Content-Type', 'text/html; charset=utf-8'); 21 | response.setHeader('Cache-Control', 'public, s-maxage=1'); 22 | return response.status(200).send(generateHomeHtml()); 23 | } 24 | 25 | if (request.method === 'GET' && request.url?.split('/').length === 2) { 26 | response.setHeader('Cache-Control', 'public, s-maxage=1'); 27 | return response.status(200).json({ name: 'John Doe' }); 28 | } 29 | 30 | return response.status(404).send('Not Found'); 31 | } 32 | 33 | function generateHomeHtml () { 34 | return ` 35 | 36 | 37 | 38 | 39 | 40 | HumanEdited 41 | 42 | 43 | 44 |
45 |

Welcome to HumanEdited

46 |

Your platform for human-edited content.

47 |
48 |
49 |
${readmeMarkdown}
50 |
51 | 52 | 53 | `; 54 | } 55 | 56 | // export default main 57 | --------------------------------------------------------------------------------