├── .babelrc ├── .blueprintrc ├── .codeclimate.yml ├── .eslintrc ├── .gitignore ├── .istanbul.yml ├── .mocha.opts ├── .npmignore ├── .nvmrc ├── .travis.yml ├── bin ├── blueprint.js └── bp.js ├── blueprints ├── .eslintrc ├── blueprint │ ├── files │ │ └── blueprints │ │ │ └── __name__ │ │ │ ├── files │ │ │ └── .gitkeep │ │ │ └── index.js.ejs │ └── index.js ├── duck │ ├── files │ │ ├── __root__ │ │ │ └── __duck__ │ │ │ │ └── __name__.js.ejs │ │ └── __test__ │ │ │ └── redux │ │ │ └── modules │ │ │ └── __name__.test.js.ejs │ └── index.js ├── dumb │ ├── files │ │ ├── __root__ │ │ │ └── __dumb__ │ │ │ │ └── __name__.js.ejs │ │ └── __test__ │ │ │ └── __dumb__ │ │ │ └── __name__.test.js.ejs │ └── index.js ├── form │ ├── files │ │ ├── __root__ │ │ │ └── forms │ │ │ │ └── __name__.js.ejs │ │ └── __test__ │ │ │ └── forms │ │ │ └── __name__.test.js.ejs │ └── index.js └── smart │ ├── files │ ├── __root__ │ │ └── __smart__ │ │ │ └── __name__.js.ejs │ └── __test__ │ │ └── __smart__ │ │ └── __name__.test.js.ejs │ └── index.js ├── codecov.yml ├── design.md ├── jest.config.js ├── package.json ├── readme.md ├── redux-cli.gif ├── src ├── cli │ ├── cmds │ │ ├── config.js │ │ ├── generate.js │ │ ├── generate │ │ │ ├── build-blueprint-command.js │ │ │ ├── build-blueprint-commands.js │ │ │ └── handlers.js │ │ ├── init.js │ │ └── new.js │ ├── environment.js │ ├── handler.js │ ├── index.js │ ├── parser.js │ └── yargs.js ├── config.js ├── models │ ├── blueprint-collection.js │ ├── blueprint.js │ ├── file-info.js │ ├── project-settings.js │ ├── sub-command.js │ ├── task.js │ └── ui.js ├── prompts │ ├── initPrompt.js │ └── setup.js ├── sub-commands │ ├── config.js │ ├── generate.js │ ├── init.js │ └── new.js ├── tasks │ ├── create-and-step-into-directory.js │ ├── generate-from-blueprint.js │ └── git-pull.js ├── util │ ├── fs.js │ ├── mixin.js │ └── text-helper.js └── version.js ├── templates ├── .blueprintrc └── .starterrc ├── test ├── .eslintrc ├── cli │ ├── cmds │ │ ├── config.test.js │ │ ├── generate.test.js │ │ ├── generate │ │ │ ├── build-blueprint-command.test.js │ │ │ ├── build-blueprint-commands.test.js │ │ │ └── handlers.test.js │ │ ├── init.test.js │ │ └── new.test.js │ ├── environment.test.js │ ├── handler.test.js │ └── parser.test.js ├── fixtures │ ├── argv.blueprintrc │ ├── basic │ │ ├── files │ │ │ └── expected-file.js │ │ └── index.js │ ├── blueprints │ │ ├── basic │ │ │ ├── files │ │ │ │ └── expected-file.js │ │ │ └── index.js │ │ └── duplicate │ │ │ ├── files │ │ │ └── expected-file.js │ │ │ └── index.js │ ├── env.blueprintrc │ └── file-info-template.txt ├── helpers │ ├── fs-helpers.js │ ├── mock-settings.js │ ├── mock-ui.js │ └── regex-utils.js ├── models │ ├── blueprint-collection.test.js │ ├── blueprint.test.js │ ├── file-info.test.js │ ├── project-settings.test.js │ ├── sub-command.test.js │ ├── task.test.js │ └── ui.test.js ├── prompts │ └── setup.test.js ├── setup.js └── util │ ├── fs.test.js │ ├── mixin.test.js │ └── text-helper.test.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"], 3 | "plugins": ["transform-object-rest-spread"], 4 | "env": { 5 | "test": { 6 | "presets": ["es2015"], 7 | "plugins": ["transform-object-rest-spread"], 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.blueprintrc: -------------------------------------------------------------------------------- 1 | { 2 | "sourceBase": "src", 3 | "testBase": "test", 4 | "smartPath": "container", 5 | "dumbPath": "component", 6 | "fileCasing": "default", 7 | "location": "project", 8 | "blueprints": true 9 | } 10 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | engines: 3 | duplication: 4 | enabled: false 5 | config: 6 | languages: 7 | javascript: 8 | mass_threshold: 60 9 | eslint: 10 | enabled: true 11 | fixme: 12 | enabled: true 13 | ratings: 14 | paths: 15 | - "**.inc" 16 | - "**.js" 17 | - "**.jsx" 18 | exclude_paths: 19 | - test/ 20 | - templates/ 21 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": "prettier", 4 | "rules": { 5 | "indent": [2, 2], 6 | "quotes": [2, "single", {"avoidEscape": true}], 7 | "linebreak-style": [2, "unix"], 8 | "semi": [2, "always"], 9 | "no-console": [0] 10 | }, 11 | "env": { 12 | "es6": true, 13 | "node": true, 14 | "browser": true 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib 3 | npm-debug.log 4 | coverage/ 5 | tmp/ 6 | .reduxrc 7 | yarn-error.log 8 | -------------------------------------------------------------------------------- /.istanbul.yml: -------------------------------------------------------------------------------- 1 | instrumentation: 2 | root: src 3 | -------------------------------------------------------------------------------- /.mocha.opts: -------------------------------------------------------------------------------- 1 | --require ./test/setup.js 2 | --full-trace 3 | --recursive ./test/**/*.js 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 5.1.0 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "5.1.0" 4 | addons: 5 | code_climate: 6 | repo_token: bc3997ee81bddc88a39799cac43d7e6a71e7f0a2334331a7f02ae486433ddc1f 7 | before_install: 8 | - pip install --user codecov 9 | after_success: 10 | - codecov --file coverage/lcov.info --disable search 11 | -------------------------------------------------------------------------------- /bin/blueprint.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const cli = require('../lib/cli'); 4 | 5 | cli(); 6 | -------------------------------------------------------------------------------- /bin/bp.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const cli = require('../lib/cli'); 4 | 5 | cli(); 6 | -------------------------------------------------------------------------------- /blueprints/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.eslintrc", 3 | "env": { 4 | "mocha": true 5 | }, 6 | "rules": { 7 | "no-unused-vars": 0 8 | }, 9 | "plugins": ["ejs"], 10 | "globals": { 11 | "expect": false, 12 | "should": false, 13 | "sinon": false 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /blueprints/blueprint/files/blueprints/__name__/files/.gitkeep: -------------------------------------------------------------------------------- 1 | put your files here 2 | -------------------------------------------------------------------------------- /blueprints/blueprint/files/blueprints/__name__/index.js.ejs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | <% if (description) { %> 3 | description: function() { 4 | return '<%- description %>'; 5 | }, 6 | <% } %> 7 | <% if (command) { %> 8 | command: { 9 | aliases: <%- aliases %>, 10 | options: { 11 | opt: { 12 | alias: 'o', 13 | description: 'blueprint option', 14 | type: 'string' 15 | } 16 | }, 17 | check: (argv, options) => true, 18 | examples: [], 19 | sanitize: argv => argv 20 | }, 21 | <% } %> 22 | <% if (locals) { %> 23 | locals(options) { 24 | // Return custom template variables here 25 | return {}; 26 | }, 27 | <% } %> 28 | <% if (fileMapTokens) { %> 29 | fileMapTokens: function(options) { 30 | // Return custom tokens to be replaced in path and file names 31 | return { 32 | __token__: function(options) { 33 | // logic to determine value goes here 34 | return 'value'; 35 | }, 36 | }; 37 | }, 38 | <% } %> 39 | <% if (beforeInstall) { %> 40 | beforeInstall: function(options, locals) {}, 41 | <% } %> 42 | <% if (afterInstall) { %> 43 | afterInstall: function(options) {}, 44 | <% } %> 45 | }; 46 | -------------------------------------------------------------------------------- /blueprints/blueprint/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | description() { 3 | return 'Generates a blueprint with desired hooks'; 4 | }, 5 | 6 | command: { 7 | aliases: ['bp'], 8 | options: { 9 | description: { 10 | alias: 'D', 11 | describe: 'override default description', 12 | type: 'string' 13 | }, 14 | command: { 15 | alias: 'c', 16 | describe: 'add hook to specify generate command options', 17 | type: 'boolean' 18 | }, 19 | aliases: { 20 | alias: 'A', 21 | describe: 'specify aliases for the blueprint', 22 | type: 'array', 23 | default: [] 24 | }, 25 | locals: { 26 | alias: 'l', 27 | describe: 'add hook to specify locals', 28 | type: 'boolean' 29 | }, 30 | 'file-map-tokens': { 31 | alias: 'm', 32 | describe: 'add hook for fileMapTokens', 33 | type: 'boolean' 34 | }, 35 | 'before-install': { 36 | alias: 'b', 37 | describe: 'add hook for beforeInstall', 38 | type: 'boolean' 39 | }, 40 | 'after-install': { 41 | alias: 'a', 42 | describe: 'add hook for afterInstall', 43 | type: 'boolean' 44 | }, 45 | 'all-hooks': { 46 | alias: 'H', 47 | describe: 'shortcut to add all hooks, equivalent to -clmba', 48 | type: 'boolean' 49 | } 50 | }, 51 | examples: [ 52 | '$0 generate blueprint files_only', 53 | '$0 generate blueprint complex_bp --aliases cpx --all-hooks' 54 | ], 55 | epilogue: 56 | 'Documentation: https://github.com/SpencerCDixon/redux-cli#creating-blueprints', 57 | sanitize: argv => { 58 | // aliases imply command 59 | if (argv.aliases.length) { 60 | argv.command = true; 61 | } 62 | // aliases to be rendered as a string 63 | argv.aliases = JSON.stringify(argv.aliases); 64 | 65 | // NB: if command was specified but aliases is an empty array it will 66 | // still be rendered. This is harmless and serves as a reminder 67 | // to the blueprint author of the supported feature and syntax 68 | 69 | // allHooks? 70 | if (argv.allHooks) { 71 | argv.command = true; 72 | argv.locals = true; 73 | argv.fileMapTokens = true; 74 | argv.beforeInstall = true; 75 | argv.afterInstall = true; 76 | } 77 | 78 | return argv; 79 | } 80 | }, 81 | 82 | locals({ entity: { options } }) { 83 | return options; 84 | } 85 | }; 86 | -------------------------------------------------------------------------------- /blueprints/duck/files/__root__/__duck__/__name__.js.ejs: -------------------------------------------------------------------------------- 1 | // Constants 2 | 3 | // export const constants = { }; 4 | 5 | // Action Creators 6 | 7 | // export const actions = { }; 8 | 9 | // Reducer 10 | export const defaultState = {}; 11 | 12 | export default function(state = defaultState, action) { 13 | switch (action.type) { 14 | default: 15 | return state; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /blueprints/duck/files/__test__/redux/modules/__name__.test.js.ejs: -------------------------------------------------------------------------------- 1 | import reducer, { defaultState } from 'redux/modules/<%= camelEntityName %>'; 2 | import deepFreeze from 'deep-freeze'; 3 | 4 | describe('(Redux) <%= camelEntityName %>', () => { 5 | describe('(Reducer)', () => { 6 | it('sets up initial state', () => { 7 | expect(reducer(undefined, {})).to.eql(defaultState); 8 | }); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /blueprints/duck/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | description() { 3 | return 'Generates a reducer/actions/constant Duck Module for redux'; 4 | }, 5 | fileMapTokens() { 6 | return { 7 | __duck__: options => { 8 | return 'redux/modules'; 9 | } 10 | }; 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /blueprints/dumb/files/__root__/__dumb__/__name__.js.ejs: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | 3 | const propTypes = { 4 | }; 5 | 6 | class <%= pascalEntityName %> extends Component { 7 | render() { 8 | return ( 9 |
10 | ); 11 | } 12 | } 13 | 14 | <%= pascalEntityName %>.propTypes = propTypes; 15 | export default <%= pascalEntityName %>; 16 | -------------------------------------------------------------------------------- /blueprints/dumb/files/__test__/__dumb__/__name__.test.js.ejs: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow } from 'enzyme'; 3 | 4 | describe('(Component) <%= pascalEntityName %>', function() { 5 | it('should exist', function() {}); 6 | }); 7 | -------------------------------------------------------------------------------- /blueprints/dumb/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | description() { 3 | return 'Generates a dumb (aka Pure) component'; 4 | }, 5 | fileMapTokens() { 6 | return { 7 | __dumb__: options => { 8 | return options.settings.getSetting('dumbPath'); 9 | } 10 | }; 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /blueprints/form/files/__root__/forms/__name__.js.ejs: -------------------------------------------------------------------------------- 1 | <% /* eslint-disable */ %> 2 | import React, { Component, PropTypes } from 'react'; 3 | import { reduxForm } from 'redux-form'; 4 | 5 | export const fields = []; 6 | 7 | const validate = (values) => { 8 | const errors = {}; 9 | return errors; 10 | }; 11 | 12 | const propTypes = { 13 | handleSubmit: PropTypes.func.isRequired, 14 | fields: PropTypes.object.isRequired 15 | }; 16 | 17 | export class <%= pascalEntityName %> extends Component { 18 | render() { 19 | const { 20 | fields: {}, 21 | handleSubmit 22 | } = this.props; 23 | 24 | return ( 25 | 27 | ); 28 | } 29 | }; 30 | 31 | <%= pascalEntityName %>.propTypes = propTypes; 32 | <%= pascalEntityName %> = reduxForm({ 33 | form: '<%= pascalEntityName %>', 34 | fields, 35 | validate 36 | })(<%= pascalEntityName %>); 37 | 38 | export default <%= pascalEntityName %>; 39 | -------------------------------------------------------------------------------- /blueprints/form/files/__test__/forms/__name__.test.js.ejs: -------------------------------------------------------------------------------- 1 | import { shallow } from 'enzyme'; 2 | 3 | describe('(Form) <%= pascalEntityName %>', () => { 4 | it('exists', () => {}); 5 | }); 6 | -------------------------------------------------------------------------------- /blueprints/form/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | description() { 3 | return 'Generates a connected form component using redux-form'; 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /blueprints/smart/files/__root__/__smart__/__name__.js.ejs: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { bindActionCreators } from 'redux'; 4 | 5 | const propTypes = { 6 | }; 7 | 8 | class <%= pascalEntityName %> extends Component { 9 | render() { 10 | return ( 11 | 12 | ); 13 | } 14 | } 15 | 16 | const mapStateToProps = (state) => { 17 | return {}; 18 | }; 19 | const mapDispatchToProps = (dispatch) => { 20 | return {}; 21 | }; 22 | 23 | <%= pascalEntityName %>.propTypes = propTypes; 24 | export default connect( 25 | mapStateToProps, 26 | mapDispatchToProps 27 | )(<%= pascalEntityName %>); 28 | -------------------------------------------------------------------------------- /blueprints/smart/files/__test__/__smart__/__name__.test.js.ejs: -------------------------------------------------------------------------------- 1 | import { shallow } from 'enzyme'; 2 | 3 | describe('(Component) <%= pascalEntityName %>', () => { 4 | it('exists', () => {}); 5 | }); 6 | -------------------------------------------------------------------------------- /blueprints/smart/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | description() { 3 | return 'Generates a smart (aka container) component'; 4 | }, 5 | fileMapTokens() { 6 | return { 7 | __smart__: options => { 8 | return options.settings.getSetting('smartPath'); 9 | } 10 | }; 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | fixes: 2 | - "src/::lib/" -------------------------------------------------------------------------------- /design.md: -------------------------------------------------------------------------------- 1 | ## Design Decisions 1.0 2 | 3 | Would like to emulate the good parts of ember-cli and not use the parts I don't 4 | like/think would work well within the React eco-system. 5 | 6 | **Commands** will be controlled by commander. 7 | **Tasks** will act like rake tasks in rails. 8 | **Blueprints** will be what gets created via generate. These can be overridden 9 | by individual projects so that way projects can manage their own blueprints. 10 | 11 | Commander will be used to spawn off top level commands. The primary purpose of 12 | commander will be to parse options/args and display useful help for all the 13 | different options. Once args have been parsed they can be passed down to 14 | SubCommands. 15 | 16 | ## Design Decisions 2.0 17 | 18 | Rename to blueprint-cli 19 | 20 | Take the foundation laid by 1.0 and extend the capabilities. 21 | 22 | Blueprints are most useful when able to be shared, copied and customized. 23 | The Blueprints directory discovery will be expanded to include home directories, 24 | ENV defined directories, npm packages, config file defined. A single 25 | directory may be defined as the default directory for blueprint generation. 26 | Provide a way to copy blueprints into the default directory in order to 27 | increase the ease of customizing your own version of a default or shared 28 | blueprint. 29 | 30 | Enhance the .blueprintrc experience. Add the ability to have home directory 31 | and ENV var defined locations. Allow merging of multiple .blueprintrc files. 32 | Allow defining blueprint directories in the file. 33 | 34 | Enhance the Generator experience. Look to Ruby on Rails for inspiration. 35 | Look for ways to enable generator composition. Look for ways to insert code 36 | into existing files. Find ways to share and use partials in generators 37 | 38 | Define a generator to create a npm package dir ready to share blueprints with 39 | the community. 40 | 41 | 42 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | // jest.config.js 2 | module.exports = { 3 | testEnvironment: "node", 4 | setupTestFrameworkScriptFile: "