├── CHANGELOG.md ├── .eslintignore ├── .gitignore ├── karma.entry.js ├── .babelrc ├── src ├── js │ ├── ducks │ │ ├── reducers.js │ │ ├── counter.js │ │ └── counter.test.js │ ├── components │ │ ├── Reset.js │ │ ├── Decrement.js │ │ ├── Increment.js │ │ ├── Button.js │ │ ├── Counter.js │ │ ├── App.test.js │ │ ├── App.js │ │ ├── Counter.test.js │ │ ├── Reset.test.js │ │ ├── Decrement.test.js │ │ ├── Increment.test.js │ │ └── Button.test.js │ ├── containers │ │ ├── CounterContainer.js │ │ ├── ResetContainer.js │ │ ├── DecrementContainer.js │ │ ├── IncrementContainer.js │ │ ├── ResetContainer.test.js │ │ ├── DecrementContainer.test.js │ │ └── IncrementContainer.test.js │ ├── index.js │ └── store.js └── index.html ├── karma.config.js ├── webpack.config.js ├── config ├── karma │ ├── single.js │ ├── watch.js │ ├── ci.js │ └── _base.js ├── webpack │ ├── stage.js │ ├── development.js │ ├── plugins │ │ └── html-inject.js │ ├── production.js │ └── _base.js └── index.js ├── .travis.yml ├── .eslintrc ├── bin ├── changelog.js └── webpack-dev-server.js ├── server.babel.js ├── Makefile ├── package.json └── README.md /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | dist/* 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules/ 3 | coverage/ 4 | npm-debug.log 5 | selenium-debug.log 6 | dist/ 7 | .DS_Store 8 | -------------------------------------------------------------------------------- /karma.entry.js: -------------------------------------------------------------------------------- 1 | const context = require.context('./src/js/', true, /.+\.test\.jsx?$/); 2 | context.keys().forEach(context); 3 | module.exports = context; 4 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react", "es2015", "stage-0"], 3 | "env": { 4 | "development": { 5 | "presets": ["react-hmre"] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/js/ducks/reducers.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | 3 | import counter from './counter'; 4 | 5 | export default combineReducers({ 6 | counter, 7 | }); 8 | -------------------------------------------------------------------------------- /karma.config.js: -------------------------------------------------------------------------------- 1 | require('babel-register'); 2 | const config = require('./config').default; 3 | module.exports = require('./config/karma/' + config.get('globals').TEST_ENV).default; 4 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | require('babel-register'); 2 | const config = require('./config/index').default; 3 | module.exports = require('./config/webpack/' + config.get('globals').NODE_ENV).default; 4 | -------------------------------------------------------------------------------- /config/karma/single.js: -------------------------------------------------------------------------------- 1 | export default config => { 2 | const base = require('./_base').default(config); 3 | 4 | config.set({ 5 | ...base, 6 | autoWatch: false, 7 | singleRun: true, 8 | reporters: ['progress'], 9 | }); 10 | 11 | return config; 12 | }; 13 | -------------------------------------------------------------------------------- /src/js/components/Reset.js: -------------------------------------------------------------------------------- 1 | import { h } from 'react-hyperscript-helpers'; 2 | 3 | import Button from 'components/Button'; 4 | 5 | 6 | const Reset = (props) => ( 7 | h(Button, { 8 | ...props, 9 | onClick: props.handleOnClick, 10 | }, 11 | 'Reset') 12 | ); 13 | 14 | 15 | export default Reset; 16 | -------------------------------------------------------------------------------- /src/js/components/Decrement.js: -------------------------------------------------------------------------------- 1 | import { h } from 'react-hyperscript-helpers'; 2 | 3 | import Button from 'components/Button'; 4 | 5 | 6 | const Decrement = (props) => ( 7 | h(Button, { 8 | ...props, 9 | onClick: props.handleOnClick, 10 | }, 11 | 'Minus') 12 | ); 13 | 14 | 15 | export default Decrement; 16 | -------------------------------------------------------------------------------- /src/js/components/Increment.js: -------------------------------------------------------------------------------- 1 | import { h } from 'react-hyperscript-helpers'; 2 | 3 | import Button from 'components/Button'; 4 | 5 | 6 | const Increment = (props) => ( 7 | h(Button, { 8 | ...props, 9 | onClick: props.handleOnClick, 10 | }, 11 | 'Plus') 12 | ); 13 | 14 | 15 | export default Increment; 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 'stable' 4 | sudo: false 5 | notifications: 6 | email: false 7 | cache: 8 | directories: 9 | - node_modules 10 | before_install: 11 | - npm config set spin false 12 | after_script: 13 | - npm install codecov 14 | - ./node_modules/.bin/codecov < coverage/lcov.info 15 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React Webpack Example 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/js/components/Button.js: -------------------------------------------------------------------------------- 1 | import { PropTypes } from 'react'; 2 | import { button } from 'react-hyperscript-helpers'; 3 | 4 | const Button = ({ children, ...rest }) => ( 5 | button( 6 | { ...rest }, 7 | children 8 | ) 9 | ); 10 | 11 | 12 | Button.propTypes = { 13 | children: PropTypes.node, 14 | }; 15 | 16 | 17 | export default Button; 18 | -------------------------------------------------------------------------------- /config/karma/watch.js: -------------------------------------------------------------------------------- 1 | export default config => { 2 | const base = require('./_base').default(config); 3 | 4 | config.set({ 5 | ...base, 6 | autoWatch: true, 7 | singleRun: false, 8 | reporters: ['mocha'], 9 | mochaReporter: { 10 | output: 'autowatch', 11 | showDiff: true, 12 | }, 13 | }); 14 | 15 | return config; 16 | }; 17 | -------------------------------------------------------------------------------- /src/js/components/Counter.js: -------------------------------------------------------------------------------- 1 | import { PropTypes } from 'react'; 2 | import { div } from 'react-hyperscript-helpers'; 3 | 4 | const Counter = ({ count, operation, ...rest }) => ( 5 | div( 6 | { ...rest }, 7 | `${operation} Count: ${count}` 8 | ) 9 | ); 10 | 11 | 12 | Counter.propTypes = { 13 | children: PropTypes.node, 14 | }; 15 | 16 | 17 | export default Counter; 18 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | "parser": "babel-eslint", 4 | "rules": { 5 | "strict": 0 6 | }, 7 | "env": { 8 | "browser": true, 9 | "mocha": true, 10 | "jasmine": true 11 | }, 12 | "plugins": [ 13 | "react" 14 | ], 15 | "globals": { 16 | "__BASE__": true, 17 | "__DEBUG__": true, 18 | "__DEV__": true, 19 | "sinon": true 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/js/containers/CounterContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { h } from 'react-hyperscript-helpers'; 3 | import Counter from 'components/Counter'; 4 | 5 | const CounterContainer = (props) => h(Counter, props); 6 | 7 | const mapStateToProps = ({ counter: { count, operation } }) => ({ count, operation }); 8 | 9 | export default connect(mapStateToProps)(CounterContainer); 10 | -------------------------------------------------------------------------------- /src/js/index.js: -------------------------------------------------------------------------------- 1 | import 'babel-polyfill'; 2 | import ReactDOM from 'react-dom'; 3 | import { h } from 'react-hyperscript-helpers'; 4 | import { Provider } from 'react-redux'; 5 | 6 | import configureStore from './store'; 7 | import App from 'components/App'; 8 | 9 | const store = configureStore({}); 10 | 11 | ReactDOM.render( 12 | h(Provider, { store }, h(App)), 13 | document.getElementById('react-webpack-example') 14 | ); 15 | -------------------------------------------------------------------------------- /bin/changelog.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | var fs = require('graceful-fs'); 4 | var join = require('path').join; 5 | var cc = require('conventional-changelog'); 6 | var wstream = fs.createWriteStream(join(__dirname, '../CHANGELOG.md'), 'utf-8'); 7 | 8 | cc( 9 | { 10 | preset: 'angular', 11 | releaseCount: 0 12 | }, // options 13 | {}, // context 14 | {}, // gitRawCommitsOpts 15 | {}, // parserOpts 16 | {} 17 | ) 18 | .pipe(wstream); 19 | -------------------------------------------------------------------------------- /src/js/components/App.test.js: -------------------------------------------------------------------------------- 1 | import { shallow } from 'enzyme'; 2 | import { h } from 'react-hyperscript-helpers'; 3 | 4 | import App from './App'; 5 | 6 | describe('', () => { 7 | it('should exist', () => { 8 | const wrapper = shallow(h(App)); 9 | expect(wrapper.type()).to.equal('div'); 10 | }); 11 | 12 | it('should contain top level components', () => { 13 | const wrapper = shallow(h(App)); 14 | expect(wrapper.children()).to.have.length(4); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /server.babel.js: -------------------------------------------------------------------------------- 1 | require('babel-register'); 2 | 3 | const chalk = require('chalk'); 4 | const config = require('./config').default; 5 | const devServer = require('./bin/webpack-dev-server').default; 6 | 7 | const host = config.get('webpack_host'); 8 | const port = config.get('webpack_port'); 9 | 10 | 11 | devServer.listen(port, host, () => { 12 | console.log(`⚡ Server running at ${chalk.white(`${host}:${port}`)}`); 13 | console.log(` Proxying to API running at ${chalk.white(config.get('proxy'))}`); 14 | }); 15 | -------------------------------------------------------------------------------- /src/js/containers/ResetContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { h } from 'react-hyperscript-helpers'; 3 | 4 | import Reset from 'components/Reset'; 5 | 6 | import { reset } from 'ducks/counter'; 7 | 8 | export const ResetContainer = (props) => ( 9 | h(Reset, { 10 | ...props, 11 | handleOnClick: props.reset, 12 | }) 13 | ); 14 | 15 | export const mapDispatchToProps = (dispatch) => ({ 16 | reset: () => dispatch(reset()), 17 | }); 18 | 19 | export default connect( 20 | () => ({}), 21 | mapDispatchToProps, 22 | )(ResetContainer); 23 | -------------------------------------------------------------------------------- /src/js/components/App.js: -------------------------------------------------------------------------------- 1 | import { div, h } from 'react-hyperscript-helpers'; 2 | 3 | import IncrementContainer from 'containers/IncrementContainer'; 4 | import DecrementContainer from 'containers/DecrementContainer'; 5 | import ResetContainer from 'containers/ResetContainer'; 6 | import CounterContainer from 'containers/CounterContainer'; 7 | 8 | 9 | const App = () => ( 10 | div({ 11 | children: [ 12 | h(IncrementContainer), 13 | h(DecrementContainer), 14 | h(ResetContainer), 15 | h(CounterContainer), 16 | ], 17 | }) 18 | ); 19 | 20 | export default App; 21 | -------------------------------------------------------------------------------- /src/js/components/Counter.test.js: -------------------------------------------------------------------------------- 1 | import { shallow } from 'enzyme'; 2 | import { div, h } from 'react-hyperscript-helpers'; 3 | 4 | import Counter from './Counter'; 5 | 6 | describe('', () => { 7 | it('should exist', () => { 8 | const wrapper = shallow(h(Counter)); 9 | expect(wrapper.type()).to.equal('div'); 10 | }); 11 | 12 | it('should take props', () => { 13 | const wrapper = shallow(h(Counter, { 14 | operation: 'Increment', 15 | count: 5, 16 | }, '')); 17 | expect(wrapper.equals(div('Increment Count: 5'))).to.equal(true); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /src/js/containers/DecrementContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { h } from 'react-hyperscript-helpers'; 3 | 4 | import Decrement from 'components/Decrement'; 5 | 6 | import { decrement } from 'ducks/counter'; 7 | 8 | export const DecrementContainer = (props) => ( 9 | h(Decrement, { 10 | ...props, 11 | handleOnClick: props.decrement, 12 | }) 13 | ); 14 | 15 | export const mapDispatchToProps = (dispatch) => ({ 16 | decrement: () => dispatch(decrement()), 17 | }); 18 | 19 | export default connect( 20 | () => ({}), 21 | mapDispatchToProps, 22 | )(DecrementContainer); 23 | -------------------------------------------------------------------------------- /src/js/containers/IncrementContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { h } from 'react-hyperscript-helpers'; 3 | 4 | import Increment from 'components/Increment'; 5 | 6 | import { increment } from 'ducks/counter'; 7 | 8 | export const IncrementContainer = (props) => ( 9 | h(Increment, { 10 | ...props, 11 | handleOnClick: props.increment, 12 | }) 13 | ); 14 | 15 | export const mapDispatchToProps = (dispatch) => ({ 16 | increment: () => dispatch(increment()), 17 | }); 18 | 19 | export default connect( 20 | () => ({}), 21 | mapDispatchToProps, 22 | )(IncrementContainer); 23 | -------------------------------------------------------------------------------- /src/js/components/Reset.test.js: -------------------------------------------------------------------------------- 1 | import { shallow } from 'enzyme'; 2 | import { h } from 'react-hyperscript-helpers'; 3 | 4 | import Button from 'components/Button'; 5 | import Reset from './Reset'; 6 | 7 | describe('', () => { 8 | it('should exist', () => { 9 | const wrapper = shallow(h(Reset)); 10 | expect(wrapper.type()).to.equal(Button); 11 | }); 12 | 13 | it('should handle onClick', () => { 14 | const props = { 15 | handleOnClick: sinon.spy(), 16 | }; 17 | shallow(h(Reset, props)).simulate('click'); 18 | expect(props.handleOnClick.called).to.equal(true); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /src/js/components/Decrement.test.js: -------------------------------------------------------------------------------- 1 | import { shallow } from 'enzyme'; 2 | import { h } from 'react-hyperscript-helpers'; 3 | 4 | import Button from 'components/Button'; 5 | import Decrement from './Decrement'; 6 | 7 | describe('', () => { 8 | it('should exist', () => { 9 | const wrapper = shallow(h(Decrement)); 10 | expect(wrapper.type()).to.equal(Button); 11 | }); 12 | 13 | it('should handle onClick', () => { 14 | const props = { 15 | handleOnClick: sinon.spy(), 16 | }; 17 | shallow(h(Decrement, props)).simulate('click'); 18 | expect(props.handleOnClick.called).to.equal(true); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /src/js/components/Increment.test.js: -------------------------------------------------------------------------------- 1 | import { shallow } from 'enzyme'; 2 | import { h } from 'react-hyperscript-helpers'; 3 | 4 | import Button from 'components/Button'; 5 | import Increment from './Increment'; 6 | 7 | describe('', () => { 8 | it('should exist', () => { 9 | const wrapper = shallow(h(Increment)); 10 | expect(wrapper.type()).to.equal(Button); 11 | }); 12 | 13 | it('should handle onClick', () => { 14 | const props = { 15 | handleOnClick: sinon.spy(), 16 | }; 17 | shallow(h(Increment, props)).simulate('click'); 18 | expect(props.handleOnClick.called).to.equal(true); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /config/webpack/stage.js: -------------------------------------------------------------------------------- 1 | import webpack from 'webpack'; 2 | 3 | import HtmlInject from './plugins/html-inject'; 4 | 5 | import config from '../'; 6 | import webpackConfig from './_base'; 7 | 8 | const LIBS_BUNDLE = 'libs'; 9 | 10 | export default { 11 | ...webpackConfig, 12 | entry: { 13 | ...webpackConfig.entry, 14 | [LIBS_BUNDLE]: config.get('dependencies') 15 | }, 16 | output: { 17 | ...webpackConfig.output, 18 | filename: '[name].[hash].js', 19 | chunkFilename: '[id].js' 20 | }, 21 | plugins: [ 22 | ...webpackConfig.plugins, 23 | new webpack.optimize.CommonsChunkPlugin(LIBS_BUNDLE, `${LIBS_BUNDLE}.[hash].js`), 24 | new HtmlInject() 25 | ] 26 | }; 27 | -------------------------------------------------------------------------------- /src/js/containers/ResetContainer.test.js: -------------------------------------------------------------------------------- 1 | import { shallow, mount } from 'enzyme'; 2 | import { h } from 'react-hyperscript-helpers'; 3 | 4 | import Reset from 'components/Reset'; 5 | import { ResetContainer } from './ResetContainer'; 6 | 7 | describe('', () => { 8 | it('should exist', () => { 9 | const wrapper = shallow(h(ResetContainer)); 10 | expect(wrapper.type()).to.equal(Reset); 11 | }); 12 | 13 | it('should handle onClick', () => { 14 | const props = { 15 | reset: sinon.spy(), 16 | }; 17 | const wrapper = mount(h(ResetContainer, props)); 18 | wrapper.find(Reset).simulate('click'); 19 | expect(props.reset.called).to.equal(true); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/js/components/Button.test.js: -------------------------------------------------------------------------------- 1 | import { shallow } from 'enzyme'; 2 | import { span, button, h } from 'react-hyperscript-helpers'; 3 | 4 | import Button from './Button'; 5 | 6 | describe('