├── .gitignore ├── LICENSE.md ├── README.md ├── components ├── counter.js └── counter.jsx ├── gulpfile.js ├── index.js ├── package.json ├── pagejs └── index.js ├── public └── js │ └── index.js └── views └── index.jade /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Example of a general react.js work flow/build process using koa to serve pages 2 | 3 | Jade is used as the templating engine, you can mix and match any engine that is compatible with `co-views` 4 | 5 | This is meant to serve as a more concrete real world type example where the jsx is rendered as part of a build process. 6 | 7 | ###Build: 8 | -- 9 | `gulp` 10 | 11 | ###Develop (watch): 12 | -- 13 | `gulp dev` 14 | 15 | ###Run: 16 | -- 17 | `node --harmony-generators index.js` 18 | You will need node 0.11.x to run this with either the `harmony` flag or `harmony-generators` flag enabled 19 | 20 | ##Directory structure: 21 | - components/ 22 | - counter.js 23 | - counter.jsx 24 | - pagejs/ 25 | - index.js 26 | - public/ 27 | - js/ 28 | - index.js 29 | - views/ 30 | - index.jade 31 | 32 | - components : houses the react components, these are shared between client and server. The *.jsx files are built to the corresponding .js file 33 | - public : these are the static files 34 | - pagejs : these modules are built with browserify to include react. All of the js files in this directory build into public/js/ 35 | - views : where we store our front end templates 36 | 37 | 38 | ###License 39 | --- 40 | MIT 41 | -------------------------------------------------------------------------------- /components/counter.js: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require("react"); 4 | 5 | module.exports = React.createClass({displayName: 'exports', 6 | getInitialState : function(){ 7 | return { 8 | count : this.props.count ? this.props.count : 0 9 | }; 10 | }, 11 | onClick : function(event){ 12 | this.setState({ 13 | count : this.state.count + 1 14 | }); 15 | }, 16 | render : function(){ 17 | return ( 18 | React.DOM.div( {onClick:this.onClick}, 19 | "Count (Click to increment): ", this.state.count 20 | ) 21 | ) 22 | } 23 | }); 24 | 25 | -------------------------------------------------------------------------------- /components/counter.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | var React = require("react"); 4 | 5 | module.exports = React.createClass({ 6 | getInitialState : function(){ 7 | return { 8 | count : this.props.count ? this.props.count : 0 9 | }; 10 | }, 11 | onClick : function(event){ 12 | this.setState({ 13 | count : this.state.count + 1 14 | }); 15 | }, 16 | render : function(){ 17 | return ( 18 |
19 | Count (Click to increment): {this.state.count} 20 |
21 | ) 22 | } 23 | }); 24 | 25 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var nodemon = require('gulp-nodemon'); 3 | var browserify = require('gulp-browserify'); 4 | var react = require('gulp-react'); 5 | 6 | 7 | gulp.task('browserify', function(){ 8 | gulp.src('pagejs/*.js') 9 | .pipe(browserify({ 10 | transform : ['reactify'] 11 | })) 12 | .pipe(gulp.dest('public/js/')); 13 | }); 14 | 15 | gulp.task('jsxcompile', function(){ 16 | gulp.src('components/*.jsx') 17 | .pipe(react()) 18 | .pipe(gulp.dest('components')); 19 | }); 20 | 21 | gulp.task('watch', function(){ 22 | gulp.watch('components/*.js', ['browserify']); 23 | gulp.watch('components/*.jsx', ['jsxcompile','browserify']); 24 | }); 25 | 26 | 27 | gulp.task('dev', ['browserify','jsxcompile','watch'], function(){ 28 | nodemon({script:'index.js', options: '--harmony-generators --debug'}); 29 | }); 30 | 31 | gulp.task('default',['browserify','jsxcompile']); 32 | 33 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var react = require('react'); 2 | var koa = require('koa'); 3 | var koa_static = require('koa-static'); 4 | var views = require('co-views'); 5 | var app = koa(); 6 | app.use(koa_static(__dirname + '/public')); 7 | 8 | /********* 9 | RENDER IN MIDDLEWARE 10 | CHANGE THIS UP IF YOU WANT TO USE A DIFFERENT TEMPLATING ENGINE 11 | **********/ 12 | app.use(function*(next){ 13 | this.render = views('views', { 14 | map: { 15 | jade: 'jade' 16 | }, 17 | default : 'jade' 18 | }); 19 | yield next; 20 | }); 21 | 22 | var counterComponent = require('./components/counter'); 23 | app.use(function*(next){ 24 | var opts = {count:1}; 25 | var counterHTML = react.renderComponentToString(counterComponent(opts)); 26 | this.body = yield this.render('index', {container:{counter:counterHTML}, metadata:{counter:opts}}); 27 | this.status=200; 28 | }); 29 | 30 | app.listen(3000); 31 | console.log('Listening on port 3000!'); 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-example-koa", 3 | "version": "0.0.0", 4 | "description": "Example of a general react.js work flow/build process using koa to serve pages", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node --harmony-generators index.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Daryl Lau daryl@weak.io", 11 | "license": "MIT", 12 | "dependencies": { 13 | "koa": "~0.6.2", 14 | "browserify": "~4.1.8", 15 | "reactify": "~0.13.1", 16 | "envify": "~1.2.1", 17 | "react": "~0.10.0", 18 | "gulp": "~3.7.0", 19 | "gulp-browserify": "~0.5.0", 20 | "gulp-react": "~0.3.3", 21 | "gulp-nodemon": "~1.0.4", 22 | "co-views": "~0.2.0", 23 | "koa-static": "~1.4.5", 24 | "thunkify": "2.1.2", 25 | "jade": "~1.3.1" 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /pagejs/index.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var counter = require('../components/counter'); 3 | var container = document.getElementById('container'); 4 | 5 | React.renderComponent(counter(window.c), container); 6 | -------------------------------------------------------------------------------- /views/index.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | body 5 | h1 This is a static header! 6 | #container 7 | |!{container.counter} 8 | script(type='text/javascript'). 9 | window.c = !{JSON.stringify(metadata.counter)}; 10 | script(type='text/javascript',src='/js/index.js') 11 | --------------------------------------------------------------------------------