Futuristic, production-ready development environment for building Component-Driven, Flux Single Page Applications with React, Redux and TypeScript - utilizing power of Static Type Checking, ES2016, Async/Await, ES6 Modules, Linting, Git-Hooks, fast in browser (on-the-fly) transpilation, bundling with tree-shaking - powered by JSPM (SystemJS with Hot-Reload & Rollup).
Table of Contents
- Innovations
- Features
- Roadmap
- JSPM integration
- Pros & Cons
- Project Structure
- Installation
- Workflows Guide
- CLI Commands
RAPID-SPEED DEVELOPMENT WORKFLOW - TypeScript source file hot-reload and in-the-browser transpilation
Super-fast development experience by loading TypeScript source files directly in the browser (using plugin-typescript) while seperately type-checking them in the IDE or in the command-line in watch mode, without transpilation for intermediate JS files or bundling.
Joined together with single-file hot-reload gives you almost instant feedback-loop as there is no costly project-wide transpilation or bundling step involved.
Great explanation from @jonaskello of how this works compared to Webpack workflow: microsoft/TypeScript#1564 (comment)
Local HTTP dev server with hot-reload out-of-the-box - highly reliable leveraging SystemJS import feature and scalable for speed with increasing modules count using systemjs-hot-reloader It can load each src file separately eliminating extra bundling step. First it will delete old module from the SystemJS registry and then re-imports updated module with additional modules which have imported the updated module, ensuring to always have the latest changes. This approach gives you great scalability with increasing modules count as you reload only necessary modules.
If you are coding in a NO-IDE environment (notepad/vim) or expecting to have a common way across a team to target a specific version of typescript compiler even while using different Editors/IDEs, you can utilize CLI type-checking workflow using tsc -p src --watch
command for fast incremental type-checking or tsc -p src
command for project-wide type-check, there is no JS emit so it will not clutter your project or disrupt different build processes based on various IDE plugins or gulp/grunt based tasks.
Own concept to achieve locally scoped CSS styles using csjs with statically typed CSS class-names using TypeScript.
- Full CSS support with pseudo-classes & media queries, encapsulated in ES6 Modules that can be nicely imported by your UI components.
- Define interfaces with your CSS classes and you get className property type-checking, solid auto-completion and easy refactoring. You can also add doc comments and auto-generate docs of your styles library for your team and utilize IntelliSense features of your IDE.
EXAMPLE: Consumer Component and it's CSS Module Styles in TypeScript Format with Class-Names Interface
DEMO: http://piotrwitek.github.io/react-redux-typescript-starter-kit/#/css-modules
Overview Video: https://youtu.be/67pPYqom2a0
Beware of TypeScript boilerplates using Babel transformation step after the TypeScript compiler to transform "async & generator functions" when targeting ES3/ES5. This is costly process and introduces additional build step into the build workflow.
- Async/Await - starting from version 2.1 TypeScript provide native support for transformation to ES3/ES5, so in this case you are fine using only TS (microsoft/TypeScript#9175)
- Generators - TypeScript Team have dropped adding support for generator transformation to (ES3/ES5) so consider alternative solution covered below (microsoft/TypeScript#3975 (comment))
Alternative solution to resolve Generator transformation to ES3/ES5: My solution prefer using only Facebook Regenerator Project instead of adding Babel as dependency (NOTE: Babel internally is using the same approach, running "regenerator runtime" internally for async and generator functions transformations - https://babeljs.io/docs/usage/caveats/)
When building for production use npm run regenerator
CLI command just after a build command to apply transform to app.js bundle, or use an alias command npm run build:regenerator
to run it automatically with each build.
- PRODUCTION-WORKFLOW - npm scripts for production bundling & deployment, github-hooks, linter, test runner etc.
- TYPESAFE-API-CALLS - type checking contracts of REST API calls - forget constantly checking for API docs and let your IDE guide you
- GREAT-TOOLING - type cheking for JavaScript and DOM API with autocompletion and docs right in your editor - no more silly typos and runtime exceptions
- REACT-ROUTER - included
react-router-redux
to store your routing in state for Time-Travel capabilities - REDUX-DEV-TOOLS - installed Redux DevTools capabilities with chrome-extension
- IMMUTABLE-STORE - using
seamless-immutable
for simplicity and backwards-compatibility with vanilla JS (no hassle withtoJS()
,get()
,getIn()
in your containers and components) - BEM & ITCSS - using BEM with Inverted Triangle conventions to give meaning and context to CSS classes
- EASY TESTING IN TYPESCRIPT - write your tests only in TypeScript - don't worry about transpilation, easily import and run your TS source files from a command line (use
npm test
CLI command). Test harness with Tape with Promise support from blue-tape
- no mixins -> use ES6 style PureRenderMixin with PureComponent
- no ref strings -> use ref callbacks
- no method binding -> use ES Class Fields
- no new function/objects creation in render methods -> declare them in outer scope and use a reference
- render big collections in separate dedicated components -> omit re-renders invoked by other prop changes
- don't use array index as key property -> use item unique id property to eliminate bugs
- Flux Standard Actions for Redux - https://github.com/acdlite/redux-actions
- Redux Reducer Modules - https://github.com/erikras/ducks-modular-redux
- Redux async flow with redux-saga - https://github.com/yelouafi/redux-saga/
- Testing async flow in redux sagas
- REDUX INNOVATION - using TS 2.0 "Tagged Union Types" - for solid Redux integration (https://blogs.msdn.microsoft.com/typescript/2016/08/30/announcing-typescript-2-0-rc)
- Reactive Programming with RxJS
- Testing with Enzyme (JSDOM)
- Testing Component markup (shallowRender)
- Testing Component behaviour/interactions (renderIntoDocument, Simulate)
- Integration Testing in Redux Store
- JSPM 0.17.X - production ready set-up with best-practices from real-world projects
- optimized loading speed by utilizing
vendor
dev-bundle (read below) - using Rollup for bundling and tree-shaking optimizations
- bundles for production - seperate vendor & app bundles
- importing and bundling CSS / SCSS / JSON / Image files using plugins
I saw people complaining about JSPM loading very slow in development when there are a lot of modules loaded in the project.
This can be optimized in development using bundles.
I have found that a lot of modules are external dependencies so we can speed up a page reload considerably by creating a vendor
bundle. As external dependencies don't need to be hot-reloaded and they'll only change when updated through NPM, so it is obvious to bundle them all together and load as one file without any drawbacks.
Using this approach you will have both faster full page reload and your own src
modules loaded separately utilizing hot-reload.
This is the best of two development approaches (hot-reload vs. bundling) mixed together.
Check yourself using this easy test procedure:
- run
npm run unbundle
-> open network tab in chrome dev tools -> reload the page -> check logged results - run
npm run bundle-dev
-> open network tab in chrome dev tools -> reload page -> compare logged results with previous
-
be aware that using TypeScript compiler built-in into the editor/IDE like in WebStorm, can give you some trouble because of compiler version over which you don't have any control. Instead you should depend on a local npm TypeScript installation included in project. Visual Studio Code and alm.tools allows the possibility to use a locally installed TS package in your project. In case you want to stick to using IDE with "baked-in TS" you could turn off it's compilation process and run type-checking from a command line utilizing provided helper scripts
npm run tsc
ornpm run tsc:watch
commands for one-time check or a continuous-incremental type-checking. -
During development there is no need to emit intermediate JS files, this workflow will load your TS files directly in-browser, otherwise you'll lose Hot-Reload capability (new files emitted after each change) and experience much slower workflow without any advantages.
.
├── assets # static assets copied to dist folder
├── configs # dev / prod bundle config
| ├── vendor.config.dev.js # packages included in "vendor" bundle for dev
| └── vendor.config.prod.js # packages included in "vendor" bundle for prod
├── src # app source code
│ ├── components # global reusable presentational components
│ ├── containers # container components providing redux context
│ ├── layouts # components defining page layouts
│ ├── reducers # modules containing redux reducers/constants/action creators
│ ├── services # modules abstracting communication with web services
│ ├── typings # custom TypeScript definitions
│ ├── utils # app utility modules
│ ├── app.tsx # app entry module with routing config
│ ├── store.tsx # app store module
│ ├── test-runner.tsx # test suites config
│ └── tsconfig.tsx # TypeScript compiler config
├── index.html # index.html
├── index.prod.html # index.html configured for production use
├── jspm.config.js # system.js config for app dependencies
├── server.js # dev-server entry module
└── tslint.json # linter config
- Node.js and Git
- Install JSPM with global flag to have jspm command available:
npm install jspm -g
(otherwise you'll have to use a local version from~/node_modules/
)
git clone https://github.com/piotrwitek/react-redux-typescript-starter-kit.git my-project-folder
npm install
npm start
NOTE: Use index.prod.html for production, it have slightly different loading logic. Add references to your static assets like links/scripts and copy to the dist folder during a build process.
npm run bundle-dev
- create bundle of vendor packages to speed-up full-page reload during development (re-run only when project dependencies has changed)npm start
- browser will open automatically
npm run tsc:watch
- if you don't use IDE with typescript integration, run tsc compiler in watch mode for fast incremental type-checking (NOTE: this will not emit any JS files, only type-checking - it's OK because you load ts file on-the-fly)npm run tsc
- one-time project wide type-safety check
npm run build
- create app.js & vendor.js bundles in 'dist' folder
npm run build:app
- build only app.js bundle (run when project source code has changed)npm run build:vendor
- build only vendor.js bundle (run when project dependencies has changed)
- open
http://localhost/dist/
- check prod build on local server npm run regenerator
- if you want to add "async/generator functions" downlevel transformation - use when targeting ES3/ES5
npm start
- start local dev server with hot-reload jspm-hmr
npm run tsc
- run project-wide type-checking with TypeScript CLI (/src
folder)
npm run tsc:watch
- start TypeScript CLI in watch mode for fast incremental type-checking (/src
folder)
npm run build:dev
- build vendor packages into vendor.dev.js bundle to speed-up full-page reload during development - non-minified with source-maps (dev bundle)
npm run unbundle
- delete vendor.dev.js bundle package (WARNING: it will result in loading all of vendor packages as seperate requests - use it only if you know what you are doing e.g. when leveraging HTTP/2 multiplexing/pipelining)
npm run build
- build both app.js & vendor.js bundle for production
npm run build:app
- build app source code into app.js (prod bundle) - minified, no source-maps
npm run build:vendor
- build vendor packages into vendor.prod.js (prod bundle) - minified, no source-maps
npm run build:debug
- build app source code into app.js (dev bundle) - non-minified with source-maps
npm run regenerator
- transform "generator functions" in app.js (prod bundle) to ES3/ES5 using regenerator runtime (not supported natively by TypeScript)
npm run build:regenerator
- alias to run both npm run build:app
and npm run regenerator
.
npm run init:dist
- clean dist folder and copy assets
npm run init:deploy
- clone git repository in /dist
folder (gh-pages branch)
npm run deploy
- commit and push all changes found in /dist
folder
npm run bad
- build app.js and deploy
npm run bvd
- build vendor.js and deploy
npm run lint
- run linter
npm run test
or npm test
- run test suites
npm run precommit
- pre commit git hook - runs linter
npm run prepush
- pre push git hook - runs linter and tests
Copyright (c) 2016 Piotr Witek piotrek.witek@gmail.com (http://piotrwitek.github.io/)
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:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
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.