├── .gitattributes
├── src
├── gen
│ ├── component
│ │ ├── templates
│ │ │ ├── Helper.js
│ │ │ ├── Scss.js
│ │ │ ├── Fixtures.js
│ │ │ ├── Component_test.js
│ │ │ └── Component.js
│ │ └── index.js
│ ├── redux
│ │ ├── templates
│ │ │ ├── Constants.js
│ │ │ ├── Selectors.js
│ │ │ ├── Actions.js
│ │ │ ├── Actions_test.js
│ │ │ ├── Integration_test.js
│ │ │ ├── Selectors_test.js
│ │ │ ├── Reducer.js
│ │ │ ├── Reducer_test.js
│ │ │ └── index.js
│ │ └── index.js
│ └── base
│ │ └── index.js
├── app
│ ├── packages.js
│ └── index.js
├── constants.js
└── helpers.js
├── __tests__
├── __mocks__
│ ├── Component.js
│ └── helper.js
├── __snapshots__
│ └── helpers.test.js.snap
├── helpers.test.js
└── app.test.js
├── .eslintignore
├── .gitignore
├── .travis.yml
├── .yo-rc.json
├── .eslintrc.js
├── .editorconfig
├── .babelrc
├── LICENSE
├── package.json
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 |
--------------------------------------------------------------------------------
/src/gen/component/templates/Helper.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/gen/component/templates/Scss.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/__tests__/__mocks__/Component.js:
--------------------------------------------------------------------------------
1 | // mock
--------------------------------------------------------------------------------
/__tests__/__mocks__/helper.js:
--------------------------------------------------------------------------------
1 | // fixtures
--------------------------------------------------------------------------------
/src/gen/component/templates/Fixtures.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | coverage
2 | **/templates
3 | generators
4 | src/components
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | lib
2 | coverage
3 | node_modules
4 | src/components
5 | .yo-rc.json
6 | .DS_Store
--------------------------------------------------------------------------------
/src/app/packages.js:
--------------------------------------------------------------------------------
1 | const PACKAGES = ['react-redux-test-utils'];
2 |
3 | export default PACKAGES;
4 |
--------------------------------------------------------------------------------
/src/gen/redux/templates/Constants.js:
--------------------------------------------------------------------------------
1 | export const <%= name_upper %>_CHANGE_BOOL = '<%= name_upper %>_CHANGE_BOOL';
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - v10
4 | - v8
5 | after_script: cat ./coverage/lcov.info | coveralls
6 |
--------------------------------------------------------------------------------
/src/gen/redux/templates/Selectors.js:
--------------------------------------------------------------------------------
1 | export const select<%= name %> = state => state.<%= name_lower %>;
2 | export const selectBool = state => select<%= name %>(state).bool;
3 |
--------------------------------------------------------------------------------
/.yo-rc.json:
--------------------------------------------------------------------------------
1 | {
2 | "generator-react-domain": {
3 | "templatesPath": "overrides",
4 | "componentsPath": "src/components",
5 | "yarn": true,
6 | "depsInstalled": true
7 | }
8 | }
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "extends": ["airbnb", "prettier"],
3 | "env": {
4 | "jest": true
5 | },
6 | "rules": {
7 | "linebreak-style": "off"
8 | }
9 | };
--------------------------------------------------------------------------------
/.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 = false
12 |
--------------------------------------------------------------------------------
/src/gen/redux/templates/Actions.js:
--------------------------------------------------------------------------------
1 | import { <%= name_upper %>_CHANGE_BOOL } from './<%= name %>Constants';
2 |
3 | export const changeBool = resource => dispatch => {
4 | dispatch({
5 | type: <%= name_upper %>_CHANGE_BOOL,
6 | payload: resource,
7 | });
8 | };
9 |
--------------------------------------------------------------------------------
/src/gen/redux/index.js:
--------------------------------------------------------------------------------
1 | import BaseGenerator from '../base';
2 | import { REDUX_TPL } from '../../constants';
3 |
4 | class ReduxGenerator extends BaseGenerator {
5 | writing() {
6 | this.copyTemplates(REDUX_TPL, this.options.name, this.options.path);
7 | }
8 | }
9 |
10 | module.exports = ReduxGenerator;
11 |
--------------------------------------------------------------------------------
/src/gen/component/index.js:
--------------------------------------------------------------------------------
1 | import BaseGenerator from '../base';
2 | import { REACT_TPL } from '../../constants';
3 |
4 | class ComponentGenerator extends BaseGenerator {
5 | writing() {
6 | this.copyTemplates(REACT_TPL, this.options.name, this.options.path);
7 | }
8 | }
9 |
10 | module.exports = ComponentGenerator;
11 |
--------------------------------------------------------------------------------
/src/gen/redux/templates/Actions_test.js:
--------------------------------------------------------------------------------
1 | import { testActionSnapshotWithFixtures } from 'react-redux-test-utils';
2 | import { changeBool } from '../<%= name %>Actions';
3 |
4 | const fixtures = {
5 | 'should changeBool': () => changeBool({ bool: true }),
6 | };
7 |
8 | describe('<%= name %> actions', () => testActionSnapshotWithFixtures(fixtures));
9 |
--------------------------------------------------------------------------------
/src/constants.js:
--------------------------------------------------------------------------------
1 | export const REDUX_TPL = [
2 | 'Actions',
3 | 'Constants',
4 | 'Reducer',
5 | 'Selectors',
6 | 'Actions_test',
7 | 'Reducer_test',
8 | 'Selectors_test',
9 | 'Integration_test',
10 | 'index'
11 | ];
12 |
13 | export const REACT_TPL = [
14 | 'Component',
15 | 'Scss',
16 | 'Fixtures',
17 | 'Helper',
18 | 'Component_test'
19 | ];
20 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "env",
5 | {
6 | "targets": {
7 | "node": true
8 | }
9 | }
10 | ]
11 | ],
12 | "plugins": [
13 | [
14 | "transform-runtime",
15 | {
16 | "polyfill": true,
17 | "regenerator": true
18 | }
19 | ],
20 | ["transform-object-rest-spread"]
21 | ],
22 | "ignore": ["**/templates/*"]
23 | }
24 |
--------------------------------------------------------------------------------
/src/gen/component/templates/Component_test.js:
--------------------------------------------------------------------------------
1 | import { testComponentSnapshotsWithFixtures } from 'react-redux-test-utils';
2 |
3 | import <%= name %> from '../<%= name %>';
4 |
5 | const fixtures = {
6 | 'render without Props': {},
7 | /** fixtures, props for the component */
8 | };
9 |
10 | describe('<%= name %>', () => {
11 | describe('rendering', () =>
12 | testComponentSnapshotsWithFixtures(<%= name %>, fixtures));
13 | });
14 |
--------------------------------------------------------------------------------
/src/gen/redux/templates/Integration_test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { IntegrationTestHelper } from 'react-redux-test-utils';
3 |
4 | import <%= name %>, { reducers } from '../index';
5 |
6 | describe('<%= name %> integration test', () => {
7 | it('should flow', async () => {
8 | const integrationTestHelper = new IntegrationTestHelper(reducers);
9 | const component = integrationTestHelper.mount(<<%= name %> />);
10 | component.update();
11 | /** Create a Flow test */
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/src/gen/redux/templates/Selectors_test.js:
--------------------------------------------------------------------------------
1 | import { testSelectorsSnapshotWithFixtures } from 'react-redux-test-utils';
2 | import { select<%= name %>, selectBool } from '../<%= name %>Selectors';
3 |
4 | const state = {
5 | <%= name_lower %>: {
6 | bool: false,
7 | },
8 | };
9 |
10 | const fixtures = {
11 | 'should return <%= name %>': () => select<%= name %>(state),
12 | 'should return <%= name %> bool': () => selectBool(state),
13 | };
14 |
15 | describe('<%= name %> selectors', () => testSelectorsSnapshotWithFixtures(fixtures));
16 |
--------------------------------------------------------------------------------
/src/gen/redux/templates/Reducer.js:
--------------------------------------------------------------------------------
1 | import Immutable from 'seamless-immutable';
2 |
3 | import { <%= name_upper %>_CHANGE_BOOL } from './<%= name %>Constants';
4 |
5 | const initialState = Immutable({
6 | /** insert <%= name %> state here */
7 | bool: false,
8 | });
9 |
10 | export default (state = initialState, action) => {
11 | const { payload } = action;
12 |
13 | switch (action.type) {
14 | case <%= name_upper %>_CHANGE_BOOL:
15 | return state.set('bool', payload.bool);
16 |
17 | default:
18 | return state;
19 | }
20 | };
21 |
--------------------------------------------------------------------------------
/src/gen/component/templates/Component.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | class <%= name %> extends Component {
5 | componentDidMount() {
6 | // Do Something here
7 | }
8 |
9 | render() {
10 | const { children, className } = this.props;
11 | return
{children}
;
12 | }
13 | }
14 |
15 | <%= name %>.propTypes = {
16 | children: PropTypes.node,
17 | className: PropTypes.string,
18 | };
19 |
20 | <%= name %>.defaultProps = {
21 | className: '',
22 | children: null,
23 | };
24 |
25 | export default <%= name %>;
26 |
--------------------------------------------------------------------------------
/src/gen/redux/templates/Reducer_test.js:
--------------------------------------------------------------------------------
1 | import { testReducerSnapshotWithFixtures } from 'react-redux-test-utils';
2 |
3 | import { <%= name_upper %>_CHANGE_BOOL } from '../<%= name %>Constants';
4 | import reducer from '../<%= name %>Reducer';
5 |
6 | const fixtures = {
7 | 'should return the initial state': {},
8 | 'should handle <%= name_upper %>_CHANGE_BOOL': {
9 | action: {
10 | type: <%= name_upper %>_CHANGE_BOOL,
11 | payload: {
12 | bool: true,
13 | },
14 | },
15 | },
16 | };
17 |
18 | describe('<%= name %> reducer', () =>
19 | testReducerSnapshotWithFixtures(reducer, fixtures));
20 |
--------------------------------------------------------------------------------
/src/gen/base/index.js:
--------------------------------------------------------------------------------
1 | import Generator from 'yeoman-generator';
2 | import { getPath, caseNames, getTemplatePath } from '../../helpers';
3 |
4 | class BaseGenerator extends Generator {
5 | copyTemplates(templates, name, path) {
6 | // check if consumer has templates
7 | const consumerPath = this.config.get('templatesPath');
8 |
9 | templates.forEach(file =>
10 | this.fs.copyTpl(
11 | getTemplatePath(file, consumerPath, this.sourceRoot()),
12 | this.destinationPath(getPath(path, name, file)),
13 | caseNames(name)
14 | )
15 | );
16 | }
17 | }
18 |
19 | export default BaseGenerator;
20 |
--------------------------------------------------------------------------------
/src/gen/redux/templates/index.js:
--------------------------------------------------------------------------------
1 | import { bindActionCreators } from 'redux';
2 | import { connect } from 'react-redux';
3 |
4 | import * as actions from './<%= name %>Actions';
5 | import reducer from './<%= name %>Reducer';
6 | import { selectBool } from './<%= name %>Selectors';
7 |
8 | import <%= name %> from './<%= name %>';
9 |
10 | // map state to props
11 | const mapStateToProps = state => ({
12 | /** add state keys here */
13 | bool: selectBool(state),
14 | });
15 |
16 | // map action dispatchers to props
17 | const mapDispatchToProps = dispatch => bindActionCreators(actions, dispatch);
18 |
19 | // export reducers
20 | export const reducers = { <%= name_lower %>: reducer };
21 |
22 | // export connected component
23 | export default connect(
24 | mapStateToProps,
25 | mapDispatchToProps
26 | )(<%= name %>);
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Gilad Lekner
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 |
--------------------------------------------------------------------------------
/__tests__/__snapshots__/helpers.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`helpers caseNames 1`] = `
4 | Object {
5 | "name": "CoMpoNent",
6 | "name_lower": "coMpoNent",
7 | "name_upper": "COMPONENT",
8 | }
9 | `;
10 |
11 | exports[`helpers getPath 1`] = `
12 | Object {
13 | "actionsPath": "some/path/ComponentName/ComponentNameActions.js",
14 | "componentTestPath": "some/path/ComponentName/__tests__/ComponentNameComponentTest.test.js",
15 | "fixturesPath": "some/path/ComponentName/ComponentNameFixtures.js",
16 | "indexPath": "some/path/ComponentName/index.js",
17 | "scssPath": "some/path/ComponentName/ComponentNameScss.js",
18 | "testPath": "some/path/ComponentName/__tests__/ComponentNameIndex.test.js",
19 | }
20 | `;
21 |
22 | exports[`helpers initializePrompts 1`] = `
23 | Array [
24 | Object {
25 | "default": "src/components",
26 | "message": "Enter your Components path",
27 | "name": "path",
28 | "type": "input",
29 | "validate": [Function],
30 | },
31 | Object {
32 | "message": "Enter your Component name",
33 | "name": "name",
34 | "type": "input",
35 | "validate": [Function],
36 | },
37 | ]
38 | `;
39 |
--------------------------------------------------------------------------------
/__tests__/helpers.test.js:
--------------------------------------------------------------------------------
1 | import {
2 | initializePrompts,
3 | getPath,
4 | caseNames,
5 | validatePrompt,
6 | getTemplatePath
7 | } from '../src/helpers';
8 |
9 | describe('helpers', () => {
10 | it('initializePrompts', () => {
11 | const allPrompts = initializePrompts({}, {});
12 | expect(allPrompts).toMatchSnapshot();
13 |
14 | // should return empty prompts
15 | const emptyPrompts = initializePrompts({
16 | path: 'some/path',
17 | name: 'name',
18 | redux: true
19 | });
20 | expect(emptyPrompts.length).toBe(0);
21 |
22 | // test validation
23 | const shouldFalse = validatePrompt('ss');
24 | const shouldTrue = validatePrompt('sss');
25 |
26 | expect(shouldFalse).toBeFalsy();
27 | expect(shouldTrue).toBeTruthy();
28 | });
29 |
30 | it('getPath', () => {
31 | const paths = {
32 | indexPath: getPath('some/path', 'component name', 'index'),
33 | testPath: getPath('some/path', 'component name', 'index_test'),
34 | componentTestPath: getPath(
35 | 'some/path',
36 | 'component name',
37 | 'component.test'
38 | ),
39 | scssPath: getPath('some/path', 'component name', 'scss'),
40 | fixturesPath: getPath('some/path', 'component name', 'fixtures'),
41 | actionsPath: getPath('some/path', 'component name', 'Actions')
42 | };
43 | expect(paths).toMatchSnapshot();
44 | });
45 |
46 | it('caseNames', () => {
47 | const name = caseNames('coMpoNent');
48 | expect(name).toMatchSnapshot();
49 | });
50 |
51 | it('getTemplatePath w/lowercased Name', () => {
52 | const lowerNamePath = getTemplatePath('Helper', '', '__tests__');
53 | expect(lowerNamePath).toBe('__tests__/helper.js');
54 | });
55 | });
56 |
--------------------------------------------------------------------------------
/src/app/index.js:
--------------------------------------------------------------------------------
1 | import Generator from 'yeoman-generator';
2 | import chalk from 'chalk';
3 | import { initializePrompts } from '../helpers';
4 | import PACKAGES from './packages';
5 |
6 | class InitialGenerator extends Generator {
7 | constructor(args, opts) {
8 | super(args, opts);
9 |
10 | this.argument('path', { type: String, required: false });
11 | this.argument('name', { type: String, required: false });
12 | this.option('redux');
13 | }
14 |
15 | async prompting() {
16 | this.log(`\n${chalk.bold(chalk.blue('react-domain-generator'))}\n`);
17 |
18 | const prompts = initializePrompts(this.options, this.config.getAll());
19 | this.answers = await this.prompt(prompts);
20 | this.results = { ...this.options, ...this.answers };
21 | }
22 |
23 | writing() {
24 | const genProps = {
25 | name: this.results.name,
26 | path: this.results.path || this.config.get('componentsPath'),
27 | redux: this.results.redux || this.config.get('redux')
28 | };
29 |
30 | this.composeWith(require.resolve('../gen/component'), genProps);
31 | if (genProps.redux)
32 | this.composeWith(require.resolve('../gen/redux'), genProps);
33 | }
34 |
35 | install() {
36 | if (!this.config.get('depsInstalled')) {
37 | this.log(
38 | `\n${chalk.bold(chalk.blueBright('Installing required packages...'))}\n`
39 | );
40 | if (this.config.get('yarn')) {
41 | this.yarnInstall(PACKAGES, { dev: true });
42 | } else {
43 | this.npmInstall(PACKAGES, { saveDev: true });
44 | }
45 | this.config.set('depsInstalled', true);
46 | }
47 | }
48 |
49 | end() {
50 | this.log(`\n${chalk.bold(chalk.greenBright('done!'))}\n`);
51 | }
52 | }
53 |
54 | module.exports = InitialGenerator;
55 |
--------------------------------------------------------------------------------
/src/helpers.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import toPascalCase from 'to-pascal-case';
3 | import { camelCase } from 'lodash';
4 |
5 | export const validatePrompt = value => value && value.length >= 3;
6 |
7 | export const initializePrompts = (args, config) => {
8 | const prompts = [];
9 |
10 | if (!args.path && !config.componentsPath) {
11 | prompts.push({
12 | type: 'input',
13 | name: 'path',
14 | message: 'Enter your Components path',
15 | default: 'src/components',
16 | validate: validatePrompt
17 | });
18 | }
19 | if (!args.name) {
20 | prompts.push({
21 | type: 'input',
22 | name: 'name',
23 | message: 'Enter your Component name',
24 | validate: validatePrompt
25 | });
26 | }
27 | return prompts;
28 | };
29 |
30 | export const getPath = (path, name, type) => {
31 | const pascalName = toPascalCase(name);
32 | if (type.includes('test')) {
33 | if (type.includes('Component'))
34 | return `${path}/${pascalName}/__tests__/${pascalName}.test.js`;
35 |
36 | return `${path}/${pascalName}/__tests__/${toPascalCase(name) +
37 | toPascalCase(type.split('_')[0])}.test.js`;
38 | }
39 |
40 | switch (type) {
41 | case 'index':
42 | return `${path}/${pascalName}/index.js`;
43 |
44 | case 'Component':
45 | return `${path}/${pascalName}/${pascalName}.js`;
46 |
47 | case 'Fixtures':
48 | return `${path}/${pascalName}/${pascalName}.fixtures.js`;
49 |
50 | case 'Scss':
51 | return `${path}/${pascalName}/${camelCase(name)}.scss`;
52 |
53 | default:
54 | return `${path}/${pascalName}/${toPascalCase(name) +
55 | toPascalCase(type)}.js`;
56 | }
57 | };
58 |
59 | export const caseNames = name => ({
60 | name: toPascalCase(name),
61 | name_lower: camelCase(name),
62 | name_upper: name.replace(/\s+/g, '').toUpperCase()
63 | });
64 |
65 | export const getTemplatePath = (file, consumerPath, sourceRoot) => {
66 | const lowerCasedName = file.toLowerCase();
67 |
68 | if (consumerPath) {
69 | if (fs.existsSync(`${consumerPath}/${lowerCasedName}.js`))
70 | return `${consumerPath}/${lowerCasedName}.js`;
71 |
72 | if (fs.existsSync(`${consumerPath}/${file}.js`))
73 | return `${consumerPath}/${file}.js`;
74 | }
75 |
76 | if (fs.existsSync(`${sourceRoot}/${file}.js`))
77 | return `${sourceRoot}/${file}.js`;
78 |
79 | return `${sourceRoot}/${lowerCasedName}.js`;
80 | };
81 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "generator-react-domain",
3 | "version": "0.1.18",
4 | "description": "Generate React Components with Domain-Driven File Structuring",
5 | "homepage": "https://github.com/glekner/generator-react-domain#readme",
6 | "author": "Gilad Lekner",
7 | "files": [
8 | "lib/generators"
9 | ],
10 | "main": "index.js",
11 | "keywords": [
12 | "react",
13 | "generator",
14 | "domain",
15 | "component",
16 | "yeoman-generator",
17 | "yeoman-generator"
18 | ],
19 | "devDependencies": {
20 | "babel-cli": "^6.26.0",
21 | "babel-plugin-transform-object-rest-spread": "^6.26.0",
22 | "babel-plugin-transform-runtime": "^6.23.0",
23 | "babel-preset-env": "^1.7.0",
24 | "coveralls": "^3.0.2",
25 | "eslint": "^5.10.0",
26 | "eslint-config-airbnb": "^17.1.0",
27 | "eslint-config-prettier": "^3.3.0",
28 | "eslint-plugin-import": "^2.14.0",
29 | "eslint-plugin-jsx-a11y": "^6.1.2",
30 | "eslint-plugin-react": "^7.11.1",
31 | "husky": "^1.2.0",
32 | "jest": "^23.6.0",
33 | "lint-staged": "^13.1.0",
34 | "prettier": "^1.15.3",
35 | "yeoman-assert": "^3.1.0",
36 | "yeoman-test": "^1.7.0"
37 | },
38 | "engines": {
39 | "npm": ">= 4.0.0"
40 | },
41 | "scripts": {
42 | "build": "babel src -d lib/generators --copy-files",
43 | "test": "jest",
44 | "pretest": "eslint .",
45 | "start": "npm run build && npm link && yo react-domain"
46 | },
47 | "repository": {
48 | "type": "git",
49 | "url": "git+https://github.com/glekner/generator-react-domain.git"
50 | },
51 | "license": "MIT",
52 | "bugs": {
53 | "url": "https://github.com/glekner/generator-react-domain/issues"
54 | },
55 | "dependencies": {
56 | "babel-runtime": "^6.26.0",
57 | "chalk": "^2.4.1",
58 | "lodash": "^4.17.11",
59 | "to-pascal-case": "^1.0.0",
60 | "yeoman-generator": "^2.0.1"
61 | },
62 | "jest": {
63 | "testEnvironment": "node",
64 | "testMatch": [
65 | "**/__tests__/*.test.js"
66 | ],
67 | "collectCoverage": true,
68 | "collectCoverageFrom": [
69 | "src/**/*.js",
70 | "!src/**/templates/**/*"
71 | ],
72 | "coverageReporters": [
73 | "lcov"
74 | ]
75 | },
76 | "lint-staged": {
77 | "*.js": [
78 | "eslint --fix",
79 | "git add"
80 | ],
81 | "*.json": [
82 | "prettier --write",
83 | "git add"
84 | ]
85 | },
86 | "eslintConfig": {
87 | "extends": [
88 | "xo",
89 | "prettier"
90 | ],
91 | "env": {
92 | "jest": true,
93 | "node": true
94 | },
95 | "rules": {
96 | "prettier/prettier": "error"
97 | },
98 | "plugins": [
99 | "prettier"
100 | ]
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # generator-react-domain
2 | [](https://coveralls.io/github/glekner/generator-react-domain?branch=master)
3 | [](https://travis-ci.org/glekner/generator-react-domain)
4 |
5 | This Generator helps you create connected React components with the Domain file structure :snowflake:
6 |
7 | ```sh
8 | $ tree
9 | .
10 | ├── Component.fixtures.js
11 | ├── Component.js
12 | ├── ComponentActions.js
13 | ├── ComponentConstants.js
14 | ├── ComponentHelper.js
15 | ├── ComponentReducer.js
16 | ├── ComponentSelectors.js
17 | ├── __tests__
18 | │ ├── Component.test.js
19 | │ ├── ComponentActions.test.js
20 | │ ├── ComponentIntegration.test.js
21 | │ ├── ComponentReducer.test.js
22 | │ └── ComponentSelectors.test.js
23 | ├── component.scss
24 | └── index.js
25 | ```
26 |
27 | ## Usage
28 |
29 | ```sh
30 | # install
31 | npm install --global yo generator-react-domain
32 |
33 | # run it # destination # name
34 | yo react-domain src/components ComponentName
35 | ```
36 |
37 | ## Options
38 |
39 | - `--redux` - Create Redux files.
40 |
41 | ## Tests
42 | This generator is using an external package called [react-redux-test-utils](https://github.com/sharvit/react-redux-test-utils) to create light and readable test templates for your components. The package uses `enzyme` at its core.
43 |
44 | ## Config
45 |
46 | create a `.yo-rc.json` file in your project's root folder and fill it:
47 |
48 | ```sh
49 | {
50 | "generator-react-domain": {
51 | "templatesPath": "path to your templates folder",
52 | "componentsPath": "path to your components folder",
53 | "redux": true # create redux files,
54 | "yarn": true # use yarn instead of npm
55 | }
56 | }
57 | ```
58 |
59 | ## Replacing Templates
60 |
61 | *To learn how to create Templates, refer to [ejs.co](https://ejs.co/)*
62 |
63 |
64 | 1) fill your `.yo-rc.json` file in your project's root folder with `templatesPath` as seen above.
65 |
66 | 2. Put supported files in your templates folder, make sure to be case-sensitive.
67 |
68 | | File | Description | Has Template
69 | | ------------- | ------------- | ------------- |
70 | | Component.js | Component | :white_check_mark:
71 | | Component_test.js | Component Test | :white_check_mark:
72 | | Actions.js | Redux Actions | :white_check_mark:
73 | | Actions_test.js | Actions Test | :white_check_mark:
74 | | Reducer.js | Redux Reducer | :white_check_mark:
75 | | Reducer_test.js | Reducer Test | :white_check_mark:
76 | | Selectors.js | Redux Selectors | :white_check_mark:
77 | | Selectors_test.js | Selectors Test | :white_check_mark:
78 | | index.js | Index file | :white_check_mark:
79 | | Integration_test.js | Redux Flow Test | :white_check_mark:
80 | | Constants.js | Constants | :white_check_mark:
81 | | Helper.js | Helper methods |
82 | | Scss.js | SCSS File |
83 | | Fixtures.js | Fixtures/Mocks |
84 |
85 |
86 | All Templates receive the following props
87 |
88 |
89 | ```sh
90 | <%= name %> # Pascal case name
91 | <%= name_upper %> # Uppercased name
92 | <%= name_lower %> # Camel case name
93 | ```
94 | ## License
95 |
96 | [MIT](https://github.com/glekner/generator-react-domain/blob/master/LICENSE)
97 |
--------------------------------------------------------------------------------
/__tests__/app.test.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import path from 'path';
3 | import assert from 'yeoman-assert';
4 | import helpers from 'yeoman-test';
5 |
6 | describe('generator-react-domain:app', () => {
7 | const generatorPath = path.join(__dirname, '../src/app');
8 |
9 | it('react-domain flow', async () => {
10 | await helpers
11 | .run(generatorPath)
12 | .inTmpDir(dir => {
13 | fs.copyFileSync(
14 | path.join(__dirname, '__mocks__/Component.js'),
15 | path.join(dir, 'Component.js')
16 | );
17 | })
18 | .withPrompts({ name: 'component', redux: false })
19 | .withLocalConfig({ componentsPath: 'src/components', "test-utils-installed": true })
20 | .then(dir => {
21 | assert.file(`${dir}/Component.js`);
22 | });
23 | });
24 |
25 | it('react-domain flow w/redux and yarn install', async () => {
26 | await helpers
27 | .run(generatorPath)
28 | .inTmpDir(dir => {
29 | fs.copyFileSync(
30 | path.join(__dirname, '../src/gen/component/templates/Component.js'),
31 | path.join(dir, 'Component.js')
32 | );
33 | })
34 | .withPrompts({ name: 'component' })
35 | .withLocalConfig({ componentsPath: 'src/components', redux: true, yarn: true })
36 | .then(dir => {
37 | assert.file(`${dir}/Component.js`);
38 | });
39 | });
40 |
41 | it('react-domain flow w/redux and no package installations', async () => {
42 | await helpers
43 | .run(generatorPath)
44 | .inTmpDir(dir => {
45 | fs.copyFileSync(
46 | path.join(__dirname, '../src/gen/component/templates/Component.js'),
47 | path.join(dir, 'Component.js')
48 | );
49 | })
50 | .withPrompts({ name: 'component' })
51 | .withLocalConfig({ componentsPath: 'src/components', redux: true, depsInstalled: true })
52 | .then(dir => {
53 | assert.file(`${dir}/Component.js`);
54 | });
55 | });
56 |
57 | it('base generator flow override template', async () => {
58 | await helpers
59 | .run(path.join(__dirname, '../src/gen/component'))
60 | .inTmpDir(dir => {
61 | fs.mkdirSync(`${dir}/templates`);
62 | fs.copyFileSync(
63 | path.join(__dirname, '__mocks__/Component.js'),
64 | path.join(`${dir}/templates`, 'Component.js')
65 | );
66 | })
67 | .withOptions({ name: 'component', path: 'src/components' })
68 | .withLocalConfig({
69 | templatesPath: 'templates'
70 | })
71 | .then(dir => {
72 | assert.file(`${dir}/src/components/Component/Component.js`);
73 | });
74 | });
75 |
76 | it('base generator flow override template w/lowercased File', async () => {
77 | await helpers
78 | .run(path.join(__dirname, '../src/gen/component'))
79 | .inTmpDir(dir => {
80 | fs.mkdirSync(`${dir}/templates`);
81 | fs.copyFileSync(
82 | path.join(__dirname, '__mocks__/helper.js'),
83 | path.join(`${dir}/templates`, 'helper.js')
84 | );
85 | })
86 | .withOptions({ name: 'component', path: 'src/components' })
87 | .withLocalConfig({
88 | templatesPath: 'templates'
89 | })
90 | .then(dir => {
91 | assert.file(`${dir}/src/components/Component/ComponentHelper.js`);
92 | });
93 | });
94 | });
95 |
--------------------------------------------------------------------------------