0.3.1 • Published 7 years ago

@mod-cli/mod-cli v0.3.1

Weekly downloads
1
License
MIT
Repository
github
Last release
7 years ago

npm version Build Status Maintainability Test Coverage

Mod CLI

It isn't always possible to eliminate boilerplate code. Perhaps it's just part of the language/framework, or maybe you're unable to figure out an abstraction. It could be that the team can't agree on how, or they don't even consider it a problem. Whatever the reason, you're stuck rewriting code over... and over... and over again.

Mod CLI is a tool that can help automate these tasks. You install it along with the plugin of your choice (or likely, you'll need to write your own). Then in the command line type mod <your> <command> --with --options. And on enter, all that repetitive code is added to your project!

Prerequisites

Install nvm and then nvm use 8.11.2. No guarantees that anything will work if you're not using both. If you decide not to do this, and you encounter errors, you might try prefixing every mod command with npx (which you can get from npm). You can also try adding the location of mod to your PATH.

Example

To start, clone the following repo.

$ git clone https://github.com/jonroby/mod-react.git

cd into it and $ npm i && npm start.

It's a simple counter app with incrementing and decrementing. Now let's say we want to add functionality for resetting the counter. If you've written any Redux, you know that this involves several steps including writing an action creator, an action constant that is placed in a reducer's case statement, etc. And that doesn't even include all the importing and exporting. Of course, this is price to pay for Redux's simplicity, but wouldn't it be nice to avoid writing all of this repetitious code?

To help, install Mod CLI globally.

$ npm i -g @mod-cli/mod-cli

By itself, Mod CLI can't do anything. It needs a plugin that specifies all of the desired file transformations. I've written one for this project.

$ npm i -S @mod-cli/mod-react-plugin

Write the plugin name in a .mod file so that Mod CLI will use this plugin to execute the transformations:

$ echo '@mod-cli/mod-react-plugin' > .mod

We're ready to write our reset functionality. Enter the following command.

Screenshot

We can see that it made modifications to a number of files! White text indicates modifications, whereas grey text indicates no modifications. To see the particular modifications, here are the git diffs.

Screenshot

All of the boilerplate has been added! So all we have to do in order to create our reset functionality is to update our reducer in src/redux/reducers/counter.js:

...
  case RESET:
    return { ...state, count: 0 };
...

And then in src/components/Counter.js, right below the decrement button, add a button with its onClick handler set to the newly generated reset function, which is already available from this.props.

...
  <button onClick={this.props.decrement}>-</button>
  <button onClick={this.props.reset}>0</button>
...

That's it! Check it out in the browser.

If you forget anything, you can type mod for info on usage. It is not advised to deviate from the commands, as I haven't added extensive handling of user input.

Example with Sagas

In the previous example we modified several items that already existed. But what if we wanted to generate something new? Enter the following command.

Screenshot

Unsurprisingly, the files that are colored green are new files. Again, files with white colored text indicates they have been modified. As you can see, it's just as easy to generate files as it was to modify them.

Please note that components, reducers, sagas aren't required to share the same name. This syntax is just a convenience if they do. If you wanted to use different names you'd enter

mod a -a <actionName> -c <ComponentName> -r <reducerName>.

Now let's look at what's been generated and modified by adding async.

Screenshot

One obvious difference is that the actions now include success and failure. Additionally, several files were created for sagas!

Screenshot

Let's make an API call with GraphQL (traditional gets and posts are possible too). I'll use Github's API. In src/redux/sagas/requests/repo.js enter the following information.

const url = "https://developer.github.com/v4/explorer/";
const graphqlString = `
query { 
  repository(owner: "jonroby", name: "mod-cli") {
    name
    stargazers(last: 10) {
      edges {
	node {
	  name
	}
      }
    }
  }
}
`;

If you want to follow along using Github you'll need to add a proxy to the requests, since the Github API doesn't allow CORS requests. Alternatively, you can try

const url = "https://fakerql.com/graphql";
const graphqlString = `
{
  Todo(id: 1) {
    id
    title
  }
}
`;

We know that we'll want these values in our reducer store, so we can do that by

$ mod x {name,stargazers} -c Repo -r repo (warning: do not add a space between name, and stargazers!)

This will add the keys, name and stargazers to initialState in the repo reducer and to mapStateToProps in the Repo component!

// src/redux/reducers/repo.js
...
const initialState = {
  name: null,
  stargazers: null,
};
// src/components/Component.jsx
...
const mapStateToProps = state => ({
  name: state.repo.name,
  stargazers: state.repo.stargazers
});

If you're using the FakerQL endpoint, try this: (of course you wouldn't want your Todos held in state this way; this is only for the purpose of illustration):

$ mod x {id,title} -c Repo -r repo (warning: do not add a space between id, and title!)

Now we'll want to include our new Repo component in the app, instead of the Counter. So in App.jsx, make the following changes.

...
import Repo from "./Repo.jsx";

...

<Switch>
  <Route path="/" component={Repo} exact />
</Switch>

...
// src/components/Repo.jsx
...
return (
  <div>
    <div>Repo: {this.props.name}</div>
    <div>Repo: {this.props.stargazers.map(s => (
      <div>{s}</div> 
    ))}</div>
    <button onClick={this.props.fetchGithubRepo}></button>     
  </div>
);
...
// src/redux/reducers/repo.js
...
  case FETCH_GITHUB_REPO_SUCCESS:
    return {
      ...state,
      name: action.payload.data.repository.name,
      stargazers: action.payload.data.repository.edges.map(e => e.node.name)	
    };
...

Now you're ready! Try clicking and you'll see everything works!

For more information on Mod React checkout the repo

Mod CLI mod-react-plugin

Usage: mod <command> <flag> <name>
Sugar: mod <action> <component|reducer>

command                         name
  a - add Redux action(s)         user choice
  x - add Redux state

flag                            chains
  -o: actionConstant              -a: [-o, -e]
  -e: actionCreator
  -c: component
  -d: reducer                     -r: [-d, -t]
  -t: rootReducer
  -u: sagaGenerator               -s: [-u, -g, -q]
  -g: rootSaga
  -q: request

Create your own plugin

Coming soon...

0.3.1

7 years ago

0.3.0

7 years ago

0.2.3

7 years ago

0.2.2

7 years ago

0.2.1

7 years ago

0.2.0

7 years ago

0.1.2

7 years ago

0.1.1

7 years ago

0.1.0

7 years ago

1.0.0

7 years ago