├── .editorconfig ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── license ├── package.json ├── readme.md ├── rollup.config.js ├── src └── index.ts ├── test └── index.ts └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_size = 2 6 | indent_style = tab 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.{json,yml,md}] 13 | indent_style = space 14 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | name: Node.js v${{ matrix.nodejs }} 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | nodejs: [12, 14, 16] 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: actions/setup-node@v2 15 | with: 16 | node-version: ${{ matrix.nodejs }} 17 | 18 | - name: Install 19 | run: | 20 | npm install 21 | npm install -g nyc 22 | 23 | - name: Compiles 24 | run: npm run build 25 | 26 | - name: Test w/ Coverage 27 | run: nyc --include=src npm test 28 | 29 | - name: Report 30 | if: matrix.nodejs >= 16 31 | run: | 32 | nyc report --reporter=text-lcov > coverage.lcov 33 | bash <(curl -s https://codecov.io/bash) 34 | env: 35 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | *-lock.* 4 | *.lock 5 | *.log 6 | 7 | /dist 8 | /types 9 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) TODO_YOUR_NAME (https://TODO_WEBSITE.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.0.0", 3 | "name": "TODO", 4 | "umd:name": "TODO", 5 | "repository": "TODO_USERNAME/TODO_REPO", 6 | "description": "TODO_MODULE_DESCRIPTION", 7 | "unpkg": "dist/index.min.js", 8 | "module": "dist/index.mjs", 9 | "main": "dist/index.js", 10 | "types": "types/index.d.ts", 11 | "license": "MIT", 12 | "author": { 13 | "name": "TODO_YOUR_NAME", 14 | "email": "YOUR_EMAIL@TODO.com", 15 | "url": "https://TODO_WEBSITE.com" 16 | }, 17 | "files": [ 18 | "dist", 19 | "types" 20 | ], 21 | "exports": { 22 | ".": { 23 | "import": "./dist/index.mjs", 24 | "require": "./dist/index.js" 25 | }, 26 | "./package.json": "./package.json" 27 | }, 28 | "engines": { 29 | "node": ">=12" 30 | }, 31 | "scripts": { 32 | "build": "rollup -c", 33 | "prepublishOnly": "npm run build", 34 | "types": "tsc --noEmit", 35 | "test": "uvu -r tsm test" 36 | }, 37 | "keywords": [ 38 | "TODO", 39 | "module", 40 | "keywords" 41 | ], 42 | "devDependencies": { 43 | "@rollup/plugin-node-resolve": "13.1.3", 44 | "rollup": "2.66.1", 45 | "rollup-plugin-terser": "7.0.2", 46 | "rollup-plugin-typescript2": "0.27.1", 47 | "tsm": "2.2.1", 48 | "typescript": "4.5.5", 49 | "uvu": "0.5.3" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Template: TypeScript Module [![CI](https://github.com/lukeed/typescript-module/workflows/CI/badge.svg)](https://github.com/lukeed/typescript-module/actions) [![codecov](https://badgen.now.sh/codecov/c/github/lukeed/typescript-module)](https://codecov.io/gh/lukeed/typescript-module) 2 | 3 | This is a [clonable template repository](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-from-a-template) for authoring a `npm` module with TypeScript. Out of the box, it: 4 | 5 | * Provides minimally-viable `tsconfig.json` settings 6 | * Scaffolds a silly arithmetic module (`src/index.ts`) 7 | * Scaffolds test suites for full test coverage (`test/index.ts`) 8 | * Scaffolds a GitHub Action for Code Integration (CI) that: 9 | * checks if compilation is successful 10 | * runs the test suite(s) 11 | * reports test coverage 12 | * Generates type definitions (`types/*.d.ts`) 13 | * Generates multiple distribution formats: 14 | * ES Module (`dist/index.mjs`) 15 | * CommonJS (`dist/index.js`) 16 | * UMD (`dist/index.min.js`) 17 | 18 | All configuration is accessible via the `rollup.config.js` and a few `package.json` keys: 19 | 20 | * `name` — the name of your module 21 | * `main` — the destination file for your CommonJS build 22 | * `module` — the destination file for your ESM build (optional but recommended) 23 | * `unpkg` — the destination file for your UMD build (optional for [unpkg.com](https://unpkg.com/)) 24 | * `umd:name` — the UMD global name for your module (optional) 25 | 26 | ## Setup 27 | 28 | 1. [Clone this template](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-from-a-template) 29 | 2. Replace all instances of `TODO` within the `license` and `package.json` files 30 | 3. Create [CodeCov](https://codecov.io) account (free for OSS) 31 | 4. Copy the provided CodeCov token as the `CODECOV_TOKEN` [repository secret](https://docs.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets#creating-encrypted-secrets-for-a-repository) (for CI reporting) 32 | 5. Replace `src/index.ts` and `test/index.ts` with your own code! 🎉 33 | 34 | ## Commands 35 | 36 | ### build 37 | 38 | Builds your module for distribution in multiple formats (ESM, CommonJS, and UMD). 39 | 40 | ```sh 41 | $ npm run build 42 | ``` 43 | 44 | ### test 45 | 46 | Runs your test suite(s) (`/tests/**`) against your source code (`/src/**`).
Doing so allows for accurate code coverage. 47 | 48 | > **Note:** Coverage is only collected and reported through the "CI" Github Action (`.github/workflows/ci.yml`). 49 | 50 | ```sh 51 | $ npm test 52 | ``` 53 | 54 | ## Publishing 55 | 56 | > **Important:** Please finish [Setup](#setup) before continuing! 57 | 58 | Once all `TODO` notes have been updated & your new module is ready to be shared, all that's left to do is decide its new version — AKA, do the changes consitute a `patch`, `minor`, or `major` release? 59 | 60 | Once decided, you can run the following: 61 | 62 | ```sh 63 | $ npm version && git push origin master --tags && npm publish 64 | # Example: 65 | # npm version patch && git push origin master --tags && npm publish 66 | ``` 67 | 68 | This command sequence will: 69 | * version your module, updating the `package.json` "version" 70 | * create and push a `git` tag (matching the new version) to your repository 71 | * build your module (via the `prepublishOnly` script) 72 | * publish the module to the npm registry 73 | 74 | ## License 75 | 76 | MIT © [Luke Edwards](https://lukeed.com) 77 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve'; 2 | import typescript from 'rollup-plugin-typescript2'; 3 | import { terser } from 'rollup-plugin-terser'; 4 | import pkg from './package.json'; 5 | 6 | export default { 7 | input: 'src/index.ts', 8 | output: [{ 9 | format: 'esm', 10 | file: pkg.module, 11 | sourcemap: false, 12 | }, { 13 | format: 'cjs', 14 | file: pkg.main, 15 | sourcemap: false, 16 | esModule: false, 17 | }, { 18 | name: pkg['umd:name'] || pkg.name, 19 | format: 'umd', 20 | file: pkg.unpkg, 21 | sourcemap: false, 22 | esModule: false, 23 | plugins: [ 24 | terser() 25 | ] 26 | }], 27 | external: [ 28 | ...require('module').builtinModules, 29 | ...Object.keys(pkg.dependencies || {}), 30 | ...Object.keys(pkg.peerDependencies || {}), 31 | ], 32 | plugins: [ 33 | resolve(), 34 | typescript({ 35 | useTsconfigDeclarationDir: true 36 | }) 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export function sum(...nums: number[]): number { 2 | let i=0, total=0; 3 | for (; i < nums.length; i++) total += nums[i]; 4 | return total; 5 | } 6 | 7 | export function substract(...nums: number[]): number { 8 | let i=0, total = nums[i++] | 0; 9 | for (; i < nums.length; i++) total -= nums[i]; 10 | return total; 11 | } 12 | 13 | export function average(...nums: number[]): number { 14 | let i=0, len=nums.length, total=0; 15 | for (; i < len; i++) total += nums[i]; 16 | return total / len; 17 | } 18 | -------------------------------------------------------------------------------- /test/index.ts: -------------------------------------------------------------------------------- 1 | import { suite } from 'uvu'; 2 | import * as assert from 'uvu/assert'; 3 | import * as math from '../src'; 4 | 5 | const API = suite('exports'); 6 | 7 | API('should export an object', () => { 8 | assert.type(math, 'object'); 9 | }); 10 | 11 | API.run(); 12 | 13 | // --- 14 | 15 | const sum = suite('sum'); 16 | 17 | sum('should be a function', () => { 18 | assert.type(math.sum, 'function'); 19 | }); 20 | 21 | sum('should default to 0', () => { 22 | assert.is(math.sum(), 0); 23 | }); 24 | 25 | sum('should handle one argument', () => { 26 | assert.is(math.sum(5), 5); 27 | assert.is(math.sum(-12), -12); 28 | }); 29 | 30 | sum('should handle two arguments', () => { 31 | assert.is(math.sum(1, 2), 3); 32 | assert.is(math.sum(11, 12), 23); 33 | 34 | assert.is(math.sum(-1, -2), -3); 35 | assert.is(math.sum(-11, -12), -23); 36 | 37 | assert.is(math.sum(1, -2), -1); 38 | assert.is(math.sum(-11, 12), 1); 39 | }); 40 | 41 | sum('should handle multiple arguments', () => { 42 | assert.is(math.sum(0, 0, -1, -2, 4, 9, 10), 20); 43 | assert.is(math.sum(1, 2, 3, 4, 5, 6), 21); 44 | assert.is(math.sum(10, 20, 30), 60); 45 | }); 46 | 47 | sum.run(); 48 | 49 | // --- 50 | 51 | const substract = suite('substract'); 52 | 53 | substract('should be a function', () => { 54 | assert.type(math.substract, 'function'); 55 | }); 56 | 57 | substract('should default to 0', () => { 58 | assert.is(math.substract(), 0); 59 | }); 60 | 61 | substract('should handle one argument', () => { 62 | assert.is(math.substract(5), 5); 63 | assert.is(math.substract(-12), -12); 64 | }); 65 | 66 | substract('should handle two arguments', () => { 67 | assert.is(math.substract(1, 2), -1); 68 | assert.is(math.substract(11, 12), -1); 69 | 70 | assert.is(math.substract(-1, -2), 1); 71 | assert.is(math.substract(-11, -12), 1); 72 | 73 | assert.is(math.substract(1, -2), 3); 74 | assert.is(math.substract(-11, 12), -23); 75 | }); 76 | 77 | substract('should handle multiple arguments', () => { 78 | assert.is(math.substract(0, 0, -1, -2, 4, 9, 10), -20); 79 | assert.is(math.substract(1, 2, 3, 4, 5, 6), -19); 80 | assert.is(math.substract(10, 20, 30), -40); 81 | }); 82 | 83 | substract.run(); 84 | 85 | // --- 86 | 87 | const average = suite('average'); 88 | 89 | average('should be a function', () => { 90 | assert.type(math.average, 'function'); 91 | }); 92 | 93 | average('should default to NaN', () => { 94 | assert.equal(math.average(), NaN); 95 | }); 96 | 97 | average('should handle one argument', () => { 98 | assert.is(math.average(5), 5); 99 | assert.is(math.average(-12), -12); 100 | }); 101 | 102 | average('should handle two arguments', () => { 103 | assert.is(math.average(1, 2), 1.5); 104 | assert.is(math.average(11, 12), 11.5); 105 | 106 | assert.is(math.average(-1, -2), -1.5); 107 | assert.is(math.average(-11, -12), -11.5); 108 | 109 | assert.is(math.average(1, -2), -0.5); 110 | assert.is(math.average(-11, 12), 0.5); 111 | }); 112 | 113 | average('should handle multiple arguments', () => { 114 | assert.is(math.average(0, 0, -1, -2, 4, 9, 10), 20 / 7); 115 | assert.is(math.average(1, 2, 3, 4, 5, 6), 21 / 6); 116 | assert.is(math.average(10, 20, 30), 20); 117 | }); 118 | 119 | average.run(); 120 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "ts-node": { 3 | "transpileOnly": true, 4 | "compilerOptions": { 5 | "module": "commonjs", 6 | "sourceMap": true, 7 | }, 8 | "include": [ 9 | "test/**/*" 10 | ] 11 | }, 12 | "compilerOptions": { 13 | "target": "esnext", 14 | "module": "esnext", 15 | "declaration": true, 16 | "declarationDir": "types", 17 | "forceConsistentCasingInFileNames": true, 18 | "moduleResolution": "node", 19 | "strictNullChecks": true, 20 | "noImplicitAny": true, 21 | "strict": true 22 | }, 23 | "include": [ 24 | "@types/**/*", 25 | "src/**/*" 26 | ], 27 | "exclude": [ 28 | "node_modules" 29 | ] 30 | } 31 | --------------------------------------------------------------------------------