# @recli/cli

> Tool to create tasks in node js

Latest version **1.0.23** (published 2019-05-14) · ISC license · 0 weekly downloads

## Install

```sh
npm install @recli/cli
pnpm add @recli/cli
yarn add @recli/cli
bun add @recli/cli
```

Provides the command `recli`.

## Health

**Score 15/100 (F)** — status: abandoned.

Positive: no vulnerabilities.

Warnings: low downloads; no types; no esm support.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 1.0.23 |
| Published | 2019-05-14 |
| First published | 2019-04-28 |
| Weekly downloads | 0 |
| License | ISC |
| TypeScript types | none |
| Module format | CommonJS |
| Dependencies | 15 |
| Unpacked size | 158 KB |
| Known vulnerabilities | 0 (+2 in 1 direct dependencies) |
| Install scripts | no |
| GitHub stars | 4 |
| Author | eguurt@gmail.com |
| Maintainers | wegorich |
| Keywords | yeoman-generator, cli, recli, @recli/cli, automation, generator, task |

## Links

- npm: https://www.npmjs.com/package/@recli/cli
- Repository: https://github.com/recli/cli
- Homepage: https://github.com/recli/cli#readme
- Issues: https://github.com/recli/cli/issues
- npm.io page: https://npm.io/package/@recli/cli

## Dependencies (15)

- [ejs](https://npm.io/package/ejs.md) ^2.6.1
- [ncp](https://npm.io/package/ncp.md) ^2.0.0
- [ora](https://npm.io/package/ora.md) ^3.4.0
- [glob](https://npm.io/package/glob.md) ^7.1.3
- [colors](https://npm.io/package/colors.md) ^1.1.2
- [inquirer](https://npm.io/package/inquirer.md) ^6.3.1
- [cross-env](https://npm.io/package/cross-env.md) ^5.2.0
- [pluralize](https://npm.io/package/pluralize.md) ^4.0.0
- [@types/ncp](https://npm.io/package/@types/ncp.md) ^2.0.1
- [@types/ora](https://npm.io/package/@types/ora.md) ^3.2.0
- [@types/glob](https://npm.io/package/@types/glob.md) ^7.1.1
- [pretty-error](https://npm.io/package/pretty-error.md) ^2.1.1
- [@types/lodash](https://npm.io/package/@types/lodash.md) ^4.14.123
- [@types/inquirer](https://npm.io/package/@types/inquirer.md) ^6.0.0
- [@types/pluralize](https://npm.io/package/@types/pluralize.md) ^0.0.29

## Alternatives

- [@salesforce/cli](https://npm.io/package/@salesforce/cli.md) — 389.7K weekly downloads
- [@mintlify/cli](https://npm.io/package/@mintlify/cli.md) — 208.9K weekly downloads
- [@grafana/e2e-selectors](https://npm.io/package/@grafana/e2e-selectors.md) — 128.7K weekly downloads
- [mintlify](https://npm.io/package/mintlify.md) — 112.0K weekly downloads
- [@intlayer/cli](https://npm.io/package/@intlayer/cli.md) — 22.8K weekly downloads

## Recent versions

- 1.0.23 (latest) — 2019-05-14
- 1.0.22 — 2019-05-13
- 1.0.21 — 2019-05-13
- 1.0.20 — 2019-05-09
- 1.0.19 — 2019-04-29
- 1.0.18 — 2019-04-28
- 1.0.17 — 2019-04-28
- 1.0.16 — 2019-04-28
- 1.0.15 — 2019-04-28
- 1.0.14 — 2019-04-28

## README

# ⌘ ReCli generator

> Powerful but simple cli boilerplate generator

* **Clear**: Human readable config file do what you mean
* **Useful**: Can insert code in already existing modules
* **Flexible**: Can transform, reuse and extend answers on the fly

## Table of Contents
-   [Motivation](#motivation)
-   [Install](#install)
-   [Usage](#usage)
-   [Examples & Demos](./examples/generators)
-   [API](#api)
-   [Contribute](#contribute)
-   [License](#license)


## Motivation
Reduce file creation routine from dev process for one side. From other is to increase the code quality by automating new code injection. It allows you to have strict code agreements cross over their project, and decrease the onboarding process.

## Install
```sh
$ npm install --save @recli/cli
```
or if you use yarn
```sh
$ yarn add @recli/cli
```

## Running from command line

You can run ReCli directly from the CLI (if it's globally available in your `PATH`, e.g. by `yarn global add @recli/cli` or `npm install @recli/cli --global`) with a variety of useful options.

Here's how to run ReCli and have `debug` output:

```
recli --test
```

## Create generator

After install you can start using it
```javascript
// using ES6 modules
import { cliOf } from '@recli/cli'

// using CommonJS modules
const { cliOf } = require("@recli/cli");
```

## Usage
On init ou must pass [module](https://nodejs.org/api/globals.html#globals_module) to second argument.

For work with user inputs used [Inquirer.js](https://github.com/SBoudrias/Inquirer.js/#questions). You can freely use it api [his API](https://github.com/SBoudrias/Inquirer.js/#questions) as is.


```javascript
cliOf('Create something', module) // global node.js module
  .ask({
    name: 'variableName',
    message: 'How we name it?',
    type: 'input'
  })
```

## API

Full API for generators are here:

```js
const { cliOf, useImport, usePath, useCustom, file, childProcess } = require("@recli/cli");

cliOf('Create reducer', module)
  .ask({
    name: 'reducerName',
    message: 'Reducer name',
    type: 'input'
  })
  .setKey('key')
  .ask({
    name: 'model',
    message: 'Reducer data model',
    type: 'input'
  })
  .setAnswers((answers) => {
    // extend answers object with new data
    return {
      reducerName: answers.reducerName,
      model: answers.model,
      upperCaseReducerName: answers.reducerName.toUpperCase(),
      otherVariable: 'My name is John Cena',
    }
  });
  .move(['./reducer.template.js'], '../../fake/destination')
  .rename('../../fake/destination/reducer.template.js', (answers) => {
    return `${answers.reducerName}.js`;
  })
  .useHooks('../../fake/destination/store.js', (answers) => [
    useImport(`./${answers.file2}`, answers.file2),
    usePath(`./${answers.file2}`),
    useCustom({regex, content}),
  ])
  .ask({
    message: 'Witch styles do you use?',
    type: 'list',
    name: 'style',
    choices: [
      {name: 'style.template.scss', value: 'Scss'},
      {name: 'style.template.less', value: 'Less'},
    ],
  })
  .call((answers) => {
    // do any wiered stuff you need,
    // use setAnswers other vice to store result
    return axios.get(`./ping-hook/?${answers.style}`)
  })
  .check((answers, goTo) => {
    if (answers.continue) {
      goTo('begining')
    }
  })
  .move((answers) => [
    {from: './' + answers.style, to: 'style/' + answers.style}
  ], '../../fake/destination')
  //---
  .ask({
    name: 'nestedGenerator',
    message: 'Pick one of nesteds...',
    type: 'list',
    choices: () => file.getAvailableGenerators('examples/generators/**/index.js'),
  })
  .useGenerator((answers) => require(answers.nestedGenerator))
  //---
  .ask({
    name: 'pickTask',
    message: 'Which task does we run?',
    type: 'list',
    choices: () => {
      return [
        'npm run build',
        'tsc --emitDeclarationOnly'
      ];
    }
  })
  .call(async (answers) => {
    await childProcess.spawn(answers.pickTask);
  })
```

**Notes** all callback functions can be async or return promise, to apply pause on the task.

## Formatters & validators

Case conversion:
```js
var { to } = require('@recli/cli')

to.camel('what_the_heck')      // "whatTheHeck"
to.capital('what the heck')    // "What The Heck"
to.constant('whatTheHeck')     // "WHAT_THE_HECK"
to.dot('whatTheHeck')          // "what.the.heck"
to.inverse('whaT tHe HeCK')    // "WHAt ThE HeCK"
to.lower('whatTheHeck')        // "what the heck"
to.pascal('what.the.heck')     // "WhatTheHeck"
to.sentence('WHAT THE HECK.')  // "What the heck."
to.slug('whatTheHeck')         // "what-the-heck"
to.snake('whatTheHeck')        // "what_the_heck"
to.space('what.the.heck')      // "what the heck"
to.title('what the heck')      // "What the Heck"
to.upper('whatTheHeck')        // "WHAT THE HECK"
```

Validators:
```js
var { validation } = require('@recli/cli')

cliOf('validation', module)
  // adding question
  .ask({
    name: 'libraryName',
    message: 'Please enter the name of module you want to import.',
    type: 'input',
    validate: validation.validate([
      validation.isNoWhitespace,
      validation.isDashFormat,
      validation.isFirstLetterUpper,
      validation.isFirstLetterLower,
      validation.isFileIsNew('directory/**/components'),
      validation.isCamelFormat
    ]),
    default: 'lodash'
  })
```

## ⚛️ Hooks

The core feature all around is code injectors to existed files. We call it hooks, to be on hype.

It works like so. Let's imagine you have a file called `router.js`. After new `route` generation you want to append new route here.

So, let's add some hook to the `router.js`,

```js
import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";

import { HomeRoute } from './home-route';
/* recli:use-import */

function AppRouter() {
  return (
    <Router>
        <Route path="/about/" component={HomeRoute} />
        {/* recli:use-route */}
      </div>
    </Router>
  );
}

export default AppRouter;
```

So step after generation we expect to have like this

```js
import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";

import { HomeRoute } from './home-route';
import { NewRoute } from './new-route';
/* recli:use-import */

function AppRouter() {
  return (
    <Router>
        <Route path="/about/" component={HomeRoute} />
        <Route path="/new-route/" component={NewRoute} />
        {/* recli:use-route */}
      </div>
    </Router>
  );
}

export default AppRouter;
```

To make it done we have hooks:

- useImport -> useImport(`{ NewRoute }`, `./new-route`) -> import { NewRoute } from './new-route';
- usePath -> usePath(`./new-route`) -> `'./new-route',`
- useModuleName -> useModuleName(`NodeModule`) -> `NodeModule,`
- useCustom -> useCustom({ regex, content }) -> content

they are applied to file by

```js
  cliOf('My awesome task')
    ...
    .useHooks('path', (answers) => [
      useImport(`{${answers.camelCaseName}}`, `./${answers.name}.js`),
      usePath(`./${answers.name}.js`),
      useCustom({
        regex: /(\s*)(\/\*.*recli:use-module-name.*\*\/)/,
        content: `$1${moduleName}$1$2,`,
      }),
    ])
```

## 🚀 Setup

```js
npm install @recli/cli
// or
yarn add @recli/cli
```

it's possible to use it by global setup

```js
npm install @recli/cli -g
// or
yarn add @recli/cli -g
```

## Placing generators right way

The default agreements you should have `generators` folder at project root with your own generators are inside folders.

Other words, your generators should match the path: `generators/**/index*`

To override default behavior is simple (following same markup: https://storybook.js.org/docs/guides/guide-react/)

To do that, create a file at `.recli/config.js` with the following content:

```js
const { configure } = require("@recli/cli");

function loadStories() {
  require("../generators/**/index*");
  // You can require as many stories as you need.
}

configure(loadStories, module);
```

But the full file path will resolved to `node.js` module and will execute it.

Let's say you wan't to have generators like stand alone module, to share it cross over the projects you have. Let's say it have name: `@recli/xxx-generators`

The code markup can looks like:

- create a file at `index.js` with the following content:

  ```js
  const { configure } = require("recli");

  function loadStories() {
    require("./generators/**/*.gen.js");
  }

  configure(loadStories, module);
  ```

- make sure you have `package.json` main section like that:

  ```json
  "main": "index.js",
  ```

- the @recli/cli will try to find the generators in that node module by using the `path` you prodive at `index.js`

## Tech notes

You can't use modern syntax different to Node.js you have installed. Cause we doesn't use `babel`, `webpack` inside.

`@recli/cli` is written by using TS. So you will receive the extra IDE help by using TS for generators. But, you have to compile them. It should be simple for stand alone set of generators.

---

[MIT](./LICENSE)

---
_Source: https://npm.io/package/@recli/cli · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
