├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .eslintrc.js ├── .gitattributes ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .npmignore ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── .travis.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── DEVELOPMENT.md ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── src ├── generators │ ├── ReactUp.js │ ├── common │ │ ├── helper.js │ │ ├── logger.js │ │ └── path.js │ ├── index.js │ └── setup │ │ ├── index.js │ │ └── questions.js ├── gulpfile.js └── test │ └── app.js └── templates ├── README.md ├── _test └── index.js ├── common ├── package.json └── styles.scss ├── component ├── class.js ├── class.long.js ├── connected.js └── pure.js └── mappings.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015", 4 | "stage-0" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = true 12 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | ######################################## 2 | # Files 3 | ######################################## 4 | 5 | ######################################## 6 | # Folders 7 | ######################################## 8 | build/templates/ 9 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | # https://github.com/yannickcr/eslint-plugin-react 2 | { 3 | extends: "airbnb", 4 | 5 | env: { 6 | browser: true, 7 | mocha: true, 8 | node: true, 9 | }, 10 | 11 | globals: { 12 | __ENVIRONMENT__: true, 13 | }, 14 | 15 | parser: "babel-eslint", 16 | 17 | parserOptions: { 18 | ecmaFeatures: { 19 | jsx: true, 20 | }, 21 | ecmaVersion: 6, 22 | sourceType: "module", 23 | }, 24 | 25 | plugins: [ 26 | "react" 27 | ], 28 | 29 | # Any customization we want to override 30 | rules: { 31 | "comma-dangle": ["error", "never"], 32 | 33 | "new-cap": ["error", { 34 | "capIsNew": false, 35 | "capIsNewExceptions": ["CSSModules", "Schema", "React"], 36 | "newIsCapExceptions": ["events"] 37 | }], 38 | 39 | "no-underscore-dangle": ["error", { 40 | "allow": [ 41 | "__ENVIRONMENT__", # Environment information 42 | "__SOBRIETY__", # Used to prefetch our Redux State 43 | "_id" # disabled for MongoDB `_id` 44 | ] 45 | }], 46 | 47 | "padded-blocks": ["error", { 48 | "blocks": "never", 49 | "classes": "never", 50 | "switches": "never" 51 | }], 52 | 53 | "prefer-arrow-callback": ["error", { 54 | "allowNamedFunctions": true 55 | }], 56 | 57 | "react/jsx-curly-spacing": [2, "always", { 58 | "allowMultiline": true, 59 | }], 60 | 61 | "react/jsx-filename-extension": [1, { 62 | "extensions": [ 63 | ".js", 64 | ".jsx" 65 | ] 66 | }], 67 | 68 | "react/jsx-first-prop-new-line": [2, "never"], 69 | 70 | "react/jsx-closing-bracket-location": ["error", { 71 | "nonEmpty": "after-props", 72 | "selfClosing": "after-props" 73 | }], 74 | 75 | "space-in-parens": ["error", "never"], 76 | "template-curly-spacing": ["error", "always"] 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/yannickcr/eslint-plugin-react 2 | module.exports = { 3 | extends: 'airbnb', 4 | env: { 5 | browser: true, 6 | node: true 7 | }, 8 | globals: { 9 | jest: true, 10 | mount: true, 11 | render: true, 12 | shallow: true 13 | }, 14 | parser: 'babel-eslint', 15 | parserOptions: { 16 | ecmaFeatures: { 17 | jsx: true 18 | }, 19 | ecmaVersion: 6, 20 | sourceType: 'module' 21 | }, 22 | 23 | plugins: ['react', 'import'], 24 | 25 | // Any customization we want to override 26 | rules: { 27 | 'array-callback-return': 0, 28 | 'arrow-parens': 0, 29 | 'class-methods-use-this': 0, 30 | 'comma-dangle': ['error', 'never'], 31 | 'consistent-return': ['warn', { treatUndefinedAsUnspecified: false }], 32 | 'import/prefer-default-export': 1, 33 | 'no-restricted-globals': ['error', 'event', 'fdescribe'], 34 | quotes: ['warn', 'single'], 35 | 'react/forbid-prop-types': 0, 36 | 'react/no-unescaped-entities': 0, 37 | 'react/require-default-props': 0, 38 | 'react/sort-comp': [ 39 | 1, 40 | { 41 | order: ['static-methods', 'lifecycle', 'everything-else', 'render'] 42 | } 43 | ], 44 | 'jsx-a11y/no-static-element-interactions': 0, 45 | 'import/no-extraneous-dependencies': 0, 46 | 'import/extensions': 0, 47 | 'new-cap': [ 48 | 'error', 49 | { 50 | capIsNew: false, 51 | capIsNewExceptions: ['CSSModules', 'Schema', 'React'], 52 | newIsCapExceptions: ['events'] 53 | } 54 | ], 55 | 'no-underscore-dangle': [ 56 | 'error', 57 | { 58 | allow: [] 59 | } 60 | ], 61 | 'padded-blocks': [ 62 | 'error', 63 | { 64 | blocks: 'never', 65 | classes: 'never', 66 | switches: 'never' 67 | } 68 | ], 69 | 'prefer-arrow-callback': [ 70 | 'error', 71 | { 72 | allowNamedFunctions: true 73 | } 74 | ], 75 | 'space-in-parens': ['error', 'never'], 76 | 'template-curly-spacing': ['error', 'never'] 77 | }, 78 | 79 | settings: { 80 | 'import/resolver': { 81 | webpack: { 82 | config: 'webpack.config.js' 83 | } 84 | } 85 | } 86 | }; 87 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing: 2 | 3 | 🤘 __First off, thanks for taking the time to contribute!__ 🤘 4 | 5 | The following is a set of guidelines for contributing to `Generator-React-Up` which are hosted at [ReactUp](https://github.com/visormatt/generator-react-up) on GitHub. 6 | These are just guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request. 7 | 8 | #### Table Of Contents 9 | 10 | [What should I know before I get started?](#what-should-i-know-before-i-get-started) 11 | * [Code of Conduct](#code-of-conduct) 12 | * [Atom and Packages](#atom-and-packages) 13 | * [Atom Design Decisions](#design-decisions) 14 | 15 | [GitHub Project Management](#gitHub-project-management) 16 | * [Issues](https://github.com/visormatt/generator-react-up/issues) 17 | * [Projects](https://github.com/visormatt/generator-react-up/projects) 18 | * [Wiki](https://github.com/visormatt/generator-react-up/wiki) 19 | 20 | 21 | ## What should I know before I get started? 22 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 23 | 24 | ### Code of Conduct 25 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 26 | 27 | ### React Up and Packages 28 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 29 | 30 | 31 | ## GitHub Project Management 32 | We use GitHub and all the nice new features they are adding for project management and collaboration. It makes it easy to tie all the ideas, bugs 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Overview: 2 | Please make use of the correct tag when creating an issue. I'm more than happy to take a look at any feature requests but as this is a utility I can't guarantee a turn around time on anything new. However, I'd like to make this a reliable tool so if you do come across an issue let me know. 3 | 4 | **Additional info:** 5 | - Steps to reproduce 6 | - Desired / Expected result 7 | - Browser specifics 8 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Overview: 2 | Outline what was changed and why. Sometimes the diff is simple enough that the PR might not require much information but try to keep me from having to work too hard. 3 | 4 | **Requirements:** 5 | Brief overview of what needs to be done to test locally. Might be checking out another app, running a migration or adjusting some other settings. 6 | 7 | - [ ] It Should... 8 | - [ ] Then it should... 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignored Folders 2 | node_modules/ 3 | 4 | # don't commit compiled files 5 | /generators/ 6 | /test/ 7 | /gulpfile.js 8 | 9 | # keep the source versions 10 | !/src/generators 11 | !/src/test 12 | !/src/gulpfile.js 13 | 14 | # Ignored Files 15 | .DS_Store 16 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Files we do not push to NPM 2 | src/ 3 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v8.10.0 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # File types 3 | ######################################################################## 4 | *.css 5 | *.json 6 | *.md 7 | *.scss 8 | 9 | ######################################################################## 10 | # Folders 11 | ######################################################################## 12 | build/templates/ 13 | dist/ 14 | docs/ 15 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "singleQuote": true 4 | } 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - v7 4 | - v6 5 | - v5 6 | - v4 7 | - '0.12' 8 | - '0.10' 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 1.0.0 / 2016-12-20 2 | ================== 3 | #### Real Release 4 | This is officially the first full release of the `react-up` generator. 5 | - [x] [Bug fix](https://github.com/visormatt/generator-react-up/issues/5): Setup will delete previous templates folder 6 | - [x] Added & customized [Github Page](visormatt.github.io/generator-react-up) 7 | 8 | 0.1.6 / 2016-12-12 9 | ================== 10 | #### Generator Overhaul: 11 | * Updated package to ES6 (babel) 12 | * Removed duplicate template files / directories 13 | * Two primary generators 14 | - `yo react-up:setup` 15 | - `yo react-up ` 16 | * Updated documentation and commenting throughout 17 | * Improved User flow, messaging and prompts 18 | 19 | 20 | 0.0.12 / 2015-12-12 21 | ================== 22 | #### Bare bones generator: 23 | This is essentially the Proof of Concept version and my learning how to hook up a very basic Yeoman Generator. It's not the prettiest kid on the block but it saved me **many, many hours**. 24 | * Initial Generator creation 25 | * Single (default) method for stubbing a React Component 26 | 27 | 28 | Example / YYYY-MM-DD 29 | ================== 30 | #### Release Description: 31 | Any additional user information we'd like to add to briefly describe the changes. 32 | * Added feature xxx 33 | * Added feature xxx 34 | * Added template [file](/) 35 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at dewayne.shelley@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /DEVELOPMENT.md: -------------------------------------------------------------------------------- 1 | ## Development: 2 | Yeoman expects our generator files to be located in a `generators/` directory so we compile all of our ES6 code down to ES5 and voila. 3 | 4 | We also need to setup our `.gitignore` and `.npmignore` so we don't keep the wrong versions of the files in the incorrect place. We don't want to commit our compiled generators to our repo so we ignore the `generators` folder which is created by our `npm run build` and `npm run develop` scripts. On the NPM registry side of things, we only want to add the compiled versions so we ignore the `src` folder. 5 | 6 | **Local Development:** 7 | - Pull this repository locally 8 | - CD into root directory 9 | - `npm link` 10 | - `npm run develop` 11 | 12 | This will kick off an initial run of babel and watch for any file changes. Since we've run the `npm link` command any instances of the `react-up` will reference this directory vs. the installed version from `npm`. 13 | 14 | 15 | **References:** 16 | - [Yeoman creating a generators](http://yeoman.io/authoring/index.html) 17 | - [Yeoman generators in ES6](http://alexfedoseev.com/post/67/yeoman-generator-es6) 18 | - [Compile with Babel](http://jamesknelson.com/writing-npm-packages-with-es6-using-the-babel-6-cli/) 19 | 20 | **Notes:** 21 | - The `.prompts` method is powered by [Inquirer.js](https://github.com/SBoudrias/Inquirer.js). 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016 Matthew D. Shelley (http://www.visualmarvel.com) 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Github Stars ](https://img.shields.io/github/stars/visormatt/generator-react-up.svg?style=social&label=Stars)](https://goo.gl/jpazNR) 3 | [![Github Forks ](https://img.shields.io/github/forks/visormatt/generator-react-up.svg?style=social&label=Forks)](https://goo.gl/oVDLCr) 4 | [![GitHub watchers ](https://img.shields.io/github/watchers/visormatt/generator-react-up.svg?style=social&label=Watchers)](https://goo.gl/i7yY2y) 5 | [![Github Issues ](https://img.shields.io/github/issues/visormatt/generator-react-up.svg?style=social&label=Issues)](https://goo.gl/MTnEPt) 6 | 7 | 8 | [![NPM Total Downloads ](https://img.shields.io/npm/dt/generator-react-up.svg?style=flat-square)](https://goo.gl/DHr44f) 9 | [![NPM Monthly Downloads ](https://img.shields.io/npm/dm/generator-react-up.svg?style=flat-square)](https://goo.gl/DHr44f) 10 | 11 | [![NPM License ](https://img.shields.io/npm/l/generator-react-up.svg?style=flat-square)](https://goo.gl/DHr44f) 12 | [![NPM Version ](https://img.shields.io/npm/v/generator-react-up.svg?style=flat-square)](https://goo.gl/DHr44f) 13 | 14 | [![Node Dependencies ](https://img.shields.io/versioneye/d/visormatt/generator-react-up.svg)](https://goo.gl/DHr44f) 15 | 16 | # React-Up 17 | This is a simple [Yeoman Generator](http://yeoman.io/) to help speed up the `stubbing` of a React ``. Each component is treated as an individual Module / Package with all files being co-located including optional tests. 18 | 19 | ### Installation 20 | Install [Yeoman](http://yeoman.io) and this generator using [NPM](https://www.npmjs.com/). Assuming [NodeJS](https://nodejs.org/) is installed: 21 | 22 | ```bash 23 | # Install yeoman and the generator 24 | npm install -g yo 25 | npm install -g generator-react-up 26 | 27 | # Run the initial setup (should be run from the project root) 28 | yo react-up 29 | ``` 30 | 31 | ### Default Methods: 32 | Since Yeoman is kind enough to create a `generator-generator...` I've made this `generator` even more customizable for you. 33 | 34 | The goal of `react-up` was to reduce the boiler plate required and improve consistency. So far, this generator has saved me a ton of time, enough so that I've decided to upgrade yet again. 35 | 36 | This generator is now much more customizable. You can easily create mappings and exist the current templates to fit your particular needs. 37 | 38 | ```bash 39 | # Pure function 40 | yo react-up 41 | 42 | # React Component 43 | yo react-up class 44 | 45 | # Test file 46 | yo react-up test 47 | 48 | # Setup (can be run at anytime) 49 | yo react-up:setup 50 | ``` 51 | 52 | ### Customization: 53 | To create or modify existing methods available, simply update the `mappings.js` within your local template folder. 54 | 55 | #### License 56 | Apache-2.0 © [Matthew D. Shelley](http://www.visualmarvel.com) 57 | 58 | [npm-image]: https://goo.gl/4WOLxL 59 | [npm-url]: https://goo.gl/0pEQL6 60 | [travis-image]: https://goo.gl/E49AER 61 | [travis-url]: https://goo.gl/l9lbpx 62 | [daviddm-image]: https://goo.gl/4kZ8N1 63 | [daviddm-url]: https://goo.gl/AEw1JM 64 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generator-react-up", 3 | "version": "1.0.1", 4 | "author": { 5 | "name": "Matthew D. Shelley", 6 | "email": "dewayne.shelley@gmail.com", 7 | "url": "http://www.visualmarvel.com" 8 | }, 9 | "description": "A simple and customizabe React Generator. \nWritten in ES6 (babel) this generator allows the end user to configure the templating of custom templates... \nSpend less time writing boiler-plate code and more time writing the fun stuff.", 10 | "homepage": "https://visormatt.github.io/generator-react-up/", 11 | "files": [ 12 | "generators", 13 | "templates" 14 | ], 15 | "main": "generators/index.js", 16 | "keywords": [ 17 | "React Generator", 18 | "boiler-plate", 19 | "component", 20 | "ES6 Classes", 21 | "Pure Functions", 22 | "Stateless Component", 23 | "Yeoman Generator", 24 | "yeoman-generator" 25 | ], 26 | "license": "Apache-2.0", 27 | "repository": "visormatt/generator-react-up", 28 | "scripts": { 29 | "build": "babel -d ./ ./src/ --ignore spec.js", 30 | "develop": "babel -d ./ ./src/ --source-maps inline --watch --ignore spec.js", 31 | "postversion": "git push && git push --tags", 32 | "stop-prepublish": "gulp prepublish", 33 | "test": "gulp", 34 | "version": "npm run build && git add -A" 35 | }, 36 | "dependencies": { 37 | "chalk": "^1.0.0", 38 | "fs-extra": "^1.0.0", 39 | "gulp-beautify": "^2.0.0", 40 | "lodash": "^4.17.2", 41 | "yeoman-generator": "^0.24.1", 42 | "yosay": "^1.0.0" 43 | }, 44 | "devDependencies": { 45 | "babel-cli": "^6.18.0", 46 | "babel-eslint": "^7.1.1", 47 | "babel-plugin-transform-object-assign": "^6.8.0", 48 | "babel-preset-es2015": "^6.18.0", 49 | "babel-preset-react": "^6.16.0", 50 | "babel-preset-stage-0": "^6.16.0", 51 | "eslint": "^3.12.0", 52 | "eslint-config-airbnb": "^13.0.0", 53 | "eslint-config-xo-space": "^0.13.0", 54 | "eslint-plugin-import": "^2.2.0", 55 | "eslint-plugin-jsx-a11y": "^2.2.3", 56 | "eslint-plugin-react": "^6.8.0", 57 | "gulp": "^3.9.1", 58 | "gulp-eslint": "^2.0.0", 59 | "gulp-exclude-gitignore": "^1.0.0", 60 | "gulp-istanbul": "^0.10.3", 61 | "gulp-line-ending-corrector": "^1.0.1", 62 | "gulp-mocha": "^2.0.0", 63 | "gulp-nsp": "^2.1.0", 64 | "gulp-plumber": "^1.0.0", 65 | "yeoman-assert": "^2.0.0", 66 | "yeoman-test": "^1.0.0" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/generators/ReactUp.js: -------------------------------------------------------------------------------- 1 | // Vendor 2 | import { Base } from 'yeoman-generator'; 3 | import { kebabCase } from 'lodash'; 4 | 5 | // Internal 6 | import helper from './common/helper'; 7 | 8 | /** 9 | * @class ReactUp 10 | * @description The ReactUp class to gather all the command line information 11 | * and store it during initialization which we extend from for each generator 12 | */ 13 | class ReactUp extends Base { 14 | // eslint-disable-line padded-blocks 15 | 16 | /** 17 | * @description Anytime we extend a Class as done above, we need to call super 18 | * to ensure the original Class is initialized. From there we save some basic 19 | * data and let the generator do it's thing. 20 | */ 21 | constructor(args, options) { 22 | // we store this before yeoman resets the root 23 | const current = process.cwd(); 24 | 25 | // Always call super to ensure things are setup 26 | super(args, options); 27 | 28 | const name = args[0] ? helper.componentName(args[0]) : 'Example'; 29 | const type = args[1] ? args[1].toLowerCase() : 'pure'; 30 | 31 | const config = this.config.getAll(); 32 | const date = helper.date(); 33 | const slug = name ? kebabCase(name) : false; 34 | const tag = name ? `<${name} />` : false; 35 | 36 | // We'll use this for more data as well later on 37 | this.data = { 38 | args, // Hold onto all the arguments we are given 39 | config, // Any saved information (ie: domain, template path) 40 | current, // The directory the generator is fired from 41 | date, // Current data string 42 | name, // Name is usally used for filesnames and folders 43 | options, // Any flags used `ie: --pure, --connected` 44 | slug, // Lowercased and `-` seperated words 45 | tag, // A tag created from the `name` above 46 | type // not used yet 47 | }; 48 | 49 | // Used to stop running any public methods left to run 50 | this.stop = false; 51 | 52 | // Setup the default or custom template path 53 | helper.templates(this); 54 | } 55 | } 56 | 57 | // Export our generator 58 | module.exports = ReactUp; 59 | -------------------------------------------------------------------------------- /src/generators/common/helper.js: -------------------------------------------------------------------------------- 1 | // Vendor 2 | import path from 'path'; 3 | import { camelCase, upperFirst } from 'lodash'; 4 | 5 | // Setup 6 | const now = new Date(); 7 | const months = [ 8 | 'January', 'February', 'March', 'April', 'May', 'June', 9 | 'July', 'August', 'September', 'October', 'November', 'December' 10 | ]; 11 | 12 | const month = months[now.getMonth()]; 13 | 14 | /** 15 | * @description Some generic helpers I can see using in each generator 16 | */ 17 | export default { 18 | 19 | // Our Components and classes should be generated consistently 20 | componentName(str) { 21 | return upperFirst(camelCase(str)); 22 | }, 23 | 24 | // User friendly date string 25 | date() { 26 | return `${ month } ${ now.getDate() }, ${ now.getFullYear() }`; 27 | }, 28 | 29 | // Allows users to copy all templates into a local (project) folder for customization. 30 | templates(generator) { 31 | const projectPath = generator.sourceRoot(); 32 | const userPath = generator.config.get('templates'); 33 | const rootPath = userPath || path.resolve(projectPath, '../../templates'); 34 | 35 | generator.sourceRoot(rootPath); 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /src/generators/common/logger.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | // Vendor 4 | import chalk from 'chalk'; 5 | 6 | /** 7 | * @description Stylized logger 8 | */ 9 | export default { 10 | 11 | // Used when we use a default value 12 | default(value) { 13 | console.log(chalk.gray('- using default'), value); 14 | }, 15 | 16 | // Good place to throw a rollbar possibly 17 | error(err) { 18 | console.error(chalk.red('- error'), err); 19 | }, 20 | 21 | // Call out when a user tries to use a template that doesn't exist 22 | missing(path) { 23 | const message = chalk.gray('- template not found'); 24 | const file = chalk.yellow(`"${ path }"`); 25 | const skipping = chalk.gray('skipping.'); 26 | console.error(message, file, skipping); 27 | }, 28 | 29 | // Prompt a question to a user 30 | question(question, value) { 31 | console.log(question, chalk.cyan(value)); 32 | }, 33 | 34 | // When we skip a prompt 35 | skip(value) { 36 | console.log(chalk.gray('- skipping'), value); 37 | }, 38 | 39 | // We've saved a setting so done something good 40 | success(message, value) { 41 | console.log(chalk.gray(message), chalk.green(value)); 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /src/generators/common/path.js: -------------------------------------------------------------------------------- 1 | // Vendor 2 | import fse from 'fs-extra'; 3 | import path from 'path'; 4 | 5 | /** 6 | * @description Interact with the file system as we need to 7 | */ 8 | export default { 9 | // Clean a folder removing ALL contents 10 | clean(folder) { 11 | return new Promise((resolve, reject) => { 12 | fse.emptyDir(folder, err => { 13 | if (err) reject(err); 14 | if (!err) resolve(folder); 15 | }); 16 | }); 17 | }, 18 | 19 | // Copy our templates into a local directory 20 | copy(src, destination) { 21 | const templates = path.resolve(__dirname, '../../templates/'); 22 | return new Promise((resolve, reject) => { 23 | fse.copy(templates, destination, err => { 24 | if (err) reject(err); 25 | if (!err) resolve(src); 26 | }); 27 | }); 28 | }, 29 | 30 | // Create a folder locally, we use this for copying our templates 31 | create(folder) { 32 | return new Promise((resolve, reject) => { 33 | fse.ensureDir(folder, err => { 34 | if (err) reject(err); 35 | if (!err) resolve(folder); 36 | }); 37 | }); 38 | }, 39 | 40 | // Check a file exists, used before we copy an item over 41 | file(location, config) { 42 | const dirTemplates = config.get('templates'); 43 | const pathTemplates = 44 | dirTemplates.substr(-1) !== '/' ? `${dirTemplates}/` : dirTemplates; 45 | const pathRoot = config.get('root'); 46 | const filePath = `${pathRoot}/${pathTemplates}${location}`; 47 | 48 | return new Promise((resolve, reject) => { 49 | let file; 50 | 51 | try { 52 | file = require(`${filePath}`); 53 | } catch (e) { 54 | return reject(e); 55 | } 56 | 57 | return resolve(file); 58 | }); 59 | } 60 | }; 61 | -------------------------------------------------------------------------------- /src/generators/index.js: -------------------------------------------------------------------------------- 1 | // Vendor 2 | import chalk from 'chalk'; 3 | 4 | // Internal 5 | import logger from './common/logger'; 6 | import path from './common/path'; 7 | import ReactUp from './ReactUp'; 8 | 9 | /** 10 | * @class Custom 11 | * @description Allows the user to define more custom workflows by updating 12 | * the object found locally in `_templates/mappings.js` 13 | */ 14 | class Custom extends ReactUp { 15 | // eslint-disable-line padded-blocks 16 | 17 | /** 18 | * @description We use this method to ensure the user has copied the 19 | * templates over before running the `react-up:custom` generator. 20 | */ 21 | check() { 22 | const { config } = this.data; 23 | if (!config || !config.templates) { 24 | this.stop = true; 25 | this.composeWith('react-up:setup'); 26 | } 27 | } 28 | 29 | /** 30 | * @description This will set an author / domain name which is used in the 31 | * `package.json` files as we create new Customs 32 | */ 33 | setup() { 34 | if (this.stop) return Promise.resolve(); 35 | 36 | return path.file('mappings.js', this.config).then(mappings => { 37 | const customBuilds = mappings(this.data); 38 | const type = this.data.type; 39 | const node = customBuilds[type]; 40 | 41 | this._confirm(node.detail) // eslint-disable-line no-underscore-dangle 42 | .then(() => { 43 | this._copy(node); // eslint-disable-line no-underscore-dangle 44 | }) 45 | .catch(() => { 46 | logger.skip(node.description || 'custom template'); 47 | }); 48 | }); 49 | } 50 | 51 | _copy(node) { 52 | if (this.stop) return; 53 | 54 | const type = this.data.type; 55 | 56 | if (node) { 57 | this._copyFiles(node.files); // eslint-disable-line no-underscore-dangle 58 | } else { 59 | const file = chalk.bold('folder -> mappings.js'); 60 | const str = chalk.bold(type); 61 | const fileError = `File mapping for "${str}" not found.`; 62 | const templateError = `Please check your local templates ${file}`; 63 | 64 | logger.error(`${fileError} ${templateError}`); 65 | } 66 | } 67 | 68 | /** 69 | * @description Each custom setup can have a `description` and `details` 70 | * which are used to guide a the end user. 71 | */ 72 | _confirm(str) { 73 | const question = { 74 | type: 'confirm', 75 | name: 'confirm', 76 | message: str, 77 | default: true 78 | }; 79 | 80 | return new Promise((resolve, reject) => { 81 | this.prompt(question) 82 | .then(answers => { 83 | if (answers.confirm) resolve(answers.confirm); 84 | if (!answers.confirm) reject(answers.confirm); 85 | }) 86 | .catch(logger.error); 87 | }); 88 | } 89 | 90 | /** 91 | * @description We loop over an array of arrays. The top level key is used 92 | * to indicate the templates to be used. 93 | */ 94 | _copyFiles(files) { 95 | const { config, current } = this.data; 96 | 97 | files.map(file => { 98 | const src = file[0]; 99 | const out = file[1] || file[0]; 100 | const dest = config.relative ? `${current}/${out}` : out; 101 | 102 | try { 103 | this.template(src, dest, this.data); 104 | } catch (e) { 105 | logger.missing(src); 106 | } 107 | }); 108 | } 109 | } 110 | 111 | // Export our generator available via CLI: `yo react-up ` 112 | module.exports = Custom; 113 | -------------------------------------------------------------------------------- /src/generators/setup/index.js: -------------------------------------------------------------------------------- 1 | // Vendor 2 | import chalk from 'chalk'; 3 | import path from 'path'; 4 | import yosay from 'yosay'; 5 | 6 | // Internal 7 | import logger from '../common/logger'; 8 | import pathHelper from '../common/path'; 9 | import questions from './questions'; 10 | import ReactUp from '../ReactUp'; 11 | 12 | // Setup 13 | const successMessage = (config) => (` 14 | Setup complete, get started using any of the generators below: 15 | 16 | ${ chalk.gray(' * yo react-up Name') } 17 | ${ chalk.gray(' * yo react-up Name class') } 18 | ${ chalk.gray(' * yo react-up example-test test') } 19 | 20 | Create your own templates or customize the defaults: 21 | 22 | ${ chalk.gray(` * ${ config.root }/${ config.templates }mappings.js`) } 23 | 24 | Or run setup again at any point using: 25 | 26 | ${ chalk.gray(' * yo react-up:setup') } 27 | `); 28 | 29 | /** 30 | * @class Setup 31 | * @description This class helps the user further customize the generator. 32 | */ 33 | class Setup extends ReactUp { // eslint-disable-line padded-blocks 34 | 35 | /** 36 | * @description A nicer way to get started 37 | */ 38 | welcome() { 39 | const message = `Hello, and welcome to the ${ chalk.red('react-up') } generator!`; 40 | this.log(yosay(message)); 41 | 42 | return this.prompt(questions.customize) 43 | .then((answer) => { 44 | this.stop = !answer.customize; 45 | if (this.stop) logger.skip('setup'); 46 | }) 47 | .catch(logger.error); 48 | } 49 | 50 | /** 51 | * @description This will set an domain / domain name which is used in the 52 | * `package.json` files as we create new Components 53 | */ 54 | setDomain() { 55 | if (this.stop) return Promise.resolve(); 56 | 57 | return this.prompt(questions.domain) 58 | .then((answer) => { 59 | this.config.set('domain', answer.domain); 60 | this.config.set('package', __dirname); 61 | this.config.set('root', process.cwd()); 62 | }) 63 | .catch(logger.error); 64 | } 65 | 66 | /** 67 | * @description Setting up relative paths allows the user to run any of the 68 | * generators from within any sub-folder. Setting this value to false, will 69 | * run all reducers relative to the project root where the users `.yo-rc.json` 70 | */ 71 | setRelative() { 72 | if (this.stop) return Promise.resolve(); 73 | 74 | return this.prompt(questions.relative) 75 | .then((answer) => { 76 | this.config.set('relative', answer.relative); 77 | }) 78 | .catch(logger.error); 79 | } 80 | 81 | /** 82 | * @description Setting a custom template path will copy the defaults to 83 | * a template folder in the project root for further customization. 84 | */ 85 | setTemplates() { 86 | if (this.stop) return Promise.resolve(); 87 | 88 | return this.prompt(questions.template) 89 | .then((answer) => { 90 | const { templates } = answer; 91 | this.config.set('templates', templates); 92 | this._setupTemplatePath(templates); // eslint-disable-line no-underscore-dangle 93 | }) 94 | .catch(logger.error); 95 | } 96 | 97 | complete() { 98 | if (this.stop) return; 99 | const config = this.config.getAll(); // get our new config 100 | this.log(successMessage(config)); 101 | } 102 | 103 | /** 104 | * @description Anytime we run the setup we now use the Yeoman method 105 | * which protects us from overwritting any changes the user may have made 106 | * to their templates. Always grabbing the default templates which exist 107 | * inside of the NPM package 108 | */ 109 | _setupTemplatePath(dest) { 110 | const templates = path.resolve(__dirname, '../../templates'); 111 | 112 | pathHelper.create(dest) 113 | .then(() => { 114 | this.template(templates, dest); 115 | }) 116 | .catch(logger.error); 117 | } 118 | } 119 | 120 | // Export our generator available via CLI: `yo react-up:setup` 121 | module.exports = Setup; 122 | -------------------------------------------------------------------------------- /src/generators/setup/questions.js: -------------------------------------------------------------------------------- 1 | // Just to keep the Class a bit cleaner 2 | export default { 3 | customize: { 4 | default: true, 5 | message: 'Would you like to run the setup?', 6 | name: 'customize', 7 | type: 'confirm' 8 | }, 9 | 10 | domain: { 11 | default: 'example.com', 12 | message: 'Enter a custom domain:', 13 | name: 'domain', 14 | type: 'input' 15 | }, 16 | 17 | relative: { 18 | default: true, 19 | message: 'Run generator from the current directory?', 20 | name: 'relative', 21 | type: 'confirm' 22 | }, 23 | 24 | template: { 25 | default: '_templates/', 26 | message: 'Enter a local template path', 27 | name: 'templates', 28 | type: 'input' 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /src/gulpfile.js: -------------------------------------------------------------------------------- 1 | /* eslint import/no-extraneous-dependencies: ["error", {"devDependencies": true}] */ 2 | 3 | // Vendor 4 | import istanbul from 'gulp-istanbul'; 5 | import eslint from 'gulp-eslint'; 6 | import excludeGitignore from 'gulp-exclude-gitignore'; 7 | import gulp from 'gulp'; 8 | import mocha from 'gulp-mocha'; 9 | import nsp from 'gulp-nsp'; 10 | import path from 'path'; 11 | import plumber from 'gulp-plumber'; 12 | 13 | gulp.task('static', () => 14 | gulp.src('**/*.js') 15 | .pipe(excludeGitignore()) 16 | .pipe(eslint()) 17 | .pipe(eslint.format()) 18 | .pipe(eslint.failAfterError()) 19 | ); 20 | 21 | gulp.task('nsp', (cb) => { 22 | nsp({ 23 | package: path.resolve('package.json') 24 | }, cb); 25 | }); 26 | 27 | gulp.task('pre-test', () => 28 | gulp.src('generators/**/*.js') 29 | .pipe(excludeGitignore()) 30 | .pipe(istanbul({ 31 | includeUntested: true 32 | })) 33 | .pipe(istanbul.hookRequire()) 34 | ); 35 | 36 | gulp.task('test', ['pre-test'], (cb) => { 37 | let mochaErr; 38 | 39 | gulp.src('test/**/*.js') 40 | .pipe(plumber()) 41 | .pipe(mocha({ 42 | reporter: 'spec' 43 | })) 44 | .on('error', (err) => { 45 | mochaErr = err; 46 | }) 47 | .pipe(istanbul.writeReports()) 48 | .on('end', () => { 49 | cb(mochaErr); 50 | }); 51 | }); 52 | 53 | gulp.task('watch', () => { 54 | gulp.watch([ 55 | 'generators/**/*.js', 56 | 'test/**' 57 | ], ['test']); 58 | }); 59 | 60 | gulp.task('prepublish', ['nsp']); 61 | gulp.task('default', ['static', 'test']); 62 | -------------------------------------------------------------------------------- /src/test/app.js: -------------------------------------------------------------------------------- 1 | // Vendor 2 | import path from 'path'; 3 | import assert from 'yeoman-assert'; 4 | import helpers from 'yeoman-test'; 5 | 6 | describe('generator-react-up TestComponent', () => { 7 | before(() => { 8 | return helpers.run(path.join(__dirname, '../generators')) 9 | .withPrompts({ 10 | create: true, 11 | tests: true 12 | }) 13 | .toPromise(); 14 | }); 15 | 16 | it('creates files', () => { 17 | assert.file([ 18 | '__test__/TestComponentSpec.js', 19 | 'package.json', 20 | 'TestComponent.js', 21 | 'styles.scss' 22 | ]); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /templates/README.md: -------------------------------------------------------------------------------- 1 | # Generator Templates: 2 | What started as a generator to simply create some pre-defined files and stub them has evolved to included user defined boiler plates. Yeoman uses [EJS.co](http://ejs.co/) as it's templating engine so we've just built off of that. 3 | 4 | Using the object returned from `mappings.js` we get some basic information. From there we loop over the array of files return which each item is an array of `[source, destination]` and copy the files in place. `Destination` is optional and if not provided we'll copy the structure it had coming in. 5 | 6 | 7 | ```js 8 | // Here we output the marker tags with `%%` 9 | const before = { 10 | args: [ 11 | <%%% args.forEach((args) => { %> 12 | '<%%%= args -%>', 13 | <%%% }); -%> 14 | ], 15 | config: { 16 | domain: '<%%%= config.domain %>', 17 | root: '<%%%= config.root %>', 18 | templates: '<%%%= config.templates %>', 19 | }, 20 | current: '<%%%= current %>', 21 | date: '<%%%= date %>', 22 | name: '<%%%= name %>', 23 | slug: '<%%%= slug %>', 24 | tag: '<%%%- tag %>', 25 | type: '<%%%= type %>', 26 | } 27 | 28 | // And this is the generated output 29 | const after = { 30 | args: [ 31 | <%% args.forEach((args) => { -%> 32 | '<%%= args -%>', 33 | <%% }); -%> 34 | ], 35 | config: { 36 | domain: '<%%= current %>', 37 | root: '<%%= current %>', 38 | templates: '<%%= current %>' 39 | }, 40 | current: '<%%= current %>', 41 | date: '<%%= date %>', 42 | name: '<%%= name %>', 43 | slug: '<%%= slug %>', 44 | tag: '<%%- tag %>', 45 | type: '<%%= type %>' 46 | } 47 | ``` 48 | -------------------------------------------------------------------------------- /templates/_test/index.js: -------------------------------------------------------------------------------- 1 | // Vendor 2 | import React from 'react'; 3 | import td from 'testdouble'; 4 | import { expect } from 'chai'; 5 | import { shallow } from 'enzyme'; 6 | 7 | // Internal 8 | import <%%= name %> from '../<%%= name %>'; 9 | 10 | describe('<%%= name %>: test', () => { 11 | let props; 12 | 13 | // Any initialization 14 | beforeEach(() => { 15 | props = { 16 | // stub your component's props here 17 | }; 18 | }); 19 | 20 | // test cases 21 | it('renders', () => { 22 | const wrapper = shallow(<<%%= name %> {...props} />); 23 | 24 | expect(wrapper).to.not.be.empty(); 25 | }); 26 | 27 | // Replace this with your own specs 28 | it('should have more unit tests', () => { 29 | expect(false).to.be.true(); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /templates/common/package.json: -------------------------------------------------------------------------------- 1 | { 2 | <%% if (config.domain) { %> 3 | "author": "<%%= config.domain %>", 4 | <%% } -%> 5 | "dependencies": {}, 6 | "devDependencies": {}, 7 | "main": "./<%%= name %>.js", 8 | "name": "<%%= name %>", 9 | "private": true, 10 | "version": "1.0.0" 11 | } 12 | -------------------------------------------------------------------------------- /templates/common/styles.scss: -------------------------------------------------------------------------------- 1 | .<%%= slug %> { 2 | border: 1px solid #ccc; 3 | } 4 | -------------------------------------------------------------------------------- /templates/component/class.js: -------------------------------------------------------------------------------- 1 | // Vendor 2 | import React from 'react'; 3 | 4 | // Internal 5 | 6 | // Setup 7 | 8 | /** 9 | * @class <%%= name %> 10 | * @description Brief description 11 | */ 12 | class <%%= name %> extends React.Component { 13 | // https://goo.gl/g1KBEL 14 | constructor() { 15 | super(); 16 | 17 | this.state = { 18 | open: false 19 | }; 20 | 21 | // Chance to bind anything we need to. 22 | this.onClick = this.onClick.bind(this); 23 | } 24 | 25 | // https://goo.gl/JF9C1l 26 | componentWillMount() {} 27 | 28 | // https://goo.gl/cYkK3s 29 | componentDidMount() {} 30 | 31 | // https://goo.gl/5wgkZj 32 | componentWillReceiveProps(nextProps) {} 33 | 34 | // https://goo.gl/eIiU56 35 | shouldComponentUpdate(nextProps, nextState) {} 36 | 37 | // https://goo.gl/lLPyps 38 | componentWillUpdate(nextProps, nextState) {} 39 | 40 | // https://goo.gl/WgsPWE 41 | componentDidUpdate(prevProps, prevState) {} 42 | 43 | // https://goo.gl/cGM9sI 44 | componentWillUnmount() {} 45 | 46 | /** 47 | * Just a sample click event 48 | */ 49 | onClick() { 50 | console.log(`- onClick event`, this.state); 51 | } 52 | 53 | // https://goo.gl/HBJp32 54 | render() { 55 | return ( 56 |
58 |

<%%= name %>

59 |
60 | ); 61 | } 62 | }; 63 | 64 | // Enforce required properies or methods 65 | <%%= name %>.propTypes = { 66 | // active: React.PropTypes.bool.isRequired 67 | }; 68 | 69 | export default <%%= name %>; 70 | -------------------------------------------------------------------------------- /templates/component/class.long.js: -------------------------------------------------------------------------------- 1 | // Vendor 2 | import React from 'react'; 3 | 4 | // Internal 5 | 6 | // Setup 7 | 8 | /** 9 | * @class <%%= name %> 10 | * @description Brief description 11 | */ 12 | class <%%= name %> extends React.Component { 13 | 14 | /** 15 | * @link https://goo.gl/g1KBEL 16 | * @description The constructor for a React component is called before it is 17 | * mounted. When implementing the constructor for a React.Component subclass, 18 | * you should call super(props) before any other statement. Otherwise, this.props 19 | * will be undefined in the constructor, which can lead to bugs. 20 | */ 21 | constructor() { 22 | super(); 23 | 24 | this.state = { 25 | open: false 26 | }; 27 | 28 | // Chance to bind anything we need to. 29 | this.onClick = this.onClick.bind(this); 30 | } 31 | 32 | /** 33 | * @link https://goo.gl/JF9C1l 34 | * @description Invoked immediately before mounting occurs. It is called before 35 | * render(), therefore setting state in this method will not trigger a re-rendering. 36 | * Avoid introducing any side-effects or subscriptions in this method. This is the 37 | * only lifecycle hook called on server rendering. Generally, we recommend using 38 | * the constructor() instead. 39 | */ 40 | componentWillMount() {} 41 | 42 | /** 43 | * @link https://goo.gl/cYkK3s 44 | * @description Invoked immediately after a component is mounted. Initialization 45 | * that requires DOM nodes should go here. If you need to load data from a remote 46 | * endpoint, this is a good place to instantiate the network request. Setting 47 | * state in this method will trigger a re-rendering. 48 | */ 49 | componentDidMount() {} 50 | 51 | /** 52 | * @link https://goo.gl/5wgkZj 53 | * @description Invoked before a mounted component receives new props. If you 54 | * need to update the state in response to prop changes (for example, to reset 55 | * it), you may compare this.props and nextProps and perform state transitions 56 | * using this.setState() in this method. 57 | * 58 | * NOTE: React may call this method even if the props have not changed, so make 59 | * sure to compare the current and next values if you only want to handle changes. 60 | * This may occur when the parent component causes your component to re-render. 61 | * 62 | * componentWillReceiveProps() is not invoked if you just call this.setState() 63 | */ 64 | componentWillReceiveProps(nextProps) {} 65 | 66 | /** 67 | * @link https://goo.gl/eIiU56 68 | * @description Use shouldComponentUpdate() to let React know if a component's 69 | * output is not affected by the current change in state or props. The default 70 | * behavior is to re-render on every state change, and in the vast majority of 71 | * cases you should rely on the default behavior. 72 | */ 73 | shouldComponentUpdate(nextProps, nextState) {} 74 | 75 | /** 76 | * @link https://goo.gl/lLPyps 77 | * @description Invoked immediately before rendering when new props or state 78 | * are being received. Use this as an opportunity to perform preparation before 79 | * an update occurs. This method is not called for the initial render. 80 | * 81 | * NOTE: You cannot call this.setState() here. If you need to update state in 82 | * response to a prop change, use componentWillReceiveProps() instead. 83 | */ 84 | componentWillUpdate(nextProps, nextState) {} 85 | 86 | /** 87 | * @link https://goo.gl/WgsPWE 88 | * @description invoked immediately after updating occurs. This method is not 89 | * called for the initial render. Use this as an opportunity to operate on the 90 | * DOM when the component has been updated. This is also a good place to do 91 | * network requests as long as you compare the current props to previous props 92 | * (e.g. a network request may not be necessary if the props have not changed). 93 | * 94 | * NOTE: Will not be invoked if shouldComponentUpdate() returns false. 95 | */ 96 | componentDidUpdate(prevProps, prevState) {} 97 | 98 | /** 99 | * @link https://goo.gl/cGM9sI 100 | * @description Invoked immediately before a component is unmounted and destroyed. 101 | * Perform any necessary cleanup in this method, such as invalidating timers, 102 | * canceling network requests, or cleaning up any DOM elements that were created 103 | * in componentDidMount 104 | */ 105 | componentWillUnmount() {} 106 | 107 | /** 108 | * @link https://goo.gl/HBJp32 109 | * @description When called, it should examine this.props and this.state and 110 | * return a single React element. This element can be either a representation 111 | * of a native DOM component, such as
, or another composite component 112 | * that you've defined yourself. 113 | * 114 | * You can also return null or false to indicate that you don't want anything 115 | * rendered. When returning null or false, ReactDOM.findDOMNode(this) will 116 | * return null. 117 | * 118 | * The render() function should be pure, meaning that it does not modify 119 | * component state, it returns the same result each time it's invoked, and it 120 | * does not directly interact with the browser. If you need to interact with 121 | * the browser, perform your work in componentDidMount() or the other lifecycle 122 | * methods instead. Keeping render() pure makes components easier to think about. 123 | * 124 | * NOTE: Not be invoked if shouldComponentUpdate() returns false. 125 | */ 126 | render() { 127 | return ( 128 |
129 |

<%%= name %>

130 |
131 | ); 132 | } 133 | }; 134 | 135 | // Enforce required properies or methods 136 | <%%= name %>.propTypes = { 137 | // active: React.PropTypes.bool.isRequired 138 | }; 139 | 140 | export default <%%= name %>; 141 | -------------------------------------------------------------------------------- /templates/component/connected.js: -------------------------------------------------------------------------------- 1 | // Vendor 2 | import React from 'react'; 3 | 4 | /** 5 | * @class <%%= name %> 6 | * @description Brief description 7 | */ 8 | class <%%= name %> extends React.Component { 9 | render() { 10 | return ( 11 |
12 |

<%%= name %>

13 |
14 | ); 15 | } 16 | } 17 | 18 | // Enforce required properies or methods 19 | <%%= name %>.propTypes = { 20 | // active: React.PropTypes.bool.isRequired 21 | }; 22 | 23 | const mapStateToProps = (state, ownProps) => ({ 24 | active: ownProps.filter === state.visibilityFilter 25 | }); 26 | 27 | const mapDispatchToProps = (dispatch, ownProps) => ({ 28 | onClick: () => { 29 | dispatch(setVisibilityFilter(ownProps.filter)) 30 | } 31 | }); 32 | 33 | // Export the Redux connected component 34 | export default connect( 35 | mapStateToProps, 36 | mapDispatchToProps 37 | )(<%%= name %>); 38 | -------------------------------------------------------------------------------- /templates/component/pure.js: -------------------------------------------------------------------------------- 1 | // Vendor 2 | import React from 'react'; 3 | 4 | // Internal 5 | 6 | // Setup 7 | 8 | /** 9 | * @name <%%= name %> 10 | * @description Stateless Component / Pure Function 11 | */ 12 | const <%%= name %> = () => ( 13 |
14 |

<%%= name %>

15 |
16 | ); 17 | 18 | // Enforce required properies or methods 19 | <%%= name %>.propTypes = { 20 | // active: React.PropTypes.bool.isRequired 21 | }; 22 | 23 | export default <%%= name %>; 24 | -------------------------------------------------------------------------------- /templates/mappings.js: -------------------------------------------------------------------------------- 1 | // Setup any preferred defaults 2 | const TEST_EXT = '.test.js'; 3 | const TEST_FOLDER = '_test'; 4 | 5 | /** 6 | * @description Modify the existing configurations or create your own 7 | * @example yo react-up 8 | */ 9 | const mappings = (data = {}) => { 10 | const { args, name } = data; 11 | 12 | // NOTE: Uncomment the line below to see the data available to the templates 13 | // console.log('- available template data:', data); 14 | 15 | /** 16 | * @description The idea is that you can setup your own template sets and 17 | */ 18 | return { 19 | 20 | /** 21 | * @example yo react-up class 22 | */ 23 | class: { 24 | description: 'React Component', 25 | detail: 'Create a `React Class` w/ testing folder + stub.', 26 | files: [ 27 | ['_test/index.js', `${ name }/${ TEST_FOLDER }/${ name }${ TEST_EXT }`], 28 | ['common/package.json', `${ name }/package.json`], 29 | ['common/styles.scss', `${ name }/styles.scss`], 30 | ['component/class.js', `${ name }/${ name }.js`] 31 | ] 32 | }, 33 | 34 | /** 35 | * @example yo react-up pure 36 | */ 37 | pure: { 38 | description: 'Functional Component', 39 | detail: 'Create a `Functional Component` w/ testing folder + stub.', 40 | files: [ 41 | ['_test/index.js', `${ name }/${ TEST_FOLDER }/${ name }${ TEST_EXT }`], 42 | ['common/package.json', `${ name }/package.json`], 43 | ['common/styles.scss', `${ name }/styles.scss`], 44 | ['component/pure.js', `${ name }/${ name }.js`] 45 | ] 46 | }, 47 | 48 | /** 49 | * @example yo react-up test 50 | */ 51 | test: { 52 | description: 'Test Stub', 53 | detail: `Create a test file: ${ args[0] }`, 54 | files: [ 55 | ['_test/index.js', `${ args[0] }${ TEST_EXT }`] 56 | ] 57 | }, 58 | 59 | /** 60 | * @example yo react-up example 61 | * @description This one is for demonstration purposes. 62 | * 63 | * Demonstrates: 64 | * - uses TEST_FOLDER and TEST_EXT constants set in `_templates/defaults.js` 65 | * - intentional template file is missing to demonstrate feedback 66 | * - contains working example of various template tags 67 | * - simple files renaming and multi-level folder structures 68 | */ 69 | example: { 70 | description: 'example', 71 | detail: 'Create an example component?', 72 | files: [ 73 | ['_test/index.js', `${ name }/${ TEST_FOLDER }/${ name }${ TEST_EXT }`], 74 | ['README.md', `${ name }/sub-folder/custom.md`], 75 | ['README.md', `${ name }/README.md`], 76 | ['something/that/doesnt/exist.md', `${ name }/README.md`] 77 | ] 78 | } 79 | 80 | /** 81 | * @example yo react-up create-your-own 82 | */ 83 | }; 84 | }; 85 | 86 | module.exports = mappings; 87 | --------------------------------------------------------------------------------