@ -0,0 +1,27 @@ |
|||
{ |
|||
"presets": [ |
|||
["env", { |
|||
"targets": { "node": 7 }, |
|||
"useBuiltIns": true |
|||
}], |
|||
"stage-0", |
|||
"react" |
|||
], |
|||
"plugins": ["add-module-exports", "dynamic-import-webpack"], |
|||
"env": { |
|||
"production": { |
|||
"presets": ["react-optimize"], |
|||
"plugins": ["babel-plugin-dev-expression"] |
|||
}, |
|||
"development": { |
|||
"plugins": [ |
|||
"transform-class-properties", |
|||
"transform-es2015-classes", |
|||
["flow-runtime", { |
|||
"assert": true, |
|||
"annotate": true |
|||
}] |
|||
] |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,20 @@ |
|||
root = true |
|||
|
|||
[*] |
|||
indent_style = space |
|||
indent_size = 2 |
|||
end_of_line = lf |
|||
charset = utf-8 |
|||
trim_trailing_whitespace = true |
|||
insert_final_newline = true |
|||
|
|||
[*.{json,js,jsx,html,css,yml}] |
|||
indent_style = space |
|||
indent_size = 2 |
|||
|
|||
[.eslintrc] |
|||
indent_style = space |
|||
indent_size = 2 |
|||
|
|||
[*.md] |
|||
trim_trailing_whitespace = false |
@ -0,0 +1,53 @@ |
|||
# Logs |
|||
logs |
|||
*.log |
|||
|
|||
# Runtime data |
|||
pids |
|||
*.pid |
|||
*.seed |
|||
|
|||
# Directory for instrumented libs generated by jscoverage/JSCover |
|||
lib-cov |
|||
|
|||
# Coverage directory used by tools like istanbul |
|||
coverage |
|||
|
|||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) |
|||
.grunt |
|||
|
|||
# node-waf configuration |
|||
.lock-wscript |
|||
|
|||
# Compiled binary addons (http://nodejs.org/api/addons.html) |
|||
build/Release |
|||
.eslintcache |
|||
|
|||
# Dependency directory |
|||
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git |
|||
node_modules |
|||
app/node_modules |
|||
|
|||
# OSX |
|||
.DS_Store |
|||
|
|||
# flow-typed |
|||
flow-typed/npm/* |
|||
!flow-typed/npm/module_vx.x.x.js |
|||
|
|||
# App packaged |
|||
release |
|||
app/main.prod.js |
|||
app/main.prod.js.map |
|||
app/renderer.prod.js |
|||
app/renderer.prod.js.map |
|||
app/style.css |
|||
app/style.css.map |
|||
dist |
|||
dll |
|||
main.js |
|||
main.js.map |
|||
|
|||
.idea |
|||
npm-debug.log.* |
|||
__snapshots__ |
@ -0,0 +1,50 @@ |
|||
{ |
|||
"parser": "babel-eslint", |
|||
"parserOptions": { |
|||
"sourceType": "module", |
|||
"allowImportExportEverywhere": true |
|||
}, |
|||
"extends": "airbnb", |
|||
"env": { |
|||
"browser": true, |
|||
"node": true |
|||
}, |
|||
"rules": { |
|||
"arrow-parens": ["off"], |
|||
"compat/compat": "error", |
|||
"consistent-return": "off", |
|||
"comma-dangle": "off", |
|||
"flowtype-errors/show-errors": "error", |
|||
"generator-star-spacing": "off", |
|||
"import/no-unresolved": "error", |
|||
"import/no-extraneous-dependencies": "off", |
|||
"no-console": "off", |
|||
"no-use-before-define": "off", |
|||
"no-multi-assign": "off", |
|||
"promise/param-names": "error", |
|||
"promise/always-return": "error", |
|||
"promise/catch-or-return": "error", |
|||
"promise/no-native": "off", |
|||
"react/sort-comp": ["error", { |
|||
"order": ["type-annotations", "static-methods", "lifecycle", "everything-else", "render"] |
|||
}], |
|||
"react/jsx-no-bind": "off", |
|||
"react/jsx-filename-extension": ["error", { "extensions": [".js", ".jsx"] }], |
|||
"react/prefer-stateless-function": "off" |
|||
}, |
|||
"plugins": [ |
|||
"flowtype", |
|||
"flowtype-errors", |
|||
"import", |
|||
"promise", |
|||
"compat", |
|||
"react" |
|||
], |
|||
"settings": { |
|||
"import/resolver": { |
|||
"webpack": { |
|||
"config": "webpack.config.eslint.js" |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,26 @@ |
|||
[ignore] |
|||
<PROJECT_ROOT>/node_modules/* |
|||
<PROJECT_ROOT>/app/main.prod.js |
|||
<PROJECT_ROOT>/app/main.prod.js.map |
|||
<PROJECT_ROOT>/app/dist/.* |
|||
<PROJECT_ROOT>/resources/.* |
|||
<PROJECT_ROOT>/release/.* |
|||
<PROJECT_ROOT>/dll/.* |
|||
<PROJECT_ROOT>/release/.* |
|||
<PROJECT_ROOT>/git/.* |
|||
|
|||
[include] |
|||
|
|||
[libs] |
|||
|
|||
[options] |
|||
esproposal.class_static_fields=enable |
|||
esproposal.class_instance_fields=enable |
|||
esproposal.export_star_as=enable |
|||
module.name_mapper.extension='css' -> '<PROJECT_ROOT>/internals/flow/CSSModule.js.flow' |
|||
module.name_mapper.extension='styl' -> '<PROJECT_ROOT>/internals/flow/CSSModule.js.flow' |
|||
module.name_mapper.extension='scss' -> '<PROJECT_ROOT>/internals/flow/CSSModule.js.flow' |
|||
module.name_mapper.extension='png' -> '<PROJECT_ROOT>/internals/flow/WebpackAsset.js.flow' |
|||
module.name_mapper.extension='jpg' -> '<PROJECT_ROOT>/internals/flow/WebpackAsset.js.flow' |
|||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe |
|||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue |
@ -0,0 +1,4 @@ |
|||
* text eol=lf |
|||
*.png binary |
|||
*.ico binary |
|||
*.icns binary |
@ -0,0 +1,52 @@ |
|||
# Logs |
|||
logs |
|||
*.log |
|||
|
|||
# Runtime data |
|||
pids |
|||
*.pid |
|||
*.seed |
|||
|
|||
# Directory for instrumented libs generated by jscoverage/JSCover |
|||
lib-cov |
|||
|
|||
# Coverage directory used by tools like istanbul |
|||
coverage |
|||
|
|||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) |
|||
.grunt |
|||
|
|||
# node-waf configuration |
|||
.lock-wscript |
|||
|
|||
# Compiled binary addons (http://nodejs.org/api/addons.html) |
|||
build/Release |
|||
.eslintcache |
|||
|
|||
# Dependency directory |
|||
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git |
|||
node_modules |
|||
app/node_modules |
|||
|
|||
# OSX |
|||
.DS_Store |
|||
|
|||
# flow-typed |
|||
flow-typed/npm/* |
|||
!flow-typed/npm/module_vx.x.x.js |
|||
|
|||
# App packaged |
|||
release |
|||
app/main.prod.js |
|||
app/main.prod.js.map |
|||
app/renderer.prod.js |
|||
app/renderer.prod.js.map |
|||
app/style.css |
|||
app/style.css.map |
|||
dist |
|||
dll |
|||
main.js |
|||
main.js.map |
|||
|
|||
.idea |
|||
npm-debug.log.* |
@ -0,0 +1,3 @@ |
|||
{ |
|||
"extends": "stylelint-config-standard" |
|||
} |
@ -0,0 +1,42 @@ |
|||
sudo: false |
|||
|
|||
language: node_js |
|||
|
|||
node_js: |
|||
- 8 |
|||
- 7 |
|||
|
|||
cache: |
|||
yarn: true |
|||
directories: |
|||
- node_modules |
|||
- app/node_modules |
|||
|
|||
addons: |
|||
apt: |
|||
sources: |
|||
- ubuntu-toolchain-r-test |
|||
packages: |
|||
- g++-4.8 |
|||
- icnsutils |
|||
- graphicsmagick |
|||
- xz-utils |
|||
- xorriso |
|||
|
|||
install: |
|||
- export CXX="g++-4.8" |
|||
- yarn |
|||
- cd app && yarn && cd .. |
|||
- "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16" |
|||
|
|||
before_script: |
|||
- export DISPLAY=:99.0 |
|||
- sh -e /etc/init.d/xvfb start & |
|||
- sleep 3 |
|||
|
|||
script: |
|||
- node --version |
|||
- yarn lint |
|||
- yarn package |
|||
- yarn test |
|||
- yarn test-e2e |
@ -0,0 +1,19 @@ |
|||
{ |
|||
"javascript.validate.enable": false, |
|||
"flow.useNPMPackagedFlow": true, |
|||
"search.exclude": { |
|||
".git": true, |
|||
".eslintcache": true, |
|||
"app/dist": true, |
|||
"app/main.prod.js": true, |
|||
"app/main.prod.js.map": true, |
|||
"bower_components": true, |
|||
"dll": true, |
|||
"flow-typed": true, |
|||
"release": true, |
|||
"node_modules": true, |
|||
"npm-debug.log.*": true, |
|||
"test/**/__snapshots__": true, |
|||
"yarn.lock": true |
|||
} |
|||
} |
@ -0,0 +1,384 @@ |
|||
# 0.12.0 (2017.7.8) |
|||
|
|||
#### Misc |
|||
- Removed `babel-polyfill` |
|||
- Renamed and alphabetized npm scripts |
|||
|
|||
#### Breaking |
|||
- Changed node dev `__dirname` and `__filename` to node built in fn's (https://github.com/chentsulin/electron-react-boilerplate/pull/1035) |
|||
- Renamed `app/bundle.js` to `app/renderer.prod.js` for consistency |
|||
- Renamed `dll/vendor.js` to `dll/renderer.dev.dll.js` for consistency |
|||
|
|||
#### Additions |
|||
- Enable node_modules cache on CI |
|||
|
|||
# 0.11.2 (2017.5.1) |
|||
|
|||
Yay! Another patch release. This release mostly includes refactorings and router bug fixes. Huge thanks to @anthonyraymond! |
|||
|
|||
⚠️ Windows electron builds are failing because of [this issue](https://github.com/electron/electron/issues/9321). This is not an issue with the boilerplate ⚠️ |
|||
|
|||
#### Breaking |
|||
- **Renamed `./app/main.development.js` => `./app/main.{dev,prod}.js`:** [#963](https://github.com/chentsulin/electron-react-boilerplate/pull/963) |
|||
|
|||
#### Fixes |
|||
- **Fixed reloading when not on `/` path:** [#958](https://github.com/chentsulin/electron-react-boilerplate/pull/958) [#949](https://github.com/chentsulin/electron-react-boilerplate/pull/949) |
|||
|
|||
#### Additions |
|||
- **Added support for stylefmt:** [#960](https://github.com/chentsulin/electron-react-boilerplate/pull/960) |
|||
|
|||
# 0.11.1 (2017.4.23) |
|||
|
|||
You can now debug the production build with devtools like so: |
|||
``` |
|||
DEBUG_PROD=true npm run package |
|||
``` |
|||
|
|||
🎉🎉🎉 |
|||
|
|||
#### Additions |
|||
- **Added support for debugging production build:** [#fab245a](https://github.com/chentsulin/electron-react-boilerplate/pull/941/commits/fab245a077d02a09630f74270806c0c534a4ff95) |
|||
|
|||
#### Bug Fixes |
|||
- **Fixed bug related to importing native dependencies:** [#933](https://github.com/chentsulin/electron-react-boilerplate/pull/933) |
|||
|
|||
#### Improvements |
|||
- **Updated all deps to latest semver** |
|||
|
|||
# 0.11.0 (2017.4.19) |
|||
|
|||
Here's the most notable changes since `v0.10.0`. Its been about a year since a release has been pushed. Expect a new release to be published every 3-4 weeks. |
|||
|
|||
#### Breaking Changes |
|||
|
|||
- **Dropped support for node < 6** |
|||
- **Refactored webpack config files** |
|||
- **Migrate to two-package.json project structure** |
|||
- **Updated all devDeps to latest semver** |
|||
- **Migrated to Jest:** [#768](https://github.com/chentsulin/electron-react-boilerplate/pull/768) |
|||
- **Migrated to `react-router@4`** |
|||
- **Migrated to `electron-builder@4`** |
|||
- **Migrated to `webpack@2`** |
|||
- **Migrated to `react-hot-loader@3`** |
|||
- **Changed default live reload server PORT to `1212` from `3000`** |
|||
|
|||
#### Additions |
|||
|
|||
- **Added support for Yarn:** [#451](https://github.com/chentsulin/electron-react-boilerplate/pull/451) |
|||
- **Added support for Flow:** [#425](https://github.com/chentsulin/electron-react-boilerplate/pull/425) |
|||
- **Added support for stylelint:** [#911](https://github.com/chentsulin/electron-react-boilerplate/pull/911) |
|||
- **Added support for electron-builder:** [#876](https://github.com/chentsulin/electron-react-boilerplate/pull/876) |
|||
- **Added optional support for SASS:** [#880](https://github.com/chentsulin/electron-react-boilerplate/pull/880) |
|||
- **Added support for eslint-plugin-flowtype:** [#911](https://github.com/chentsulin/electron-react-boilerplate/pull/911) |
|||
- **Added support for appveyor:** [#280](https://github.com/chentsulin/electron-react-boilerplate/pull/280) |
|||
- **Added support for webpack dlls:** [#860](https://github.com/chentsulin/electron-react-boilerplate/pull/860) |
|||
- **Route based code splitting:** [#884](https://github.com/chentsulin/electron-react-boilerplate/pull/884) |
|||
- **Added support for Webpack Bundle Analyzer:** [#922](https://github.com/chentsulin/electron-react-boilerplate/pull/922) |
|||
|
|||
#### Improvements |
|||
|
|||
- **Parallelize renderer and main build processes when running `npm run build`** |
|||
- **Dynamically generate electron app menu** |
|||
- **Improved vscode integration:** [#856](https://github.com/chentsulin/electron-react-boilerplate/pull/856) |
|||
|
|||
#### Bug Fixes |
|||
|
|||
- **Fixed hot module replacement race condition bug:** [#917](https://github.com/chentsulin/electron-react-boilerplate/pull/917) [#920](https://github.com/chentsulin/electron-react-boilerplate/pull/920) |
|||
|
|||
# 0.10.0 (2016.4.18) |
|||
|
|||
#### Improvements |
|||
|
|||
- **Use Babel in main process with Webpack build:** [#201](https://github.com/chentsulin/electron-react-boilerplate/pull/201) |
|||
- **Change targets to built-in support by webpack:** [#197](https://github.com/chentsulin/electron-react-boilerplate/pull/197) |
|||
- **use es2015 syntax for webpack configs:** [#195](https://github.com/chentsulin/electron-react-boilerplate/pull/195) |
|||
- **Open application when webcontent is loaded:** [#192](https://github.com/chentsulin/electron-react-boilerplate/pull/192) |
|||
- **Upgraded dependencies** |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **Fix `npm list electron-prebuilt` in package.js:** [#188](https://github.com/chentsulin/electron-react-boilerplate/pull/188) |
|||
|
|||
|
|||
# 0.9.0 (2016.3.23) |
|||
|
|||
#### Improvements |
|||
|
|||
- **Added [redux-logger](https://github.com/fcomb/redux-logger)** |
|||
- **Upgraded [react-router-redux](https://github.com/reactjs/react-router-redux) to v4** |
|||
- **Upgraded dependencies** |
|||
- **Added `npm run dev` command:** [#162](https://github.com/chentsulin/electron-react-boilerplate/pull/162) |
|||
- **electron to v0.37.2** |
|||
|
|||
#### Breaking Changes |
|||
|
|||
- **css module as default:** [#154](https://github.com/chentsulin/electron-react-boilerplate/pull/154). |
|||
- **set default NODE_ENV to production:** [#140](https://github.com/chentsulin/electron-react-boilerplate/issues/140) |
|||
|
|||
|
|||
# 0.8.0 (2016.2.17) |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **Fix lint errors** |
|||
- **Fix Webpack publicPath for production builds**: [#119](https://github.com/chentsulin/electron-react-boilerplate/issues/119). |
|||
- **package script now chooses correct OS icon extension** |
|||
|
|||
#### Improvements |
|||
|
|||
- **babel 6** |
|||
- **Upgrade Dependencies** |
|||
- **Enable CSS source maps** |
|||
- **Add json-loader**: [#128](https://github.com/chentsulin/electron-react-boilerplate/issues/128). |
|||
- **react-router 2.0 and react-router-redux 3.0** |
|||
|
|||
|
|||
# 0.7.1 (2015.12.27) |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **Fixed npm script on windows 10:** [#103](https://github.com/chentsulin/electron-react-boilerplate/issues/103). |
|||
- **history and react-router version bump**: [#109](https://github.com/chentsulin/electron-react-boilerplate/issues/109), [#110](https://github.com/chentsulin/electron-react-boilerplate/pull/110). |
|||
|
|||
#### Improvements |
|||
|
|||
- **electron 0.36** |
|||
|
|||
|
|||
|
|||
# 0.7.0 (2015.12.16) |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **Fixed process.env.NODE_ENV variable in webpack:** [#74](https://github.com/chentsulin/electron-react-boilerplate/pull/74). |
|||
- **add missing object-assign**: [#76](https://github.com/chentsulin/electron-react-boilerplate/pull/76). |
|||
- **packaging in npm@3:** [#77](https://github.com/chentsulin/electron-react-boilerplate/pull/77). |
|||
- **compatibility in windows:** [#100](https://github.com/chentsulin/electron-react-boilerplate/pull/100). |
|||
- **disable chrome debugger in production env:** [#102](https://github.com/chentsulin/electron-react-boilerplate/pull/102). |
|||
|
|||
#### Improvements |
|||
|
|||
- **redux** |
|||
- **css-modules** |
|||
- **upgrade to react-router 1.x** |
|||
- **unit tests** |
|||
- **e2e tests** |
|||
- **travis-ci** |
|||
- **upgrade to electron 0.35.x** |
|||
- **use es2015** |
|||
- **check dev engine for node and npm** |
|||
|
|||
|
|||
# 0.6.5 (2015.11.7) |
|||
|
|||
#### Improvements |
|||
|
|||
- **Bump style-loader to 0.13** |
|||
- **Bump css-loader to 0.22** |
|||
|
|||
|
|||
# 0.6.4 (2015.10.27) |
|||
|
|||
#### Improvements |
|||
|
|||
- **Bump electron-debug to 0.3** |
|||
|
|||
|
|||
# 0.6.3 (2015.10.26) |
|||
|
|||
#### Improvements |
|||
|
|||
- **Initialize ExtractTextPlugin once:** [#64](https://github.com/chentsulin/electron-react-boilerplate/issues/64). |
|||
|
|||
|
|||
# 0.6.2 (2015.10.18) |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **Babel plugins production env not be set properly:** [#57](https://github.com/chentsulin/electron-react-boilerplate/issues/57). |
|||
|
|||
|
|||
# 0.6.1 (2015.10.17) |
|||
|
|||
#### Improvements |
|||
|
|||
- **Bump electron to v0.34.0** |
|||
|
|||
|
|||
# 0.6.0 (2015.10.16) |
|||
|
|||
#### Breaking Changes |
|||
|
|||
- **From react-hot-loader to react-transform** |
|||
|
|||
|
|||
# 0.5.2 (2015.10.15) |
|||
|
|||
#### Improvements |
|||
|
|||
- **Run tests with babel-register:** [#29](https://github.com/chentsulin/electron-react-boilerplate/issues/29). |
|||
|
|||
|
|||
# 0.5.1 (2015.10.12) |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **Fix #51:** use `path.join(__dirname` instead of `./`. |
|||
|
|||
|
|||
# 0.5.0 (2015.10.11) |
|||
|
|||
#### Improvements |
|||
|
|||
- **Simplify webpack config** see [#50](https://github.com/chentsulin/electron-react-boilerplate/pull/50). |
|||
|
|||
#### Breaking Changes |
|||
|
|||
- **webpack configs** |
|||
- **port changed:** changed default port from 2992 to 3000. |
|||
- **npm scripts:** remove `start-dev` and `dev-server`. rename `hot-dev-server` to `hot-server`. |
|||
|
|||
|
|||
# 0.4.3 (2015.9.22) |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **Fix #45 zeromq crash:** bump version of `electron-prebuilt`. |
|||
|
|||
|
|||
# 0.4.2 (2015.9.15) |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **run start-hot breaks chrome refresh(CTRL+R) (#42)**: bump `electron-debug` to `0.2.1` |
|||
|
|||
|
|||
# 0.4.1 (2015.9.11) |
|||
|
|||
#### Improvements |
|||
|
|||
- **use electron-prebuilt version for packaging (#33)** |
|||
|
|||
|
|||
# 0.4.0 (2015.9.5) |
|||
|
|||
#### Improvements |
|||
|
|||
- **update dependencies** |
|||
|
|||
|
|||
# 0.3.0 (2015.8.31) |
|||
|
|||
#### Improvements |
|||
|
|||
- **eslint-config-airbnb** |
|||
|
|||
|
|||
# 0.2.10 (2015.8.27) |
|||
|
|||
#### Features |
|||
|
|||
- **custom placeholder icon** |
|||
|
|||
#### Improvements |
|||
|
|||
- **electron-renderer as target:** via [webpack-target-electron-renderer](https://github.com/chentsulin/webpack-target-electron-renderer) |
|||
|
|||
|
|||
# 0.2.9 (2015.8.18) |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **Fix hot-reload** |
|||
|
|||
|
|||
# 0.2.8 (2015.8.13) |
|||
|
|||
#### Improvements |
|||
|
|||
- **bump electron-debug** |
|||
- **babelrc** |
|||
- **organize webpack scripts** |
|||
|
|||
|
|||
# 0.2.7 (2015.7.9) |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **defaultProps:** fix typos. |
|||
|
|||
|
|||
# 0.2.6 (2015.7.3) |
|||
|
|||
#### Features |
|||
|
|||
- **menu** |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **package.js:** include webpack build. |
|||
|
|||
|
|||
# 0.2.5 (2015.7.1) |
|||
|
|||
#### Features |
|||
|
|||
- **NPM Script:** support multi-platform |
|||
- **package:** `--all` option |
|||
|
|||
|
|||
# 0.2.4 (2015.6.9) |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **Eslint:** typo, [#17](https://github.com/chentsulin/electron-react-boilerplate/issues/17) and improve `.eslintrc` |
|||
|
|||
|
|||
# 0.2.3 (2015.6.3) |
|||
|
|||
#### Features |
|||
|
|||
- **Package Version:** use latest release electron version as default |
|||
- **Ignore Large peerDependencies** |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **Npm Script:** typo, [#6](https://github.com/chentsulin/electron-react-boilerplate/pull/6) |
|||
- **Missing css:** [#7](https://github.com/chentsulin/electron-react-boilerplate/pull/7) |
|||
|
|||
|
|||
# 0.2.2 (2015.6.2) |
|||
|
|||
#### Features |
|||
|
|||
- **electron-debug** |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **Webpack:** add `.json` and `.node` to extensions for imitating node require. |
|||
- **Webpack:** set `node_modules` to externals for native module support. |
|||
|
|||
|
|||
# 0.2.1 (2015.5.30) |
|||
|
|||
#### Bug fixed |
|||
|
|||
- **Webpack:** #1, change build target to `atom`. |
|||
|
|||
|
|||
# 0.2.0 (2015.5.30) |
|||
|
|||
#### Features |
|||
|
|||
- **Ignore:** `test`, `tools`, `release` folder and devDependencies in `package.json`. |
|||
- **Support asar** |
|||
- **Support icon** |
|||
|
|||
|
|||
# 0.1.0 (2015.5.27) |
|||
|
|||
#### Features |
|||
|
|||
- **Webpack:** babel, react-hot, ... |
|||
- **Flux:** actions, api, components, containers, stores.. |
|||
- **Package:** darwin (osx), linux and win32 (windows) platform. |
@ -0,0 +1,22 @@ |
|||
The MIT License (MIT) |
|||
|
|||
Copyright (c) 2015-present C. T. Lin |
|||
|
|||
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. |
|||
|
@ -0,0 +1,334 @@ |
|||
# electron-react-boilerplate |
|||
|
|||
### A Boilerplate for Scalable Cross-Platform Desktop Apps |
|||
|
|||
<br/> |
|||
|
|||
[![Build Status][travis-image]][travis-url] |
|||
[![Appveyor Build Status][appveyor-image]][appveyor-url] |
|||
[![Dependency Status][david_img]][david_site] |
|||
[![Github Tag][github-tag-image]][github-tag-url] |
|||
[![Join the chat at https://gitter.im/electron-react-boilerplate/Lobby](https://badges.gitter.im/electron-react-boilerplate/Lobby.svg)](https://gitter.im/electron-react-boilerplate/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) |
|||
[![OpenCollective](https://opencollective.com/electron-react-boilerplate/backers/badge.svg)](#backers) |
|||
[![OpenCollective](https://opencollective.com/electron-react-boilerplate/sponsors/badge.svg)](#sponsors) |
|||
|
|||
[![React](/internals/img/react-padded-90.png)](https://facebook.github.io/react/) |
|||
[![Webpack](/internals/img/webpack-padded-90.png)](https://webpack.github.io/) |
|||
[![Redux](/internals/img/redux-padded-90.png)](http://redux.js.org/) |
|||
[![React Router](/internals/img/react-router-padded-90.png)](https://github.com/ReactTraining/react-router) |
|||
[![Flow](/internals/img/flow-padded-90.png)](https://flowtype.org/) |
|||
[![ESLint](/internals/img/eslint-padded-90.png)](http://eslint.org/) |
|||
[![Jest](/internals/img/jest-padded-90.png)](https://facebook.github.io/jest/) |
|||
[![Yarn](/internals/img/yarn-padded-90.png)](https://yarnpkg.com/) |
|||
|
|||
[Electron](http://electron.atom.io/) application boilerplate based on [React](https://facebook.github.io/react/), [Redux](https://github.com/reactjs/redux), [React Router](https://github.com/reactjs/react-router), [Webpack](http://webpack.github.io/docs/), [React Transform HMR](https://github.com/gaearon/react-transform-hmr) for rapid application development. |
|||
|
|||
## Screenshot |
|||
|
|||
![Electron Boilerplate Demo](https://cloud.githubusercontent.com/assets/3382565/10557547/b1f07a4e-74e3-11e5-8d27-79ab6947d429.gif) |
|||
|
|||
## Install |
|||
|
|||
* **Note: requires a node version >= 7 and an npm version >= 4.** |
|||
* **If you have installation or compilation issues with this project, please see [our debugging guide](https://github.com/chentsulin/electron-react-boilerplate/issues/400)** |
|||
|
|||
First, clone the repo via git: |
|||
|
|||
```bash |
|||
git clone --depth=1 https://github.com/chentsulin/electron-react-boilerplate.git your-project-name |
|||
``` |
|||
|
|||
And then install dependencies with yarn. |
|||
|
|||
```bash |
|||
$ cd your-project-name |
|||
$ yarn |
|||
``` |
|||
**Note**: If you can't use [yarn](https://github.com/yarnpkg/yarn) for some reason, try `npm install`. |
|||
|
|||
## Run |
|||
|
|||
Start the app in the `dev` environment. This starts the renderer process in [**hot-module-replacement**](https://webpack.js.org/guides/hmr-react/) mode and starts a server that sends hot updates to the renderer process: |
|||
|
|||
```bash |
|||
$ npm run dev |
|||
``` |
|||
|
|||
You Run these two commands __simultaneously__ in different console tabs: |
|||
|
|||
```bash |
|||
$ npm run start-renderer-dev |
|||
$ npm run start-main-dev |
|||
``` |
|||
|
|||
## Editor Configuration |
|||
**Atom** |
|||
```bash |
|||
apm install editorconfig es6-javascript atom-ternjs javascript-snippets linter linter-eslint language-babel autocomplete-modules file-icons |
|||
``` |
|||
|
|||
**VSCode** |
|||
* [Editorconfig](https://github.com/editorconfig/editorconfig-vscode) |
|||
* [ESLint](https://github.com/Microsoft/vscode-eslint) |
|||
* [Flow](https://github.com/flowtype/flow-for-vscode) |
|||
* [Babel](https://github.com/dzannotti/vscode-babel) |
|||
* [Jest](https://github.com/orta/vscode-jest) |
|||
* [ES6 Snippets](https://marketplace.visualstudio.com/items?itemName=xabikos.JavaScriptSnippets) |
|||
* [React Snippets](https://marketplace.visualstudio.com/items?itemName=xabikos.ReactSnippets) |
|||
:bulb: *If you are using the `flow-for-vscode` plugin, make sure to disable the `flowtype-errors/show-errors` eslint rule in the `.eslintrc` by setting it to `0`* |
|||
|
|||
**Sublime** |
|||
* [Editorconfig Integration](https://github.com/sindresorhus/editorconfig-sublime#readme) |
|||
* [Linting](https://github.com/SublimeLinter/SublimeLinter3) |
|||
* [ESLint Integration](https://github.com/roadhump/SublimeLinter-eslint) |
|||
* [Syntax Highlighting](https://github.com/babel/babel-sublime) |
|||
* [Autocompletion](https://github.com/ternjs/tern_for_sublime) |
|||
* [Node Snippets](https://packagecontrol.io/packages/JavaScript%20%26%20NodeJS%20Snippets) |
|||
* [ES6 Snippets](https://packagecontrol.io/packages/ES6-Toolkit) |
|||
|
|||
**Others** |
|||
* [Editorconfig](http://editorconfig.org/#download) |
|||
* [ESLint](http://eslint.org/docs/user-guide/integrations#editors) |
|||
* Babel Syntax Plugin |
|||
|
|||
## DevTools |
|||
|
|||
#### Toggle Chrome DevTools |
|||
|
|||
- OS X: <kbd>Cmd</kbd> <kbd>Alt</kbd> <kbd>I</kbd> or <kbd>F12</kbd> |
|||
- Linux: <kbd>Ctrl</kbd> <kbd>Shift</kbd> <kbd>I</kbd> or <kbd>F12</kbd> |
|||
- Windows: <kbd>Ctrl</kbd> <kbd>Shift</kbd> <kbd>I</kbd> or <kbd>F12</kbd> |
|||
|
|||
*See [electron-debug](https://github.com/sindresorhus/electron-debug) for more information.* |
|||
|
|||
#### DevTools extension |
|||
|
|||
This boilerplate includes the following DevTools extensions: |
|||
|
|||
* [Devtron](https://github.com/electron/devtron) - Install via [electron-debug](https://github.com/sindresorhus/electron-debug). |
|||
* [React Developer Tools](https://github.com/facebook/react-devtools) - Install via [electron-devtools-installer](https://github.com/GPMDP/electron-devtools-installer). |
|||
* [Redux DevTools](https://github.com/zalmoxisus/redux-devtools-extension) - Install via [electron-devtools-installer](https://github.com/GPMDP/electron-devtools-installer). |
|||
|
|||
You can find the tabs on Chrome DevTools. |
|||
|
|||
If you want to update extensions version, please set `UPGRADE_EXTENSIONS` env, just run: |
|||
|
|||
```bash |
|||
$ UPGRADE_EXTENSIONS=1 npm run dev |
|||
|
|||
# For Windows |
|||
$ set UPGRADE_EXTENSIONS=1 && npm run dev |
|||
``` |
|||
|
|||
:bulb: You can debug your production build with devtools by simply setting the `DEBUG_PROD` env variable: |
|||
``` |
|||
DEBUG_PROD=true npm run package |
|||
``` |
|||
|
|||
|
|||
## CSS Modules |
|||
|
|||
This boilerplate is configured to use [css-modules](https://github.com/css-modules/css-modules) out of the box. |
|||
|
|||
All `.css` file extensions will use css-modules unless it has `.global.css`. |
|||
|
|||
If you need global styles, stylesheets with `.global.css` will not go through the |
|||
css-modules loader. e.g. `app.global.css` |
|||
|
|||
If you want to import global css libraries (like `bootstrap`), you can just write the following code in `.global.css`: |
|||
|
|||
```css |
|||
@import "~bootstrap/dist/css/bootstrap.css"; |
|||
``` |
|||
|
|||
## Sass support |
|||
|
|||
If you want to use Sass in your app, you only need to import `.sass` files instead of `.css` once: |
|||
```js |
|||
import './app.global.scss'; |
|||
``` |
|||
|
|||
## Packaging |
|||
|
|||
To package apps for the local platform: |
|||
|
|||
```bash |
|||
$ npm run package |
|||
``` |
|||
|
|||
To package apps for all platforms: |
|||
|
|||
First, refer to [Multi Platform Build](https://github.com/electron-userland/electron-builder/wiki/Multi-Platform-Build) for dependencies. |
|||
|
|||
Then, |
|||
```bash |
|||
$ npm run package-all |
|||
``` |
|||
|
|||
To package apps with options: |
|||
|
|||
```bash |
|||
$ npm run package -- --[option] |
|||
``` |
|||
|
|||
## Further commands |
|||
|
|||
To run the application without packaging run |
|||
|
|||
```bash |
|||
$ npm run build |
|||
$ npm start |
|||
``` |
|||
|
|||
To run End-to-End Test |
|||
|
|||
```bash |
|||
$ npm run build |
|||
$ npm run test-e2e |
|||
``` |
|||
|
|||
#### Options |
|||
|
|||
See [electron-builder CLI Usage](https://github.com/electron-userland/electron-builder#cli-usage) |
|||
|
|||
## How to add modules to the project |
|||
|
|||
You will need to add other modules to this boilerplate, depending on the requirements of your project. For example, you may want to add [node-postgres](https://github.com/brianc/node-postgres) to communicate with PostgreSQL database, or |
|||
[material-ui](http://www.material-ui.com/) to reuse react UI components. |
|||
|
|||
⚠️ Please read following section before installing any dependencies ⚠️ |
|||
|
|||
### Module Structure |
|||
|
|||
This boilerplate uses a [two package.json structure](https://github.com/electron-userland/electron-builder/wiki/Two-package.json-Structure). This means, you will have two `package.json` files. |
|||
|
|||
1. `./package.json` in the root of your project |
|||
1. `./app/package.json` inside `app` folder |
|||
|
|||
### Which `package.json` file to use |
|||
|
|||
**Rule of thumb** is: all modules go into `./package.json` except native modules. Native modules go into `./app/package.json`. |
|||
|
|||
1. If the module is native to a platform (like node-postgres) or otherwise should be included with the published package (i.e. bcrypt, openbci), it should be listed under `dependencies` in `./app/package.json`. |
|||
2. If a module is `import`ed by another module, include it in `dependencies` in `./package.json`. See [this ESLint rule](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-extraneous-dependencies.md). Examples of such modules are `material-ui`, `redux-form`, and `moment`. |
|||
3. Otherwise, modules used for building, testing and debugging should be included in `devDependencies` in `./package.json`. |
|||
|
|||
### Further Readings |
|||
|
|||
See the wiki page, [Module Structure — Two package.json Structure](https://github.com/chentsulin/electron-react-boilerplate/wiki/Module-Structure----Two-package.json-Structure) to understand what is native module, the rationale behind two package.json structure and more. |
|||
|
|||
For an example app that uses this boilerplate and packages native dependencies, see [erb-sqlite-example](https://github.com/amilajack/erb-sqlite-example). |
|||
|
|||
## Static Type Checking |
|||
This project comes with Flow support out of the box! You can annotate your code with types, [get Flow errors as ESLint errors](https://github.com/amilajack/eslint-plugin-flowtype-errors), and get [type errors during runtime](https://github.com/codemix/flow-runtime) during development. Types are completely optional. |
|||
|
|||
## Native-like UI |
|||
|
|||
If you want to have native-like User Interface (OS X El Capitan and Windows 10), [react-desktop](https://github.com/gabrielbull/react-desktop) may perfect suit for you. |
|||
|
|||
## Dispatching redux actions from main process |
|||
|
|||
see discusses in [#118](https://github.com/chentsulin/electron-react-boilerplate/issues/118) and [#108](https://github.com/chentsulin/electron-react-boilerplate/issues/108) |
|||
|
|||
## How to keep the boilerplate updated |
|||
|
|||
If your application is a fork from this repo, you can add this repo to another git remote: |
|||
|
|||
```sh |
|||
git remote add upstream https://github.com/chentsulin/electron-react-boilerplate.git |
|||
``` |
|||
|
|||
Then, use git to merge some latest commits: |
|||
|
|||
```sh |
|||
git pull upstream master |
|||
``` |
|||
|
|||
## Maintainers |
|||
|
|||
- [C. T. Lin](https://github.com/chentsulin) |
|||
- [Jhen-Jie Hong](https://github.com/jhen0409) |
|||
- [Amila Welihinda](https://github.com/amilajack) |
|||
|
|||
## Backers |
|||
|
|||
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/electron-react-boilerplate#backer)] |
|||
|
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/0/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/0/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/1/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/1/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/2/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/2/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/3/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/3/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/4/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/4/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/5/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/5/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/6/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/6/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/7/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/7/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/8/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/8/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/9/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/9/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/10/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/10/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/11/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/11/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/12/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/12/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/13/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/13/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/14/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/14/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/15/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/15/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/16/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/16/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/17/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/17/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/18/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/18/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/19/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/19/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/20/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/20/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/21/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/21/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/22/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/22/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/23/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/23/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/24/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/24/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/25/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/25/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/26/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/26/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/27/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/27/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/28/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/28/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/backer/29/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/29/avatar.svg"></a> |
|||
|
|||
## Sponsors |
|||
|
|||
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/electron-react-boilerplate#sponsor)] |
|||
|
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/0/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/0/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/1/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/1/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/2/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/2/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/3/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/3/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/4/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/4/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/5/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/5/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/6/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/6/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/7/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/7/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/8/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/8/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/9/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/9/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/10/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/10/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/11/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/11/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/12/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/12/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/13/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/13/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/14/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/14/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/15/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/15/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/16/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/16/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/17/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/17/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/18/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/18/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/19/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/19/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/20/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/20/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/21/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/21/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/22/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/22/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/23/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/23/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/24/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/24/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/25/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/25/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/26/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/26/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/27/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/27/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/28/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/28/avatar.svg"></a> |
|||
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/29/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/29/avatar.svg"></a> |
|||
|
|||
## License |
|||
MIT © [C. T. Lin](https://github.com/chentsulin) |
|||
|
|||
[npm-image]: https://img.shields.io/npm/v/electron-react-boilerplate.svg?style=flat-square |
|||
[github-tag-image]: https://img.shields.io/github/tag/chentsulin/electron-react-boilerplate.svg |
|||
[github-tag-url]: https://github.com/chentsulin/electron-react-boilerplate/releases/latest |
|||
[travis-image]: https://travis-ci.org/chentsulin/electron-react-boilerplate.svg?branch=master |
|||
[travis-url]: https://travis-ci.org/chentsulin/electron-react-boilerplate |
|||
[appveyor-image]: https://ci.appveyor.com/api/projects/status/github/chentsulin/electron-react-boilerplate?svg=true |
|||
[appveyor-url]: https://ci.appveyor.com/project/chentsulin/electron-react-boilerplate/branch/master |
|||
[david_img]: https://img.shields.io/david/chentsulin/electron-react-boilerplate.svg |
|||
[david_site]: https://david-dm.org/chentsulin/electron-react-boilerplate |
@ -0,0 +1,22 @@ |
|||
{ |
|||
"rules": { |
|||
"flowtype/boolean-style": [2, "boolean"], |
|||
"flowtype/define-flow-type": 1, |
|||
"flowtype/delimiter-dangle": [2, "never"], |
|||
"flowtype/generic-spacing": [2, "never"], |
|||
"flowtype/no-primitive-constructor-types": 2, |
|||
"flowtype/no-weak-types": 1, |
|||
"flowtype/object-type-delimiter": [2, "comma"], |
|||
"flowtype/require-parameter-type": 0, |
|||
"flowtype/require-return-type": 0, |
|||
"flowtype/require-valid-file-annotation": 0, |
|||
"flowtype/semi": [2, "always"], |
|||
"flowtype/space-after-type-colon": [2, "always"], |
|||
"flowtype/space-before-generic-bracket": [2, "never"], |
|||
"flowtype/space-before-type-colon": [2, "never"], |
|||
"flowtype/union-intersection-spacing": [2, "always"], |
|||
"flowtype/use-flow-type": 2, |
|||
"flowtype/valid-syntax": 2, |
|||
"flowtype-errors/show-errors": 2 |
|||
} |
|||
} |
@ -0,0 +1,41 @@ |
|||
// @flow
|
|||
import type { counterStateType } from '../reducers/counter'; |
|||
|
|||
type actionType = { |
|||
+type: string |
|||
}; |
|||
|
|||
export const INCREMENT_COUNTER = 'INCREMENT_COUNTER'; |
|||
export const DECREMENT_COUNTER = 'DECREMENT_COUNTER'; |
|||
|
|||
export function increment() { |
|||
return { |
|||
type: INCREMENT_COUNTER |
|||
}; |
|||
} |
|||
|
|||
export function decrement() { |
|||
return { |
|||
type: DECREMENT_COUNTER |
|||
}; |
|||
} |
|||
|
|||
export function incrementIfOdd() { |
|||
return (dispatch: (action: actionType) => void, getState: () => counterStateType) => { |
|||
const { counter } = getState(); |
|||
|
|||
if (counter % 2 === 0) { |
|||
return; |
|||
} |
|||
|
|||
dispatch(increment()); |
|||
}; |
|||
} |
|||
|
|||
export function incrementAsync(delay: number = 1000) { |
|||
return (dispatch: (action: actionType) => void) => { |
|||
setTimeout(() => { |
|||
dispatch(increment()); |
|||
}, delay); |
|||
}; |
|||
} |
@ -0,0 +1,39 @@ |
|||
@import "~font-awesome/css/font-awesome.css"; |
|||
|
|||
body { |
|||
position: relative; |
|||
color: white; |
|||
height: 100vh; |
|||
background-color: #000; |
|||
background-image: linear-gradient(45deg, rgba(29, 29, 29, 0.5) 10%, rgba(235, 184, 100, 0.7)); |
|||
font-family: Arial, Helvetica, Helvetica Neue, serif; |
|||
overflow-y: hidden; |
|||
} |
|||
|
|||
h2 { |
|||
margin: 0; |
|||
font-size: 2.25rem; |
|||
font-weight: bold; |
|||
letter-spacing: -0.025em; |
|||
color: #fff; |
|||
} |
|||
|
|||
p { |
|||
font-size: 24px; |
|||
} |
|||
|
|||
li { |
|||
list-style: none; |
|||
} |
|||
|
|||
a { |
|||
color: white; |
|||
opacity: 0.75; |
|||
text-decoration: none; |
|||
} |
|||
|
|||
a:hover { |
|||
opacity: 1; |
|||
text-decoration: none; |
|||
cursor: pointer; |
|||
} |
@ -0,0 +1,46 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<meta charset="utf-8"> |
|||
<title>Hello Electron React!</title> |
|||
<script> |
|||
(function() { |
|||
if (!process.env.HOT) { |
|||
const link = document.createElement('link'); |
|||
link.rel = 'stylesheet'; |
|||
link.href = './dist/style.css'; |
|||
// HACK: Writing the script path should be done with webpack |
|||
document.getElementsByTagName('head')[0].appendChild(link); |
|||
} |
|||
}()); |
|||
</script> |
|||
</head> |
|||
<body> |
|||
<div id="root"></div> |
|||
<script> |
|||
{ |
|||
const scripts = []; |
|||
|
|||
// Dynamically insert the DLL script in development env in the |
|||
// renderer process |
|||
if (process.env.NODE_ENV === 'development') { |
|||
scripts.push('../dll/renderer.dev.dll.js'); |
|||
} |
|||
|
|||
// Dynamically insert the bundled app script in the renderer process |
|||
const port = process.env.PORT || 1212; |
|||
scripts.push( |
|||
(process.env.HOT) |
|||
? 'http://localhost:' + port + '/dist/renderer.dev.js' |
|||
: './dist/renderer.prod.js' |
|||
); |
|||
|
|||
document.write( |
|||
scripts |
|||
.map(script => '<script defer src="' + script + '"><\/script>') |
|||
.join('') |
|||
); |
|||
} |
|||
</script> |
|||
</body> |
|||
</html> |
@ -0,0 +1,37 @@ |
|||
.backButton { |
|||
position: absolute; |
|||
} |
|||
|
|||
.counter { |
|||
position: absolute; |
|||
top: 30%; |
|||
left: 45%; |
|||
font-size: 10rem; |
|||
font-weight: bold; |
|||
letter-spacing: -0.025em; |
|||
} |
|||
|
|||
.btnGroup { |
|||
position: relative; |
|||
top: 500px; |
|||
width: 480px; |
|||
margin: 0 auto; |
|||
} |
|||
|
|||
.btn { |
|||
font-size: 1.6rem; |
|||
font-weight: bold; |
|||
background-color: #fff; |
|||
border-radius: 50%; |
|||
margin: 10px; |
|||
width: 100px; |
|||
height: 100px; |
|||
opacity: 0.7; |
|||
cursor: pointer; |
|||
font-family: Arial, Helvetica, Helvetica Neue; |
|||
} |
|||
|
|||
.btn:hover { |
|||
color: white; |
|||
background-color: rgba(0, 0, 0, 0.5); |
|||
} |
@ -0,0 +1,42 @@ |
|||
// @flow
|
|||
import React, { Component } from 'react'; |
|||
import { Link } from 'react-router-dom'; |
|||
import styles from './Counter.css'; |
|||
|
|||
class Counter extends Component { |
|||
props: { |
|||
increment: () => void, |
|||
incrementIfOdd: () => void, |
|||
incrementAsync: () => void, |
|||
decrement: () => void, |
|||
counter: number |
|||
}; |
|||
|
|||
render() { |
|||
const { increment, incrementIfOdd, incrementAsync, decrement, counter } = this.props; |
|||
return ( |
|||
<div> |
|||
<div className={styles.backButton} data-tid="backButton"> |
|||
<Link to="/"> |
|||
<i className="fa fa-arrow-left fa-3x" /> |
|||
</Link> |
|||
</div> |
|||
<div className={`counter ${styles.counter}`} data-tid="counter"> |
|||
{counter} |
|||
</div> |
|||
<div className={styles.btnGroup}> |
|||
<button className={styles.btn} onClick={increment} data-tclass="btn"> |
|||
<i className="fa fa-plus" /> |
|||
</button> |
|||
<button className={styles.btn} onClick={decrement} data-tclass="btn"> |
|||
<i className="fa fa-minus" /> |
|||
</button> |
|||
<button className={styles.btn} onClick={incrementIfOdd} data-tclass="btn">odd</button> |
|||
<button className={styles.btn} onClick={() => incrementAsync()} data-tclass="btn">async</button> |
|||
</div> |
|||
</div> |
|||
); |
|||
} |
|||
} |
|||
|
|||
export default Counter; |
@ -0,0 +1,14 @@ |
|||
.container { |
|||
position: absolute; |
|||
top: 30%; |
|||
left: 10px; |
|||
text-align: center; |
|||
} |
|||
|
|||
.container h2 { |
|||
font-size: 5rem; |
|||
} |
|||
|
|||
.container a { |
|||
font-size: 1.4rem; |
|||
} |
@ -0,0 +1,17 @@ |
|||
// @flow
|
|||
import React, { Component } from 'react' |
|||
import { Link } from 'react-router-dom' |
|||
import InlineSVG from 'react-inline-svg' |
|||
import styles from './Home.css' |
|||
|
|||
export default class Home extends Component { |
|||
render() { |
|||
return ( |
|||
<div> |
|||
<div className={styles.container} data-tid="container"> |
|||
|
|||
</div> |
|||
</div> |
|||
); |
|||
} |
|||
} |
@ -0,0 +1,17 @@ |
|||
// @flow
|
|||
import React, { Component } from 'react'; |
|||
import type { Children } from 'react'; |
|||
|
|||
export default class App extends Component { |
|||
props: { |
|||
children: Children |
|||
}; |
|||
|
|||
render() { |
|||
return ( |
|||
<div> |
|||
{this.props.children} |
|||
</div> |
|||
); |
|||
} |
|||
} |
@ -0,0 +1,16 @@ |
|||
import { bindActionCreators } from 'redux'; |
|||
import { connect } from 'react-redux'; |
|||
import Counter from '../components/Counter'; |
|||
import * as CounterActions from '../actions/counter'; |
|||
|
|||
function mapStateToProps(state) { |
|||
return { |
|||
counter: state.counter |
|||
}; |
|||
} |
|||
|
|||
function mapDispatchToProps(dispatch) { |
|||
return bindActionCreators(CounterActions, dispatch); |
|||
} |
|||
|
|||
export default connect(mapStateToProps, mapDispatchToProps)(Counter); |
@ -0,0 +1,11 @@ |
|||
// @flow
|
|||
import React, { Component } from 'react'; |
|||
import Home from '../components/Home'; |
|||
|
|||
export default class HomePage extends Component { |
|||
render() { |
|||
return ( |
|||
<Home /> |
|||
); |
|||
} |
|||
} |
@ -0,0 +1,20 @@ |
|||
// @flow
|
|||
import React from 'react'; |
|||
import { Provider } from 'react-redux'; |
|||
import { ConnectedRouter } from 'react-router-redux'; |
|||
import Routes from '../routes'; |
|||
|
|||
type RootType = { |
|||
store: {}, |
|||
history: {} |
|||
}; |
|||
|
|||
export default function Root({ store, history }: RootType) { |
|||
return ( |
|||
<Provider store={store}> |
|||
<ConnectedRouter history={history}> |
|||
<Routes /> |
|||
</ConnectedRouter> |
|||
</Provider> |
|||
); |
|||
} |
@ -0,0 +1,27 @@ |
|||
import React from 'react'; |
|||
import { render } from 'react-dom'; |
|||
import { AppContainer } from 'react-hot-loader'; |
|||
import Root from './containers/Root'; |
|||
import { configureStore, history } from './store/configureStore'; |
|||
import './app.global.css'; |
|||
|
|||
const store = configureStore(); |
|||
|
|||
render( |
|||
<AppContainer> |
|||
<Root store={store} history={history} /> |
|||
</AppContainer>, |
|||
document.getElementById('root') |
|||
); |
|||
|
|||
if (module.hot) { |
|||
module.hot.accept('./containers/Root', () => { |
|||
const NextRoot = require('./containers/Root'); // eslint-disable-line global-require
|
|||
render( |
|||
<AppContainer> |
|||
<NextRoot store={store} history={history} /> |
|||
</AppContainer>, |
|||
document.getElementById('root') |
|||
); |
|||
}); |
|||
} |
@ -0,0 +1,86 @@ |
|||
/* eslint global-require: 1, flowtype-errors/show-errors: 0 */ |
|||
|
|||
/** |
|||
* This module executes inside of electron's main process. You can start |
|||
* electron renderer process from here and communicate with the other processes |
|||
* through IPC. |
|||
* |
|||
* When running `npm run build` or `npm run build-main`, this file is compiled to |
|||
* `./app/main.prod.js` using webpack. This gives us some performance wins. |
|||
* |
|||
* @flow |
|||
*/ |
|||
import { app, BrowserWindow } from 'electron'; |
|||
import MenuBuilder from './menu'; |
|||
|
|||
let mainWindow = null; |
|||
|
|||
if (process.env.NODE_ENV === 'production') { |
|||
const sourceMapSupport = require('source-map-support'); |
|||
sourceMapSupport.install(); |
|||
} |
|||
|
|||
if (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true') { |
|||
require('electron-debug')(); |
|||
const path = require('path'); |
|||
const p = path.join(__dirname, '..', 'app', 'node_modules'); |
|||
require('module').globalPaths.push(p); |
|||
} |
|||
|
|||
const installExtensions = async () => { |
|||
const installer = require('electron-devtools-installer'); |
|||
const forceDownload = !!process.env.UPGRADE_EXTENSIONS; |
|||
const extensions = [ |
|||
'REACT_DEVELOPER_TOOLS', |
|||
'REDUX_DEVTOOLS' |
|||
]; |
|||
|
|||
return Promise |
|||
.all(extensions.map(name => installer.default(installer[name], forceDownload))) |
|||
.catch(console.log); |
|||
}; |
|||
|
|||
|
|||
/** |
|||
* Add event listeners... |
|||
*/ |
|||
|
|||
app.on('window-all-closed', () => { |
|||
// Respect the OSX convention of having the application in memory even
|
|||
// after all windows have been closed
|
|||
if (process.platform !== 'darwin') { |
|||
app.quit(); |
|||
} |
|||
}); |
|||
|
|||
|
|||
app.on('ready', async () => { |
|||
if (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true') { |
|||
await installExtensions(); |
|||
} |
|||
|
|||
mainWindow = new BrowserWindow({ |
|||
show: false, |
|||
width: 1024, |
|||
height: 728 |
|||
}); |
|||
|
|||
mainWindow.loadURL(`file://${__dirname}/app.html`); |
|||
|
|||
// @TODO: Use 'ready-to-show' event
|
|||
// https://github.com/electron/electron/blob/master/docs/api/browser-window.md#using-ready-to-show-event
|
|||
mainWindow.webContents.on('did-finish-load', () => { |
|||
if (!mainWindow) { |
|||
throw new Error('"mainWindow" is not defined'); |
|||
} |
|||
mainWindow.show(); |
|||
mainWindow.focus(); |
|||
}); |
|||
|
|||
mainWindow.on('closed', () => { |
|||
mainWindow = null; |
|||
}); |
|||
|
|||
const menuBuilder = new MenuBuilder(mainWindow); |
|||
menuBuilder.buildMenu(); |
|||
}); |
@ -0,0 +1,186 @@ |
|||
// @flow
|
|||
import { app, Menu, shell, BrowserWindow } from 'electron'; |
|||
|
|||
export default class MenuBuilder { |
|||
mainWindow: BrowserWindow; |
|||
|
|||
constructor(mainWindow: BrowserWindow) { |
|||
this.mainWindow = mainWindow; |
|||
} |
|||
|
|||
buildMenu() { |
|||
if (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true') { |
|||
this.setupDevelopmentEnvironment(); |
|||
} |
|||
|
|||
let template; |
|||
|
|||
if (process.platform === 'darwin') { |
|||
template = this.buildDarwinTemplate(); |
|||
} else { |
|||
template = this.buildDefaultTemplate(); |
|||
} |
|||
|
|||
const menu = Menu.buildFromTemplate(template); |
|||
Menu.setApplicationMenu(menu); |
|||
|
|||
return menu; |
|||
} |
|||
|
|||
setupDevelopmentEnvironment() { |
|||
this.mainWindow.openDevTools(); |
|||
this.mainWindow.webContents.on('context-menu', (e, props) => { |
|||
const { x, y } = props; |
|||
|
|||
Menu |
|||
.buildFromTemplate([{ |
|||
label: 'Inspect element', |
|||
click: () => { |
|||
this.mainWindow.inspectElement(x, y); |
|||
} |
|||
}]) |
|||
.popup(this.mainWindow); |
|||
}); |
|||
} |
|||
|
|||
buildDarwinTemplate() { |
|||
const subMenuAbout = { |
|||
label: 'Electron', |
|||
submenu: [ |
|||
{ label: 'About ElectronReact', selector: 'orderFrontStandardAboutPanel:' }, |
|||
{ type: 'separator' }, |
|||
{ label: 'Services', submenu: [] }, |
|||
{ type: 'separator' }, |
|||
{ label: 'Hide ElectronReact', accelerator: 'Command+H', selector: 'hide:' }, |
|||
{ label: 'Hide Others', accelerator: 'Command+Shift+H', selector: 'hideOtherApplications:' }, |
|||
{ label: 'Show All', selector: 'unhideAllApplications:' }, |
|||
{ type: 'separator' }, |
|||
{ label: 'Quit', accelerator: 'Command+Q', click: () => { app.quit(); } } |
|||
] |
|||
}; |
|||
const subMenuEdit = { |
|||
label: 'Edit', |
|||
submenu: [ |
|||
{ label: 'Undo', accelerator: 'Command+Z', selector: 'undo:' }, |
|||
{ label: 'Redo', accelerator: 'Shift+Command+Z', selector: 'redo:' }, |
|||
{ type: 'separator' }, |
|||
{ label: 'Cut', accelerator: 'Command+X', selector: 'cut:' }, |
|||
{ label: 'Copy', accelerator: 'Command+C', selector: 'copy:' }, |
|||
{ label: 'Paste', accelerator: 'Command+V', selector: 'paste:' }, |
|||
{ label: 'Select All', accelerator: 'Command+A', selector: 'selectAll:' } |
|||
] |
|||
}; |
|||
const subMenuViewDev = { |
|||
label: 'View', |
|||
submenu: [ |
|||
{ label: 'Reload', accelerator: 'Command+R', click: () => { this.mainWindow.webContents.reload(); } }, |
|||
{ label: 'Toggle Full Screen', accelerator: 'Ctrl+Command+F', click: () => { this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen()); } }, |
|||
{ label: 'Toggle Developer Tools', accelerator: 'Alt+Command+I', click: () => { this.mainWindow.toggleDevTools(); } } |
|||
] |
|||
}; |
|||
const subMenuViewProd = { |
|||
label: 'View', |
|||
submenu: [ |
|||
{ label: 'Toggle Full Screen', accelerator: 'Ctrl+Command+F', click: () => { this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen()); } } |
|||
] |
|||
}; |
|||
const subMenuWindow = { |
|||
label: 'Window', |
|||
submenu: [ |
|||
{ label: 'Minimize', accelerator: 'Command+M', selector: 'performMiniaturize:' }, |
|||
{ label: 'Close', accelerator: 'Command+W', selector: 'performClose:' }, |
|||
{ type: 'separator' }, |
|||
{ label: 'Bring All to Front', selector: 'arrangeInFront:' } |
|||
] |
|||
}; |
|||
const subMenuHelp = { |
|||
label: 'Help', |
|||
submenu: [ |
|||
{ label: 'Learn More', click() { shell.openExternal('http://electron.atom.io'); } }, |
|||
{ label: 'Documentation', click() { shell.openExternal('https://github.com/atom/electron/tree/master/docs#readme'); } }, |
|||
{ label: 'Community Discussions', click() { shell.openExternal('https://discuss.atom.io/c/electron'); } }, |
|||
{ label: 'Search Issues', click() { shell.openExternal('https://github.com/atom/electron/issues'); } } |
|||
] |
|||
}; |
|||
|
|||
const subMenuView = process.env.NODE_ENV === 'development' |
|||
? subMenuViewDev |
|||
: subMenuViewProd; |
|||
|
|||
return [ |
|||
subMenuAbout, |
|||
subMenuEdit, |
|||
subMenuView, |
|||
subMenuWindow, |
|||
subMenuHelp |
|||
]; |
|||
} |
|||
|
|||
buildDefaultTemplate() { |
|||
const templateDefault = [{ |
|||
label: '&File', |
|||
submenu: [{ |
|||
label: '&Open', |
|||
accelerator: 'Ctrl+O' |
|||
}, { |
|||
label: '&Close', |
|||
accelerator: 'Ctrl+W', |
|||
click: () => { |
|||
this.mainWindow.close(); |
|||
} |
|||
}] |
|||
}, { |
|||
label: '&View', |
|||
submenu: (process.env.NODE_ENV === 'development') ? [{ |
|||
label: '&Reload', |
|||
accelerator: 'Ctrl+R', |
|||
click: () => { |
|||
this.mainWindow.webContents.reload(); |
|||
} |
|||
}, { |
|||
label: 'Toggle &Full Screen', |
|||
accelerator: 'F11', |
|||
click: () => { |
|||
this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen()); |
|||
} |
|||
}, { |
|||
label: 'Toggle &Developer Tools', |
|||
accelerator: 'Alt+Ctrl+I', |
|||
click: () => { |
|||
this.mainWindow.toggleDevTools(); |
|||
} |
|||
}] : [{ |
|||
label: 'Toggle &Full Screen', |
|||
accelerator: 'F11', |
|||
click: () => { |
|||
this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen()); |
|||
} |
|||
}] |
|||
}, { |
|||
label: 'Help', |
|||
submenu: [{ |
|||
label: 'Learn More', |
|||
click() { |
|||
shell.openExternal('http://electron.atom.io'); |
|||
} |
|||
}, { |
|||
label: 'Documentation', |
|||
click() { |
|||
shell.openExternal('https://github.com/atom/electron/tree/master/docs#readme'); |
|||
} |
|||
}, { |
|||
label: 'Community Discussions', |
|||
click() { |
|||
shell.openExternal('https://discuss.atom.io/c/electron'); |
|||
} |
|||
}, { |
|||
label: 'Search Issues', |
|||
click() { |
|||
shell.openExternal('https://github.com/atom/electron/issues'); |
|||
} |
|||
}] |
|||
}]; |
|||
|
|||
return templateDefault; |
|||
} |
|||
} |
@ -0,0 +1,18 @@ |
|||
{ |
|||
"name": "electron-react-boilerplate", |
|||
"productName": "electron-react-boilerplate", |
|||
"version": "1.0.0", |
|||
"description": "Electron application boilerplate based on React, React Router, Webpack, React Hot Loader for rapid application development", |
|||
"main": "./main.prod.js", |
|||
"author": { |
|||
"name": "C. T. Lin", |
|||
"email": "chentsulin@gmail.com", |
|||
"url": "https://github.com/chentsulin" |
|||
}, |
|||
"scripts": { |
|||
"postinstall": "npm rebuild --runtime=electron --target=1.6.6 --disturl=https://atom.io/download/atom-shell --build-from-source" |
|||
}, |
|||
"license": "MIT", |
|||
"dependencies": { |
|||
} |
|||
} |
@ -0,0 +1,21 @@ |
|||
// @flow
|
|||
import { INCREMENT_COUNTER, DECREMENT_COUNTER } from '../actions/counter'; |
|||
|
|||
export type counterStateType = { |
|||
+counter: number |
|||
}; |
|||
|
|||
type actionType = { |
|||
+type: string |
|||
}; |
|||
|
|||
export default function counter(state: number = 0, action: actionType) { |
|||
switch (action.type) { |
|||
case INCREMENT_COUNTER: |
|||
return state + 1; |
|||
case DECREMENT_COUNTER: |
|||
return state - 1; |
|||
default: |
|||
return state; |
|||
} |
|||
} |
@ -0,0 +1,11 @@ |
|||
// @flow
|
|||
import { combineReducers } from 'redux'; |
|||
import { routerReducer as router } from 'react-router-redux'; |
|||
import counter from './counter'; |
|||
|
|||
const rootReducer = combineReducers({ |
|||
counter, |
|||
router, |
|||
}); |
|||
|
|||
export default rootReducer; |
@ -0,0 +1,15 @@ |
|||
/* eslint flowtype-errors/show-errors: 0 */ |
|||
import React from 'react'; |
|||
import { Switch, Route } from 'react-router'; |
|||
import App from './containers/App'; |
|||
import HomePage from './containers/HomePage'; |
|||
import CounterPage from './containers/CounterPage'; |
|||
|
|||
export default () => ( |
|||
<App> |
|||
<Switch> |
|||
<Route path="/counter" component={CounterPage} /> |
|||
<Route path="/" component={HomePage} /> |
|||
</Switch> |
|||
</App> |
|||
); |
@ -0,0 +1,62 @@ |
|||
import { createStore, applyMiddleware, compose } from 'redux'; |
|||
import thunk from 'redux-thunk'; |
|||
import { createHashHistory } from 'history'; |
|||
import { routerMiddleware, routerActions } from 'react-router-redux'; |
|||
import { createLogger } from 'redux-logger'; |
|||
import rootReducer from '../reducers'; |
|||
import * as counterActions from '../actions/counter'; |
|||
import type { counterStateType } from '../reducers/counter'; |
|||
|
|||
const history = createHashHistory(); |
|||
|
|||
const configureStore = (initialState?: counterStateType) => { |
|||
// Redux Configuration
|
|||
const middleware = []; |
|||
const enhancers = []; |
|||
|
|||
// Thunk Middleware
|
|||
middleware.push(thunk); |
|||
|
|||
// Logging Middleware
|
|||
const logger = createLogger({ |
|||
level: 'info', |
|||
collapsed: true |
|||
}); |
|||
middleware.push(logger); |
|||
|
|||
// Router Middleware
|
|||
const router = routerMiddleware(history); |
|||
middleware.push(router); |
|||
|
|||
// Redux DevTools Configuration
|
|||
const actionCreators = { |
|||
...counterActions, |
|||
...routerActions, |
|||
}; |
|||
// If Redux DevTools Extension is installed use it, otherwise use Redux compose
|
|||
/* eslint-disable no-underscore-dangle */ |
|||
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ |
|||
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ |
|||
// Options: http://zalmoxisus.github.io/redux-devtools-extension/API/Arguments.html
|
|||
actionCreators, |
|||
}) |
|||
: compose; |
|||
/* eslint-enable no-underscore-dangle */ |
|||
|
|||
// Apply Middleware & Compose Enhancers
|
|||
enhancers.push(applyMiddleware(...middleware)); |
|||
const enhancer = composeEnhancers(...enhancers); |
|||
|
|||
// Create Store
|
|||
const store = createStore(rootReducer, initialState, enhancer); |
|||
|
|||
if (module.hot) { |
|||
module.hot.accept('../reducers', () => |
|||
store.replaceReducer(require('../reducers')) // eslint-disable-line global-require
|
|||
); |
|||
} |
|||
|
|||
return store; |
|||
}; |
|||
|
|||
export default { configureStore, history }; |
@ -0,0 +1,6 @@ |
|||
// @flow
|
|||
if (process.env.NODE_ENV === 'production') { |
|||
module.exports = require('./configureStore.prod'); // eslint-disable-line global-require
|
|||
} else { |
|||
module.exports = require('./configureStore.dev'); // eslint-disable-line global-require
|
|||
} |
@ -0,0 +1,17 @@ |
|||
// @flow
|
|||
import { createStore, applyMiddleware } from 'redux'; |
|||
import thunk from 'redux-thunk'; |
|||
import { createBrowserHistory } from 'history'; |
|||
import { routerMiddleware } from 'react-router-redux'; |
|||
import rootReducer from '../reducers'; |
|||
import type { counterStateType } from '../reducers/counter'; |
|||
|
|||
const history = createBrowserHistory(); |
|||
const router = routerMiddleware(history); |
|||
const enhancer = applyMiddleware(thunk, router); |
|||
|
|||
function configureStore(initialState?: counterStateType) { |
|||
return createStore(rootReducer, initialState, enhancer); |
|||
} |
|||
|
|||
export default { configureStore, history }; |
@ -0,0 +1,4 @@ |
|||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. |
|||
# yarn lockfile v1 |
|||
|
|||
|
@ -0,0 +1,35 @@ |
|||
os: unstable |
|||
|
|||
environment: |
|||
matrix: |
|||
- nodejs_version: 8 |
|||
- nodejs_version: 7 |
|||
|
|||
cache: |
|||
- "%LOCALAPPDATA%/Yarn" |
|||
- node_modules -> package.json |
|||
- app/node_modules -> app/package.json |
|||
|
|||
matrix: |
|||
fast_finish: true |
|||
|
|||
build: off |
|||
|
|||
version: '{build}' |
|||
|
|||
shallow_clone: true |
|||
|
|||
clone_depth: 1 |
|||
|
|||
install: |
|||
- ps: Install-Product node $env:nodejs_version |
|||
- set CI=true |
|||
- yarn |
|||
- cd app && yarn |
|||
|
|||
test_script: |
|||
- node --version |
|||
- yarn lint |
|||
- yarn package |
|||
- yarn test |
|||
- yarn test-e2e |
@ -0,0 +1,3 @@ |
|||
declare module 'module' { |
|||
declare module.exports: any; |
|||
} |
@ -0,0 +1,3 @@ |
|||
// @flow |
|||
|
|||
declare export default { [key: string]: string } |
@ -0,0 +1,2 @@ |
|||
// @flow |
|||
declare export default string |
After Width: | Height: | Size: 6.6 KiB |
After Width: | Height: | Size: 9.5 KiB |
After Width: | Height: | Size: 9.9 KiB |
After Width: | Height: | Size: 5.3 KiB |
After Width: | Height: | Size: 5.6 KiB |
After Width: | Height: | Size: 6.2 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 6.4 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 5.5 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 5.4 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 6.5 KiB |
After Width: | Height: | Size: 2.1 KiB |
@ -0,0 +1 @@ |
|||
export default 'test-file-stub'; |
@ -0,0 +1,24 @@ |
|||
// @flow
|
|||
// Check if the renderer and main bundles are built
|
|||
import path from 'path'; |
|||
import chalk from 'chalk'; |
|||
import fs from 'fs'; |
|||
|
|||
function CheckBuildsExist() { |
|||
const mainPath = path.join(__dirname, '..', '..', 'app', 'main.prod.js'); |
|||
const rendererPath = path.join(__dirname, '..', '..', 'app', 'dist', 'renderer.prod.js'); |
|||
|
|||
if (!fs.existsSync(mainPath)) { |
|||
throw new Error(chalk.whiteBright.bgRed.bold( |
|||
'The main process is not built yet. Build it by running "npm run build-main"' |
|||
)); |
|||
} |
|||
|
|||
if (!fs.existsSync(rendererPath)) { |
|||
throw new Error(chalk.whiteBright.bgRed.bold( |
|||
'The renderer process is not built yet. Build it by running "npm run build-renderer"' |
|||
)); |
|||
} |
|||
} |
|||
|
|||
CheckBuildsExist(); |
@ -0,0 +1,15 @@ |
|||
// @flow
|
|||
import chalk from 'chalk'; |
|||
|
|||
export default function CheckNodeEnv(expectedEnv: string) { |
|||
if (!expectedEnv) { |
|||
throw new Error('"expectedEnv" not set'); |
|||
} |
|||
|
|||
if (process.env.NODE_ENV !== expectedEnv) { |
|||
console.log(chalk.whiteBright.bgRed.bold( |
|||
`"process.env.NODE_ENV" must be "${expectedEnv}" to use this webpack config` |
|||
)); |
|||
process.exit(2); |
|||
} |
|||
} |
@ -0,0 +1,210 @@ |
|||
{ |
|||
"name": "bolt-desktop", |
|||
"productName": "ElectronReact", |
|||
"version": "0.0.1", |
|||
"description": "Electron application boilerplate based on React, React Router, Webpack, React Hot Loader for rapid application development", |
|||
"scripts": { |
|||
"build": "concurrently \"npm run build-main\" \"npm run build-renderer\"", |
|||
"build-dll": "cross-env NODE_ENV=development node --trace-warnings -r babel-register ./node_modules/webpack/bin/webpack --config webpack.config.renderer.dev.dll.js --colors", |
|||
"build-main": "cross-env NODE_ENV=production node --trace-warnings -r babel-register ./node_modules/webpack/bin/webpack --config webpack.config.main.prod.js --colors", |
|||
"build-renderer": "cross-env NODE_ENV=production node --trace-warnings -r babel-register ./node_modules/webpack/bin/webpack --config webpack.config.renderer.prod.js --colors", |
|||
"dev": "cross-env START_HOT=1 npm run start-renderer-dev", |
|||
"flow": "flow", |
|||
"flow-typed": "rimraf flow-typed/npm && flow-typed install --overwrite || true", |
|||
"lint": "eslint --cache --format=node_modules/eslint-formatter-pretty .", |
|||
"lint-fix": "npm run lint -- --fix", |
|||
"lint-styles": "stylelint app/*.css app/components/*.css --syntax scss", |
|||
"lint-styles-fix": "stylefmt -r app/*.css app/components/*.css", |
|||
"package": "npm run build && build --publish never", |
|||
"package-all": "npm run build && build -mwl", |
|||
"package-linux": "npm run build && build --linux", |
|||
"package-win": "npm run build && build --win --x64", |
|||
"postinstall": "concurrently \"npm run flow-typed\" \"npm run build-dll\" \"electron-builder install-app-deps\" \"node node_modules/fbjs-scripts/node/check-dev-engines.js package.json\"", |
|||
"prestart": "npm run build", |
|||
"start": "cross-env NODE_ENV=production electron ./app/", |
|||
"start-main-dev": "cross-env HOT=1 NODE_ENV=development electron -r babel-register ./app/main.dev", |
|||
"start-renderer-dev": "cross-env NODE_ENV=development node --trace-warnings -r babel-register ./node_modules/webpack-dev-server/bin/webpack-dev-server --config webpack.config.renderer.dev.js", |
|||
"test": "cross-env NODE_ENV=test BABEL_DISABLE_CACHE=1 node --trace-warnings ./test/runTests.js", |
|||
"test-all": "npm run lint && npm run flow && npm run build && npm run test && npm run test-e2e", |
|||
"test-e2e": "cross-env NODE_ENV=test BABEL_DISABLE_CACHE=1 node --trace-warnings ./test/runTests.js e2e", |
|||
"test-watch": "npm test -- --watch" |
|||
}, |
|||
"browserslist": "electron 1.6", |
|||
"build": { |
|||
"productName": "ElectronReact", |
|||
"appId": "org.develar.ElectronReact", |
|||
"files": [ |
|||
"dist/", |
|||
"node_modules/", |
|||
"app.html", |
|||
"main.prod.js", |
|||
"main.prod.js.map", |
|||
"package.json" |
|||
], |
|||
"dmg": { |
|||
"contents": [ |
|||
{ |
|||
"x": 130, |
|||
"y": 220 |
|||
}, |
|||
{ |
|||
"x": 410, |
|||
"y": 220, |
|||
"type": "link", |
|||
"path": "/Applications" |
|||
} |
|||
] |
|||
}, |
|||
"win": { |
|||
"target": [ |
|||
"nsis" |
|||
] |
|||
}, |
|||
"linux": { |
|||
"target": [ |
|||
"deb", |
|||
"AppImage" |
|||
] |
|||
}, |
|||
"directories": { |
|||
"buildResources": "resources", |
|||
"output": "release" |
|||
} |
|||
}, |
|||
"repository": { |
|||
"type": "git", |
|||
"url": "git+https://github.com/chentsulin/electron-react-boilerplate.git" |
|||
}, |
|||
"author": "C. T. Lin <chentsulin@gmail.com> (https://github.com/chentsulin)", |
|||
"license": "MIT", |
|||
"bugs": { |
|||
"url": "https://github.com/chentsulin/electron-react-boilerplate/issues" |
|||
}, |
|||
"keywords": [ |
|||
"electron", |
|||
"boilerplate", |
|||
"react", |
|||
"redux", |
|||
"flow", |
|||
"sass", |
|||
"webpack", |
|||
"hot", |
|||
"reload" |
|||
], |
|||
"homepage": "https://github.com/chentsulin/electron-react-boilerplate#readme", |
|||
"jest": { |
|||
"moduleNameMapper": { |
|||
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/internals/mocks/fileMock.js", |
|||
"\\.(css|less|sass|scss)$": "identity-obj-proxy" |
|||
}, |
|||
"moduleFileExtensions": [ |
|||
"js" |
|||
], |
|||
"moduleDirectories": [ |
|||
"node_modules", |
|||
"app/node_modules" |
|||
], |
|||
"transform": { |
|||
"^.+\\.js$": "babel-jest" |
|||
}, |
|||
"setupFiles": [ |
|||
"./internals/scripts/CheckBuiltsExist.js" |
|||
] |
|||
}, |
|||
"devDependencies": { |
|||
"babel-core": "^6.24.1", |
|||
"babel-eslint": "^7.2.3", |
|||
"babel-jest": "^20.0.3", |
|||
"babel-loader": "^7.1.0", |
|||
"babel-plugin-add-module-exports": "^0.2.1", |
|||
"babel-plugin-dev-expression": "^0.2.1", |
|||
"babel-plugin-dynamic-import-webpack": "^1.0.1", |
|||
"babel-plugin-flow-runtime": "^0.11.1", |
|||
"babel-plugin-transform-class-properties": "^6.24.1", |
|||
"babel-plugin-transform-es2015-classes": "^6.24.1", |
|||
"babel-preset-env": "^1.5.1", |
|||
"babel-preset-react": "^6.24.1", |
|||
"babel-preset-react-hmre": "^1.1.1", |
|||
"babel-preset-react-optimize": "^1.0.1", |
|||
"babel-preset-stage-0": "^6.24.1", |
|||
"babel-register": "^6.24.1", |
|||
"babili-webpack-plugin": "^0.1.2", |
|||
"chalk": "^2.0.1", |
|||
"concurrently": "^3.5.0", |
|||
"cross-env": "^5.0.0", |
|||
"cross-spawn": "^5.1.0", |
|||
"css-loader": "^0.28.3", |
|||
"electron": "^1.6.10", |
|||
"electron-builder": "^19.8.0", |
|||
"electron-devtools-installer": "^2.2.0", |
|||
"enzyme": "^2.9.1", |
|||
"enzyme-to-json": "^1.5.1", |
|||
"eslint": "^3.19.0", |
|||
"eslint-config-airbnb": "^15.0.1", |
|||
"eslint-formatter-pretty": "^1.1.0", |
|||
"eslint-import-resolver-webpack": "^0.8.3", |
|||
"eslint-plugin-compat": "^1.0.4", |
|||
"eslint-plugin-flowtype": "^2.33.0", |
|||
"eslint-plugin-flowtype-errors": "^3.3.0", |
|||
"eslint-plugin-import": "^2.6.0", |
|||
"eslint-plugin-jest": "^20.0.3", |
|||
"eslint-plugin-jsx-a11y": "5.0.3", |
|||
"eslint-plugin-promise": "^3.5.0", |
|||
"eslint-plugin-react": "^7.1.0", |
|||
"express": "^4.15.3", |
|||
"extract-text-webpack-plugin": "^2.1.0", |
|||
"fbjs-scripts": "^0.8.0", |
|||
"file-loader": "^0.11.1", |
|||
"flow-bin": "^0.48.0", |
|||
"flow-runtime": "^0.13.0", |
|||
"flow-typed": "^2.1.2", |
|||
"html-webpack-plugin": "^2.29.0", |
|||
"identity-obj-proxy": "^3.0.0", |
|||
"jest": "^20.0.4", |
|||
"jsdom": "^11.0.0", |
|||
"minimist": "^1.2.0", |
|||
"node-sass": "^4.5.3", |
|||
"react-addons-test-utils": "^15.6.0", |
|||
"react-test-renderer": "^15.6.1", |
|||
"redux-logger": "^3.0.6", |
|||
"rimraf": "^2.6.1", |
|||
"sass-loader": "^6.0.6", |
|||
"sinon": "^2.3.5", |
|||
"spectron": "^3.7.0", |
|||
"style-loader": "^0.18.1", |
|||
"stylefmt": "^6.0.0", |
|||
"stylelint": "^7.12.0", |
|||
"stylelint-config-standard": "^16.0.0", |
|||
"url-loader": "^0.5.8", |
|||
"webpack": "^3.0.0", |
|||
"webpack-bundle-analyzer": "^2.8.2", |
|||
"webpack-dev-server": "^2.5.0", |
|||
"webpack-merge": "^4.1.0" |
|||
}, |
|||
"dependencies": { |
|||
"devtron": "^1.4.0", |
|||
"electron-debug": "^1.2.0", |
|||
"font-awesome": "^4.7.0", |
|||
"history": "^4.6.3", |
|||
"react": "^15.6.1", |
|||
"react-dom": "^15.6.1", |
|||
"react-hot-loader": "3.0.0-beta.6", |
|||
"react-inline-svg": "^1.1.0", |
|||
"react-redux": "^5.0.5", |
|||
"react-router": "^4.1.1", |
|||
"react-router-dom": "^4.1.1", |
|||
"react-router-redux": "^5.0.0-alpha.6", |
|||
"redux": "^3.7.1", |
|||
"redux-thunk": "^2.2.0", |
|||
"source-map-support": "^0.4.15" |
|||
}, |
|||
"devEngines": { |
|||
"node": ">=7.x", |
|||
"npm": ">=4.x", |
|||
"yarn": ">=0.21.3" |
|||
}, |
|||
"main": "webpack.config.base.js", |
|||
"directories": { |
|||
"test": "test" |
|||
} |
|||
} |
After Width: | Height: | Size: 361 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 156 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 954 B |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 77 KiB |
After Width: | Height: | Size: 7.3 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.0 KiB |
@ -0,0 +1,13 @@ |
|||
{ |
|||
"env": { |
|||
"jest/globals": true |
|||
}, |
|||
"plugins": [ |
|||
"jest" |
|||
], |
|||
"rules": { |
|||
"jest/no-disabled-tests": "warn", |
|||
"jest/no-focused-tests": "error", |
|||
"jest/no-identical-title": "error" |
|||
} |
|||
} |
@ -0,0 +1,13 @@ |
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP |
|||
|
|||
exports[`actions should decrement should create decrement action 1`] = ` |
|||
Object { |
|||
"type": "DECREMENT_COUNTER", |
|||
} |
|||
`; |
|||
|
|||
exports[`actions should increment should create increment action 1`] = ` |
|||
Object { |
|||
"type": "INCREMENT_COUNTER", |
|||
} |
|||
`; |
@ -0,0 +1,41 @@ |
|||
import { spy } from 'sinon'; |
|||
import * as actions from '../../app/actions/counter'; |
|||
|
|||
describe('actions', () => { |
|||
it('should increment should create increment action', () => { |
|||
expect(actions.increment()).toMatchSnapshot(); |
|||
}); |
|||
|
|||
it('should decrement should create decrement action', () => { |
|||
expect(actions.decrement()).toMatchSnapshot(); |
|||
}); |
|||
|
|||
it('should incrementIfOdd should create increment action', () => { |
|||
const fn = actions.incrementIfOdd(); |
|||
expect(fn).toBeInstanceOf(Function); |
|||
const dispatch = spy(); |
|||
const getState = () => ({ counter: 1 }); |
|||
fn(dispatch, getState); |
|||
expect(dispatch.calledWith({ type: actions.INCREMENT_COUNTER })).toBe(true); |
|||
}); |
|||
|
|||
it('should incrementIfOdd shouldnt create increment action if counter is even', () => { |
|||
const fn = actions.incrementIfOdd(); |
|||
const dispatch = spy(); |
|||
const getState = () => ({ counter: 2 }); |
|||
fn(dispatch, getState); |
|||
expect(dispatch.called).toBe(false); |
|||
}); |
|||
|
|||
// There's no nice way to test this at the moment...
|
|||
it('should incrementAsync', done => { |
|||
const fn = actions.incrementAsync(1); |
|||
expect(fn).toBeInstanceOf(Function); |
|||
const dispatch = spy(); |
|||
fn(dispatch); |
|||
setTimeout(() => { |
|||
expect(dispatch.calledWith({ type: actions.INCREMENT_COUNTER })).toBe(true); |
|||
done(); |
|||
}, 5); |
|||
}); |
|||
}); |
@ -0,0 +1,68 @@ |
|||
import { spy } from 'sinon'; |
|||
import React from 'react'; |
|||
import { shallow } from 'enzyme'; |
|||
import { BrowserRouter as Router } from 'react-router-dom'; |
|||
import renderer from 'react-test-renderer'; |
|||
import Counter from '../../app/components/Counter'; |
|||
|
|||
function setup() { |
|||
const actions = { |
|||
increment: spy(), |
|||
incrementIfOdd: spy(), |
|||
incrementAsync: spy(), |
|||
decrement: spy() |
|||
}; |
|||
const component = shallow(<Counter counter={1} {...actions} />); |
|||
return { |
|||
component, |
|||
actions, |
|||
buttons: component.find('button'), |
|||
p: component.find('.counter') |
|||
}; |
|||
} |
|||
|
|||
describe('Counter component', () => { |
|||
it('should should display count', () => { |
|||
const { p } = setup(); |
|||
expect(p.text()).toMatch(/^1$/); |
|||
}); |
|||
|
|||
it('should first button should call increment', () => { |
|||
const { buttons, actions } = setup(); |
|||
buttons.at(0).simulate('click'); |
|||
expect(actions.increment.called).toBe(true); |
|||
}); |
|||
|
|||
it('should match exact snapshot', () => { |
|||
const { actions } = setup(); |
|||
const tree = renderer |
|||
.create( |
|||
<div> |
|||
<Router> |
|||
<Counter counter={1} {...actions} /> |
|||
</Router> |
|||
</div> |
|||
) |
|||
.toJSON(); |
|||
|
|||
expect(tree).toMatchSnapshot(); |
|||
}); |
|||
|
|||
it('should second button should call decrement', () => { |
|||
const { buttons, actions } = setup(); |
|||
buttons.at(1).simulate('click'); |
|||
expect(actions.decrement.called).toBe(true); |
|||
}); |
|||
|
|||
it('should third button should call incrementIfOdd', () => { |
|||
const { buttons, actions } = setup(); |
|||
buttons.at(2).simulate('click'); |
|||
expect(actions.incrementIfOdd.called).toBe(true); |
|||
}); |
|||
|
|||
it('should fourth button should call incrementAsync', () => { |
|||
const { buttons, actions } = setup(); |
|||
buttons.at(3).simulate('click'); |
|||
expect(actions.incrementAsync.called).toBe(true); |
|||
}); |
|||
}); |
@ -0,0 +1,63 @@ |
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP |
|||
|
|||
exports[`Counter component should match exact snapshot 1`] = ` |
|||
<div> |
|||
<div> |
|||
<div |
|||
className="backButton" |
|||
data-tid="backButton" |
|||
> |
|||
<a |
|||
href="/" |
|||
onClick={[Function]} |
|||
> |
|||
<i |
|||
className="fa fa-arrow-left fa-3x" |
|||
/> |
|||
</a> |
|||
</div> |
|||
<div |
|||
className="counter counter" |
|||
data-tid="counter" |
|||
> |
|||
1 |
|||
</div> |
|||
<div |
|||
className="btnGroup" |
|||
> |
|||
<button |
|||
className="btn" |
|||
data-tclass="btn" |
|||
onClick={[Function]} |
|||
> |
|||
<i |
|||
className="fa fa-plus" |
|||
/> |
|||
</button> |
|||
<button |
|||
className="btn" |
|||
data-tclass="btn" |
|||
onClick={[Function]} |
|||
> |
|||
<i |
|||
className="fa fa-minus" |
|||
/> |
|||
</button> |
|||
<button |
|||
className="btn" |
|||
data-tclass="btn" |
|||
onClick={[Function]} |
|||
> |
|||
odd |
|||
</button> |
|||
<button |
|||
className="btn" |
|||
data-tclass="btn" |
|||
onClick={[Function]} |
|||
> |
|||
async |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
`; |
@ -0,0 +1,57 @@ |
|||
import React from 'react'; |
|||
import { mount } from 'enzyme'; |
|||
import { Provider } from 'react-redux'; |
|||
import { createBrowserHistory } from 'history'; |
|||
import { ConnectedRouter } from 'react-router-redux'; |
|||
import CounterPage from '../../app/containers/CounterPage'; |
|||
import { configureStore } from '../../app/store/configureStore'; |
|||
|
|||
function setup(initialState) { |
|||
const store = configureStore(initialState); |
|||
const history = createBrowserHistory(); |
|||
const app = mount( |
|||
<Provider store={store}> |
|||
<ConnectedRouter history={history}> |
|||
<CounterPage /> |
|||
</ConnectedRouter> |
|||
</Provider> |
|||
); |
|||
return { |
|||
app, |
|||
buttons: app.find('button'), |
|||
p: app.find('.counter') |
|||
}; |
|||
} |
|||
|
|||
describe('containers', () => { |
|||
describe('App', () => { |
|||
it('should display initial count', () => { |
|||
const { p } = setup(); |
|||
expect(p.text()).toMatch(/^0$/); |
|||
}); |
|||
|
|||
it('should display updated count after increment button click', () => { |
|||
const { buttons, p } = setup(); |
|||
buttons.at(0).simulate('click'); |
|||
expect(p.text()).toMatch(/^1$/); |
|||
}); |
|||
|
|||
it('should display updated count after descrement button click', () => { |
|||
const { buttons, p } = setup(); |
|||
buttons.at(1).simulate('click'); |
|||
expect(p.text()).toMatch(/^-1$/); |
|||
}); |
|||
|
|||
it('shouldnt change if even and if odd button clicked', () => { |
|||
const { buttons, p } = setup(); |
|||
buttons.at(2).simulate('click'); |
|||
expect(p.text()).toMatch(/^0$/); |
|||
}); |
|||
|
|||
it('should change if odd and if odd button clicked', () => { |
|||
const { buttons, p } = setup({ counter: 1 }); |
|||
buttons.at(2).simulate('click'); |
|||
expect(p.text()).toMatch(/^2$/); |
|||
}); |
|||
}); |
|||
}); |
@ -0,0 +1,113 @@ |
|||
import { Application } from 'spectron'; |
|||
import electronPath from 'electron'; |
|||
import path from 'path'; |
|||
|
|||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 15000; |
|||
|
|||
const delay = time => new Promise(resolve => setTimeout(resolve, time)); |
|||
|
|||
describe('main window', function spec() { |
|||
beforeAll(async () => { |
|||
this.app = new Application({ |
|||
path: electronPath, |
|||
args: [path.join(__dirname, '..', '..', 'app')], |
|||
}); |
|||
|
|||
return this.app.start(); |
|||
}); |
|||
|
|||
afterAll(() => { |
|||
if (this.app && this.app.isRunning()) { |
|||
return this.app.stop(); |
|||
} |
|||
}); |
|||
|
|||
const findCounter = () => this.app.client.element('[data-tid="counter"]'); |
|||
|
|||
const findButtons = async () => { |
|||
const { value } = await this.app.client.elements('[data-tclass="btn"]'); |
|||
return value.map(btn => btn.ELEMENT); |
|||
}; |
|||
|
|||
it('should open window', async () => { |
|||
const { client, browserWindow } = this.app; |
|||
|
|||
await client.waitUntilWindowLoaded(); |
|||
await delay(500); |
|||
const title = await browserWindow.getTitle(); |
|||
expect(title).toBe('Hello Electron React!'); |
|||
}); |
|||
|
|||
it('should haven\'t any logs in console of main window', async () => { |
|||
const { client } = this.app; |
|||
const logs = await client.getRenderProcessLogs(); |
|||
// Print renderer process logs
|
|||
logs.forEach(log => { |
|||
console.log(log.message); |
|||
console.log(log.source); |
|||
console.log(log.level); |
|||
}); |
|||
expect(logs).toHaveLength(0); |
|||
}); |
|||
|
|||
it('should to Counter with click "to Counter" link', async () => { |
|||
const { client } = this.app; |
|||
|
|||
await client.click('[data-tid=container] > a'); |
|||
expect(await findCounter().getText()).toBe('0'); |
|||
}); |
|||
|
|||
it('should display updated count after increment button click', async () => { |
|||
const { client } = this.app; |
|||
|
|||
const buttons = await findButtons(); |
|||
await client.elementIdClick(buttons[0]); // +
|
|||
expect(await findCounter().getText()).toBe('1'); |
|||
}); |
|||
|
|||
it('should display updated count after descrement button click', async () => { |
|||
const { client } = this.app; |
|||
|
|||
const buttons = await findButtons(); |
|||
await client.elementIdClick(buttons[1]); // -
|
|||
expect(await findCounter().getText()).toBe('0'); |
|||
}); |
|||
|
|||
it('shouldnt change if even and if odd button clicked', async () => { |
|||
const { client } = this.app; |
|||
|
|||
const buttons = await findButtons(); |
|||
await client.elementIdClick(buttons[2]); // odd
|
|||
expect(await findCounter().getText()).toBe('0'); |
|||
}); |
|||
|
|||
it('should change if odd and if odd button clicked', async () => { |
|||
const { client } = this.app; |
|||
|
|||
const buttons = await findButtons(); |
|||
await client.elementIdClick(buttons[0]); // +
|
|||
await client.elementIdClick(buttons[2]); // odd
|
|||
expect(await findCounter().getText()).toBe('2'); |
|||
}); |
|||
|
|||
it('should change if async button clicked and a second later', async () => { |
|||
const { client } = this.app; |
|||
|
|||
const buttons = await findButtons(); |
|||
await client.elementIdClick(buttons[3]); // async
|
|||
expect(await findCounter().getText()).toBe('2'); |
|||
await delay(1500); |
|||
expect(await findCounter().getText()).toBe('3'); |
|||
}); |
|||
|
|||
it('should back to home if back button clicked', async () => { |
|||
const { client } = this.app; |
|||
await client.element( |
|||
'[data-tid="backButton"] > a' |
|||
).click(); |
|||
|
|||
expect( |
|||
await client.isExisting('[data-tid="container"]') |
|||
).toBe(true); |
|||
}); |
|||
}); |
@ -0,0 +1,5 @@ |
|||
describe('description', () => { |
|||
it('should have description', () => { |
|||
expect(1 + 2).toBe(3); |
|||
}); |
|||
}); |
@ -0,0 +1,9 @@ |
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP |
|||
|
|||
exports[`reducers counter should handle DECREMENT_COUNTER 1`] = `0`; |
|||
|
|||
exports[`reducers counter should handle INCREMENT_COUNTER 1`] = `2`; |
|||
|
|||
exports[`reducers counter should handle initial state 1`] = `0`; |
|||
|
|||
exports[`reducers counter should handle unknown action type 1`] = `1`; |
@ -0,0 +1,22 @@ |
|||
import counter from '../../app/reducers/counter'; |
|||
import { INCREMENT_COUNTER, DECREMENT_COUNTER } from '../../app/actions/counter'; |
|||
|
|||
describe('reducers', () => { |
|||
describe('counter', () => { |
|||
it('should handle initial state', () => { |
|||
expect(counter(undefined, {})).toMatchSnapshot(); |
|||
}); |
|||
|
|||
it('should handle INCREMENT_COUNTER', () => { |
|||
expect(counter(1, { type: INCREMENT_COUNTER })).toMatchSnapshot(); |
|||
}); |
|||
|
|||
it('should handle DECREMENT_COUNTER', () => { |
|||
expect(counter(1, { type: DECREMENT_COUNTER })).toMatchSnapshot(); |
|||
}); |
|||
|
|||
it('should handle unknown action type', () => { |
|||
expect(counter(1, { type: 'unknown' })).toMatchSnapshot(); |
|||
}); |
|||
}); |
|||
}); |