├── .editorconfig ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── appveyor.yml ├── custom-typings └── chai-subset.d.ts ├── package.json ├── src ├── greeter.ts └── index.ts ├── test ├── greeter.spec.ts └── setup.ts ├── tsconfig.json ├── webpack.config.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | insert_final_newline = true 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | indent_style = space 8 | 9 | [*.{js,jsx,ts,tsx}] 10 | indent_size = 4 11 | 12 | [*.json] 13 | indent_size = 2 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | node_modules 28 | 29 | # Optional npm cache directory 30 | .npm 31 | 32 | # Optional REPL history 33 | .node_repl_history 34 | 35 | # TypeScript target 36 | dist 37 | 38 | # Visual Studio Code local project settings 39 | .vscode 40 | 41 | .idea -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | sudo: required 3 | language: node_js 4 | 5 | node_js: 6 | - "9" 7 | - "8" 8 | 9 | os: 10 | - linux 11 | 12 | 13 | before_install: 14 | - sudo sysctl fs.inotify.max_user_watches=524288 15 | - yarn global add greenkeeper-lockfile@1 16 | 17 | before_script: 18 | - greenkeeper-lockfile-update 19 | 20 | after_script: 21 | - greenkeeper-lockfile-upload 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Wix.com 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # typescript-boilerplate 2 | 3 | [![Greenkeeper badge](https://badges.greenkeeper.io/wix/typescript-boilerplate.svg)](https://greenkeeper.io/) 4 | [![Build Status](https://travis-ci.org/wix/typescript-boilerplate.svg?branch=master)](https://travis-ci.org/wix/typescript-boilerplate) 5 | 6 | Generic project boilerplate for isomorphic TypeScript 7 | 8 | ### project features 9 | - Typescript in strict mode (the only way to roll!) 10 | - Customised 3rd parrty typings in `./custom_typings` folder 11 | - Fully automated tests that run both in node and browser using mocha + chai 12 | - Test tools set-up phase that is loaded prior to any test being run 13 | - Automatically discoverable tests (by glob pattern defined in package.json) 14 | - Dev server for live updates in browser and transpiled files 15 | - Least opinionated setup: no run time dependencies and minimal configuration 16 | 17 | ## developer documentation 18 | how to build and test: 19 | - clone the repository 20 | - in the cloned folder, run `npm install` 21 | - run `npm test` to build and test the code in both nodejs and browser 22 | 23 | how to debug (browser): 24 | - run `npm start` to run a development server 25 | - open `http://localhost:8080/test.bundle` to run live tests that will update while you change the source code 26 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | nodejs_version: "8" 3 | 4 | # Install scripts. (runs after repo cloning) 5 | 6 | install: 7 | # Get the latest stable version of Node.js or io.js 8 | - ps: Install-Product node $env:nodejs_version 9 | # install modules 10 | - yarn 11 | 12 | # Post-install test scripts. 13 | test_script: 14 | # Output useful info for debugging. 15 | - node --version 16 | - yarn --version 17 | # run tests 18 | - yarn test 19 | 20 | # Don't actually build. 21 | build: off 22 | 23 | cache: 24 | - "%LOCALAPPDATA%\\Yarn" 25 | -------------------------------------------------------------------------------- /custom-typings/chai-subset.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for chai-subset 1.0.0 2 | // Project: https://github.com/e-conomic/chai-subset 3 | // Definitions by: Sam Noedel , Andrew Brown 4 | // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped 5 | 6 | /// 7 | 8 | declare namespace Chai { 9 | interface Assertion { 10 | containSubset(obj: object): Assertion; 11 | } 12 | } 13 | 14 | declare module "chai-subset" { 15 | function chaiSubset(chai: any, utils: any): void; 16 | namespace chaiSubset {} 17 | export = chaiSubset; 18 | } 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-boilerplate", 3 | "version": "0.0.1", 4 | "description": "Generic project boilerplate for TypeScript", 5 | "author": "Amir Arad (amira@wix.com)", 6 | "license": "MIT", 7 | "bugs": { 8 | "url": "https://github.com/wix/typescript-boilerplate/issues" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git@github.com:wix/typescript-boilerplate.git" 13 | }, 14 | "files": [ 15 | "dist/src" 16 | ], 17 | "testGlob": "./dist/test/**/*.spec.js?(x)", 18 | "homepage": "https://github.com/wix/typescript-boilerplate#readme", 19 | "keywords": [ 20 | "typescript", 21 | "tdd" 22 | ], 23 | "main": "dist/src/index.js", 24 | "browser": "dist/src/index.js", 25 | "scripts": { 26 | "build": "tsc -d", 27 | "bundle": "webpack --progress", 28 | "reset": "rimraf node_modules dist && yarn install && yarn test", 29 | "test": "run-s test:node test:browser:run", 30 | "test:node": "mocha --require ts-node/register/transpile-only --require test/setup.ts --require source-map-support/register test/*.spec.ts", 31 | "test:browser:run": "run-p -r start test:browser:run2", 32 | "test:browser:run2": "run-s start:wait test:browser", 33 | "test:browser": "cross-var mocha-headless-chrome $HL_CHROME_PARAMS -t 30000 -f http://localhost:8080/test.bundle", 34 | "start": "tsc-watch --onFirstSuccess \"webpack-dev-server --no-info\"", 35 | "start:wait": "wait-on -t 30000 http://localhost:8080/test.bundle" 36 | }, 37 | "devDependencies": { 38 | "@types/chai": "~4.1.2", 39 | "@types/mocha": "~5.2.0", 40 | "@types/sinon": "~5.0.1", 41 | "@types/sinon-chai": "~3.2.0", 42 | "chai": "~4.1.0", 43 | "chai-subset": "~1.6.0", 44 | "cross-var": "^1.1.0", 45 | "glob": "~7.1.2", 46 | "mocha": "~5.2.0", 47 | "mocha-env-reporter": "~3.0.0", 48 | "mocha-headless-chrome": "^2.0.0", 49 | "mocha-loader": "~1.1.1", 50 | "npm-run-all": "^4.1.2", 51 | "rimraf": "~2.6.1", 52 | "sinon": "~6.0.1", 53 | "sinon-chai": "~3.2.0", 54 | "source-map-support": "~0.5.0", 55 | "ts-node": "^7.0.0", 56 | "tsc-watch-2": "^0.0.2", 57 | "tslib": "~1.9.0", 58 | "typescript": "~2.9.1", 59 | "wait-on": "^2.1.0", 60 | "webpack": "~4.12.0", 61 | "webpack-cli": "^3.0.3", 62 | "webpack-dev-server": "~3.1.1" 63 | }, 64 | "peerDependencies": { 65 | "tslib": "~1.7.1" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/greeter.ts: -------------------------------------------------------------------------------- 1 | 2 | export function hello(name:string){ 3 | return "Hello "+name; 4 | } 5 | 6 | export function helloObj(name:string, greet:(a:string)=>string){ 7 | return {name, greet:greet(name)}; 8 | } 9 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | 2 | export * from './greeter'; 3 | -------------------------------------------------------------------------------- /test/greeter.spec.ts: -------------------------------------------------------------------------------- 1 | import {hello, helloObj} from "../src/greeter"; 2 | import {expect} from 'chai'; 3 | import * as sinon from 'sinon'; 4 | 5 | 6 | const NAME = 'Joe'; 7 | const PLACEHOLDER = 'placeholder'; 8 | 9 | 10 | describe('greeter', () => { 11 | // simple unit test 12 | it('greets', ()=>{ 13 | expect(hello(NAME).toLowerCase()).to.equal('hello joe'); 14 | }); 15 | 16 | //more complex unit test 17 | it('produces object', ()=>{ 18 | const mySpy = sinon.spy(()=>PLACEHOLDER); 19 | const result = helloObj(NAME, mySpy); 20 | 21 | expect(mySpy).to.have.callCount(1); 22 | expect(mySpy).to.have.been.calledWith(NAME); 23 | expect(result).to.containSubset({greet:PLACEHOLDER}); 24 | }); 25 | 26 | // integration test 27 | it('object works with provided greeter', ()=>{ 28 | expect(helloObj(NAME, hello)).to.containSubset({greet:'Hello Joe'}); 29 | }); 30 | 31 | }); 32 | -------------------------------------------------------------------------------- /test/setup.ts: -------------------------------------------------------------------------------- 1 | import * as chai from 'chai'; 2 | import * as sinonChai from 'sinon-chai'; 3 | import * as chaiSubset from 'chai-subset'; 4 | 5 | chai.use(sinonChai); 6 | chai.use(chaiSubset); 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": [ 4 | "es2015" 5 | ], 6 | "module": "commonjs", 7 | "target": "es5", 8 | "strict": true, 9 | "importHelpers": true, 10 | "noImplicitReturns": true, 11 | "strictNullChecks": true, 12 | "sourceMap": true, 13 | "outDir": "dist", 14 | "typeRoots": [ 15 | "./node_modules/@types", 16 | "./custom-typings" 17 | ] 18 | }, 19 | "exclude": [ 20 | "node_modules", 21 | "dist" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const glob = require("glob"); 3 | const {testGlob} = require('./package.json'); 4 | const testFiles = glob.sync(testGlob); 5 | 6 | const distPath = path.join(__dirname, 'dist'); 7 | 8 | const testsSetup = [path.join(__dirname, 'dist', 'test', 'setup.js')]; 9 | module.exports = { 10 | mode: 'development', 11 | devtool: 'eval', 12 | entry: { 13 | test: testsSetup.concat(testFiles.map(fileName => `mocha-loader!${fileName}`)) 14 | }, 15 | output: { 16 | path: distPath, 17 | filename: '[name].bundle.js', 18 | libraryTarget: 'umd', 19 | pathinfo: true 20 | }, 21 | devServer: { 22 | contentBase: distPath, 23 | inline: true, 24 | hot: false 25 | }, 26 | module: { 27 | noParse: [/\.min\.js$/, /\.bundle\.js$/] 28 | } 29 | }; 30 | --------------------------------------------------------------------------------