├── .editorconfig ├── .gitattributes ├── .gitignore ├── .jshintrc ├── .travis.yml ├── .yo-rc.json ├── LICENSE ├── README.md ├── app ├── index.js └── templates │ ├── _bower.json │ ├── _gulpfile.js │ ├── _package.json │ ├── app.js │ ├── bowerrc │ ├── editorconfig │ ├── gitignore │ ├── index.html │ ├── jshintrc │ ├── main.css │ └── robots.txt ├── package.json └── test └── test-app.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | *.log -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "esnext": true, 4 | "bitwise": true, 5 | "camelcase": true, 6 | "curly": true, 7 | "eqeqeq": true, 8 | "immed": true, 9 | "indent": 2, 10 | "latedef": true, 11 | "newcap": true, 12 | "noarg": true, 13 | "quotmark": "single", 14 | "regexp": true, 15 | "undef": true, 16 | "unused": true, 17 | "strict": true, 18 | "trailing": true, 19 | "smarttabs": true, 20 | "white": true 21 | } 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | before_install: 5 | - currentfolder=${PWD##*/} 6 | - if [ "$currentfolder" != 'generator-react-boilerplate' ]; then cd .. && eval "mv $currentfolder generator-react-boilerplate" && cd generator-react-boilerplate; fi 7 | 8 | -------------------------------------------------------------------------------- /.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-generator": {} 3 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Mitch Chen 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # generator-react-boilerplate 2 | 3 | > [Yeoman](http://yeoman.io) generator for Facebook's React - Integrate with gulp and browserify. 4 | 5 | 6 | ## What's inside? 7 | 8 | * react (Support for React v0.12.x and React v0.13.x) 9 | * reflux 10 | * react-router 11 | * gulp 12 | * browserify 13 | * watchify 14 | * babelify 15 | * livereload 16 | 17 | ## Getting Started 18 | 19 | ### Install Yeoman 20 | 21 | ``` 22 | $ npm install -g yo 23 | ``` 24 | 25 | ### Install and use Generators 26 | 27 | To install generator-react-boilerplate from npm, run: 28 | 29 | ``` 30 | $ npm install -g generator-react-boilerplate 31 | ``` 32 | 33 | Finally, initiate the generator: 34 | 35 | ``` 36 | $ yo react-boilerplate 37 | ``` 38 | 39 | Run watch task and begin to develop your React components. 40 | 41 | ``` 42 | $ gulp dev 43 | ``` 44 | 45 | ``` 46 | $ gulp pro 47 | ``` 48 | 49 | ## License 50 | 51 | MIT 52 | -------------------------------------------------------------------------------- /app/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var util = require('util'); 3 | var path = require('path'); 4 | var yeoman = require('yeoman-generator'); 5 | var yosay = require('yosay'); 6 | 7 | var ReactBoilerplateGenerator = yeoman.generators.Base.extend({ 8 | initializing: function () { 9 | this.pkg = require('../package.json'); 10 | }, 11 | 12 | prompting: function () { 13 | var done = this.async(); 14 | 15 | // Have Yeoman greet the user. 16 | this.log(yosay( 17 | 'Welcome to the fine ReactBoilerplate generator!' 18 | )); 19 | 20 | var prompts = [{ 21 | name: 'appName', 22 | message: 'What is your app\' name ?' 23 | }, 24 | { 25 | type: 'checkbox', 26 | name: 'features', 27 | message: 'What more would you like?', 28 | choices: [{ 29 | name: 'Bootstrap: The most popular front-end framework for developing responsive, mobile first projects on the web', 30 | value: 'includeBootstrap', 31 | checked: true 32 | }, { 33 | name: 'Cookies: Client-Side Cookie Manipulation API', 34 | value: 'includeCookies', 35 | checked: true 36 | }, { 37 | name: 'Moment: Parse, manipulate, and display dates', 38 | value: 'includeMoment', 39 | checked: true 40 | }, { 41 | name: 'ES5-shim: ECMAScript 5 compatibility shims for legacy JavaScript engines', 42 | value: 'includeES5', 43 | checked: true 44 | }, { 45 | name: 'ES6-shim: ECMAScript 6 compatibility shims for legacy JavaScript engines', 46 | value: 'includeES6', 47 | checked: true 48 | }] 49 | }, 50 | { 51 | type: 'confirm', 52 | name: 'includeReactES6', 53 | message: 'Using React 0.13.x version?', 54 | default: true 55 | }]; 56 | 57 | this.prompt(prompts, function (props) { 58 | var features = props.features; 59 | function hasFeature(feat) { return features.indexOf(feat) !== -1; } 60 | this.appName = props.appName; 61 | this.includeBootstrap = hasFeature('includeBootstrap'); 62 | this.includeCookies = hasFeature('includeCookies'); 63 | this.includeMoment = hasFeature('includeMoment'); 64 | this.includeES5 = hasFeature('includeES5'); 65 | this.includeES6 = hasFeature('includeES6'); 66 | this.includeReactES6 = props.includeReactES6; 67 | done(); 68 | }.bind(this)); 69 | }, 70 | 71 | writing: { 72 | app: function () { 73 | this.dest.mkdir('src'); 74 | this.dest.mkdir('src/assets'); 75 | this.dest.mkdir('src/assets/styles'); 76 | this.dest.mkdir('src/assets/sass'); 77 | this.dest.mkdir('src/assets/images/'); 78 | this.dest.mkdir('src/assets/fonts'); 79 | this.dest.mkdir('src/static'); 80 | this.dest.mkdir('src/config'); 81 | this.dest.mkdir('src/app'); 82 | this.dest.mkdir('src/app/components'); 83 | this.dest.mkdir('src/app/widgets'); 84 | this.dest.mkdir('src/app/views'); 85 | this.dest.mkdir('src/app/pages'); 86 | this.dest.mkdir('src/app/services'); 87 | this.dest.mkdir('src/app/utilities'); 88 | 89 | this.template('_package.json', 'package.json'); 90 | this.template('_gulpfile.js', 'gulpfile.js'); 91 | this.template('_bower.json', 'bower.json'); 92 | this.template('index.html', 'src/index.html'); 93 | 94 | this.src.copy('main.css', 'src/assets/styles/main.css'); 95 | this.src.copy('app.js', 'src/app/app.js'); 96 | this.src.copy('bowerrc', '.bowerrc'); 97 | this.src.copy('gitignore', '.gitignore'); 98 | this.src.copy('robots.txt', 'src/robots.txt'); 99 | }, 100 | 101 | projectfiles: function () { 102 | this.src.copy('editorconfig', '.editorconfig'); 103 | this.src.copy('jshintrc', '.jshintrc'); 104 | } 105 | }, 106 | 107 | end: function () { 108 | this.installDependencies(); 109 | } 110 | }); 111 | 112 | module.exports = ReactBoilerplateGenerator; 113 | -------------------------------------------------------------------------------- /app/templates/_bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%= _.slugify(appName) %>", 3 | "version": "0.0.1", 4 | "authors": [ 5 | "Mitch Chen " 6 | ], 7 | "dependencies": { 8 | "jquery": "~2.1.0"<% if (includeBootstrap) { %>, 9 | "bootstrap": "~3.2.0"<% } if (includeCookies) { %>, 10 | "Cookies": ""<% } if (includeMoment) { %>, 11 | "moment": ""<% } if (includeES5) { %>, 12 | "es5-shim": ""<% } if (includeES6) { %>, 13 | "es6-shim": ""<% } %> 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/templates/_gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'), 4 | stripDebug = require('gulp-strip-debug'), 5 | gulpif = require('gulp-if'), 6 | $ = require('gulp-load-plugins')(), 7 | browserify = require('browserify'), 8 | watchify = require('watchify'), 9 | source = require('vinyl-source-stream'), 10 | connect = $.connectMulti, 11 | wiredep = require('wiredep').stream, 12 | devServer = connect(), 13 | proServer = connect(); 14 | 15 | gulp.task('connect-dev', devServer.server({ 16 | root: ['src'], 17 | port: 8989, 18 | livereload: true 19 | })); 20 | 21 | gulp.task('connect-pro', proServer.server({ 22 | root: ['dist'], 23 | port: 9090, 24 | livereload: true 25 | })); 26 | 27 | gulp.task('clean', function() { 28 | return gulp.src(['dist'], {read: false}) 29 | .pipe($.rimraf()); 30 | }); 31 | 32 | gulp.task('lint', function() { 33 | return gulp.src(['src/app/*.js', 'src/app/**/*.js']) 34 | .pipe($.jshint('.jshintrc')) 35 | .pipe($.jshint.reporter('jshint-stylish')); 36 | }); 37 | 38 | gulp.task('robots', function() { 39 | gulp.src('src/robots.txt') 40 | .pipe(gulp.dest('dist/')); 41 | }); 42 | 43 | gulp.task('static', function() { 44 | gulp.src('src/static/*') 45 | .pipe(gulp.dest('dist/static/')); 46 | }); 47 | 48 | gulp.task('config', function() { 49 | gulp.src('src/config/*') 50 | .pipe(gulp.dest('dist/config/')); 51 | }); 52 | 53 | gulp.task('fonts', function() { 54 | gulp.src('src/bower_components/bootstrap/dist/fonts/*') 55 | .pipe(gulp.dest('dist/assets/fonts')); 56 | }); 57 | 58 | gulp.task('images', function() { 59 | gulp.src('src/assets/images/*') 60 | .pipe(gulp.dest('dist/assets/images')); 61 | }); 62 | 63 | gulp.task('styles', ['compass'], function() { 64 | gulp.src('src/assets/styles/*.css') 65 | .pipe(gulp.dest('dist/assets/styles')); 66 | }); 67 | 68 | gulp.task('compass', function() { 69 | return gulp.src('src/assets/sass/*.scss') 70 | .pipe($.compass({ 71 | sass: 'src/assets/sass', 72 | css: 'src/assets/styles' 73 | })) 74 | .pipe($.minifyCss()) 75 | .pipe(gulp.dest('src/assets/styles')) 76 | }); 77 | 78 | gulp.task('base', ['robots', 'static', 'config', 'fonts', 'images', 'styles']); 79 | 80 | gulp.task('scripts', ['lint'], function() { 81 | var bundler = browserify({ 82 | entries: ['./src/app/app.js'], 83 | transform: ['babelify'], 84 | extensions: ['.jsx'], 85 | debug: true, 86 | cache: {}, 87 | packageCache: {}, 88 | fullPaths: true 89 | }); 90 | var watcher = watchify(bundler); 91 | return watcher 92 | .on('prebundle', function(bundler) { 93 | bundler.require('react'); 94 | }) 95 | .bundle() 96 | .pipe(source('app.js')) 97 | .pipe(gulp.dest('./dist/scripts/')); 98 | }); 99 | 100 | gulp.task('html', ['base', 'scripts'], function() { 101 | var assets = $.useref.assets(); 102 | return gulp.src('src/*.html') 103 | .pipe(assets) 104 | .pipe(gulpif('*.css', $.minifyCss())) 105 | .pipe(assets.restore()) 106 | .pipe($.useref()) 107 | .pipe(gulp.dest('dist')) 108 | .pipe($.size()); 109 | }); 110 | 111 | gulp.task('compress', ['html'], function() { 112 | gulp.src(['dist/scripts/app.js', 'dist/scripts/vendor.js']) 113 | .pipe(stripDebug()) 114 | .pipe($.uglify()) 115 | .pipe(gulp.dest('dist/scripts/')); 116 | }); 117 | 118 | gulp.task('wiredep', function() { 119 | gulp.src('src/*.html') 120 | .pipe(wiredep({ 121 | directory: 'src/bower_components', 122 | ignorePath: 'src/' 123 | })) 124 | .pipe(gulp.dest('src')); 125 | }); 126 | 127 | gulp.task('browserify', function() { 128 | var bundler = browserify({ 129 | entries: ['./src/app/app.js'], 130 | transform: ['babelify'], 131 | extensions: ['.jsx'], 132 | debug: true, 133 | cache: {}, 134 | packageCache: {}, 135 | fullPaths: true 136 | }); 137 | var watcher = watchify(bundler); 138 | return watcher 139 | .on('prebundle', function(bundler) { 140 | bundler.require('react'); 141 | }) 142 | .on('update', function() { 143 | var updateStart = Date.now(); 144 | console.log('Updating!'); 145 | watcher.bundle() 146 | .pipe(source('app.js')) 147 | .pipe(gulp.dest('./src/scripts/')); 148 | console.log('Updated!', (Date.now()-updateStart)+'ms'); 149 | }) 150 | .bundle() 151 | .pipe(source('app.js')) 152 | .pipe(gulp.dest('./src/scripts/')); 153 | }); 154 | 155 | gulp.task('refresh', ['browserify'], function() { 156 | gulp.src('src/scripts/app.js') 157 | .pipe(devServer.reload()); 158 | }); 159 | 160 | gulp.task('watch', ['connect-dev'], function() { 161 | gulp.watch([ 162 | 'src/*.html', 163 | 'src/assets/styles/*.css', 164 | 'src/assets/images/*', 165 | 'src/app/*.js', 166 | 'src/app/**/*.js' 167 | ], function(event) { 168 | return gulp.src(event.path) 169 | .pipe(devServer.reload()); 170 | }); 171 | 172 | gulp.watch(['src/assets/sass/*.scss'], ['compass']); 173 | gulp.watch(['src/app/*.js', 'src/app/**/*.js'], ['refresh']); 174 | gulp.watch('bower.json', ['wiredep']); 175 | }); 176 | 177 | gulp.task('dev', ['browserify'], function() { 178 | gulp.start('watch'); 179 | }); 180 | 181 | gulp.task('build', ['compress'], function() { 182 | gulp.start('connect-pro'); 183 | }); 184 | 185 | gulp.task('pro', ['clean'], function() { 186 | gulp.start('build'); 187 | }); 188 | 189 | gulp.task('deploy', ['compress'], function() { 190 | gulp.doneCallback = function(err) { 191 | process.exit(err ? 1:0); 192 | } 193 | }); 194 | 195 | gulp.task('production', ['clean'], function() { 196 | gulp.start('deploy'); 197 | }); -------------------------------------------------------------------------------- /app/templates/_package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%= _.slugify(appName) %>", 3 | "version": "0.0.1", 4 | "dependencies": {}, 5 | "devDependencies": { 6 | "gulp": "~3.8.8", 7 | "gulp-load-plugins": "~0.6.0", 8 | "gulp-util": "~3.0.1", 9 | "gulp-autoprefixer": "~1.0.0", 10 | "gulp-jshint": "~1.8.4", 11 | "gulp-rimraf": "~0.1.0", 12 | "gulp-bower": "0.0.6", 13 | "gulp-size": "~1.1.0", 14 | "gulp-connect-multi": "~1.0.8", 15 | "gulp-useref": "~1.0.1", 16 | "gulp-uglify": "1.0.1", 17 | "gulp-compass": "2.0.4", 18 | "gulp-strip-debug": "1.0.2", 19 | "gulp-minify-css": "~1.1.0", 20 | "gulp-if": "~1.2.5", 21 | "jshint-stylish": "0.4.0", 22 | "wiredep": "~1.8.5", 23 | "vinyl-source-stream": "1.1.0", 24 | "browserify": "10.0.0", 25 | "watchify": "~3.2.1", 26 | "babelify": "~6.0.2", 27 | "reflux": "~0.2.7",<% if (includeReactES6) { %> 28 | "react": "^0.13.0", 29 | "react-router": "~0.13.3"<% } else { %> 30 | "react": "^0.12.0", 31 | "react-router": "~0.11.6"<% } %> 32 | }, 33 | "engines": { 34 | "node": ">=0.10.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/templates/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var React = require('react'), 4 | ExampleApp; 5 | 6 | ExampleApp = React.createClass({ 7 | render: function() { 8 | return ( 9 | /*jshint ignore:start */ 10 |
11 |

Hello, React

12 |
13 | /*jshint ignore:end */ 14 | ); 15 | } 16 | }); 17 | 18 | React.render( 19 | /*jshint ignore:start */ 20 | , 21 | /*jshint ignore:end */ 22 | document.getElementById('app') 23 | ); -------------------------------------------------------------------------------- /app/templates/bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "src/bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /app/templates/editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /app/templates/gitignore: -------------------------------------------------------------------------------- 1 | ### OSX ### 2 | .DS_Store 3 | 4 | ### Windows ### 5 | # Windows image file caches 6 | Thumbs.db 7 | ehthumbs.db 8 | 9 | node_modules/ 10 | /dist -------------------------------------------------------------------------------- /app/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= appName %> 9 | 10 | 11 | <% if (includeBootstrap) { %> 12 | 13 | 14 | 15 | 16 | 17 | 18 | <% } %> 19 | 20 | 21 | 22 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | <% if (includeBootstrap) { %><% } %> 32 | <% if (includeCookies) { %><% } %> 33 | <% if (includeMoment) { %><% } %> 34 | <% if (includeES5) { %><% } %> 35 | <% if (includeES6) { %><% } %> 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/templates/jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": true, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": 4, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "undef": true, 16 | "strict": true, 17 | "globals": { 18 | "$": false, 19 | "moment": false 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/templates/main.css: -------------------------------------------------------------------------------- 1 | /* Main.css */ -------------------------------------------------------------------------------- /app/templates/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org/ 2 | 3 | User-agent: * 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generator-react-boilerplate", 3 | "version": "0.1.9", 4 | "description": "React Boilerplate", 5 | "license": "MIT", 6 | "main": "app/index.js", 7 | "repository": "mitchbox/generator-react-boilerplate", 8 | "author": { 9 | "name": "Mitch Chen", 10 | "email": "mitchblog@gmail.com", 11 | "url": "https://github.com/mitchbox" 12 | }, 13 | "engines": { 14 | "node": ">=0.10.0", 15 | "npm": ">=1.2.10" 16 | }, 17 | "scripts": { 18 | "test": "mocha" 19 | }, 20 | "files": [ 21 | "app" 22 | ], 23 | "keywords": [ 24 | "yeoman-generator", 25 | "react", 26 | "es6", 27 | "browserify", 28 | "watchify", 29 | "babelify", 30 | "gulp" 31 | ], 32 | "dependencies": { 33 | "chalk": "^0.5.0", 34 | "yeoman-generator": "^0.17.0", 35 | "yosay": "^0.3.0", 36 | "underscore.string": "^2.3.3" 37 | }, 38 | "devDependencies": { 39 | "mocha": "*" 40 | }, 41 | "peerDependencies": { 42 | "yo": ">=1.4.6" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/test-app.js: -------------------------------------------------------------------------------- 1 | /*global describe, beforeEach, it*/ 2 | 'use strict'; 3 | 4 | var path = require('path'); 5 | var assert = require('yeoman-generator').assert; 6 | var helpers = require('yeoman-generator').test; 7 | var os = require('os'); 8 | 9 | describe('react-boilerplate:app', function () { 10 | before(function (done) { 11 | helpers.run(path.join(__dirname, '../app')) 12 | .inDir(path.join(os.tmpdir(), './temp-test')) 13 | .withOptions({ 'skip-install': true }) 14 | .withPrompt({ 15 | someOption: true 16 | }) 17 | .on('end', done); 18 | }); 19 | 20 | it('creates files', function () { 21 | assert.file([ 22 | 'bower.json', 23 | 'package.json', 24 | '.editorconfig', 25 | '.jshintrc' 26 | ]); 27 | }); 28 | }); 29 | --------------------------------------------------------------------------------