Full Code of DiegoZoracKy/magicli for AI

master 7c50b8e49dd0 cached
25 files
27.3 KB
8.5k tokens
5 symbols
1 requests
Download .txt
Repository: DiegoZoracKy/magicli
Branch: master
Commit: 7c50b8e49dd0
Files: 25
Total size: 27.3 KB

Directory structure:
gitextract_ynsia_i4/

├── .gitignore
├── .jshintrc
├── .travis.yml
├── README.md
├── bin/
│   └── cli.js
├── lib/
│   └── magicli.js
├── package.json
└── tests/
    ├── cli.test.js
    ├── main.test.js
    └── test-modules/
        ├── function-simple-concat/
        │   ├── bin/
        │   │   └── cli.js
        │   ├── lib/
        │   │   └── index.js
        │   ├── package.json
        │   └── specs.js
        ├── general-test-module/
        │   ├── bin/
        │   │   └── cli.js
        │   ├── lib/
        │   │   └── index.js
        │   ├── package.json
        │   └── specs.js
        ├── general-test-module-async/
        │   ├── bin/
        │   │   └── cli.js
        │   ├── lib/
        │   │   └── index.js
        │   ├── package.json
        │   └── specs.js
        └── object-flat/
            ├── bin/
            │   └── cli.js
            ├── lib/
            │   └── index.js
            ├── package.json
            └── specs.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
node_modules/
_private/
package-lock.json

================================================
FILE: .jshintrc
================================================
{
	"esversion": 6,
	"node": true
}

================================================
FILE: .travis.yml
================================================
language: node_js
node_js:
  - 8
  - 10

================================================
FILE: README.md
================================================
# MagiCLI

[![Build Status](https://api.travis-ci.org/DiegoZoracKy/magicli.svg)](https://travis-ci.org/DiegoZoracKy/magicli) [![npm](https://img.shields.io/npm/v/magicli.svg)]() [![npm](https://img.shields.io/npm/l/magicli.svg)]()

Automagically generates command-line interfaces (CLI), for any module.
Just `require('magicli')();` and your module is ready to be executed via CLI.

The main goal is to have any module prepared to be executed via CLI (installed globally with `-g`, or by using **npx**):

To see why I believe you should plug it on your module, even if you don't need a CLI (it probably will serve someone on the community), read here: [Introducing MagiCLI: Automagically generates a command-line interface (CLI) for any module
](https://hackernoon.com/introducing-magicli-automagically-generates-a-command-line-interface-cli-for-any-module-49543e50f86d)

**It can be installed globally, in order to *execute* any module or .js file via CLI.**

## Goals

 * Minimal setup (*one line*)
 * Automatic options names based on functions parameters
 * Out of the box support to async functions (`Promises`, or any *thenable* lib)
 * A specific help section for each nested property (*"subcommands"*)
 * *Name*, *Description* and *Version* extracted from package.json
 * Simple API to hook into the execution flow (*stdin*, *before*, *after*)
 * Cover all possible cases of module.exports (*Function*, *Object* with nested properties, Destructuring parameters)
 * Provide a CLI to be used to execute any given module or .js file via CLI

## Usage (the most simple and minimal way)

 * `npm install magicli`
 * Add the property **bin** to your package.json containing the value **./bin/magicli.js**
 * Create the file **./bin/magicli.js** with the following content:

```javascript
#!/usr/bin/env node

require('magicli')();
```

**Done!** Install your module with `-g`, or use it via **[npx](http://blog.npmjs.org/post/162869356040/introducing-npx-an-npm-package-runner)**, and run it with `--help` to see the result. The `--version` option will show the same value found at *package.json*. In the same way you can just run `node ./bin/magicli.js --help` to test it quickly, without installing it.

Let's suppose that **your-module** exports the function:

```javascript
module.exports = function(param1, param2) {
    return param1 + param2;
}
```

When calling it via CLI, with `--help`, you will get:

```bash
Description:

  Same description found at package.json

Usage:

  $ your-module [options]

Options:

  --param1
  --param2
```

The program will be expecting options with the same name as the parameters declared at the exported function, and it doesn't need to follow the same order. Example:

`$ your-module --param2="K" --param1="Z"` would result in: `ZK`.

Important: MagiCLI requires the module in order to analyse it, and provide the command-line interface for it. Keep that in mind in case your module does something just by being required.

## Usage via CLI

In order to **execute** any module or .js file via CLI, install it globally:

```bash
$ npm install magicli -g
```

Then just pass in as the first argument, the path to a module or a .js file. Examples:
 * `$ magicli . --help`
 * `$ magicli ./path/to-some-module --help`
 * `$ magicli ./path/to-a-file.js --help`

Or use it via **[npx](http://blog.npmjs.org/post/162869356040/introducing-npx-an-npm-package-runner)** without the need to install it.

Let's suppose that you have a simple .js file as this one:

```javascript
module.exports = {
	sum: (n1, n2) => n1 + n2,
	ec: {
		ho: str => `${str} !!!`
	}
}
```

Just execute **magicli** on it, as `$ magicli ./path/to-the-file-above.js --help` and you will get:

```bash
Commands:
  sum
  ec-ho
```

`$ magicli ./path/to-the-file-above.js sum --help` will give you:
```bash
Usage:
  $ sum [options]

Options:
  --n1
  --n2
```
and `$ magicli ./path/to-the-file-above.js sum --n1=4 --n2=2` will result in `6`

### How it works

MagiCLI is capable of handling many styles of `exports`, like:

 * Functions
 * Object Literal
 * Nested properties
 * Class with static methods

And also any kind of parameters declaration (*Destructuring Parameters*, *Rest Parameters*).

If **your-module** were like this:
```javascript
// An Arrow function with Destructuring assignment and Default values
const mainMethod = ([p1, [p2]] = ['p1Default', ['p2Default']], { p3 = 'p3Default' } = {}) => `${p1}-${p2}-${p3}`;

// Object Literal containing a nested method
module.exports = {
	mainMethod,
	nested: {
		method: param => `nested method param value is: "${param}`
	}
};
```

`$ your-module --help` would result in:

```bash
Description:

  Same description found at package.json

Usage:

  $ your-module <command>

Commands:

  mainMethod
  nested-method
```

`$ your-module mainMethod --help` would be:

```bash
Usage:

  $ your-module mainMethod [options]

Options:

  --p1
  --p2
  --p3
```

`$ your-module nested-method --help` returns:

```bash
Usage:

  $ your-module nested-method [options]

Options:

  --param
```

Calling *mainMethod* without any parameter:
`$ your-module mainMethod`

results in:
` p1Default-p2Default-p3Default`

While defining the parameter for *nested-method*:
`$ your-module mainMethod nested-method --param=paramValue`

would return:
` nested method param value is: "paramValue"`

Note: Nested methods/properties will be turned into commands separated by `-`, and it can be configurable via options (`subcommandDelimiter`).

## Usage Options
`magicli({ commands = {}, validateRequiredParameters = false, help = {}, version = {}, pipe = {}, enumerability = 'enumerable', subcommandDelimiter = '-'})`

Options are provided to add more information about commands and its options, and also to support a better control of a command execution flow, without the need to change the source code of the module itself (for example, to `JSON.stringify` an `Object Literal` that is returned).



### enumerability

By default, only the enumerable nested properties will be considered. The possible values are: `'enumerable'` (default), `'nonenumerable'` or `'all'`.

### validateRequiredParameters
MagiCLI can validate the required parameters for a command and show the help in case some of them are missing. The default value is `false`.

### help

**help.option**
To define a different option name to show the help section. For example, if `'modulehelp'` is chosen, `--modulehelp` must be used instead of `--help` to show the help section.

**help.stripAnsi**
	Set to `true` to strip all ansi escape codes (colors, underline, etc.) and output just a raw text.



### version
**version.option**
To define a different option name to show the version. For example, if `'moduleversion'` is chosen, `--moduleversion` must be used instead of `--version` to show the version number.

### pipe (stdin, before and after)

The pipeline of a command execution is:

**stdin** (command.pipe.stdin || magicliOptions.pipe.stdin) =>

**magicliOptions.pipe.before** =>

**command.pipe.before** =>

**command.action** (the method in case) =>

**command.pipe.after** =>

**magicliOptions.pipe.after** =>

**stdout**

Where each of these steps can be handled if needed.

As it can be defined on *commands* option, for each command, **pipe** can also be defined in *options* to implement a common handler for all commands. The expected properties are:

**pipe.stdin**
`(stdinValue, args, positionalArgs, argsAfterEndOfOptions)`

Useful to get a value from *stdin* and set it to one of the expected *args*.

**pipe.before**
`(args, positionalArgs, argsAfterEndOfOptions)`

To transform the data being input, before it is passed in to the main command action.

**pipe.after**
`(result, parsedArgs, positionalArgs, argsAfterEndOfOptions)`

Note: **stdin** and **before** must always return *args*, and **after** must always return *result*, as these values will be passed in for the next function in the pipeline.

### commands
The options are effortlessly extracted from the parameters names, however it is possible to give more information about a command and its options, and also give instructions to the options parser.

**commands** expects an `Object Literal` where each key is the command name. It would be the module's name for the main function that is exported, and the command's name as it is shown at the *Commands:* section of `--help`. For example:
```javascript
commands: {
    'mainmodulename': {},
    'some-nested-method': {}
}
```

For each command the following properties can be configurable:

#### options
Is an *Array* of *Objects*, where each contains:

**name** (*required*)
The name of the parameter that will be described

**required**
To tell if the parameter is required.

**description**
To give hints or explain what the option is about.

**type**
To define how the parser should treat the option (Array, Object, String, Number, etc.).  Check [yargs-parser](https://github.com/yargs/yargs-parser) for instructions about *type*, as it is the engine being used to parse the options.

**alias**
To define an alias for the option.

#### pipe (stdin, before and after)

The pipeline of a command execution is:

**stdin** (command.pipe.stdin || magicliOptions.pipe.stdin) =>

**magicliOptions.pipe.before** =>

**command.pipe.before** =>

**command.action** (the method in case) =>

**command.pipe.after** =>

**magicliOptions.pipe.after** =>

**stdout**

Where each of these steps can be handled if needed.

As it can be defined on *options* to implement a common handler for all commands, **pipe** can also be defined for each command.

**pipe.stdin**
`(stdinValue, args, positionalArgs, argsAfterEndOfOptions)`

Useful to get a value from *stdin* and set it to one of the expected *args*.

**pipe.before**
`(args, positionalArgs, argsAfterEndOfOptions)`

To transform the data being input, before it is passed in to the main command action.

**pipe.after**
`(result, parsedArgs, positionalArgs, argsAfterEndOfOptions)`

Note: **stdin** and **before** must always return *args*, and **after** must always return *result*, as these values will be passed in for the next function in the pipeline.

If needed, a more thorough guide about this section can be found at [cliss](https://github.com/DiegoZoracKy/cliss) (as this is the module under the hood to handle that)

A full featured use of the module would look like:

```javascript
magicli({
	commands,
	enumerability,
	subcommandDelimiter,
	validateRequiredParameters,
	help: {
		option,
		stripAnsi
	},
	version: {
		option
	},
	pipe: {
		stdin: (stdinValue, args, positionalArgs, argsAfterEndOfOptions) => {},
		before: (args, positionalArgs, argsAfterEndOfOptions) => {},
		after: (result, parsedArgs, positionalArgs, argsAfterEndOfOptions) => {}
	}
});
```

## Example

To better explain with an example, let's get the following module and configure it with MagiCLI to:

 * Define **p1** as `String` (*mainMethod*)
 * Write a description for **p2** (*mainMethod*)
 * Define **p3** as required (*mainMethod*)
 * Get **p2** from stdin (*mainMethod*)
 * Use **before** (command) to upper case **param** (*nested-method*)
 * Use **after** (command) to JSON.stringify the result of (*nested-method*)
 * Use **after** (options) to decorate all outputs (*nested-method*)

**module** ("main" property of package.json)
```javascript
'use strict';

module.exports = {
	mainMethod: (p1, p2, { p3 = 'p3Default' } = {}) => `${p1}-${p2}-${p3}`,
	nested: {
		method: param => {

			// Example of a Promise being handled
			return new Promise((resolve, reject) => {
				setTimeout(() => {
					resolve({ param });
				}, 2000);
			});
		}
	}
};
```

**magicli.js** ("bin" property of package.json)
```javascript
#!/usr/bin/env node


require('../magicli')({
	commands: {
		'mainMethod': {
			options: [{
				name: 'p1',
				description: 'Number will be converted to String',
				type: 'String'
			}, {
				name: 'p2',
				description: 'This parameter can be defined via stdin'
			}, {
				name: 'p3',
				required: true
			}],
			pipe: {
				stdin: (stdinValue, args, positionalArgs, argsAfterEndOfOptions) => {
					args.p2 = stdinValue;
					return args;
				}
			}
		},
		'nested-method': {
			options: [{
				name: 'param',
				description: 'Wait for it...'
			}],
			pipe: {
				before: (args, positionalArgs, argsAfterEndOfOptions) => {
					if (args.param) {
						args.param = args.param.toUpperCase();
					}
					return args;
				},

				after: JSON.stringify
			}
		}
	},
	pipe: {
		after: (result, positionalArgs, argsAfterEndOfOptions) => `======\n${result}\n======`
	}
});
```

## Tests

There is another repository called [MagiCLI Test Machine](https://github.com/DiegoZoracKy/magicli-test-machine), where many real published modules are being successfully tested. As the idea is to keep increasing the number of real modules tested, it made more sense to maintain a separated repository for that, instead of being constantly increasing the size of MagiCLI itself over time. I ask you to contribute with the growing numbers of those tests by adding your own module there via a pull request.

If you find some case that isn't being handled properly, please open an *issue* or feel free to create a PR ;)


================================================
FILE: bin/cli.js
================================================
#!/usr/bin/env node

'use strict'

const { name, description, version } = require('../package.json');
const path = require('path');
const fs = require('fs');

if (!process.argv[2]) {
	console.error('The first argument must be the path to a module or to a .js file');
	return;
}

const modulePath = path.resolve(process.argv[2]);
if (!fs.existsSync(modulePath)) {
	console.error('The first argument must be a valid path to a module or to a .js file');
	return;
}

process.argv = [process.argv[0], ...process.argv.slice(2)];
require('../')({ modulePath });

================================================
FILE: lib/magicli.js
================================================
#!/usr/bin/env node

'use strict';

const fs = require('fs');
const path = require('path');
const forEachProperty = require('for-each-property');
const inspectProperty = require('inspect-property');
const cliss = require('cliss');
const findUp = require('find-up');

function requireMainModule(currentPath) {
	let packageJson = path.resolve(path.join(currentPath, '/package.json'));

	if (!fs.existsSync(packageJson)) {
		packageJson = findUp.sync('package.json', {
			cwd: packageJson
		});
	}

	const modulePath = path.dirname(packageJson);
	return {
		moduleRef: require(modulePath),
		packageJson: require(packageJson)
	};
}

function requireJsFile(currentPath) {
	return {
		moduleRef: require(currentPath),
		packageJson: {}
	};
}

function magicli({ commands = {}, validateRequiredParameters = false, help = {}, version = {}, pipe = {}, enumerability = 'enumerable', subcommandDelimiter = '-', modulePath } = {}) {
	const { moduleRef, packageJson } = modulePath && path.extname(modulePath) === '.js' ? requireJsFile(modulePath) : requireMainModule(modulePath || require.main.filename);
	const moduleName = packageJson.name;
	const moduleVersion = packageJson.version;
	const moduleDescription = packageJson.description;
	const moduleApi = inspectProperty(moduleRef, null, { enumerability, delimiter: subcommandDelimiter, inspectProperties: true });

	// CLIss cliSpec
	const cliSpec = { name: moduleName, version: moduleVersion, description: moduleDescription };

	// Main command
	if (moduleApi.type === 'function') {
		cliSpec.action = moduleApi.functionInspection.fn;

		if (commands[moduleName]) {
			Object.assign(cliSpec, commands[moduleName]);
		}
	}

	// Subcommands
	forEachProperty(moduleApi.properties, (value, key) => {
		if (value.type !== 'function' || value.isClass) {
			return;
		}

		cliSpec.commands = cliSpec.commands || [];
		const subcommand = {
			name: key,
			action: value.functionInspection.fn
		};

		if (commands[subcommand.name]) {
			Object.assign(subcommand, commands[subcommand.name]);
		}

		cliSpec.commands.push(subcommand);
	});

	cliss(cliSpec, { options: { validateRequiredParameters }, help, version, pipe });
}

module.exports = magicli;

================================================
FILE: package.json
================================================
{
  "name": "magicli",
  "version": "0.2.1",
  "description": "Automagically generates command-line interfaces (CLI) for any module. Expected options and help sections are created automatically based on parameters names, with support to async. It can be installed globally, in order to *execute* any module, or .js file, via CLI.",
  "main": "lib/magicli.js",
  "bin": "bin/cli.js",
  "scripts": {
    "test": "mocha ./tests",
    "test-main": "mocha ./tests/main.test.js",
    "test-cli": "mocha ./tests/cli.test.js"
  },
  "author": {
    "name": "Diego ZoracKy",
    "email": "diego.zoracky@gmail.com",
    "url": "https://github.com/DiegoZoracKy/"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/DiegoZoracKy/magicli"
  },
  "keywords": [
    "bin",
    "cli",
    "async",
    "simple",
    "command-line",
    "interface"
  ],
  "license": "MIT",
  "dependencies": {
    "cliss": "0.0.8",
    "find-up": "^2.1.0",
    "for-each-property": "0.0.4",
    "inspect-property": "0.0.7"
  },
  "devDependencies": {
    "mocha": "^4.0.1"
  }
}


================================================
FILE: tests/cli.test.js
================================================
'use strict';

const assert = require('assert');
const path = require('path');
const fs = require('fs');
const { exec } = require('child_process');

const testsPath = path.resolve(__dirname, './test-modules');
const tests = [
	'function-simple-concat',
	'object-flat'
];

tests.forEach(test => {
	describe(`CLI :: ${test}`, function() {
		const modulePath = path.resolve(testsPath, test);
		const moduleCliPath = path.resolve(__dirname, '../bin/cli.js') + ' ' + modulePath;
		const specs = require(path.resolve(modulePath, 'specs.js'));

		specs.forEach(spec => {
			const { input, output, description, stdin } = spec;

			it(description || input, function() {
				return execCli(moduleCliPath, input, stdin).then(result => assert.equal(result, output));
			});
		});
	});
});

function execCli(moduleCliPath, args, stdin = '') {
	return new Promise((resolve, reject) => {
		const cmd = `${stdin} node ${moduleCliPath} ${args}`;
		exec(cmd, (err, stdout, stderr) => {
			if (err || stderr) {
				return reject(err || stderr);
			}

			resolve(stdout.replace(/ +$/gm, '').replace(/\n$/, ''));
		}).stdin.end();
	});
}

================================================
FILE: tests/main.test.js
================================================
'use strict';

const assert = require('assert');
const path = require('path');
const fs = require('fs');
const { exec } = require('child_process');

const testsPath = path.resolve(__dirname, './test-modules');
const tests = fs.readdirSync(testsPath);

tests.forEach(test => {
	describe(test, function() {
		const modulePath = path.resolve(testsPath, test);
		const packageJson = require(path.resolve(modulePath, 'package.json'));
		const moduleCliPath = path.resolve(modulePath, packageJson.bin);
		const specs = require(path.resolve(modulePath, 'specs.js'));

		specs.forEach(spec => {
			const { input, output, description, stdin } = spec;

			it(description || input, function() {
				return execCli(moduleCliPath, input, stdin).then(result => assert.equal(result, output));
			});
		});
	});
});

function execCli(moduleCliPath, args, stdin = '') {
	return new Promise((resolve, reject) => {
		const cmd = `${stdin} node ${moduleCliPath} ${args}`;
		exec(cmd, (err, stdout, stderr) => {
			if (err || stderr) {
				return reject(err || stderr);
			}

			resolve(stdout.replace(/ +$/gm, '').replace(/\n$/, ''));
		}).stdin.end();
	});
}

================================================
FILE: tests/test-modules/function-simple-concat/bin/cli.js
================================================
#!/usr/bin/env node

'use strict';

require('../../../../')();

================================================
FILE: tests/test-modules/function-simple-concat/lib/index.js
================================================
`use strict`;

module.exports = (p1, p2) => `${p1} ${p2}`;

================================================
FILE: tests/test-modules/function-simple-concat/package.json
================================================
{
  "name": "function-simple-concat",
  "version": "1.0.0",
  "private": true,
  "description": "Test function-simple-concat",
  "main": "lib/index.js",
  "bin": "bin/cli.js"
}


================================================
FILE: tests/test-modules/function-simple-concat/specs.js
================================================
'use strict';

const tests = [{
	description: 'Version --version',
	input: '--version',
	output: `1.0.0`
}, {
	description: 'Help --help',
	input: '--help',
	output: `
Description:

  Test function-simple-concat

Usage:

  $ function-simple-concat [options]

Options:

  --p1
  --p2
`
}, {
	description: '--p1=P1 --p2=P2',
	input: '--p1=P1 --p2=2',
	output: 'P1 2'
}];

module.exports = tests;

================================================
FILE: tests/test-modules/general-test-module/bin/cli.js
================================================
#!/usr/bin/env node

'use strict';

require('../../../../')({
	commands: {
		'a-b-c-d-e-f': {
			options: [{
				name: 'f1',
				required: true
			}],
			pipe: {
				after: JSON.stringify
			}
		},
		'general-test-module': {
			pipe: {
				stdin: (stdinValue, args) => {
					args.param2 = stdinValue;
					return args;
				},
				after: JSON.stringify
			}
		}
	},
	validateRequiredParameters: true,
	enumerability: 'all',
	help: {
		option: 'modulehelp',
		stripAnsi: true
	},
	version: {
		option: 'moduleversion'
	},
	pipe: {
		after: result => `${result}\n=========`
	}
});

================================================
FILE: tests/test-modules/general-test-module/lib/index.js
================================================
'use strict';

const main = function(param1, param2) {
	return new Promise((resolve, reject) => {
		setTimeout(() => {
			resolve(`o/ ${param1} ${param2}`);
		}, 1500);
	});
};

main.methodA = function(paramA1, paramA2) {
	return `${paramA1}=${paramA2}`;
};

main.methodB = function() {
	return `${paramB1}-${paramB2}`;
};

Object.defineProperty(main, 'methodNonEnumerable', {
	value: function(paramC1, paramC2) {
		return `${paramC1}-${paramC2}`;
	}
});

main.a = {
	b: (b1, b2) => `main.a.b: ${b1} ${b2}`
};

main.a.b.c = (c1, c2) => `main.a.b.c: ${c1} ${c2}`;
main.a.b.c['d-e'] = {};
main.a.b.c['d-e'].f = ({f1}, [[f2 = 'F2Default']] = [[]]) => ({ f1, f2 });

module.exports = main;

================================================
FILE: tests/test-modules/general-test-module/package.json
================================================
{
  "name": "general-test-module",
  "version": "0.0.1",
  "private": true,
  "description": "general-test-module description",
  "main": "./lib/index.js",
  "bin": "./bin/cli.js"
}


================================================
FILE: tests/test-modules/general-test-module/specs.js
================================================
'use strict';

const tests = [{
	description: 'Version with different option --moduleversion',
	input: '--moduleversion',
	output: `0.0.1`
}, {
	description: 'Help with different option --modulehelp + help.stripAnsi = true + non-enumerable method',
	input: '--modulehelp',
	output: `
Description:

  general-test-module description

Usage:

  $ general-test-module [options]
  $ general-test-module [command]

Options:

  --param1
  --param2

Commands:

  methodA
  methodB
  methodNonEnumerable
  a-b
  a-b-c
  a-b-c-d-e-f
`
},{
	description: 'Async + STDIN',
	stdin: 'echo "PARAM2" | ',
	input: '--param1=111',
	output: `"o/ 111 PARAM2"
=========`
}, {
	description: 'Method non-enumerable --modulehelp',
	input: 'methodNonEnumerable --modulehelp',
	output: `
Usage:

  $ general-test-module methodNonEnumerable [options]

Options:

  --paramC1
  --paramC2
`
}, {
	description: 'Method non-enumerable call',
	input: 'methodNonEnumerable --paramC1=val1 --paramC2=val2',
	output: `val1-val2
=========`
}, {
	description: 'Nested method with no required options',
	input: 'a-b-c --modulehelp',
	output: `
Usage:

  $ general-test-module a-b-c [options]

Options:

  --c1
  --c2
`
}, {
	description: 'Nested method with required options',
	input: 'a-b-c-d-e-f --modulehelp',
	output: `
Usage:

  $ general-test-module a-b-c-d-e-f <options>

Options:

  --f1    Required
  --f2
`
}, {
	description: 'Nested method without one required options',
	input: 'a-b-c-d-e-f --f2=2',
	output: `
Usage:

  $ general-test-module a-b-c-d-e-f <options>

Options:

  --f1    Required
  --f2
`
}, {
	description: 'Nested method passing only the required option',
	input: 'a-b-c-d-e-f --f1=2',
	output: `{"f1":2,"f2":"F2Default"}
=========`
}];

module.exports = tests;

================================================
FILE: tests/test-modules/general-test-module-async/bin/cli.js
================================================
#!/usr/bin/env node

'use strict';

require('../../../../')({
	commands: {
		'a-b-c-d-e-f': {
			options: [{
				name: 'f1',
				required: true
			}],
			pipe: {
				after: JSON.stringify
			}
		},
		'general-test-module-async': {
			pipe: {
				stdin: (stdinValue, args) => {
					args.param2 = stdinValue;
					return args;
				},
				after: JSON.stringify
			}
		}
	},
	validateRequiredParameters: true,
	enumerability: 'all',
	help: {
		option: 'modulehelp',
		stripAnsi: true
	},
	version: {
		option: 'moduleversion'
	},
	pipe: {
		after: result => `${result}\n=========`
	}
});

================================================
FILE: tests/test-modules/general-test-module-async/lib/index.js
================================================
'use strict';

const main = async function(param1, param2) {
	return new Promise((resolve, reject) => {
		setTimeout(() => {
			resolve(`o/ ${param1} ${param2}`);
		}, 1500);
	});
};

main.methodA = async function(paramA1, paramA2) {
	return `${paramA1}=${paramA2}`;
};

main.methodB = async function() {
	return `${paramB1}-${paramB2}`;
};

Object.defineProperty(main, 'methodNonEnumerable', {
	value: async function(paramC1, paramC2) {
		return `${paramC1}-${paramC2}`;
	}
});

main.a = {
	b: async (b1, b2) => `main.a.b: ${b1} ${b2}`
};

main.a.b.c = async (c1, c2) => `main.a.b.c: ${c1} ${c2}`;
main.a.b.c['d-e'] = {};
main.a.b.c['d-e'].f = async ({f1}, [[f2 = 'F2Default']] = [[]]) => ({ f1, f2 });

module.exports = main;

================================================
FILE: tests/test-modules/general-test-module-async/package.json
================================================
{
  "name": "general-test-module-async",
  "version": "0.0.1",
  "private": true,
  "description": "general-test-module-async description",
  "main": "./lib/index.js",
  "bin": "./bin/cli.js"
}


================================================
FILE: tests/test-modules/general-test-module-async/specs.js
================================================
'use strict';

const tests = [{
	description: 'Version with different option --moduleversion',
	input: '--moduleversion',
	output: `0.0.1`
}, {
	description: 'Help with different option --modulehelp + help.stripAnsi = true + non-enumerable method',
	input: '--modulehelp',
	output: `
Description:

  general-test-module-async description

Usage:

  $ general-test-module-async [options]
  $ general-test-module-async [command]

Options:

  --param1
  --param2

Commands:

  methodA
  methodB
  methodNonEnumerable
  a-b
  a-b-c
  a-b-c-d-e-f
`
},{
	description: 'Async + STDIN',
	stdin: 'echo "PARAM2" | ',
	input: '--param1=111',
	output: `"o/ 111 PARAM2"
=========`
}, {
	description: 'Method non-enumerable --modulehelp',
	input: 'methodNonEnumerable --modulehelp',
	output: `
Usage:

  $ general-test-module-async methodNonEnumerable [options]

Options:

  --paramC1
  --paramC2
`
}, {
	description: 'Method non-enumerable call',
	input: 'methodNonEnumerable --paramC1=val1 --paramC2=val2',
	output: `val1-val2
=========`
}, {
	description: 'Nested method with no required options',
	input: 'a-b-c --modulehelp',
	output: `
Usage:

  $ general-test-module-async a-b-c [options]

Options:

  --c1
  --c2
`
}, {
	description: 'Nested method with required options',
	input: 'a-b-c-d-e-f --modulehelp',
	output: `
Usage:

  $ general-test-module-async a-b-c-d-e-f <options>

Options:

  --f1    Required
  --f2
`
}, {
	description: 'Nested method without one required options',
	input: 'a-b-c-d-e-f --f2=2',
	output: `
Usage:

  $ general-test-module-async a-b-c-d-e-f <options>

Options:

  --f1    Required
  --f2
`
}, {
	description: 'Nested method passing only the required option',
	input: 'a-b-c-d-e-f --f1=2',
	output: `{"f1":2,"f2":"F2Default"}
=========`
}];

module.exports = tests;

================================================
FILE: tests/test-modules/object-flat/bin/cli.js
================================================
#!/usr/bin/env node

'use strict';

require('../../../../')();

================================================
FILE: tests/test-modules/object-flat/lib/index.js
================================================
`use strict`;

module.exports = {
	methodA: function(param1, param2) {
		return `${param1}-${param2}`;
	},

	methodB: (param1, param2) => `${param1-param2}`
}

================================================
FILE: tests/test-modules/object-flat/package.json
================================================
{
  "name": "object-flat",
  "version": "0.0.0",
  "private": true,
  "description": "Test object-flat",
  "main": "./lib/index.js",
  "bin": "./bin/cli.js"
}


================================================
FILE: tests/test-modules/object-flat/specs.js
================================================
'use strict';

const tests = [{
	description: 'Version --version',
	input: '--version',
	output: `0.0.0`
},{
	description: 'Help --help',
	input: '--help',
	output: `
Description:

  Test object-flat

Usage:

  $ object-flat <command>

Commands:

  methodA
  methodB
`
}, {
	description: 'methodA --help',
	input: 'methodA --help',
	output: `
Usage:

  $ object-flat methodA [options]

Options:

  --param1
  --param2
`
}, {
	description: 'methodB --help',
	input: 'methodB --help',
	output: `
Usage:

  $ object-flat methodB [options]

Options:

  --param1
  --param2
`
}, {
	description: 'methodA --param2="Z" --param1="K"',
	input: 'methodA --param2="Z" --param1="K"',
	output: `K-Z`
}, {
	description: 'methodB --param1=3 --param2=2',
	input: 'methodB --param1=3 --param2=2',
	output: `1`
}];

module.exports = tests;
Download .txt
gitextract_ynsia_i4/

├── .gitignore
├── .jshintrc
├── .travis.yml
├── README.md
├── bin/
│   └── cli.js
├── lib/
│   └── magicli.js
├── package.json
└── tests/
    ├── cli.test.js
    ├── main.test.js
    └── test-modules/
        ├── function-simple-concat/
        │   ├── bin/
        │   │   └── cli.js
        │   ├── lib/
        │   │   └── index.js
        │   ├── package.json
        │   └── specs.js
        ├── general-test-module/
        │   ├── bin/
        │   │   └── cli.js
        │   ├── lib/
        │   │   └── index.js
        │   ├── package.json
        │   └── specs.js
        ├── general-test-module-async/
        │   ├── bin/
        │   │   └── cli.js
        │   ├── lib/
        │   │   └── index.js
        │   ├── package.json
        │   └── specs.js
        └── object-flat/
            ├── bin/
            │   └── cli.js
            ├── lib/
            │   └── index.js
            ├── package.json
            └── specs.js
Download .txt
SYMBOL INDEX (5 symbols across 3 files)

FILE: lib/magicli.js
  function requireMainModule (line 12) | function requireMainModule(currentPath) {
  function requireJsFile (line 28) | function requireJsFile(currentPath) {
  function magicli (line 35) | function magicli({ commands = {}, validateRequiredParameters = false, he...

FILE: tests/cli.test.js
  function execCli (line 30) | function execCli(moduleCliPath, args, stdin = '') {

FILE: tests/main.test.js
  function execCli (line 28) | function execCli(moduleCliPath, args, stdin = '') {
Condensed preview — 25 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (32K chars).
[
  {
    "path": ".gitignore",
    "chars": 41,
    "preview": "node_modules/\n_private/\npackage-lock.json"
  },
  {
    "path": ".jshintrc",
    "chars": 34,
    "preview": "{\n\t\"esversion\": 6,\n\t\"node\": true\n}"
  },
  {
    "path": ".travis.yml",
    "chars": 39,
    "preview": "language: node_js\nnode_js:\n  - 8\n  - 10"
  },
  {
    "path": "README.md",
    "chars": 13206,
    "preview": "# MagiCLI\n\n[![Build Status](https://api.travis-ci.org/DiegoZoracKy/magicli.svg)](https://travis-ci.org/DiegoZoracKy/magi"
  },
  {
    "path": "bin/cli.js",
    "chars": 554,
    "preview": "#!/usr/bin/env node\n\n'use strict'\n\nconst { name, description, version } = require('../package.json');\nconst path = requi"
  },
  {
    "path": "lib/magicli.js",
    "chars": 2185,
    "preview": "#!/usr/bin/env node\n\n'use strict';\n\nconst fs = require('fs');\nconst path = require('path');\nconst forEachProperty = requ"
  },
  {
    "path": "package.json",
    "chars": 1067,
    "preview": "{\n  \"name\": \"magicli\",\n  \"version\": \"0.2.1\",\n  \"description\": \"Automagically generates command-line interfaces (CLI) for"
  },
  {
    "path": "tests/cli.test.js",
    "chars": 1116,
    "preview": "'use strict';\n\nconst assert = require('assert');\nconst path = require('path');\nconst fs = require('fs');\nconst { exec } "
  },
  {
    "path": "tests/main.test.js",
    "chars": 1139,
    "preview": "'use strict';\n\nconst assert = require('assert');\nconst path = require('path');\nconst fs = require('fs');\nconst { exec } "
  },
  {
    "path": "tests/test-modules/function-simple-concat/bin/cli.js",
    "chars": 62,
    "preview": "#!/usr/bin/env node\n\n'use strict';\n\nrequire('../../../../')();"
  },
  {
    "path": "tests/test-modules/function-simple-concat/lib/index.js",
    "chars": 58,
    "preview": "`use strict`;\n\nmodule.exports = (p1, p2) => `${p1} ${p2}`;"
  },
  {
    "path": "tests/test-modules/function-simple-concat/package.json",
    "chars": 177,
    "preview": "{\n  \"name\": \"function-simple-concat\",\n  \"version\": \"1.0.0\",\n  \"private\": true,\n  \"description\": \"Test function-simple-co"
  },
  {
    "path": "tests/test-modules/function-simple-concat/specs.js",
    "chars": 439,
    "preview": "'use strict';\n\nconst tests = [{\n\tdescription: 'Version --version',\n\tinput: '--version',\n\toutput: `1.0.0`\n}, {\n\tdescripti"
  },
  {
    "path": "tests/test-modules/general-test-module/bin/cli.js",
    "chars": 578,
    "preview": "#!/usr/bin/env node\n\n'use strict';\n\nrequire('../../../../')({\n\tcommands: {\n\t\t'a-b-c-d-e-f': {\n\t\t\toptions: [{\n\t\t\t\tname: '"
  },
  {
    "path": "tests/test-modules/general-test-module/lib/index.js",
    "chars": 685,
    "preview": "'use strict';\n\nconst main = function(param1, param2) {\n\treturn new Promise((resolve, reject) => {\n\t\tsetTimeout(() => {\n\t"
  },
  {
    "path": "tests/test-modules/general-test-module/package.json",
    "chars": 182,
    "preview": "{\n  \"name\": \"general-test-module\",\n  \"version\": \"0.0.1\",\n  \"private\": true,\n  \"description\": \"general-test-module descri"
  },
  {
    "path": "tests/test-modules/general-test-module/specs.js",
    "chars": 1750,
    "preview": "'use strict';\n\nconst tests = [{\n\tdescription: 'Version with different option --moduleversion',\n\tinput: '--moduleversion'"
  },
  {
    "path": "tests/test-modules/general-test-module-async/bin/cli.js",
    "chars": 584,
    "preview": "#!/usr/bin/env node\n\n'use strict';\n\nrequire('../../../../')({\n\tcommands: {\n\t\t'a-b-c-d-e-f': {\n\t\t\toptions: [{\n\t\t\t\tname: '"
  },
  {
    "path": "tests/test-modules/general-test-module-async/lib/index.js",
    "chars": 727,
    "preview": "'use strict';\n\nconst main = async function(param1, param2) {\n\treturn new Promise((resolve, reject) => {\n\t\tsetTimeout(() "
  },
  {
    "path": "tests/test-modules/general-test-module-async/package.json",
    "chars": 194,
    "preview": "{\n  \"name\": \"general-test-module-async\",\n  \"version\": \"0.0.1\",\n  \"private\": true,\n  \"description\": \"general-test-module-"
  },
  {
    "path": "tests/test-modules/general-test-module-async/specs.js",
    "chars": 1792,
    "preview": "'use strict';\n\nconst tests = [{\n\tdescription: 'Version with different option --moduleversion',\n\tinput: '--moduleversion'"
  },
  {
    "path": "tests/test-modules/object-flat/bin/cli.js",
    "chars": 62,
    "preview": "#!/usr/bin/env node\n\n'use strict';\n\nrequire('../../../../')();"
  },
  {
    "path": "tests/test-modules/object-flat/lib/index.js",
    "chars": 158,
    "preview": "`use strict`;\n\nmodule.exports = {\n\tmethodA: function(param1, param2) {\n\t\treturn `${param1}-${param2}`;\n\t},\n\n\tmethodB: (p"
  },
  {
    "path": "tests/test-modules/object-flat/package.json",
    "chars": 159,
    "preview": "{\n  \"name\": \"object-flat\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"description\": \"Test object-flat\",\n  \"main\": \"./li"
  },
  {
    "path": "tests/test-modules/object-flat/specs.js",
    "chars": 923,
    "preview": "'use strict';\n\nconst tests = [{\n\tdescription: 'Version --version',\n\tinput: '--version',\n\toutput: `0.0.0`\n},{\n\tdescriptio"
  }
]

About this extraction

This page contains the full source code of the DiegoZoracKy/magicli GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 25 files (27.3 KB), approximately 8.5k tokens, and a symbol index with 5 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!