├── .babelrc ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .stylelintrc ├── .travis.yml ├── LICENSE ├── README.md ├── config ├── main.js ├── types │ └── dev.d.ts └── webpack │ ├── dev.js │ ├── index.js │ ├── prod.js │ └── server.js ├── package-lock.json ├── package.json ├── src ├── app │ ├── components │ │ ├── Header │ │ │ ├── index.tsx │ │ │ └── style.css │ │ └── index.tsx │ ├── containers │ │ ├── App │ │ │ ├── index.tsx │ │ │ └── style.css │ │ ├── Cube │ │ │ ├── index.tsx │ │ │ └── style.css │ │ ├── Sphere │ │ │ ├── index.tsx │ │ │ └── style.css │ │ └── index.tsx │ ├── helpers │ │ └── TestHelper.tsx │ ├── models │ │ └── world.ts │ ├── redux │ │ ├── IStore.ts │ │ ├── modules │ │ │ └── world │ │ │ │ ├── __tests__ │ │ │ │ └── index.spec.ts │ │ │ │ └── index.ts │ │ ├── reducers.ts │ │ └── store.ts │ └── routes │ │ └── index.tsx ├── client.tsx ├── favicon.ico ├── index.html ├── server.tsx └── vendor │ └── main.ts ├── tsconfig.json ├── tslint.json └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015" 4 | ], 5 | "env": { 6 | "test": { 7 | "plugins": ["transform-es2015-modules-commonjs"] 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | # Change these settings to your own preference 10 | indent_style = space 11 | indent_size = 2 12 | 13 | # We recommend you to keep these unchanged 14 | end_of_line = lf 15 | charset = utf-8 16 | trim_trailing_whitespace = true 17 | insert_final_newline = true 18 | 19 | [*.md] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | 3 | # 4 | ## These files are binary and should be left untouched 5 | # 6 | 7 | # (binary is a macro for -text -diff) 8 | *.png binary 9 | *.jpg binary 10 | *.jpeg binary 11 | *.gif binary 12 | *.ico binary 13 | *.mov binary 14 | *.mp4 binary 15 | *.mp3 binary 16 | *.flv binary 17 | *.fla binary 18 | *.swf binary 19 | *.gz binary 20 | *.zip binary 21 | *.7z binary 22 | *.ttf binary 23 | *.eot binary 24 | *.woff binary 25 | *.pyc binary 26 | *.pdf binary 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folder 2 | build 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 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 | # OS X 36 | .DS_STORE 37 | 38 | # TypeScript 39 | typings 40 | 41 | # JetBrains 42 | .idea 43 | .awcache 44 | -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "block-no-empty": null, 4 | "color-no-invalid-hex": true, 5 | "declaration-colon-space-after": "always", 6 | "indentation": [2, { 7 | "except": ["value"] 8 | }], 9 | "max-empty-lines": 2, 10 | "unit-whitelist": ["px", em", "rem", "%", "s"] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "6" 5 | 6 | env: 7 | - NODE_ENV=ci 8 | 9 | install: 10 | - npm install 11 | 12 | before_script: 13 | - "export DISPLAY=:99.0" 14 | - "sh -e /etc/init.d/xvfb start" 15 | 16 | script: 17 | - npm test 18 | - npm run lint 19 | - npm run build 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2017 Hirako 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WhiteStormJS - React/Redux - three.js - TypeScript boilerplate 2 | [![Build Status](https://travis-ci.org/WhitestormJS/whitestorm-typescript-boilerplate.svg?branch=master)](https://travis-ci.org/WhitestormJS/whitestorm-typescript-boilerplate) 3 | [![Dependency Status](https://david-dm.org/whitestormJS/whitestorm-typescript-boilerplate.svg)](https://david-dm.org/whitestormJS/whitestorm-typescript-boilerplate) 4 | [![devDependency Status](https://david-dm.org/whitestormJS/whitestorm-typescript-boilerplate/dev-status.svg)](https://david-dm.org/whitestormJS/whitestorm-typescript-boilerplate#info=devDependencies) 5 | [![license](https://img.shields.io/github/license/mashape/apistatus.svg?maxAge=2592000)](https://github.com/hirako2000/polypack/blob/master/LICENSE) 6 | 7 | **WhiteStorm TypeScript Boilerplate** is a starter kit for crafting 3D applications using modern technologies: 8 | - [TypeScript](https://www.typescriptlang.org/) 9 | - [WhiteStormJS](https://whs.io) 10 | - [three.js](https://threejs.org/) 11 | - [React](https://github.com/facebook/react) & [Redux](https://github.com/reactjs/redux) 12 | 13 | ## [Video](https://streamable.com/nvvlv) 14 | 15 | [![](http://i.imgur.com/QUPK7Ej.jpg)](https://streamable.com/nvvlv) 16 | 17 | ## Dynamic updates - Redux 18 | 19 | ![](http://i.imgur.com/JQoXKp1.png) 20 | 21 | ## Libraries 22 | This starter kit also uses the following libraries and tools: 23 | 24 | #### Core 25 | - [React DOM](https://github.com/facebook/react) for views. 26 | - [React Router](https://github.com/reactjs/react-router) to handle in-app routing. 27 | - [React-Redux](https://github.com/reactjs/react-redux) to use React-Redux bindings. 28 | - [React-Router-Redux](https://github.com/reactjs/react-router-redux) to keep application state sync with route changes. 29 | 30 | #### Utilities 31 | - [Redux Thunk](https://github.com/gaearon/redux-thunk) for dispatching async actions. 32 | - [Redux Connect](https://github.com/makeomatic/redux-connect) for resolving async props in react-router. 33 | - [React Helmet](https://github.com/nfl/react-helmet) 34 | - [classnames](https://github.com/JedWatson/classnames) 35 | 36 | #### Build System 37 | - [Webpack](https://github.com/webpack/webpack) for bundling. 38 | - [Awesome TypeScript Loader](https://github.com/s-panferov/awesome-typescript-loader) as ts loader. 39 | - [Babel Loader](https://github.com/babel/babel-loader) as js loader. 40 | - [React Hot Loader](https://github.com/gaearon/react-hot-loader) for providing hot reload capability to our development server 41 | - [Style Loader](https://github.com/webpack/style-loader) 42 | - [CSS Loader](https://github.com/webpack/css-loader) 43 | - [PostCSS Loader](https://github.com/postcss/postcss) 44 | - [PostCSS cssnext](https://github.com/MoOx/postcss-cssnext) 45 | - [PostCSS Assets](https://github.com/assetsjs/postcss-assets) 46 | - [JSON Loader](https://github.com/webpack/json-loader) 47 | - [File Loader](https://github.com/webpack/file-loader) 48 | - [URL Loader](https://github.com/webpack/url-loader) 49 | - [Sourcemap Loader](https://github.com/webpack/source-map-loader) 50 | - [Manifest Plugin](https://github.com/danethurber/webpack-manifest-plugin) 51 | - [Extract Text Plugin](https://github.com/webpack/extract-text-webpack-plugin) for exporting bundled css. 52 | - [tslint Loader](https://github.com/wbuchwalter/tslint-loader) for using tslint as preloader on build process. 53 | - [stylelint Loader](https://github.com/adrianhall/stylelint-loader) for using stylelint as preloader on build process. 54 | 55 | #### Dev & Prod Server 56 | - [Webpack Dev Server](https://github.com/webpack/webpack-dev-server) 57 | - [Webpack Dev Middleware](https://github.com/webpack/webpack-dev-middleware) 58 | - [Webpack Hot Middleware](https://github.com/webpack/webpack-hot-middleware) 59 | - [Express](https://github.com/expressjs/express) for running server both on client and server side. 60 | - [Compression](https://github.com/expressjs/compression) for gzip compression 61 | - [Serve Favicon](https://github.com/expressjs/serve-favicon) for serving favicon. 62 | 63 | #### Developer Experience 64 | - [Typings](https://github.com/typings/typings) for installing type definitions of external libraries. 65 | - [tslint](https://github.com/palantir/tslint) for linting TypeScript files. 66 | - [stylelint](https://github.com/stylelint/stylelint) for linting styles. 67 | - [Redux Logger](https://github.com/theaqua/redux-logger) 68 | - [Redux DevTools](https://github.com/gaearon/redux-devtools) 69 | - [Chalk](https://github.com/chalk/chalk) for colored terminal logs. 70 | 71 | #### Testing 72 | - [Jest](https://facebook.github.io/jest/) for unit testing. 73 | - [Fetch Mock](https://github.com/wheresrhys/fetch-mock) for testing async actions. 74 | - [Redux Mock Store](https://github.com/arnaudbenard/redux-mock-store) for creating mock stores. 75 | 76 | ## Directory Structure 77 | ```bash 78 | . 79 | ├── build # Built, ready to serve app. 80 | ├── config # Root folder for configurations. 81 | │ ├── types # Global type definitions, written by us. 82 | │ ├── webpack # Webpack configurations. 83 | │ └── main.ts # Generic App configurations. 84 | ├── node_modules # Node Packages. 85 | ├── src # Source code. 86 | │ ├── app # App folder. 87 | │ │ ├── components # React Components. 88 | │ │ ├── containers # React/Redux Containers. 89 | │ │ ├── helpers # Helper Functions & Components. 90 | │ │ ├── redux # Redux related code aka data layer of the app. 91 | │ │ │ ├── modules # Redux modules. 92 | │ │ │ ├── reducers.ts # Main reducers file to combine them. 93 | │ │ │ └── store.ts # Redux store, contains global app state. 94 | │ │ └── routes.tsx # Routes. 95 | │ ├── client.tsx # Entry point for client side rendering. 96 | │ ├── index.html # root page template 97 | │ └── server.tsx # Entry point for server of static content. 98 | ├── typings # Type definitions installed with typings. 99 | ├── .gitignore # Tells git which files to ignore. 100 | ├── .stylelintrc # Configures stylelint. 101 | ├── Dockerfile # Dockerfile. 102 | ├── favicon.ico # Favicon. 103 | ├── package.json # Package configuration. 104 | ├── README.md # This file 105 | ├── tsconfig.json # TypeScript transpiler configuration. 106 | ├── tslint.json # Configures tslint. 107 | └── typings.json # Typings package configuration. 108 | ``` 109 | 110 | ## Installation 111 | 112 | You can clone from this repository or [install the latest version](https://github.com/whitestormJS/whitestorm-typescript-boilerplate/releases) as a zip file. 113 | 114 | ```bash 115 | $ git clone https://github.com/whitestormJS/whitestorm-typescript-boilerplate 116 | $ cd whitestorm-typescript-boilerplate 117 | $ npm install 118 | ``` 119 | 120 | ## Usage 121 | 122 | All commands defaults to development environment. You can set `NODE_ENV` to `production` or use the shortcuts below. 123 | 124 | ```bash 125 | # Running 126 | 127 | $ npm start # This starts the app in development mode 128 | 129 | # Starting it with the production build 130 | $ NODE_ENV=production npm start # or 131 | $ npm run start:prod 132 | 133 | # Building 134 | 135 | $ npm build # This builds the app in development mode 136 | 137 | # Commands below builds the production build 138 | $ NODE_ENV=production npm build # or 139 | $ npm run build:prod 140 | 141 | # Testing 142 | $ npm test 143 | ``` 144 | 145 | ## Notes 146 | ```bash 147 | # If you want install additional libraries, you can also install their typings from DefinitelyTyped 148 | $ typings install dt~ --global --save 149 | # or if it's located on npm 150 | $ typings install --save-dev 151 | ``` 152 | 153 | ## Credits 154 | 155 | [MIT license](LICENSE). 156 | -------------------------------------------------------------------------------- /config/main.js: -------------------------------------------------------------------------------- 1 | /** General Configurations Like PORT, HOST names and etc... */ 2 | 3 | var config = { 4 | env: process.env.NODE_ENV || 'development', 5 | host: process.env.HOST || 'localhost', 6 | port: process.env.PORT || 8889, 7 | karmaPort: 9876, 8 | 9 | // This part goes to React-Helmet for Head of our HTML 10 | app: { 11 | head: { 12 | title: 'WhiteStormJS - Typescript', 13 | titleTemplate: 'whs-typescript: %s', 14 | meta: [ 15 | { charset: 'utf-8' }, 16 | { 'http-equiv': 'x-ua-compatible', content: 'ie=edge' }, 17 | { name: 'viewport', content: 'width=device-width, initial-scale=1' }, 18 | { name: 'description', content: 'WhiteStormJS React Redux Typescript' }, 19 | ] 20 | } 21 | } 22 | }; 23 | 24 | module.exports = config; 25 | -------------------------------------------------------------------------------- /config/types/dev.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Type declerations for global development variables 3 | */ 4 | 5 | interface Window { 6 | // A hack for the Redux DevTools Chrome extension. 7 | __REDUX_DEVTOOLS_EXTENSION_COMPOSE__?: (f: F) => F; 8 | __INITIAL_STATE__?: any; 9 | } 10 | 11 | interface ObjectConstructor { 12 | assign(target: any, ...sources: any[]): any; 13 | } 14 | -------------------------------------------------------------------------------- /config/webpack/dev.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var path = require('path'); 3 | var webpack = require('webpack'); 4 | var postcssAssets = require('postcss-assets'); 5 | var postcssNext = require('postcss-cssnext'); 6 | var stylelint = require('stylelint'); 7 | var ManifestPlugin = require('webpack-manifest-plugin'); 8 | var CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin; 9 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 10 | 11 | var config = { 12 | // Enable sourcemaps for debugging webpack's output. 13 | devtool: 'source-map', 14 | 15 | resolve: { 16 | extensions: ['.ts', '.tsx', '.js', '.jsx'], 17 | modules: [path.resolve(__dirname), 'node_modules', 'app', 'app/redux'], 18 | }, 19 | 20 | entry: { 21 | app: [ 22 | 'webpack-hot-middleware/client?reload=true', 23 | './src/client.tsx', 24 | './src/vendor/main.ts' 25 | ] 26 | }, 27 | 28 | output: { 29 | path: path.resolve('./build/public'), 30 | publicPath: '/public/', 31 | filename: 'js/[name].js', 32 | pathinfo: true 33 | }, 34 | 35 | module: { 36 | rules: [{ 37 | enforce: 'pre', 38 | test: /\.tsx?$/, 39 | loader: 'tslint-loader' 40 | }, 41 | { 42 | test: /\.tsx?$/, 43 | loader: 'react-hot-loader/webpack!awesome-typescript-loader' 44 | }, 45 | { 46 | test: /\.jsx$/, 47 | loader: 'babel-loader', 48 | exclude: [ 49 | /node_modules/ 50 | ], 51 | query: { 52 | plugins: [ 53 | 'add-module-exports', 54 | 'transform-decorators-legacy', 55 | 'transform-class-properties', 56 | 'transform-object-rest-spread', 57 | ['transform-runtime', {helpers: false, polyfill: false, regenerator: true}] 58 | ] 59 | } 60 | }, 61 | { 62 | test: /\.json$/, 63 | loader: 'json-loader' 64 | }, 65 | { 66 | test: /\.css$/, 67 | include: path.resolve('./src/app'), 68 | loaders: [ 69 | 'style-loader', 70 | 'css-loader?modules&importLoaders=2&localIdentName=[local]___[hash:base64:5]', 71 | 'postcss-loader' 72 | ] 73 | }, 74 | { 75 | test: /\.css$/, 76 | exclude: path.resolve('./src/app'), 77 | loaders: [ 78 | 'style-loader', 79 | 'css-loader' 80 | ] 81 | }, 82 | 83 | { 84 | test: /\.eot(\?.*)?$/, 85 | loader: 'file-loader?name=fonts/[hash].[ext]' 86 | }, 87 | { 88 | test: /\.(woff|woff2)(\?.*)?$/, 89 | loader: 'file-loader?name=fonts/[hash].[ext]' 90 | }, 91 | { 92 | test: /\.ttf(\?.*)?$/, 93 | loader: 'url-loader?limit=10000&mimetype=application/octet-stream&name=fonts/[hash].[ext]' 94 | }, 95 | { 96 | test: /\.svg(\?.*)?$/, 97 | loader: 'url-loader?limit=10000&mimetype=image/svg+xml&name=fonts/[hash].[ext]' 98 | }, 99 | { 100 | test: /\.(jpe?g|png|gif)$/i, 101 | loader: 'url-loader?limit=1000&name=images/[hash].[ext]' 102 | } 103 | ] 104 | }, 105 | 106 | plugins: [ 107 | new CheckerPlugin(), 108 | new webpack.LoaderOptionsPlugin({ 109 | debug: true, 110 | options: { 111 | tslint: { 112 | failOnHint: true 113 | }, 114 | postcss: function () { 115 | return [ 116 | stylelint({ 117 | files: '../../src/app/*.css' 118 | }), 119 | postcssNext(), 120 | postcssAssets({ 121 | relative: true 122 | }), 123 | ]; 124 | }, 125 | } 126 | }), 127 | new ManifestPlugin({ 128 | fileName: '../manifest.json' 129 | }), 130 | new webpack.DefinePlugin({ 131 | 'process.env': { 132 | BROWSER: JSON.stringify(true), 133 | NODE_ENV: JSON.stringify('development') 134 | } 135 | }), 136 | new webpack.HotModuleReplacementPlugin(), 137 | new webpack.NoEmitOnErrorsPlugin(), 138 | new HtmlWebpackPlugin({ 139 | title: 'WhitestormJS TypeScript', 140 | filename: 'index.html', 141 | template: 'src/index.html' 142 | }) 143 | ] 144 | }; 145 | 146 | const copySync = (src, dest, overwrite) => { 147 | if (overwrite && fs.existsSync(dest)) { 148 | fs.unlinkSync(dest); 149 | } 150 | const data = fs.readFileSync(src); 151 | fs.writeFileSync(dest, data); 152 | } 153 | 154 | const createIfDoesntExist = dest => { 155 | if (!fs.existsSync(dest)) { 156 | fs.mkdirSync(dest); 157 | } 158 | } 159 | 160 | createIfDoesntExist('./build'); 161 | createIfDoesntExist('./build/public'); 162 | copySync('./src/favicon.ico', './build/public/favicon.ico', true); 163 | 164 | module.exports = config; 165 | -------------------------------------------------------------------------------- /config/webpack/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | if (process.env.NODE_ENV === 'production') { 4 | module.exports = require('./prod'); 5 | } else { 6 | module.exports = require('./dev'); 7 | } 8 | -------------------------------------------------------------------------------- /config/webpack/prod.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var path = require('path'); 3 | var webpack = require('webpack'); 4 | var postcssAssets = require('postcss-assets'); 5 | var postcssNext = require('postcss-cssnext'); 6 | var stylelint = require('stylelint'); 7 | var ManifestPlugin = require('webpack-manifest-plugin'); 8 | var ExtractTextPlugin = require('extract-text-webpack-plugin'); 9 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 10 | 11 | var config = { 12 | bail: true, 13 | 14 | resolve: { 15 | extensions: ['.ts', '.tsx', '.js', '.jsx'], 16 | modules: [path.resolve(__dirname), 'node_modules', 'app', 'app/redux'], 17 | }, 18 | 19 | entry: { 20 | app: './src/client.tsx', 21 | vendor: [ 22 | './src/vendor/main.ts', 23 | 'react', 24 | 'react-dom', 25 | 'react-router', 26 | 'react-helmet', 27 | 'react-redux', 28 | 'react-router-redux', 29 | 'redux', 30 | 'redux-connect', 31 | 'redux-thunk' 32 | ] 33 | }, 34 | 35 | output: { 36 | path: path.resolve('./build/public'), 37 | publicPath: '/public/', 38 | filename: 'js/[name].js' 39 | }, 40 | 41 | module: { 42 | rules: [{ 43 | enforce: 'pre', 44 | test: /\.tsx?$/, 45 | loader: 'tslint-loader' 46 | }, 47 | { 48 | test: /\.tsx?$/, 49 | loader: 'react-hot-loader/webpack!awesome-typescript-loader' 50 | }, 51 | { 52 | test: /\.jsx$/, 53 | loader: 'babel-loader' 54 | }, 55 | { 56 | test: /\.json$/, 57 | loader: 'json-loader' 58 | }, 59 | { 60 | test: /\.css$/, 61 | include: path.resolve('./src/app'), 62 | loader: ExtractTextPlugin.extract({ 63 | fallbackLoader: 'style-loader', 64 | loader: [ 65 | 'css-loader?modules&importLoaders=2&localIdentName=[local]___[hash:base64:5]', 66 | 'postcss-loader' 67 | ] 68 | }) 69 | }, 70 | { 71 | test: /\.css$/, 72 | exclude: path.resolve('./src/app'), 73 | loader: ExtractTextPlugin.extract({ 74 | fallbackLoader: 'style-loader', 75 | loader: [ 76 | 'css-loader', 77 | ] 78 | }) 79 | }, 80 | { 81 | test: /\.eot(\?.*)?$/, 82 | loader: 'file-loader?name=fonts/[hash].[ext]' 83 | }, 84 | { 85 | test: /\.(woff|woff2)(\?.*)?$/, 86 | loader: 'file-loader?name=fonts/[hash].[ext]' 87 | }, 88 | { 89 | test: /\.ttf(\?.*)?$/, 90 | loader: 'url-loader?limit=10000&mimetype=application/octet-stream&name=fonts/[hash].[ext]' 91 | }, 92 | { 93 | test: /\.svg(\?.*)?$/, 94 | loader: 'url-loader?limit=10000&mimetype=image/svg+xml&name=fonts/[hash].[ext]' 95 | }, 96 | { 97 | test: /\.(jpe?g|png|gif)$/i, 98 | loader: 'url-loader?limit=1000&name=images/[hash].[ext]' 99 | } 100 | ] 101 | }, 102 | 103 | plugins: [ 104 | new webpack.LoaderOptionsPlugin({ 105 | debug: true, 106 | options: { 107 | tslint: { 108 | failOnHint: true 109 | }, 110 | postcss: function () { 111 | return [ 112 | stylelint({ 113 | files: '../../src/app/*.css' 114 | }), 115 | postcssNext(), 116 | postcssAssets({ 117 | relative: true 118 | }), 119 | ]; 120 | }, 121 | } 122 | }), 123 | new webpack.optimize.OccurrenceOrderPlugin(), 124 | new webpack.optimize.CommonsChunkPlugin({ 125 | name: 'vendor', 126 | filename: 'js/[name].[chunkhash].js', 127 | minChunks: Infinity 128 | }), 129 | new webpack.optimize.UglifyJsPlugin({ 130 | compress: { 131 | warnings: false 132 | } 133 | }), 134 | new ExtractTextPlugin('css/[name].[hash].css'), 135 | new ManifestPlugin({ 136 | fileName: '../manifest.json' 137 | }), 138 | new webpack.DefinePlugin({ 139 | 'process.env': { 140 | BROWSER: JSON.stringify(true), 141 | NODE_ENV: JSON.stringify('production') 142 | } 143 | }), 144 | new HtmlWebpackPlugin({ 145 | title: 'WhitestormJS TypeScript', 146 | filename: 'index.html', 147 | template: 'src/index.html' 148 | }) 149 | ] 150 | }; 151 | 152 | const copySync = (src, dest, overwrite) => { 153 | if (overwrite && fs.existsSync(dest)) { 154 | fs.unlinkSync(dest); 155 | } 156 | const data = fs.readFileSync(src); 157 | fs.writeFileSync(dest, data); 158 | } 159 | 160 | const createIfDoesntExist = dest => { 161 | if (!fs.existsSync(dest)) { 162 | fs.mkdirSync(dest); 163 | } 164 | } 165 | 166 | createIfDoesntExist('./build'); 167 | createIfDoesntExist('./build/public'); 168 | copySync('./src/favicon.ico', './build/public/favicon.ico', true); 169 | 170 | module.exports = config; 171 | -------------------------------------------------------------------------------- /config/webpack/server.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var fs = require('fs'); 3 | var webpack = require('webpack'); 4 | var postcssAssets = require('postcss-assets'); 5 | var postcssNext = require('postcss-cssnext'); 6 | var stylelint = require('stylelint'); 7 | 8 | var nodeModules = {}; 9 | fs.readdirSync('node_modules') 10 | .filter(function (x) { 11 | return ['.bin'].indexOf(x) === -1; 12 | }) 13 | .forEach(function (mod) { 14 | nodeModules[mod] = 'commonjs ' + mod; 15 | }); 16 | 17 | var config = { 18 | externals: nodeModules, 19 | target: 'node', 20 | 21 | resolve: { 22 | extensions: ['.ts', '.tsx', '.js', '.jsx'], 23 | modules: [path.resolve(__dirname), 'node_modules', 'app', 'app/redux'], 24 | }, 25 | 26 | entry: './src/server.tsx', 27 | 28 | output: { 29 | path: path.resolve('./build/public'), 30 | filename: '../server.js', 31 | publicPath: '/public/', 32 | libraryTarget: 'commonjs2' 33 | }, 34 | 35 | module: { 36 | loaders: [{ 37 | test: /\.(jpe?g|png|gif)$/i, 38 | loader: 'url-loader?limit=1000&name=images/[hash].[ext]' 39 | }, 40 | { 41 | test: /\.json$/, 42 | loader: 'json-loader' 43 | }, 44 | { 45 | test: /\.jsx$/, 46 | loader: 'babel-loader' 47 | }, 48 | { 49 | test: /\.tsx?$/, 50 | loader: 'awesome-typescript-loader', 51 | exclude: /node_modules/ 52 | }, 53 | { 54 | test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, 55 | loader: "url-loader?limit=10000&mimetype=application/font-woff" 56 | }, 57 | { 58 | test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, 59 | loader: "file-loader" 60 | }, 61 | { 62 | test: /\.css$/, 63 | loaders: [ 64 | 'isomorphic-style-loader', 65 | 'css-loader?modules&importLoaders=2&localIdentName=[local]___[hash:base64:5]' 66 | ] 67 | } 68 | ] 69 | }, 70 | 71 | plugins: [ 72 | new webpack.LoaderOptionsPlugin({ 73 | debug: false, 74 | options: { 75 | postcss: function () { 76 | return [ 77 | postcssNext(), 78 | postcssAssets({ 79 | relative: true 80 | }), 81 | ]; 82 | }, 83 | } 84 | }) 85 | ], 86 | 87 | node: { 88 | console: false, 89 | global: false, 90 | process: false, 91 | Buffer: false, 92 | __filename: false, 93 | __dirname: false 94 | } 95 | }; 96 | 97 | const copySync = (src, dest, overwrite) => { 98 | if (overwrite && fs.existsSync(dest)) { 99 | fs.unlinkSync(dest); 100 | } 101 | const data = fs.readFileSync(src); 102 | fs.writeFileSync(dest, data); 103 | } 104 | 105 | const createIfDoesntExist = dest => { 106 | if (!fs.existsSync(dest)) { 107 | fs.mkdirSync(dest); 108 | } 109 | } 110 | 111 | createIfDoesntExist('./build'); 112 | createIfDoesntExist('./build/public'); 113 | copySync('./src/favicon.ico', './build/public/favicon.ico', true); 114 | 115 | module.exports = config; 116 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "whitestorm-typescript-boilerplate", 3 | "version": "0.2.5", 4 | "description": "A TypeScript boilerplate for building web applications with WhiteStormJS", 5 | "main": "build/server.js", 6 | "scripts": { 7 | "clean": "rimraf build", 8 | "prebuild": "npm run clean", 9 | "build": "webpack --config config/webpack/index.js", 10 | "build:prod": "cross-env NODE_ENV=production npm run build", 11 | "postbuild": "webpack --config config/webpack/server.js", 12 | "prestart": "npm run build", 13 | "start": "node build/server.js", 14 | "start:prod": "cross-env NODE_ENV=production npm start", 15 | "test": "jest", 16 | "lint": "tslint \"src/**/**.ts*\"" 17 | }, 18 | "author": "Hirako2000 ", 19 | "homepage": "https://github.com/WhiteStormJS/whitestorm-typescript-boilerplate.git", 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/WhiteStormJS/whitestorm-typescript-boilerplate.git" 23 | }, 24 | "bugs": { 25 | "url": "https://github.com/WhiteStormJS/whitestorm-typescript-boilerplate/issues" 26 | }, 27 | "keywords": [ 28 | "whitestorm", 29 | "three.js", 30 | "typescript", 31 | "webpack", 32 | "starter", 33 | "kit", 34 | "boilerplate", 35 | "hot reload", 36 | "react-hot-loader" 37 | ], 38 | "license": "MIT", 39 | "jest": { 40 | "transform": { 41 | ".(ts|tsx)": "/node_modules/ts-jest/preprocessor.js" 42 | }, 43 | "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$", 44 | "moduleDirectories": [ 45 | "node_modules", 46 | "app", 47 | "app/redux" 48 | ], 49 | "moduleFileExtensions": [ 50 | "ts", 51 | "tsx", 52 | "js" 53 | ] 54 | }, 55 | "devDependencies": { 56 | "@types/base16": "^1.0.1", 57 | "@types/classnames": "^2.2.3", 58 | "@types/history": "^4.5.0", 59 | "@types/isomorphic-fetch": "0.0.34", 60 | "@types/jest": "^22.2.2", 61 | "@types/node": "^7.0.0", 62 | "@types/react": "^16.0.34", 63 | "@types/react-dom": "^16.0.3", 64 | "@types/react-helmet": "^5.0.3", 65 | "@types/react-redux": "^5.0.14", 66 | "@types/react-router": "^4.0.26", 67 | "@types/react-router-redux": "^5.0.15", 68 | "@types/redux-devtools": "^3.0.36", 69 | "@types/redux-devtools-dock-monitor": "^1.1.29", 70 | "@types/redux-devtools-log-monitor": "^1.0.29", 71 | "@types/redux-mock-store": "0.0.13", 72 | "@types/sinon": "^4.3.1", 73 | "@types/source-map": "^0.5.0", 74 | "@types/tapable": "^0.2.5", 75 | "@types/three": "^0.84.16", 76 | "@types/uglify-js": "^2.6.28", 77 | "@types/webpack": "^2.2.2", 78 | "awesome-typescript-loader": "^3.0.0", 79 | "babel-core": "^6.26.3", 80 | "babel-loader": "^6.2.10", 81 | "babel-plugin-transform-decorators-legacy": "^1.3.5", 82 | "babel-plugin-transform-runtime": "^6.23.0", 83 | "babel-preset-es2015": "^6.22.0", 84 | "classnames": "^2.2.6", 85 | "cross-env": "^5.1.6", 86 | "css-loader": "^0.28.0", 87 | "enzyme": "^3.3.0", 88 | "extract-text-webpack-plugin": "^2.1.2", 89 | "fetch-mock": "^6.3.0", 90 | "file-loader": "^1.1.11", 91 | "html-webpack-plugin": "^2.28.0", 92 | "isomorphic-style-loader": "^2.0.0", 93 | "istanbul-instrumenter-loader": "^2.0.0", 94 | "jest": "^22.4.3", 95 | "json-loader": "^0.5.4", 96 | "postcss-assets": "^4.1.0", 97 | "postcss-cssnext": "^2.9.0", 98 | "postcss-loader": "^1.2.2", 99 | "react-hot-loader": "^3.1.3", 100 | "redux-mock-store": "^1.5.1", 101 | "rimraf": "^2.6.2", 102 | "source-map-loader": "^0.2.1", 103 | "style-loader": "^0.20.3", 104 | "stylelint": "^7.7.1", 105 | "ts-jest": "^22.4.2", 106 | "tslint": "^5.9.1", 107 | "tslint-loader": "^3.3.0", 108 | "tslint-react": "^3.4.0", 109 | "typescript": "^2.9.1", 110 | "url-loader": "^1.0.1", 111 | "webpack": "^2.4.1", 112 | "webpack-dev-middleware": "^1.9.0", 113 | "webpack-dev-server": "^2.2.0", 114 | "webpack-hot-middleware": "^2.15.0", 115 | "webpack-manifest-plugin": "^1.1.0" 116 | }, 117 | "dependencies": { 118 | "chalk": "^2.3.2", 119 | "compression": "^1.7.2", 120 | "es6-promise": "^4.2.4", 121 | "express": "^4.16.3", 122 | "history": "^4.7.2", 123 | "isomorphic-fetch": "^2.2.1", 124 | "react": "^16.2.0", 125 | "react-css-modules": "^4.7.1", 126 | "react-dom": "^16.2.0", 127 | "react-helmet": "^5.2.0", 128 | "react-redux": "^5.0.6", 129 | "react-router": "^4.2.0", 130 | "react-router-config": "^1.0.0-beta.4", 131 | "react-router-dom": "^4.2.2", 132 | "react-router-redux": "^5.0.0-alpha.9", 133 | "redux": "^4.0.0", 134 | "redux-connect": "^7.0.0", 135 | "redux-logger": "^3.0.1", 136 | "redux-thunk": "^2.3.0", 137 | "serve-favicon": "^2.5.0", 138 | "whs": "2.1.9" 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/app/components/Header/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | 4 | const style = require('./style.css'); 5 | 6 | export const Header = () => ( 7 | 13 | ); 14 | -------------------------------------------------------------------------------- /src/app/components/Header/style.css: -------------------------------------------------------------------------------- 1 | .Nav { 2 | & ul { 3 | list-style-type: none; 4 | padding: 0; 5 | 6 | & li { 7 | display: inline; 8 | padding: 5px; 9 | 10 | & a { 11 | text-shadow: 1px 1px 2px #383838; 12 | color: white; 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/app/components/index.tsx: -------------------------------------------------------------------------------- 1 | export { Header } from './Header'; 2 | -------------------------------------------------------------------------------- /src/app/containers/App/index.tsx: -------------------------------------------------------------------------------- 1 | const appConfig = require('../../../../config/main'); 2 | 3 | import * as React from 'react'; 4 | import { Helmet } from 'react-helmet'; 5 | import { Header } from 'components'; 6 | 7 | const style = require('./style.css'); 8 | 9 | class App extends React.Component { 10 | 11 | public render() { 12 | return ( 13 |
14 | 15 |
16 | {this.props.children} 17 |
18 | ); 19 | } 20 | } 21 | 22 | export { App }; 23 | -------------------------------------------------------------------------------- /src/app/containers/App/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | } 4 | 5 | .AppContainer { 6 | padding: 0; 7 | margin: 0; 8 | text-align: center; 9 | z-index: 2; 10 | position: fixed; 11 | width: 100%; 12 | color: white; 13 | text-shadow: 1px 1px 2px #383838; 14 | } 15 | -------------------------------------------------------------------------------- /src/app/containers/Cube/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {Box} from 'whs'; 3 | import * as THREE from 'three'; 4 | 5 | import { add, remove } from 'modules/world/'; 6 | import { IWorld, IWorldAction } from 'models/world'; 7 | const { connect } = require('react-redux'); 8 | import { ActionCreator } from 'redux'; 9 | 10 | interface IProps { 11 | world: IWorld; 12 | add: ActionCreator; 13 | remove: ActionCreator; 14 | } 15 | 16 | @connect( 17 | (state) => ({ world: state.world }), 18 | (dispatch) => ({ 19 | add: () => dispatch(add()), 20 | remove: () => dispatch(remove()) 21 | }) 22 | ) 23 | 24 | class Cube extends React.Component { 25 | 26 | public constructor(props) { 27 | super(props); 28 | 29 | this.state = { 30 | world: null, 31 | object: null, 32 | material: null 33 | }; 34 | } 35 | 36 | public componentWillUnmount() { 37 | const { world } = this.props; 38 | world.world.remove(this.state.object); 39 | this.setState({object: null}); 40 | } 41 | 42 | public componentWillMount() { 43 | // let world = this.state.world; 44 | const { add, world } = this.props; 45 | if (!world.world) { 46 | add(); 47 | } 48 | 49 | if (!this.state.object) { 50 | const material = new THREE.MeshBasicMaterial({ 51 | color: 0xffffff, 52 | wireframe: true 53 | }); 54 | 55 | this.setState({material}); 56 | 57 | const object = new Box({ 58 | geometry: { 59 | width: 15, 60 | height: 15, 61 | depth: 15 62 | }, 63 | 64 | material, 65 | 66 | shadow: { 67 | receive: false 68 | }, 69 | 70 | position: { 71 | x: 0, 72 | y: 0, 73 | z: 0 74 | } 75 | }); 76 | 77 | this.setState({object}); 78 | } 79 | } 80 | 81 | public render() { 82 | const { world } = this.props; 83 | if (world.world) { 84 | if (this.state.object) { 85 | world.world.add(this.state.object); 86 | } 87 | } 88 | return ( 89 |

Cube

90 | ); 91 | } 92 | } 93 | 94 | export { Cube }; 95 | -------------------------------------------------------------------------------- /src/app/containers/Cube/style.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WhitestormJS/whitestorm-typescript-boilerplate/23e86f930f9916290bb43c3a8119b2e1897e5b74/src/app/containers/Cube/style.css -------------------------------------------------------------------------------- /src/app/containers/Sphere/index.tsx: -------------------------------------------------------------------------------- 1 | const style = require('./style.css'); 2 | 3 | import * as React from 'react'; 4 | import {Sphere as wSphere} from 'whs'; 5 | import * as THREE from 'three'; 6 | 7 | import { add, remove } from 'modules/world/'; 8 | import { IWorld, IWorldAction } from 'models/world'; 9 | const { connect } = require('react-redux'); 10 | import { ActionCreator } from 'redux'; 11 | 12 | interface IProps { 13 | world: IWorld; 14 | add: ActionCreator; 15 | remove: ActionCreator; 16 | } 17 | 18 | @connect( 19 | (state) => ({ world: state.world }), 20 | (dispatch) => ({ 21 | add: () => dispatch(add()), 22 | remove: () => dispatch(remove()) 23 | }) 24 | ) 25 | 26 | class Sphere extends React.Component { 27 | 28 | public constructor(props) { 29 | super(props); 30 | 31 | this.state = { 32 | world: null, 33 | object: null, 34 | material: null 35 | }; 36 | } 37 | 38 | public componentWillUnmount() { 39 | const { world } = this.props; 40 | world.world.remove(this.state.object); 41 | this.setState({object: null}); 42 | } 43 | 44 | public componentWillMount() { 45 | const { add, world } = this.props; 46 | if (!world.world) { 47 | add(); 48 | } 49 | 50 | if (!this.state.object) { 51 | const material = new THREE.MeshBasicMaterial({ 52 | color: 0xffffff, 53 | wireframe: true 54 | }); 55 | 56 | this.setState({material}); 57 | 58 | const object = new wSphere({ 59 | geometry: { 60 | radius: 8, 61 | widthSegments: 16, 62 | heightSegments: 15 63 | }, 64 | 65 | material, 66 | 67 | shadow: { 68 | receive: false 69 | }, 70 | 71 | position: { 72 | x: 0, 73 | y: 0, 74 | z: 0 75 | } 76 | }); 77 | this.setState({object}); 78 | } 79 | } 80 | 81 | public render() { 82 | const { world } = this.props; 83 | if (world.world) { 84 | if (this.state.object) { 85 | world.world.add(this.state.object); 86 | } 87 | } 88 | return ( 89 |
90 |

Sphere

91 |
92 | ); 93 | } 94 | } 95 | 96 | export { Sphere }; 97 | -------------------------------------------------------------------------------- /src/app/containers/Sphere/style.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WhitestormJS/whitestorm-typescript-boilerplate/23e86f930f9916290bb43c3a8119b2e1897e5b74/src/app/containers/Sphere/style.css -------------------------------------------------------------------------------- /src/app/containers/index.tsx: -------------------------------------------------------------------------------- 1 | /** Exporting Containers for Easier Imports */ 2 | export { App } from './App'; 3 | export { Cube } from './Cube'; 4 | export { Sphere } from './Sphere'; 5 | -------------------------------------------------------------------------------- /src/app/helpers/TestHelper.tsx: -------------------------------------------------------------------------------- 1 | /** React Specific */ 2 | import * as React from 'react'; 3 | import { mount } from 'enzyme'; 4 | import { Provider } from 'react-redux'; 5 | import { createStore } from 'redux'; 6 | import rootReducer from 'redux/reducers'; 7 | import configureStore from 'redux-mock-store'; 8 | 9 | const fetchMock = require('fetch-mock'); 10 | 11 | /** Redux Mock Store Configuration */ 12 | import thunk from 'redux-thunk'; 13 | 14 | const middlewares = [thunk]; 15 | const mockStore = configureStore(middlewares); 16 | 17 | /** Render Component */ 18 | function renderComponent(ComponentClass, state?, props?) { 19 | const store = createStore(rootReducer, state); 20 | 21 | return mount( 22 | 23 | 24 | , 25 | ); 26 | } 27 | 28 | export { mockStore, fetchMock, renderComponent }; 29 | -------------------------------------------------------------------------------- /src/app/models/world.ts: -------------------------------------------------------------------------------- 1 | export interface IWorld { 2 | world: any; 3 | } 4 | 5 | export interface IWorldAction { 6 | type: string; 7 | } 8 | -------------------------------------------------------------------------------- /src/app/redux/IStore.ts: -------------------------------------------------------------------------------- 1 | import { IWorld } from 'models/world'; 2 | 3 | export interface IStore { 4 | world: IWorld; 5 | } 6 | -------------------------------------------------------------------------------- /src/app/redux/modules/world/__tests__/index.spec.ts: -------------------------------------------------------------------------------- 1 | import { ADD, REMOVE, worldReducer} from '../index'; 2 | 3 | describe('World Module', () => { 4 | test('has the correct ADD action', () => { 5 | expect(ADD).toEqual('world/ADD'); 6 | }); 7 | 8 | test('has the correct REMOVE action', () => { 9 | expect(REMOVE).toEqual('world/REMOVE'); 10 | }); 11 | 12 | const removeAction = {world: null, type: REMOVE}; 13 | test('supports the REMOVE action', () => { 14 | expect(worldReducer(null, removeAction)).toHaveProperty('world', null); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/app/redux/modules/world/index.ts: -------------------------------------------------------------------------------- 1 | import { IWorld, IWorldAction } from 'models/world'; 2 | import * as WHS from 'whs/build/whs'; 3 | import * as THREE from 'three'; 4 | 5 | /** Action Types */ 6 | export const ADD: string = 'world/ADD'; 7 | export const REMOVE: string = 'world/REMOVE'; 8 | 9 | /** Initial State */ 10 | const initialState: IWorld = { 11 | world: null, 12 | }; 13 | 14 | /** Reducer: WorldReducer */ 15 | export function worldReducer(state = initialState, action?: IWorldAction) { 16 | switch (action.type) { 17 | case ADD: 18 | return { 19 | world: newWorld(), 20 | }; 21 | 22 | case REMOVE: 23 | return { 24 | world: null, 25 | }; 26 | 27 | default: 28 | return state; 29 | } 30 | } 31 | 32 | /** Action Creator: Add world */ 33 | export function add(): IWorldAction { 34 | return { 35 | type: ADD, 36 | }; 37 | } 38 | 39 | /** Action Creator: Remove world */ 40 | export function remove(): IWorldAction { 41 | return { 42 | type: REMOVE, 43 | }; 44 | } 45 | 46 | function newWorld() { 47 | const cameraModule = new WHS.CameraModule({ 48 | position: { 49 | z: 30, 50 | y: 40 51 | }, 52 | far: 2000, 53 | near: 1 54 | }); 55 | 56 | const controlsModule = new WHS.OrbitControlsModule(); 57 | 58 | const world = new WHS.App([ 59 | new WHS.ElementModule(), 60 | new WHS.SceneModule(), 61 | cameraModule, 62 | new WHS.RenderingModule({ 63 | bgColor: 0x2a3340, 64 | 65 | renderer: { 66 | antialias: true, 67 | shadowmap: { 68 | type: THREE.PCFSoftShadowMap 69 | } 70 | } 71 | }), 72 | controlsModule, 73 | new WHS.ResizeModule() 74 | ]); 75 | 76 | new WHS.AmbientLight({ 77 | light: { 78 | color: 0xffffff, 79 | intensity: 0.4 80 | } 81 | }).addTo(world); 82 | world.start(); 83 | 84 | return world; 85 | } 86 | -------------------------------------------------------------------------------- /src/app/redux/reducers.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers, Reducer } from 'redux'; 2 | import { routerReducer } from 'react-router-redux'; 3 | import { worldReducer } from './modules/world'; 4 | import { IStore } from './IStore'; 5 | 6 | const { reducer } = require('redux-connect'); 7 | 8 | const rootReducer: Reducer = combineReducers({ 9 | routing: routerReducer, 10 | world: worldReducer, 11 | reduxAsyncConnect: reducer 12 | }); 13 | 14 | export default rootReducer; 15 | -------------------------------------------------------------------------------- /src/app/redux/store.ts: -------------------------------------------------------------------------------- 1 | const appConfig = require('../../../config/main'); 2 | import { createStore, applyMiddleware, compose, Middleware, Store } from 'redux'; 3 | import { routerMiddleware } from 'react-router-redux'; 4 | import thunk from 'redux-thunk'; 5 | import rootReducer from './reducers'; 6 | import { IStore } from './IStore'; 7 | import { createLogger } from 'redux-logger'; 8 | 9 | export function configureStore(history, initialState?: IStore): Store<{}> { 10 | 11 | const middlewares: Middleware[] = [ 12 | routerMiddleware(history), 13 | thunk 14 | ]; 15 | 16 | /** Add Only Dev. Middlewares */ 17 | if (appConfig.env !== 'production' && process.env.BROWSER) { 18 | const logger = createLogger(); 19 | middlewares.push(logger); 20 | } 21 | 22 | const composeEnhancers = (appConfig.env !== 'production' && 23 | typeof window === 'object' && 24 | window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose; 25 | 26 | const store = createStore(rootReducer, initialState, composeEnhancers( 27 | applyMiddleware(...middlewares) 28 | )); 29 | 30 | if (appConfig.env === 'development' && (module as any).hot) { 31 | (module as any).hot.accept('./reducers', () => { 32 | store.replaceReducer((require('./reducers'))); 33 | }); 34 | } 35 | 36 | return store; 37 | } 38 | -------------------------------------------------------------------------------- /src/app/routes/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Route, Switch } from 'react-router'; 3 | import { App, Sphere, Cube } from 'containers'; 4 | 5 | export default ( 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | ); 14 | -------------------------------------------------------------------------------- /src/client.tsx: -------------------------------------------------------------------------------- 1 | import * as e6p from 'es6-promise'; 2 | (e6p as any).polyfill(); 3 | 4 | import * as React from 'react'; 5 | import * as ReactDOM from 'react-dom'; 6 | import { Provider } from 'react-redux'; 7 | import { ConnectedRouter } from 'react-router-redux'; 8 | import createHistory from 'history/createBrowserHistory'; 9 | import { configureStore } from './app/redux/store'; 10 | import routes from './app/routes'; 11 | // require('file-loader?name=[name].[ext]!./public/index.html'); 12 | 13 | const history = createHistory(); 14 | 15 | const store = configureStore( 16 | history, 17 | window.__INITIAL_STATE__ 18 | ); 19 | 20 | ReactDOM.render( 21 | 22 | 25 | {routes} 26 | 27 | , 28 | document.getElementById('app') 29 | ); 30 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WhitestormJS/whitestorm-typescript-boilerplate/23e86f930f9916290bb43c3a8119b2e1897e5b74/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | -------------------------------------------------------------------------------- /src/server.tsx: -------------------------------------------------------------------------------- 1 | const appConfig = require('../config/main'); 2 | 3 | import * as e6p from 'es6-promise'; 4 | (e6p as any).polyfill(); 5 | 6 | const express = require('express'); 7 | const path = require('path'); 8 | const compression = require('compression'); 9 | const Chalk = require('chalk'); 10 | const favicon = require('serve-favicon'); 11 | 12 | const app = express(); 13 | 14 | app.use(compression()); 15 | 16 | if (process.env.NODE_ENV !== 'production') { 17 | const webpack = require('webpack'); 18 | const webpackConfig = require('../config/webpack/dev'); 19 | const webpackCompiler = webpack(webpackConfig); 20 | 21 | app.use(require('webpack-dev-middleware')(webpackCompiler, { 22 | publicPath: webpackConfig.output.publicPath, 23 | stats: { colors: true }, 24 | noInfo: true, 25 | hot: true, 26 | inline: true, 27 | lazy: false, 28 | historyApiFallback: true, 29 | quiet: true, 30 | })); 31 | 32 | app.use(require('webpack-hot-middleware')(webpackCompiler)); 33 | } 34 | 35 | app.use(favicon(path.join(__dirname, 'public/favicon.ico'))); 36 | 37 | app.use('/public', express.static(path.join(__dirname, 'public'))); 38 | 39 | app.get('*', (req, res) => { 40 | const location = req.url; 41 | console.info('Hits ' + location); 42 | res.sendFile(path.join(__dirname, 'public/index.html')); 43 | }); 44 | 45 | app.listen(appConfig.port, (err) => { 46 | if (err) { 47 | console.error(Chalk.bgRed(err)); 48 | } else { 49 | console.info(Chalk.black.bgGreen( 50 | `\n\n💫 Listening at http://${appConfig.host}:${appConfig.port}\n`, 51 | )); 52 | } 53 | }); 54 | -------------------------------------------------------------------------------- /src/vendor/main.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This is an entry point for additional assets, 3 | * require your assets under this file. 4 | * Example: 5 | * require('./bootstrap/css/bootstrap.min.css'); 6 | */ 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "atom": { 3 | "rewriteTsConfig": true 4 | }, 5 | "compilerOptions": { 6 | "baseUrl": ".", 7 | "paths": { 8 | "*": [ 9 | "src/app/*", 10 | "src/app/redux/*" 11 | ] 12 | }, 13 | "outDir": "build", 14 | "module": "commonjs", 15 | "target": "es6", 16 | "sourceMap": true, 17 | "inlineSourceMap": false, 18 | "inlineSources": false, 19 | "experimentalDecorators": true, 20 | "noUnusedParameters": true, 21 | "noUnusedLocals": true, 22 | "allowJs": true, 23 | "jsx": "react", 24 | "moduleResolution": "node" 25 | }, 26 | "exclude": [ 27 | "node_modules", 28 | "build", 29 | "typings/main", 30 | "typings/main.d.ts" 31 | ], 32 | "awesomeTypescriptLoaderOptions": { 33 | "useCache": true, 34 | "useBabel": true 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:latest", 4 | "tslint-react" 5 | ], 6 | "rules": { 7 | "trailing-comma": [ 8 | false, 9 | { 10 | "multiline": "never", 11 | "singleline": "never" 12 | } 13 | ], 14 | "indent": [ 15 | true, 16 | "spaces" 17 | ], 18 | "quotemark": [ 19 | true, 20 | "single", 21 | "jsx-double" 22 | ], 23 | "no-var-requires": false, 24 | "ordered-imports": false, 25 | "member-ordering": [ 26 | false 27 | ], 28 | "object-literal-sort-keys": false, 29 | "no-shadowed-variable": false, 30 | "no-console": [ 31 | false 32 | ], 33 | "jsx-alignment": [ 34 | false 35 | ], 36 | "no-implicit-dependencies": false, 37 | "no-submodule-imports": false 38 | } 39 | } 40 | --------------------------------------------------------------------------------