├── .editorconfig ├── .gitattributes ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── generators └── app │ ├── index.js │ └── prompts.js ├── jsconfig.json ├── package.json └── utils ├── config.js ├── index.js ├── options.json └── yeoman.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 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 | *.sublime-workspace 2 | .DS_Store 3 | .AppleDouble 4 | .LSOverride 5 | Icon 6 | ._* 7 | .Spotlight-V100 8 | .Trashes 9 | Thumbs.db 10 | ehthumbs.db 11 | Desktop.ini 12 | $RECYCLE.BIN/ 13 | node_modules/ 14 | npm-debug.log 15 | .idea/ 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - '4' 5 | - '6' 6 | before_install: 7 | - currentfolder=${PWD##*/} 8 | - if [ "$currentfolder" != 'generator-react-redux-express' ]; then cd .. && eval "mv $currentfolder generator-react-redux-express" && cd generator-react-redux-express; fi 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 hihl 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-redux-express 2 | 3 | ![Amount of Downloads per month](https://img.shields.io/npm/dm/generator-react-redux-express.svg "Amount of Downloads") ![Dependency Tracker](https://img.shields.io/david/hihl/generator-react-redux-express.svg "Dependency Tracker") ![Dependency Tracker](https://img.shields.io/david/dev/hihl/generator-react-redux-express.svg "Dependency Tracker") ![Node Version](https://img.shields.io/node/v/generator-react-redux-express.svg "Node Version") 4 | 5 | > Yeoman generator for [ReactJS](http://facebook.github.io/react/) - lets you quickly set up a project including karma test runner and [Webpack](http://webpack.github.io/) module system. 6 | 7 | - [x] React 8 | - [x] Redux 9 | - [x] React Router 10 | - [x] React Router Redux 11 | - [x] Babel 12 | - [x] PostCSS 13 | - [x] Less 14 | - [x] Scss 15 | - [x] Sass 16 | - [x] Stylus 17 | - [ ] ESLint TODO 2016/9/7 18 | - [x] Karma + mocha + chai 19 | - [x] live reload 20 | - [x] express 21 | - [x] pm2 22 | - [ ] database TODO 2016/9/7 23 | 24 | ## Installation 25 | ```bash 26 | # Make sure both is installed globally 27 | npm install -g yo 28 | npm install -g generator-react-redux-express 29 | ``` 30 | 31 | ## Setting up projects 32 | ```bash 33 | # Create a new directory, and `cd` into it: 34 | mkdir my-new-project && cd my-new-project 35 | 36 | # Run the generator 37 | yo react-redux-express 38 | ``` 39 | ## Thanks 40 | * [generator-react-fullstack](https://github.com/kriasoft/react-starter-kit) 41 | * [generator-react-webpack](https://github.com/react-webpack-generators/generator-react-webpack) 42 | * [antd](https://github.com/ant-design/ant-design/) React Components 43 | -------------------------------------------------------------------------------- /generators/app/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const Generators = require('yeoman-generator'); 3 | const utils = require('../../utils'); 4 | const prompts = require('./prompts'); 5 | const path = require('path'); 6 | const fs = require('fs'); 7 | const packageInfo = require('../../package.json'); 8 | 9 | const baseRootPath = path.resolve(path.dirname(require.resolve('react-redux-express'))); 10 | 11 | class AppGenerator extends Generators.Base { 12 | constructor(args, options) { 13 | 14 | super(args, options); 15 | 16 | // Make options available 17 | this.option('skip-welcome-message', { 18 | desc: 'Skip the welcome message', 19 | type: Boolean, 20 | defaults: false 21 | }); 22 | this.option('skip-install'); 23 | 24 | // Use our plain template as source 25 | this.sourceRoot(baseRootPath); 26 | 27 | this.config.save(); 28 | } 29 | 30 | initializing() { 31 | 32 | if(!this.options['skip-welcome-message']) { 33 | this.log(require('yeoman-welcome')); 34 | this.log('React全栈脚手架,前端:React/Redux/React-Router,服务端:express/artTemplate/服务端渲染.\n'); 35 | } 36 | } 37 | 38 | prompting() { 39 | 40 | return this.prompt(prompts).then((answers) => { 41 | 42 | // Make sure to get the correct app name if it is not the default 43 | if(answers.appName !== utils.yeoman.getAppName()) { 44 | answers.appName = utils.yeoman.getAppName(answers.appName); 45 | } 46 | 47 | // Set needed global vars for yo 48 | this.appName = answers.appName; 49 | this.generatedWithVersion = parseInt(packageInfo.version.split('.').shift(), 10); 50 | 51 | // Set needed keys into config 52 | this.config.set('appName', this.appName); 53 | this.config.set('appPath', this.appPath); 54 | this.config.set('generatedWithVersion', this.generatedWithVersion); 55 | }); 56 | } 57 | 58 | configuring() { 59 | 60 | // Generate our package.json. Make sure to also include the required dependencies for styles 61 | let defaultSettings = this.fs.readJSON(`${baseRootPath}/package.json`); 62 | let packageSettings = { 63 | name: this.appName, 64 | version: '0.0.1', 65 | description: `${this.appName} - Generated by generator-react-webpack`, 66 | scripts: defaultSettings.scripts, 67 | repository: '', 68 | keywords: [ 69 | 'React', 70 | 'Redux', 71 | 'React-Router', 72 | 'express', 73 | 'artTemplate', 74 | 'es6' 75 | ], 76 | author: 'Your name here', 77 | devDependencies: defaultSettings.devDependencies, 78 | dependencies: defaultSettings.dependencies 79 | }; 80 | 81 | this.fs.writeJSON(this.destinationPath('package.json'), packageSettings); 82 | } 83 | 84 | writing() { 85 | 86 | const excludeList = [ 87 | 'LICENSE', 88 | 'README.md', 89 | 'CHANGELOG.md', 90 | 'node_modules', 91 | '.istanbul.yml', 92 | '.travis.yml', 93 | '.DS_Store', 94 | '.idea' 95 | ]; 96 | 97 | // Get all files in our repo and copy the ones we should 98 | fs.readdir(this.sourceRoot(), (err, items) => { 99 | 100 | for(let item of items) { 101 | 102 | // Skip the item if it is in our exclude list 103 | if(excludeList.indexOf(item) !== -1) { 104 | continue; 105 | } 106 | 107 | // Copy all items to our root 108 | let fullPath = path.join(baseRootPath, item); 109 | if(fs.lstatSync(fullPath).isDirectory()) { 110 | this.bulkDirectory(item, item); 111 | } else { 112 | if (item === '.npmignore') { 113 | this.copy(item, '.gitignore'); 114 | } else { 115 | this.copy(item, item); 116 | } 117 | } 118 | } 119 | }); 120 | } 121 | 122 | install() { 123 | if(!this.options['skip-install']) { 124 | this.installDependencies({ bower: false }); 125 | } 126 | } 127 | 128 | end() { 129 | if(!this.options['skip-start']) { 130 | // this.spawnCommand('npm', ['run start']); 131 | } 132 | } 133 | } 134 | 135 | module.exports = AppGenerator; 136 | -------------------------------------------------------------------------------- /generators/app/prompts.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const utils = require('../../utils'); 3 | 4 | module.exports = [ 5 | { 6 | type: 'input', 7 | name: 'appName', 8 | message: 'Please choose your application name', 9 | default: utils.yeoman.getAppName() 10 | } 11 | ]; 12 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "mpilerOptions": { 3 | "target": "ES6", 4 | "module": "commonjs" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generator-react-redux-express", 3 | "version": "0.1.2", 4 | "description": "Yeoman generator for using React, Redux, Express with Webpack via Babel", 5 | "main": "generators/app/index.js", 6 | "engines": { 7 | "node": ">=4.0.0", 8 | "iojs": ">=1.1.0" 9 | }, 10 | "keywords": [ 11 | "yeoman-generator", 12 | "react", 13 | "es6", 14 | "express", 15 | "redux" 16 | ], 17 | "scripts": { 18 | "release:major": "npm version prerelease && git push --follow-tags && npm publish --tag beta", 19 | "release:minor": "npm version prerelease && git push --follow-tags && npm publish --tag beta", 20 | "release:patch": "npm version prerelease && git push --follow-tags && npm publish --tag beta" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/hihl/generator-react-redux-express.git" 25 | }, 26 | "files": [ 27 | "generators", 28 | "utils", 29 | "template" 30 | ], 31 | "author": { 32 | "name": "Zhengfeng Yao", 33 | "url": "https://github.com/hihl" 34 | }, 35 | "license": "MIT", 36 | "bugs": { 37 | "url": "https://github.com/hihl/generator-react-redux-express/issues" 38 | }, 39 | "homepage": "https://github.com/hihl/generator-react-redux-express#readme", 40 | "dependencies": { 41 | "react-redux-express": "0.1.3", 42 | "underscore.string": "^3.3.4", 43 | "yeoman-generator": "^0.24.1", 44 | "yeoman-welcome": "^1.0.1" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /utils/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | let opts = require('./options.json'); 3 | 4 | /** 5 | * Get a setting 6 | * @param {String} setting 7 | * @return {Mixed} setting or null if not found 8 | */ 9 | let getSetting = (setting) => { 10 | return opts[setting] !== undefined ? opts[setting] : null; 11 | } 12 | 13 | /** 14 | * Get choices for a given setting 15 | * @param {String} setting 16 | * @return {Mixed} Result or null if nothing was found 17 | */ 18 | let getChoices = function getChoices(setting) { 19 | 20 | let config = getSetting(setting); 21 | return config && Array.isArray(config.options) ? config.options : null; 22 | } 23 | 24 | /** 25 | * Get the wanted choice by key 26 | * @param {String} setting 27 | * @param {String} key 28 | * @return {Object} 29 | */ 30 | let getChoiceByKey = (setting, key) => { 31 | 32 | let choices = getChoices(setting); 33 | if(!choices) { 34 | return null; 35 | } 36 | 37 | let result = null; 38 | 39 | for(let choice of choices) { 40 | 41 | if(choice.name === key) { 42 | result = choice; 43 | break; 44 | } 45 | } 46 | 47 | return result; 48 | } 49 | 50 | /** 51 | * Get the default choice for a config setting 52 | * @param {String} setting 53 | * @return {Mixed} 54 | */ 55 | let getDefaultChoice = (setting) => { 56 | let config = getSetting(setting); 57 | return config && config.default !== undefined && config.default.length > 0 ? config.default : null; 58 | } 59 | 60 | module.exports = { 61 | getSetting, 62 | getChoices, 63 | getChoiceByKey, 64 | getDefaultChoice 65 | }; 66 | -------------------------------------------------------------------------------- /utils/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const config = require('./config'); 4 | const yeoman = require('./yeoman'); 5 | 6 | module.exports = { 7 | config, 8 | yeoman 9 | }; 10 | -------------------------------------------------------------------------------- /utils/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "style": { 3 | "options": [ 4 | { 5 | "name": "css", 6 | "value": "css", 7 | "suffix": ".css" 8 | }, 9 | { 10 | "name": "sass", 11 | "value": "sass", 12 | "suffix": ".sass", 13 | "packages": [ 14 | { "name": "sass-loader", "version": "^4.0.0" }, 15 | { "name": "node-sass", "version": "^3.8.0" } 16 | ] 17 | }, 18 | { 19 | "name": "scss", 20 | "value": "scss", 21 | "suffix": ".scss", 22 | "packages": [ 23 | { "name": "sass-loader", "version": "^4.0.0" }, 24 | { "name": "node-sass", "version": "^3.8.0" } 25 | ] 26 | }, 27 | { 28 | "name": "less", 29 | "value": "less", 30 | "suffix": ".less", 31 | "packages": [ 32 | { "name": "less-loader", "version": "^2.2.3" }, 33 | { "name": "less", "version": "^2.7.1" } 34 | ] 35 | }, 36 | { 37 | "name": "stylus", 38 | "value": "stylus", 39 | "suffix": ".styl", 40 | "packages": [ 41 | { "name": "stylus-loader", "version": "^2.3.1" }, 42 | { "name": "stylus", "version": "^0.54.5" }, 43 | { "name": "nib", "version": "~1.1.2" } 44 | ] 45 | } 46 | ], 47 | "default": "css" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /utils/yeoman.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const _ = require('underscore.string'); 5 | 6 | // Needed directory paths 7 | const baseName = path.basename(process.cwd()); 8 | 9 | /** 10 | * Get the base directory 11 | * @return {String} 12 | */ 13 | let getBaseDir = () => { 14 | return baseName; 15 | }; 16 | 17 | /** 18 | * Get a js friendly application name 19 | * @param {String} appName The input application name [optional] 20 | * @return {String} 21 | */ 22 | let getAppName = (appName) => { 23 | 24 | // If appName is not given, use the current directory 25 | if(appName === undefined) { 26 | appName = getBaseDir(); 27 | } 28 | 29 | return _.slugify(_.humanize(appName)); 30 | }; 31 | 32 | module.exports = { 33 | getBaseDir, 34 | getAppName 35 | } 36 | --------------------------------------------------------------------------------