Skip to content

OlgaKozlova/rcli

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

40 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

rcli

Scaffolding library for redux and react-redux projects.

Overview

rcli will help you with routine work while creating

  • new dumb component
  • new smart component
  • new react form
  • new constants-action-reducer-selector bundle for new view or feature

Install

Local installation:

$ npm install rcli --save-dev

Global installation

$ npm install rcli -g

Setting up

For setting up the library place .rcli.config.js file in the root of your working directory. The example of configuration file:

var bundles = require('./myBundles');

module.exports = {
    root: './MyRoot',
    templates: './myTemplates',
    bundles,
    v: {
        myVar: 'Hello'
    }
};

root - will be considered as the root of your project while scaffolding

templates - path to your custom templates folder

bundles - object containing your custom bundles

v - object with any variable you need in your custom templates

Usage

After local installation binary file "rcli" is available in ./node_modules/.bin directory. You can use it directly via command line:

$ ./node_modules/.bin/rcli <command> <parameters> <options>

or create script in package.json:

"scripts": {
    "rcli": "./node_modules/.bin/rcli"
}

and use it via command line in following way:

$ npm run rcli <command> <parameters> <options>

After global installation global variable rcli is available via command line:

$ rcli <command> <parameters> <options>

Commands

Following commands are available:

  • generate
  • help

Generate

generate command allows you to generate new bundle of files.

Parameters:

generate accepts 2 required parameters:

  • bundleName - name of bundle that will be used for scaffolding. See description below.
  • name - name of your feature | view | component

Options:

generate accepts any amount of options in format:

<optionName1>: option11 option12 ... option1N <optionName2>: option21 option22 ... option2N ... <optionNameM>: optionM1 optionM2 ... optionMN

Example:

$ rcli generate view myPerfectView fields: firstName lastName gender birthday buttons: ok cancel

Help

help command is not supported yet.

Bundles

generate command uses bundles for scaffolding. Bundle is a named set of items to be scaffolded.

Following default bundles are supported:

  • stateFulView

Generates following structure

<root>
    constants
        <name>Constants.js
    actions
        <name>Actions.js
    reducers
        <name>Reducer.js
        index.js
    selectors
        <name>Selector.js
    views
        <name>.jsx
        index.js
    initials
        <name>Initial.js
        index.js        

and appends references to newly generated Reducer.js, .jsx and Initial.js to the bottom of index.js files in appropriate folder.

  • stateLessView

Generates following structure

<root>
    constants
        <name>Constants.js
    actions
        <name>Actions.js    
    selectors
        <name>Selector.js
    views
        <name>.jsx
        index.js

and appends references to newly generated .jsx to the bottom of index.js files in appropriate folder.

  • stateFulViewInFolder

Generates following structure

<root>
    <name>
        <name>Constants.js
        <name>Actions.js
        <name>Reducer.js
        <name>Selector.js
        <name>Initial.js
        <name>.jsx
    views.js
    reducers.js
    initials.js

and appends references to newly generated Reducer.js, .jsx and Initial.js to the bottom of reducers.js, view.js and initials.js files in the root of project.

  • stateLessViewInFolder

Generates following structure

<root>
    <name>
        <name>Constants.js
        <name>Actions.js
        <name>Selector.js
        <name>.jsx
    views.js
    reducers.js
    initials.js

and appends references to newly generated .jsx to the bottom of view.js file in the root of project.

Custom Bundles

We realize, that project needs differ from project to project and bundles can differ either. If default bundles don't cover your project need you can create your own bundle.

Steps to create new bundle:

  1. Create folder in the root of your project where you will store your bundles, e.g. MyBundles
  2. Add .json file there. The structure of .json should be following:
{  
  "actions": {
    "templateType": "file",
    "template": "actions.ejs",
    "destination": "<%= root %>/actions/<%= t.capitalize(featureName) %>Actions.js",
    "action": "create"
  },
  "actionsIndex": {
    "templateType": "string",
    "template": "export { <%= featureName %>Actions } from './<%= t.capitalize(featureName) %>Actions.js';\n",
    "destination": "<%= root %>/actions/index.js",
    "action": "appendBottom"
  },
  ...
}

This is object, each field of which describes the one item in the bundle for scaffolding. Each bundle item is also an object with 4 required properties:

  • templateType - "file" || "string"
  • template - ejs string in case of "string" templateType or template(see below) name in case of "file" templateType
  • destination: ejs string for setting the destination path
  • action - "create" || "appendBottom". In case of "create" new file by destination path will be generated (replaced in case of existency). In case of "appendBottom" - new line(s) will be appended to the bottom of given destination file, new file will be created only if destination file doesn't exists.

So using the combination of these settings you can set up any bundle you wish.

  1. Export all created bundles to index.js file in your MyBundles folder Example:
const myView = require('./myView.json');

module.exports = {
    myView,
};

Now myView bundle name is available for usage in commands

$ rcli generate myView PerfectView fields: firstName lastName buttons: ok cancel

Templates

rcli uses templates for scaffolding. By default following set of templates is available: (output for command $ rcli generate stateFulView myView fields: firstName lastName buttons: ok cancel)

  • constants
const VIEW_NAME = 'MY_VIEW_';

export const SET_FIRST_NAME_ACTION = `${VIEW_NAME}SET_FIRST_NAME`;
export const SET_LAST_NAME_ACTION = `${VIEW_NAME}SET_LAST_NAME`;
export const HANDLE_OK_BUTTON_ACTION = `${VIEW_NAME}HANDLE_OK`;
export const HANDLE_CANCEL_BUTTON_ACTION = `${VIEW_NAME}HANDLE_CANCEL`;
  • actions
import {
    SET_FIRST_NAME_ACTION,
    SET_LAST_NAME_ACTION,
    HANDLE_OK_BUTTON_ACTION,
    HANDLE_CANCEL_BUTTON_ACTION,
} from '../constants/MyViewConstants.js';

export const MyViewActions = {
    [SET_FIRST_NAME_ACTION]: firstName => ({
        type: SET_FIRST_NAME_ACTION,
        payload: {
            firstName,
        },
    }),
    [SET_LAST_NAME_ACTION]: lastName => ({
        type: SET_LAST_NAME_ACTION,
        payload: {
            lastName,
        },
    }),
    [HANDLE_OK_BUTTON_ACTION]: () => (dispatch, getState) => {
        // place here code for button handling
    },
    [HANDLE_CANCEL_BUTTON_ACTION]: () => (dispatch, getState) => {
        // place here code for button handling
    },
};
  • actionsInFolder
import {
    SET_FIRST_NAME_ACTION,
    SET_LAST_NAME_ACTION,
    HANDLE_OK_BUTTON_ACTION,
    HANDLE_CANCEL_BUTTON_ACTION,
} from './MyViewConstants.js';

export const MyViewActions = {
    [SET_FIRST_NAME_ACTION]: firstName => ({
        type: SET_FIRST_NAME_ACTION,
        payload: {
            firstName,
        },
    }),
    [SET_LAST_NAME_ACTION]: lastName => ({
        type: SET_LAST_NAME_ACTION,
        payload: {
            lastName,
        },
    }),
    [HANDLE_OK_BUTTON_ACTION]: () => (dispatch, getState) => {
        // place here code for button handling
    },
    [HANDLE_CANCEL_BUTTON_ACTION]: () => (dispatch, getState) => {
        // place here code for button handling
    },
};
  • initials
import Immutable from 'immutable';

export const MyViewInitialState = new Immutable.Map({
    firstName: '',
    lastName: '',
});
  • reducer
import {
    SET_FIRST_NAME_ACTION,
    SET_LAST_NAME_ACTION,
} from '../constants/MyViewConstants.js';

export const MyViewReducer = (state = {}, action) => {
    switch (action.type) {
    case SET_FIRST_NAME_ACTION : {
        return state.set('firstName', action.payload.firstName);
    }
    case SET_LAST_NAME_ACTION : {
        return state.set('lastName', action.payload.lastName);
    }
    default: {
        return state;
    }
    }
};
  • reducerInFolder
import {
    SET_FIRST_NAME_ACTION,
    SET_LAST_NAME_ACTION,
} from './MyViewConstants.js';

export const MyViewReducer = (state = {}, action) => {
    switch (action.type) {
    case SET_FIRST_NAME_ACTION : {
        return state.set('firstName', action.payload.firstName);
    }
    case SET_LAST_NAME_ACTION : {
        return state.set('lastName', action.payload.lastName);
    }
    default: {
        return state;
    }
    }
};
  • selector
import { createSelector } from 'reselect';

export const getFirstName = state => state.MyViewState.get('firstName');
export const getLastName = state => state.MyViewState.get('lastName');

export const MyViewSelector = createSelector(
    [
        getFirstName,
        getLastName,
    ],
    (
        firstName,
        lastName,
    ) => {
        // place code for view selector here
        return {
            // place other computed properties here
            firstName,
            lastName,
        };
    },
);
  • view
import React from 'react';
import { connect } from 'react-redux';

import { MyViewSelector } from '../selectors/MyViewSelectors.js';
import { MyViewActions } from '../actions/MyViewActions.js';
import {
    SET_FIRST_NAME_ACTION,
    SET_LAST_NAME_ACTION,
    HANDLE_OK_BUTTON_ACTION,
    HANDLE_CANCEL_BUTTON_ACTION,
} from '../constants/MyViewConstants.js';

export const MyView = connect(MyViewSelector, MyViewActions)((props) =>
    <div></div>
    // place your components here
);
  • viewInFolder
import React from 'react';
import { connect } from 'react-redux';

import { MyViewSelector } from './MyViewSelectors.js';
import { MyViewActions } from './MyViewActions.js';
import {
    SET_FIRST_NAME_ACTION,
    SET_LAST_NAME_ACTION,
    HANDLE_OK_BUTTON_ACTION,
    HANDLE_CANCEL_BUTTON_ACTION,
} from './MyViewConstants.js';

export const MyView = connect(MyViewSelector, MyViewActions)((props) =>
    <div></div>
    // place your components here
);

Custom templates

We realize that templates could differ from project to project. You are allowed to use your own templates. Steps for creating and adding new template are following:

  1. Create folder in the root of your project where you will store your templates, e.g. MyTemplates
  2. Add .ejs file there.
  3. Fill in your .ejs file (below are source example):
<% var fields = fields || [] -%>
<% var buttons = buttons || [] -%>
import {
<% fields.forEach(function(field){ -%>
<% const transformedField = t.snakify(field); -%>
    SET_<%- transformedField %>_ACTION,%>
<% }); -%>
<% buttons.forEach(function(button){ -%>
<% const transformedButton = t.snakify(button); -%>
    HANDLE_<%- transformedButton %>_BUTTON_ACTION,%>
<% }); -%>
} from './<%= t.capitalize(featureName) -%>Constants.js';

export const <%= featureName %>Actions = {
<% fields.forEach(function(field){ -%>
<% const transformedField = t.snakify(field); -%>
    [SET_<%- transformedField %>_ACTION%>]: <%- field %> => ({
        type: SET_<%- transformedField %>_ACTION,%>
        payload: {
            <%- field %>,
        },
    }),
<% }); -%>
<% buttons.forEach(function(button){ -%>
<% const transformed = t.snakify(button); -%>
    [HANDLE_<%- transformed %>_BUTTON_ACTION%>]: () => (dispatch, getState) => {
        // place here code for button handling
    },
<% }); -%>
};

Inside template following variables are available: example is given for

$ rcli generate stateFulViewInFolder myView fields: firstName lastName buttons: ok cancel
  • t - transformers (see below)
  • name: myView (name of your view or feature (2nd given parameter))
  • fields: ['firstName', 'lastName']
  • buttons: ['ok', 'cancel']

any other options you provide appear as array

  • root - root from your settings
  • v - all variables from settings

Transformers

Following string transforming functions are available in t option inside template

  • snakify: "myView" => "MY_VIEW"
  • camelize: "my-view" => "myView"
  • capitalize: "myView" => "MyView"
  • decapitalize: "MyView" => "myView"

License

ISC

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published