├── index.ts ├── .gitignore ├── webpack.config.js ├── src ├── __tests__ │ └── index.test.tsx ├── components │ ├── App.tsx │ └── __tests__ │ │ └── App.test.tsx └── index.tsx ├── .vscode ├── extensions.json └── settings.json ├── .travis.yml ├── tslint.json ├── jest.setup.js ├── tsconfig.json ├── .babelrc ├── webpack.config.production.js ├── webpack.config.development.js ├── circle.yml ├── webpack.config.base.js ├── README.md └── package.json /index.ts: -------------------------------------------------------------------------------- 1 | import './src/index' 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | coverage 3 | dist 4 | node_modules 5 | npm-debug.log 6 | yarn-error.log 7 | .cache-loader 8 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const env = process.env.NODE_ENV || 'development' 2 | 3 | module.exports = require(`./webpack.config.${env}.js`) 4 | -------------------------------------------------------------------------------- /src/__tests__/index.test.tsx: -------------------------------------------------------------------------------- 1 | import { test } from './../index' 2 | 3 | describe('tests', () => { 4 | it('works!', () => { 5 | expect(test.isJestWorking).toEqual(true) 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "christian-kohler.path-intellisense", 4 | "christian-kohler.npm-intellisense", 5 | "dbaeumer.vscode-eslint", 6 | "eg2.tslint", 7 | "Orta.vscode-jest" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - "8.4.0" 5 | before_install: 6 | - curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.3.2 7 | - export PATH="$HOME/.yarn/bin:$PATH" 8 | install: 9 | - yarn install 10 | script: 11 | - yarn test 12 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint-config-standard", 4 | "tslint-react" 5 | ], 6 | "rules": { 7 | "no-unused-variable": [true, {"ignore-pattern": "^_"}], 8 | "jsx-no-multiline-js": false, 9 | "quotemark": [true, "single", "jsx-single"] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/components/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | 3 | interface Props { 4 | name: string 5 | } 6 | 7 | export class App extends Component { 8 | render () { 9 | return ( 10 |
11 | hi {this.props.name} from tsx! 12 |
13 | ) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /jest.setup.js: -------------------------------------------------------------------------------- 1 | const Adapter = require('enzyme-adapter-react-16') ; 2 | const Enzyme = require('enzyme'); 3 | 4 | Enzyme.configure({ 5 | adapter: new Adapter() 6 | }) 7 | 8 | // For async tests, catch all errors here so we don't have to try / catch 9 | // everywhere for safety 10 | process.on('unhandledRejection', (error) => { 11 | console.log(error) 12 | }) 13 | -------------------------------------------------------------------------------- /src/components/__tests__/App.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { App } from './../App' 3 | import { mount } from 'enzyme' 4 | 5 | describe('', () => { 6 | it('works with .tsx components', () => { 7 | const name = 'hello how are you' 8 | const wrapper = mount() 9 | expect(wrapper.text()).toContain(`hi ${name} from tsx!`) 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "tslint.autoFixOnSave": true, 3 | "files.exclude": { 4 | "**/.git": true, 5 | "**/.svn": true, 6 | "**/.hg": true, 7 | "**/.DS_Store": true, 8 | ".jest/": true, 9 | ".babel-cache/": true, 10 | "build": true 11 | }, 12 | "search.exclude": { 13 | "**/dist": true, 14 | "**/node_modules": true, 15 | ".jest/*": true 16 | }, 17 | "typescript.tsdk": "./node_modules/typescript/lib" 18 | } 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "allowSyntheticDefaultImports": true, 5 | "baseUrl": "./", 6 | "rootDir": "./", 7 | "jsx": "react", 8 | "module": "es2015", 9 | "target": "es2016", 10 | "noEmit": true, 11 | "noUnusedLocals": true, 12 | "noUnusedParameters": true, 13 | "pretty": true, 14 | "skipLibCheck": true, 15 | "sourceMap": true 16 | }, 17 | "exclude": ["node_modules", "**/node_modules/*"] 18 | } 19 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import ReactDOM from 'react-dom' 2 | import React from 'react' 3 | import { App } from './components/App' 4 | 5 | type BarProps = { 6 | a: string, 7 | b: string 8 | } 9 | 10 | type Foo = { 11 | name: string, 12 | foo (): string, 13 | bar (props: BarProps): number 14 | } 15 | 16 | const props: Foo = { 17 | name: 'world', 18 | foo: () => 'hi', 19 | bar: () => 2 20 | } 21 | 22 | ReactDOM.render(, document.body.appendChild(document.createElement('div'))) 23 | 24 | export const test = { 25 | isJestWorking: true 26 | } 27 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [[ 3 | "@babel/env", { 4 | "modules": false, 5 | "debug": true 6 | }], 7 | "@babel/stage-3", 8 | "@babel/react", 9 | "@babel/typescript" 10 | ], 11 | "plugins": [ 12 | ["module-resolver", { 13 | "extensions": [".js", ".jsx", ".ts", ".tsx"], 14 | "root": ["./src"] 15 | }] 16 | ], 17 | "env": { 18 | "test": { 19 | "presets": [[ 20 | "@babel/env", { 21 | "modules": "commonjs", 22 | "targets": { 23 | "node": "current" 24 | } 25 | }], 26 | "@babel/stage-3", 27 | "@babel/react", 28 | "@babel/typescript" 29 | ], 30 | "plugins": [ 31 | "transform-es2015-modules-commonjs" 32 | ] 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /webpack.config.production.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | const webpackFailPlugin = require('webpack-fail-plugin') 3 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin') 4 | 5 | const webpackConfig = require('./webpack.config.base.js') 6 | 7 | module.exports = function () { 8 | const myProdConfig = webpackConfig 9 | myProdConfig.output.filename = '[name].[hash].js' 10 | 11 | myProdConfig.plugins = myProdConfig.plugins.concat( 12 | new webpack.DefinePlugin({ 13 | 'process.env': { 14 | 'NODE_ENV': JSON.stringify('production') 15 | } 16 | }), 17 | new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: 'vendor.[hash].js' }), 18 | new UglifyJsPlugin({ 19 | uglifyOptions: { 20 | compress: { 21 | warnings: true 22 | } 23 | } 24 | }), 25 | webpackFailPlugin 26 | ) 27 | 28 | return myProdConfig 29 | } 30 | -------------------------------------------------------------------------------- /webpack.config.development.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | const ForkTsCheckerNotifierWebpackPlugin = require('fork-ts-checker-notifier-webpack-plugin') 3 | const webpackConfig = require('./webpack.config.base.js') 4 | 5 | module.exports = function () { 6 | const myDevConfig = webpackConfig 7 | myDevConfig.devtool = 'inline-source-map' 8 | 9 | myDevConfig.plugins = myDevConfig.plugins.concat( 10 | new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: 'vendor.js' }), 11 | new ForkTsCheckerNotifierWebpackPlugin({ title: 'Webpack build', excludeWarnings: true }) 12 | ) 13 | 14 | myDevConfig.devServer = { 15 | historyApiFallback: true, 16 | stats: 'errors-only', 17 | host: process.env.HOST, // Defaults to `localhost` 18 | port: process.env.PORT, // Defaults to 8080 19 | overlay: { 20 | errors: true, 21 | warnings: true 22 | } 23 | } 24 | return myDevConfig 25 | } 26 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | # specify the version you desire here 10 | - image: circleci/node:9-browsers 11 | 12 | # Specify service dependencies here if necessary 13 | # CircleCI maintains a library of pre-built images 14 | # documented at https://circleci.com/docs/2.0/circleci-images/ 15 | # - image: circleci/mongo:3.4.4 16 | 17 | working_directory: ~/repo 18 | 19 | steps: 20 | - checkout 21 | 22 | # Download and cache dependencies 23 | - restore_cache: 24 | keys: 25 | - v1-dependencies-{{ checksum "package.json" }} 26 | # fallback to using the latest cache if no exact match is found 27 | - v1-dependencies- 28 | 29 | - run: yarn install --production=false 30 | 31 | - save_cache: 32 | paths: 33 | - node_modules 34 | key: v1-dependencies-{{ checksum "package.json" }} 35 | 36 | # run tests! 37 | - run: yarn test 38 | 39 | 40 | -------------------------------------------------------------------------------- /webpack.config.base.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin') 3 | const HtmlWebpackPlugin = require('html-webpack-plugin') 4 | 5 | const packageJson = require('./package.json') 6 | const vendorDependencies = Object.keys(packageJson['dependencies']) 7 | 8 | const threadLoader = { 9 | loader: 'thread-loader', 10 | options: { 11 | // there should be 1 cpu for the fork-ts-checker-webpack-plugin 12 | workers: require('os').cpus().length - 1 13 | } 14 | } 15 | 16 | const babelLoader = { 17 | loader: 'babel-loader', 18 | options: { 19 | cacheDirectory: true, 20 | } 21 | } 22 | 23 | module.exports = { 24 | cache: true, 25 | entry: { 26 | main: './src/index.tsx', 27 | vendor: vendorDependencies 28 | }, 29 | output: { 30 | path: path.resolve(__dirname, './dist'), 31 | filename: '[name].js', 32 | chunkFilename: '[chunkhash].js' 33 | }, 34 | module: { 35 | rules: [{ 36 | test: /\.ts(x?)$/, 37 | exclude: /node_modules/, 38 | use: [ 39 | { loader: 'cache-loader' }, 40 | threadLoader, 41 | babelLoader 42 | ] 43 | }] 44 | }, 45 | plugins: [ 46 | new ForkTsCheckerWebpackPlugin({ 47 | checkSyntacticErrors: true, 48 | tslint: true, 49 | watch: ['./src'] // optional but improves performance (less stat calls) 50 | }), 51 | new HtmlWebpackPlugin({ 52 | title: 'Webpack demo' 53 | }) 54 | ], 55 | resolve: { 56 | // Add `.ts` and `.tsx` as a resolvable extension. 57 | extensions: ['.ts', '.tsx', '.js'] 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/damassi/babel-7-typescript-example.svg?branch=master)](https://travis-ci.org/damassi/babel-7-typescript-example) 2 | 3 | # TypeScript + Babel 7 + Webpack 4 | 5 | Example TypeScript project built on top of new Babel 7 features. Includes React 16, Jest and Enzyme (for tests). 6 | Bundeling is done via webpack. Typechecking and linting are done on seperate processes, so runs faster 7 | on modern multicore cpus. Also JS is always updated, even if Typechecking or linting throws errors. 8 | This is not the case for production builds. 9 | For development HMR is on per default, so the dev-experience is as smooth as possible. 10 | 11 | 12 | ## Installation 13 | 14 | ```sh 15 | git clone https://github.com/damassi/babel-7-typescript-example && cd babel-7-typescript-example 16 | yarn install 17 | ``` 18 | 19 | ## Development 20 | ```sh 21 | yarn start 22 | yarn test:watch 23 | ``` 24 | 25 | ## Production build 26 | ```sh 27 | yarn build 28 | ``` 29 | 30 | 31 | If using VSCode, make sure to install the recommended extensions. 32 | 33 | ## Example 34 | 35 | ```jsx 36 | // App.tsx 37 | import React, { Component } from 'react' 38 | 39 | interface Props { 40 | name: string 41 | } 42 | 43 | export const App extends Component { 44 | render () { 45 | return ( 46 |
47 | Hi {this.props.name} from .tsx! 48 |
49 | ) 50 | } 51 | } 52 | 53 | // index.ts 54 | import ReactDOM from 'react-dom/server' 55 | import { App } from './components/App' 56 | 57 | console.log(ReactDOM.renderToString()) 58 | ``` 59 | 60 | ```sh 61 | yarn build 62 | ``` 63 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-7-typescript-example", 3 | "version": "1.0.0", 4 | "description": "Example TypeScript project built on top of new Babel 7 features", 5 | "keywords": [ 6 | "babel", 7 | "babel 7", 8 | "typescript", 9 | "react", 10 | "example" 11 | ], 12 | "main": "./dist/index.js", 13 | "repository": "git@github.com:damassi/babel-7-typescript-example.git", 14 | "author": "Christopher Pappas ", 15 | "license": "MIT", 16 | "scripts": { 17 | "prebuild": "yarn clean", 18 | "build": "cross-env NODE_ENV=production webpack --config ./webpack.config.js --progress --profile --color --display-error-details --display-cached", 19 | "clean": "rimraf dist .cache-loader", 20 | "coverage": "cross-env NODE_ENV=test jest --coverage", 21 | "prepublishOnly": "yarn build", 22 | "start": "cross-env NODE_ENV=development webpack-dev-server", 23 | "test": "cross-env NODE_ENV=test jest", 24 | "test:watch": "yarn test -o --watch", 25 | "lint": "tslint \"src/**/*.ts\"" 26 | }, 27 | "jest": { 28 | "setupFiles": [ 29 | "./jest.setup.js" 30 | ], 31 | "moduleDirectories": [ 32 | "./node_modules", 33 | "./src" 34 | ], 35 | "moduleFileExtensions": [ 36 | "ts", 37 | "tsx", 38 | "js", 39 | "jsx" 40 | ], 41 | "transform": { 42 | "^.+\\.(js|jsx|ts|tsx)$": "typescript-babel-jest" 43 | }, 44 | "testMatch": [ 45 | "**/*.test.(ts|tsx|js)" 46 | ] 47 | }, 48 | "resolutions": { 49 | "babel-core": "^7.0.0-bridge.0" 50 | }, 51 | "devDependencies": { 52 | "@babel/cli": "^7.0.0-beta.40", 53 | "@babel/core": "^7.0.0-beta.40", 54 | "@babel/node": "^7.0.0-beta.40", 55 | "@babel/preset-env": "^7.0.0-beta.40", 56 | "@babel/preset-react": "^7.0.0-beta.40", 57 | "@babel/preset-stage-3": "^7.0.0-beta.40", 58 | "@babel/preset-typescript": "^7.0.0-beta.40", 59 | "@babel/register": "^7.0.0-beta.40", 60 | "@types/enzyme": "^3.1.9", 61 | "@types/jest": "^22.1.3", 62 | "@types/react": "^16.0.38", 63 | "@types/react-dom": "^16.0.4", 64 | "babel-loader": "8.0.0-beta.0", 65 | "babel-plugin-module-resolver": "^3.1.0", 66 | "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0", 67 | "cache-loader": "^1.2.0", 68 | "cross-env": "^5.1.3", 69 | "enzyme": "^3.3.0", 70 | "enzyme-adapter-react-16": "^1.1.1", 71 | "fork-ts-checker-notifier-webpack-plugin": "^0.2.0", 72 | "fork-ts-checker-webpack-plugin": "^0.3.0", 73 | "html-webpack-plugin": "^2.30.1", 74 | "jest": "22.4.0", 75 | "nodemon": "^1.15.1", 76 | "rimraf": "^2.6.2", 77 | "thread-loader": "^1.1.2", 78 | "tslint": "^5.9.1", 79 | "tslint-config-standard": "^7.0.0", 80 | "tslint-react": "^3.5.1", 81 | "typescript": "^2.7.2", 82 | "typescript-babel-jest": "^1.0.5", 83 | "uglifyjs-webpack-plugin": "^1.2.0", 84 | "webpack": "^3.11.0", 85 | "webpack-dev-server": "^2.11.1", 86 | "webpack-fail-plugin": "^2.0.0" 87 | }, 88 | "dependencies": { 89 | "react": "^16.2.0", 90 | "react-dom": "^16.2.0" 91 | }, 92 | "browserslist": [ 93 | "last 1 Chrome versions", 94 | "last 1 Firefox versions" 95 | ] 96 | } 97 | --------------------------------------------------------------------------------