├── .gitignore ├── README.md ├── app.js ├── bin └── www ├── index.js ├── package.json ├── public ├── favicon.ico └── stylesheets │ └── style.css ├── routes └── index.js ├── src ├── actions │ └── Actions.js ├── components │ └── TestComponent.jsx ├── constants │ └── .gitkeep ├── dispatcher │ └── AppDispatcher.js ├── index.jsx ├── other.jsx └── stores │ └── Store.js ├── views ├── error.jade ├── index.jade ├── layout.jade └── other.jade └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | public/build -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | react-webpack-express 2 | ===================== 3 | 4 | Boilerplate to start developing npm react components with webpack. Includes flux as npm module. 5 | 6 | 7 | Usage 8 | ===== 9 | 10 | ``` 11 | git clone https://github.com/mixxen/react-webpack-express.git myapp 12 | cd myapp 13 | npm install 14 | npm run build 15 | npm run server 16 | open http://localhost:3000 17 | ``` 18 | Use `npm start` to run webpack watch. 19 | 20 | License 21 | ======= 22 | 23 | MIT 24 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var path = require('path'); 3 | var favicon = require('serve-favicon'); 4 | var logger = require('morgan'); 5 | var cookieParser = require('cookie-parser'); 6 | var bodyParser = require('body-parser'); 7 | 8 | var routes = require('./routes/index'); 9 | 10 | var app = express(); 11 | 12 | // view engine setup 13 | app.set('views', path.join(__dirname, 'views')); 14 | app.set('view engine', 'jade'); 15 | 16 | app.use(favicon(__dirname + '/public/favicon.ico')); 17 | app.use(logger('dev')); 18 | app.use(bodyParser.json()); 19 | app.use(bodyParser.urlencoded({extended: true})); 20 | app.use(cookieParser()); 21 | app.use(express.static(path.join(__dirname, 'public'))); 22 | 23 | app.use('/', routes); 24 | 25 | /// catch 404 and forward to error handler 26 | app.use(function(req, res, next) { 27 | var err = new Error('Not Found'); 28 | err.status = 404; 29 | next(err); 30 | }); 31 | 32 | /// error handlers 33 | 34 | // development error handler 35 | // will print stacktrace 36 | if (app.get('env') === 'development') { 37 | app.use(function(err, req, res, next) { 38 | res.status(err.status || 500); 39 | res.render('error', { 40 | message: err.message, 41 | error: err 42 | }); 43 | }); 44 | } 45 | 46 | // production error handler 47 | // no stacktraces leaked to user 48 | app.use(function(err, req, res, next) { 49 | res.status(err.status || 500); 50 | res.render('error', { 51 | message: err.message, 52 | error: {} 53 | }); 54 | }); 55 | 56 | 57 | module.exports = app; 58 | -------------------------------------------------------------------------------- /bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var debug = require('debug')('MODULE_NAME'); 3 | var app = require('../app'); 4 | 5 | app.set('port', process.env.PORT || 3000); 6 | 7 | var server = app.listen(app.get('port'), function() { 8 | console.log('Express server listening on port ' + server.address().port); 9 | }); 10 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * An example for exporting as npm module. 3 | * 4 | * Use this to import component: 5 | * var TestComponent = require('MODULE_NAME').TestComponent; 6 | */ 7 | module.exports.TestComponent = require('./src/components/TestComponent.jsx'); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webpack-react-express", 3 | "version": "0.2.0", 4 | "private": true, 5 | "dependencies": { 6 | "flux": "^2.1.1", 7 | "keymirror": "^0.1.1", 8 | "object-assign": "^4.0.1", 9 | "react": "^0.14.7", 10 | "react-dom": "^0.14.7" 11 | }, 12 | "devDependencies": { 13 | "babel-core": "^6.5.1", 14 | "babel-loader": "^6.2.2", 15 | "babel-preset-es2015": "^6.5.0", 16 | "babel-preset-react": "^6.5.0", 17 | "body-parser": "~1.12.3", 18 | "cookie-parser": "~1.3.4", 19 | "debug": "~2.1.3", 20 | "express": "~4.12.3", 21 | "jade": "~1.9.2", 22 | "morgan": "~1.5.2", 23 | "serve-favicon": "^2.3.0", 24 | "statics": "~0.1.0", 25 | "webpack": "^1.12.13" 26 | }, 27 | "main": "./index.js", 28 | "scripts": { 29 | "start": "webpack --progress --colors --watch -d", 30 | "build": "webpack --progress --colors -p", 31 | "server": "node bin/www", 32 | "test": "echo \"Error: no test specified\" && exit 1" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mixxen/react-webpack-express/43553607015d6ac9bb66eb6307e87c6f9667e1dd/public/favicon.ico -------------------------------------------------------------------------------- /public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get('/', function(req, res) { 6 | res.render('index', { title: 'Main' }); 7 | }); 8 | router.get('/other', function(req, res) { 9 | res.render('other', { title: 'Other' }); 10 | }); 11 | 12 | 13 | module.exports = router; 14 | -------------------------------------------------------------------------------- /src/actions/Actions.js: -------------------------------------------------------------------------------- 1 | var Dispatcher = require('../dispatcher/AppDispatcher'), 2 | keyMirror = require('keymirror'); 3 | 4 | 5 | /** 6 | * `Actions` are generated by `Views` and processed by the `Dispacher` according to the 7 | * Flux application architecture. 8 | * 9 | */ 10 | var Actions = { 11 | 12 | TYPES: keyMirror({ 13 | SET: null, 14 | ADD: null, 15 | }), 16 | 17 | set: function(val) { 18 | Dispatcher.handleViewAction({ 19 | type: Actions.TYPES.SET, 20 | value: val 21 | }); 22 | }, 23 | 24 | add: function(n) { 25 | Dispatcher.handleViewAction({ 26 | type: Actions.TYPES.ADD, 27 | n: n 28 | }); 29 | } 30 | }; 31 | 32 | /** 33 | * Expose `Actions` 34 | */ 35 | 36 | exports = module.exports = Actions; -------------------------------------------------------------------------------- /src/components/TestComponent.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'), 2 | Actions = require('../actions/Actions'), 3 | Store = require('../stores/Store'); 4 | 5 | var TestComponent = React.createClass({ 6 | getInitialState: function() { 7 | return Store.get(); 8 | }, 9 | componentDidMount: function() { 10 | Store.addListener('change', this.changeEventHandler); 11 | }, 12 | changeEventHandler: function() { 13 | this.setState(Store.get()); 14 | }, 15 | handleChange: function(event) { 16 | Actions.set(event.target.value); 17 | }, 18 | handleButtonClick: function(event) { 19 | Actions.add(1); 20 | }, 21 | render: function() { 22 | return ( 23 |
24 | Hello 25 | 26 |
27 | {this.state.count}: {this.state.value} 28 |
29 | )} 30 | }); 31 | 32 | module.exports = TestComponent; -------------------------------------------------------------------------------- /src/constants/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mixxen/react-webpack-express/43553607015d6ac9bb66eb6307e87c6f9667e1dd/src/constants/.gitkeep -------------------------------------------------------------------------------- /src/dispatcher/AppDispatcher.js: -------------------------------------------------------------------------------- 1 | var Dispatcher = require('flux').Dispatcher, 2 | assign = require('object-assign'); 3 | 4 | 5 | var AppDispatcher = assign(new Dispatcher(), { 6 | 7 | /** 8 | * @param {object} action The details of the action, including the action's 9 | * type and additional data coming from the server. 10 | */ 11 | handleServerAction: function(action) { 12 | var payload = { 13 | source: 'server', 14 | action: action 15 | }; 16 | this.dispatch(payload); 17 | }, 18 | 19 | /** 20 | * @param {object} action The details of the action, including the action's 21 | * type and additional data coming from the view. 22 | */ 23 | handleViewAction: function(action) { 24 | var payload = { 25 | source: 'view', 26 | action: action 27 | }; 28 | this.dispatch(payload); 29 | } 30 | 31 | }); 32 | 33 | module.exports = AppDispatcher; -------------------------------------------------------------------------------- /src/index.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'), 2 | ReactDOM = require('react-dom'), 3 | TestComponent = require('./components/TestComponent.jsx'); 4 | 5 | ReactDOM.render( 6 | , 7 | document.getElementById("react-container") 8 | ); -------------------------------------------------------------------------------- /src/other.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'), 2 | ReactDOM = require('react-dom'); 3 | 4 | 5 | var AnotherComponent = React.createClass({ 6 | render: function() { 7 | return ( 8 |
Hello Again
9 | ); 10 | } 11 | }); 12 | 13 | 14 | ReactDOM.render( 15 | , 16 | document.getElementById("react-container") 17 | ); -------------------------------------------------------------------------------- /src/stores/Store.js: -------------------------------------------------------------------------------- 1 | var assign = require('object-assign'), 2 | EventEmitter = require('events').EventEmitter, 3 | Actions = require('../actions/Actions'), 4 | Dispatcher = require('../dispatcher/AppDispatcher'); 5 | 6 | var _value = 'react'; 7 | var _count = 0; 8 | 9 | var Store = assign({}, EventEmitter.prototype, { 10 | get: function() { 11 | return { 12 | value: _value, 13 | count: _count 14 | } 15 | } 16 | }); 17 | 18 | Store.dispatchToken = Dispatcher.register(function(payload) { 19 | 20 | var action = payload.action; 21 | switch(action.type) { 22 | 23 | case Actions.TYPES.SET: 24 | _value = action.value; 25 | Store.emit('change'); 26 | break; 27 | 28 | case Actions.TYPES.ADD: 29 | _count += action.n; 30 | Store.emit('change'); 31 | break; 32 | } 33 | }); 34 | 35 | 36 | /** 37 | * Expose 38 | */ 39 | exports = module.exports = Store; -------------------------------------------------------------------------------- /views/error.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= message 5 | h2= error.status 6 | pre #{error.stack} 7 | -------------------------------------------------------------------------------- /views/index.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | #react-container 5 | script(src='/build/main.js') 6 | -------------------------------------------------------------------------------- /views/layout.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title= title 5 | link(rel='stylesheet', href='/stylesheets/style.css') 6 | script(src='/build/common.js') 7 | body 8 | block content -------------------------------------------------------------------------------- /views/other.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | #react-container 5 | script(src='/build/other.js') 6 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | 3 | var definePlugin = new webpack.DefinePlugin({ 4 | __DEV__: JSON.stringify(JSON.parse(process.env.BUILD_DEV || 'true')), 5 | __PRERELEASE__: JSON.stringify(JSON.parse(process.env.BUILD_PRERELEASE || 'false')) 6 | }); 7 | 8 | var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common.js'); 9 | 10 | module.exports = { 11 | cache: true, 12 | entry: { 13 | main: './src/index.jsx', 14 | other: './src/other.jsx' 15 | }, 16 | output: { 17 | path: 'public/build', 18 | filename: '[name].js' 19 | }, 20 | module: { 21 | loaders: [ 22 | {test: /\.jsx?$/, loader: 'babel', exclude: /(node_modules|bower_components)/, query: { presets: ['react', 'es2015'] }}, 23 | ] 24 | }, 25 | plugins: [ 26 | definePlugin, 27 | commonsPlugin 28 | ] 29 | }; 30 | --------------------------------------------------------------------------------