├── generators ├── app │ ├── templates │ │ ├── public │ │ │ └── .gitkeep │ │ ├── vercel.json │ │ ├── .vercelignore │ │ ├── README.md │ │ ├── package.json │ │ └── .gitignore │ └── index.js └── __tests__ │ └── app.js ├── .npmrc ├── jest.config.js ├── .github ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── validate.yml │ └── release.yml ├── LICENSE ├── docs └── manual-releases.md ├── package.json ├── .gitignore └── README.md /generators/app/templates/public/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.org/ 2 | package-lock=false 3 | -------------------------------------------------------------------------------- /generators/app/templates/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "redirects": [] 3 | } 4 | -------------------------------------------------------------------------------- /generators/app/templates/.vercelignore: -------------------------------------------------------------------------------- 1 | /* 2 | !package.json 3 | !api 4 | !public 5 | !now.json 6 | !vercel.json 7 | !*.html 8 | -------------------------------------------------------------------------------- /generators/app/templates/README.md: -------------------------------------------------------------------------------- 1 | # <%= name %> 2 | 3 | > Personal url shortener 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | const { jest: jestConfig } = require('cod-scripts/config'); 2 | 3 | module.exports = Object.assign(jestConfig, { 4 | roots: ['./generators'], 5 | }); 6 | -------------------------------------------------------------------------------- /generators/__tests__/app.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const helpers = require('yeoman-test'); 3 | 4 | describe('generator-vercel-shortener:app', () => { 5 | beforeAll(() => helpers.run(path.join(__dirname, '../app'))); 6 | 7 | it.todo('creates files'); 8 | }); 9 | -------------------------------------------------------------------------------- /generators/app/templates/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%= name %>", 3 | "version": "1.0.0", 4 | "private": true, 5 | "bin": { 6 | "shorten": "node_modules/.bin/vercel-redirects" 7 | }, 8 | "devDependencies": {}, 9 | "scripts": { 10 | "build": "echo \"vercel dev requires a build script & public directory. don't delete\"", 11 | "shorten": "vercel-redirects" 12 | }, 13 | "vercel-redirects": { 14 | "autoPush": true 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Related Issue 2 | 3 | 4 | 5 | ### Description 6 | 7 | 8 | 9 | ### Checklist 10 | 11 | 12 | 13 | - [ ] I have reviewed the code changes myself 14 | - [ ] My code meets all of the acceptance criteria of the ticket it closes. 15 | - [ ] I have made all necessary changes to the documentation. 16 | - [ ] I have added tests to cover my changes. 17 | - [ ] All new and existing tests passed. 18 | -------------------------------------------------------------------------------- /.github/workflows/validate.yml: -------------------------------------------------------------------------------- 1 | name: Validate Code 2 | 3 | on: pull_request 4 | 5 | jobs: 6 | validate: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v3 11 | with: 12 | fetch-depth: 0 13 | 14 | - uses: actions/setup-node@v3 15 | with: 16 | node-version: 18.x 17 | 18 | - name: install dependencies 19 | run: npm install --no-save 20 | 21 | - name: lint commits 22 | run: 23 | npm run lint:commit -- --from="origin/${{ github.base_ref }}" 24 | --to="origin/${{github.head_ref }}" 25 | 26 | - name: lint js 27 | run: npm run lint 28 | 29 | - name: run tests 30 | run: npm run test 31 | env: 32 | CI: true 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Chris O'Donnell (https://codfish.io) 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 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v3 14 | 15 | - uses: actions/setup-node@v3 16 | with: 17 | node-version: 18.x 18 | 19 | - name: validate before release 20 | run: | 21 | npm install --no-save 22 | npm run lint 23 | npm run test 24 | env: 25 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 26 | CI: true 27 | 28 | - name: semantic release 29 | uses: docker://ghcr.io/codfish/semantic-release-action:v2 30 | with: 31 | branches: | 32 | [ 33 | '+([0-9])?(.{+([0-9]),x}).x', 34 | 'main', 35 | 'next', 36 | 'next-major', 37 | { 38 | name: 'beta', 39 | prerelease: true 40 | }, 41 | { 42 | name: 'alpha', 43 | prerelease: true 44 | } 45 | ] 46 | env: 47 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 48 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 49 | -------------------------------------------------------------------------------- /docs/manual-releases.md: -------------------------------------------------------------------------------- 1 | # manual-releases 2 | 3 | This project has an automated release set up. So things are only released when 4 | there are useful changes in the code that justify a release. But sometimes 5 | things get messed up one way or another and we need to trigger the release 6 | ourselves. When this happens, simply bump the number below and commit that with 7 | the following commit message based on your needs: 8 | 9 | **Major** 10 | 11 | ``` 12 | fix(release): manually release a major version 13 | 14 | There was an issue with a major release, so this manual-releases.md 15 | change is to release a new major version. 16 | 17 | Reference: # 18 | 19 | BREAKING CHANGE: 20 | ``` 21 | 22 | **Minor** 23 | 24 | ``` 25 | feat(release): manually release a minor version 26 | 27 | There was an issue with a minor release, so this manual-releases.md 28 | change is to release a new minor version. 29 | 30 | Reference: # 31 | ``` 32 | 33 | **Patch** 34 | 35 | ``` 36 | fix(release): manually release a patch version 37 | 38 | There was an issue with a patch release, so this manual-releases.md 39 | change is to release a new patch version. 40 | 41 | Reference: # 42 | ``` 43 | 44 | The number of times we've had to do a manual release is: 0 45 | 46 | **Credit** 47 | 48 | Idea for this file & copy taken originally from [Kent C. Dodds](https://github.com/kentcdodds). 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generator-vercel-shortener", 3 | "version": "0.0.0-semantically-released", 4 | "description": "Generator to create your own personal url shortener to deploy to Vercel.", 5 | "homepage": "https://github.com/codfish/generator-vercel-shortener", 6 | "author": { 7 | "name": "Chris O'Donnell", 8 | "email": "dev@codfish.io", 9 | "url": "https://codfish.io" 10 | }, 11 | "files": [ 12 | "generators" 13 | ], 14 | "main": "dist/index.js", 15 | "keywords": [ 16 | "yeoman-generator", 17 | "vercel", 18 | "vercel redirects", 19 | "zeit now", 20 | "url shortener", 21 | "short urls" 22 | ], 23 | "license": "MIT", 24 | "devDependencies": { 25 | "cod-scripts": "^4.0.3", 26 | "yeoman-test": "^2.4.1" 27 | }, 28 | "engines": { 29 | "node": ">=10", 30 | "npm": ">=6", 31 | "yarn": ">=1" 32 | }, 33 | "repository": { 34 | "type": "git", 35 | "url": "https://github.com/codfish/generator-vercel-shortener.git" 36 | }, 37 | "scripts": { 38 | "format": "cod-scripts format", 39 | "lint": "cod-scripts lint", 40 | "lint:commit": "cod-scripts commitlint", 41 | "test": "cod-scripts test" 42 | }, 43 | "eslintConfig": { 44 | "extends": [ 45 | "./node_modules/cod-scripts/eslint.js" 46 | ], 47 | "rules": { 48 | "no-underscore-dangle": "off" 49 | } 50 | }, 51 | "husky": { 52 | "hooks": { 53 | "pre-commit": "cod-scripts pre-commit", 54 | "commit-msg": "cod-scripts commitlint -E HUSKY_GIT_PARAMS" 55 | } 56 | }, 57 | "dependencies": { 58 | "@babel/runtime": "^7.9.2", 59 | "chalk": "^4.0.0", 60 | "lodash": "^4.17.15", 61 | "yeoman-generator": "^4.9.0" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Optional REPL history 57 | .node_repl_history 58 | 59 | # Output of 'npm pack' 60 | *.tgz 61 | 62 | # Yarn Integrity file 63 | .yarn-integrity 64 | 65 | # dotenv environment variables file 66 | .env 67 | .env.test 68 | 69 | # parcel-bundler cache (https://parceljs.org/) 70 | .cache 71 | 72 | # next.js build output 73 | .next 74 | 75 | # nuxt.js build output 76 | .nuxt 77 | 78 | # gatsby files 79 | .cache/ 80 | 81 | # vuepress build output 82 | .vuepress/dist 83 | 84 | # Serverless directories 85 | .serverless/ 86 | 87 | # FuseBox cache 88 | .fusebox/ 89 | 90 | # DynamoDB Local files 91 | .dynamodb/ 92 | 93 | # Custom 94 | dist 95 | build 96 | 97 | .vercel 98 | .now 99 | -------------------------------------------------------------------------------- /generators/app/templates/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Optional REPL history 57 | .node_repl_history 58 | 59 | # Output of 'npm pack' 60 | *.tgz 61 | 62 | # Yarn Integrity file 63 | .yarn-integrity 64 | 65 | # dotenv environment variables file 66 | .env 67 | .env.test 68 | 69 | # parcel-bundler cache (https://parceljs.org/) 70 | .cache 71 | 72 | # next.js build output 73 | .next 74 | 75 | # nuxt.js build output 76 | .nuxt 77 | 78 | # gatsby files 79 | .cache/ 80 | 81 | # vuepress build output 82 | .vuepress/dist 83 | 84 | # Serverless directories 85 | .serverless/ 86 | 87 | # FuseBox cache 88 | .fusebox/ 89 | 90 | # DynamoDB Local files 91 | .dynamodb/ 92 | 93 | # Custom 94 | dist 95 | build 96 | 97 | .vercel 98 | .now 99 | -------------------------------------------------------------------------------- /generators/app/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const Generator = require('yeoman-generator'); 3 | const chalk = require('chalk'); 4 | const kebabCase = require('lodash/kebabCase'); 5 | 6 | module.exports = class extends Generator { 7 | constructor(args, options) { 8 | super(args, options); 9 | 10 | this.argument('projectDirectory', { 11 | type: String, 12 | required: false, 13 | default: '.', 14 | desc: 'The directory you want to generate into.', 15 | }); 16 | } 17 | 18 | initializing() { 19 | this.cwd = this.destinationRoot(this.options.projectDirectory); 20 | const name = 21 | this.options.projectDirectory === '.' 22 | ? this.determineAppname() 23 | : kebabCase(this.options.projectDirectory); 24 | 25 | this.props = { ...this.props, ...this.options, name }; 26 | 27 | // otherwise initialize a git repo. need to make sure directory exists first 28 | if (fs.existsSync(this.destinationPath('.git'))) { 29 | return; 30 | } 31 | this.spawnCommandSync('mkdir', ['-p', this.cwd]); 32 | this.spawnCommandSync('git', ['init', '--quiet'], { cwd: this.cwd }); 33 | } 34 | 35 | install() { 36 | this.log(`Installing ${chalk.cyan('vercel-redirects')}.`); 37 | this.log(); 38 | this.npmInstall(['vercel-redirects'], { saveDev: true }, { cwd: this.cwd }); 39 | } 40 | 41 | writing() { 42 | this.fs.copyTpl( 43 | this.templatePath('**'), 44 | this.cwd, 45 | this.props, 46 | {}, 47 | { globOptions: { dot: true } }, 48 | ); 49 | } 50 | 51 | end() { 52 | // Delete yeoman auto-generated configs. 53 | // @see https://github.com/codfish/generator-codfish/blob/main/generators/BaseGenerator.js#L117 54 | this.spawnCommandSync('rm', [ 55 | '-rf', 56 | `${this.contextRoot}/.yo-repository`, 57 | this.destinationPath('.yo-repository'), 58 | ]); 59 | 60 | // make initial commit 61 | this.spawnCommandSync('git', ['add', '.'], { cwd: this.cwd }); 62 | this.spawnCommandSync('git', ['commit', '-am', 'feat: initial commit'], { 63 | cwd: this.cwd, 64 | }); 65 | 66 | // add global shorten script 67 | this.spawnCommandSync('npm', ['link'], { 68 | cwd: this.cwd, 69 | }); 70 | 71 | const dir = this.props.projectDirectory === '.' ? '' : this.props.projectDirectory; 72 | this.log(); 73 | this.log(`Success! Your url shortener was generated in ${chalk.green(`./${dir}`)}.`); 74 | this.log(); 75 | this.log("We've initialized a git repo and made an initial commit for you."); 76 | this.log(); 77 | this.log(`Next Steps:`); 78 | this.log(` 79 | 1. Create a repository for your url shortener. https://github.com/new 80 | 2. Run ${chalk.cyan('git remote add origin ')}. 81 | 3. Run ${chalk.cyan('git push origin main')}. 82 | 4. Run ${chalk.cyan('git branch -u origin/main')}. 83 | 5. Run ${chalk.cyan('vercel')} to setup & deploy your project to Vercel. 84 | 6. Add redirects: ${chalk.cyan('shorten --help')}. 85 | `); 86 | this.log(); 87 | this.log(`Other things you should do:`); 88 | this.log(` 89 | - Set up Vercel GitHub Integration. https://vercel.com/docs/v2/git-integrations/vercel-for-github 90 | - Add a custom domain to make your short url's really short! https://vercel.com/docs/v2/custom-domains 91 | `); 92 | } 93 | }; 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # generator-vercel-shortener 2 | 3 | > Generator to create your own personal url shortener to deploy to Vercel (formerly Zeit Now). 4 | 5 | [![version][version-badge]][package] [![downloads][downloads-badge]][npmcharts] 6 | [![GitHub Workflow Status][actions-badge]][actions-badge] [![MIT License][license-badge]][license] 7 | [![PRs Welcome][prs-badge]][prs] [![Semantic Release][semantic-release-badge]][semantic-release] 8 | [![Commitizen friendly][commitizen-badge]][commitizen] 9 | 10 | 11 | 12 | 13 | - [Getting Started](#getting-started) 14 | - [Installation](#installation) 15 | - [Usage](#usage) 16 | - [After you generate](#after-you-generate) 17 | - [Related Projects](#related-projects) 18 | 19 | 20 | 21 | ## Getting Started 22 | 23 | These aren't prerequisites but before you're able to actually deploy your url shortener you should: 24 | 25 | 1. Create a [Vercel account](https://vercel.com/signup). 26 | 1. Set up Vercel 27 | [GitHub Integration](https://vercel.com/docs/concepts/git/vercel-for-github). This way 28 | Vercel deploys automatically for you. 29 | 1. Buy, migrate or simply point a domain you already own to Vercel's dns. 30 | 31 | You don't technically need to have a domain. By default your new project in Vercel will be deployed 32 | with a domain like `https://my-url-shortener.vercel.app`, and your redirects will work just fine. 33 | However part of the glory of url shorteners is that they create **short urls** that are easy to type 34 | and remember! 35 | 36 | ## Installation 37 | 38 | ```sh 39 | npm install -g yo generator-vercel-shortener 40 | ``` 41 | 42 | ## Usage 43 | 44 | ```sh 45 | yo vercel-shortener 46 | ``` 47 | 48 | By default it will look to generate in your current working directory. If that's not what you want, 49 | then specify the directory in the call or create a new directory to run the generator in. 50 | 51 | ```sh 52 | yo vercel-shortener [] 53 | ``` 54 | 55 | _or..._ 56 | 57 | ```sh 58 | mkdir 59 | cd 60 | yo vercel-shortener 61 | ``` 62 | 63 | ## After you generate 64 | 65 | Next steps after generating: 66 | 67 | 1. Create a repository for your url shortener, [https://github.com/new](https://github.com/new). 68 | 1. Run `git remote add origin `. 69 | 1. Run `git push origin main`. 70 | 1. Run `git branch -u origin/main`. 71 | 1. Run `vercel` to setup & deploy your project to Vercel. 72 | 1. Add redirects: 73 | - Run `shorten []`. The generator will expose a globally available script 74 | for you. Run this from anywhere in your terminal. See 75 | [vercel-redirects](https://github.com/codfish/vercel-redirects) for the full cli documentation. 76 | - Run `npm run shorten []` from the root of the project. 77 | - Add them in `vercel.json` [manually](https://vercel.com/docs/configuration#project/redirects). 78 | You'll need to commit and push them yourself. 79 | 80 | For example: 81 | 82 | ```sh 83 | shorten https://gist.github.com/codfish/91ef26f3a56a5c5ca0912aa8c0c5c020 /linting 84 | ``` 85 | 86 | And in about ~5 seconds I can hit [codfi.sh/linting](https://codfi.sh/linting) and it will redirect 87 | for me. 88 | 89 | ## Related Projects 90 | 91 | - [`codfi.sh`](https://github.com/codfish/codfi.sh) - My own personal url shortener, using the same 92 | configuration as a project built with this generator. 93 | - [`vercel-redirects`](https://github.com/codfish/vercel-redirects) - Command-line utility to manage 94 | your Vercel project redirects. 95 | - [`netlify-shortener`](https://github.com/kentcdodds/netlify-shortener) - Your own free URL 96 | shortener with Netlify (different serverless platform). 97 | 98 | [npm]: https://www.npmjs.com/ 99 | [node]: https://nodejs.org 100 | [semantic-release]: https://github.com/semantic-release/semantic-release 101 | [semantic-release-badge]: 102 | https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg?style=flat-square 103 | [prs]: http://makeapullrequest.com 104 | [prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square 105 | [commitizen]: http://commitizen.github.io/cz-cli/ 106 | [commitizen-badge]: 107 | https://img.shields.io/badge/commitizen-friendly-brightgreen.svg?style=flat-square 108 | [npmcharts]: http://npmcharts.com/compare/generator-vercel-shortener 109 | [version-badge]: https://img.shields.io/npm/v/generator-vercel-shortener.svg?style=flat-square 110 | [package]: https://www.npmjs.com/package/generator-vercel-shortener 111 | [downloads-badge]: https://img.shields.io/npm/dm/generator-vercel-shortener.svg?style=flat-square 112 | [license-badge]: https://img.shields.io/npm/l/generator-vercel-shortener.svg?style=flat-square 113 | [license]: https://github.com/codfish/generator-vercel-shortener/blob/main/LICENSE 114 | [actions]: https://github.com/codfish/generator-vercel-shortener/actions 115 | [actions-badge]: 116 | https://img.shields.io/github/workflow/status/codfish/generator-vercel-shortener/Release/main?style=flat-square 117 | --------------------------------------------------------------------------------