Skip to content

Commit 0484de1

Browse files
feat: make module an optional option instead of an required argument
1 parent 1499e3a commit 0484de1

8 files changed

Lines changed: 79 additions & 57 deletions

File tree

README.md

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,31 @@ npm install --save-dev @faker-js/cli
1313
## Usage
1414

1515
```
16-
npx faker <moduleName> <functionName>
16+
npx faker [--module <moduleName>] <functionName>
1717
```
1818

1919
> **:information_source: Note**: [Read more about `npx`](https://docs.npmjs.com/cli/v7/commands/npx)
2020
21-
Faker-CLI expects a `moduleName` as well as a `functionName` argument.
22-
This is equivalent to calling `faker[moduleName][functionName]`.
21+
Faker-CLI expects a `functionName` argument.
22+
An additional, optional `--module` (or short `-m`) might be provided in case of naming collisions for `functionName`.
23+
24+
This is equivalent to calling `faker[moduleName][functionName]` in any JavaScript environment.
25+
If no module option was given, the CLI will search through faker to find an appropriate function for your provided `functionName`.
26+
2327
Checkout [Fakers's API](https://fakerjs.dev/api/) for information on which modules and functions are available.
2428

2529
## Examples
2630

2731
If you want a integer you can run:
2832

2933
```bash
30-
npx faker number int
34+
npx faker int
35+
```
36+
37+
of
38+
39+
```bash
40+
npx faker -m number int
3141
```
3242

3343
## What's Next?
@@ -41,7 +51,7 @@ Currently, the CLI always uses the default `en` locale.
4151
In the future, this feature could be implemented as follows:
4252

4353
```bash
44-
npx faker --locale de person firstName
54+
npx faker --locale de -m person firstName
4555
```
4656

4757
This example would print a first name from the German locale.
@@ -53,7 +63,7 @@ If you need an integer within a specific range, you'll have to do it yourself.
5363
In the future, this feature could be implemented as follows:
5464

5565
```bash
56-
npx faker number int --min 10 --max 20
66+
npx faker -m number int --min 10 --max 20
5767
```
5868

5969
This example will generate an integer between 10 and 20.

src/api.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import { faker } from '@faker-js/faker/locale/en';
77

8-
export const API: Record<string, Record<string, () => unknown>> = {
8+
export const API = {
99
datatype: { boolean: () => faker.datatype.boolean() },
1010
date: {
1111
month: () => faker.date.month(),
@@ -291,4 +291,4 @@ export const API: Record<string, Record<string, () => unknown>> = {
291291
sample: () => faker.word.sample(),
292292
words: () => faker.word.words(),
293293
},
294-
};
294+
} satisfies Record<string, Record<string, () => unknown>>;

src/build-program.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { Argument, Command, Option } from 'commander';
2+
import packageJson from '../package.json' with { type: 'json' };
3+
import { API } from './api.js';
4+
import { FunctionReferenceError } from './errors.js';
5+
6+
const ALL_ENTRYPOINTS: Record<string, () => unknown> = Object.values(
7+
API,
8+
).reduce((result, moduleRef) => ({ ...moduleRef, ...result }), {});
9+
10+
const moduleOption = new Option(
11+
'-m, --module <moduleName>',
12+
'The name of the module to use for the function invokation. You might want to explicitly set this option if the name of function you want to invoke exists in multiple modules. By default the alphanumerical first module will be choosen otherwise.',
13+
).choices(Object.keys(API));
14+
15+
const entryArgument = new Argument(
16+
'<functionName>',
17+
'The name of the function to invoke.',
18+
).choices(Object.keys(ALL_ENTRYPOINTS));
19+
20+
function actionHandler(this: Command) {
21+
// asserting the type here is fine, because Commander already verifies the user input via the required markers on the argument
22+
const [functionName] = this.args as [string];
23+
// asserting the type here is fine, because Commander already verifies the user input via .choices()
24+
const moduleName = this.getOptionValue('module') as
25+
| keyof typeof API
26+
| undefined;
27+
28+
let entrypointRef = ALL_ENTRYPOINTS;
29+
if (moduleName !== undefined) {
30+
entrypointRef = API[moduleName];
31+
}
32+
33+
const entryRef = entrypointRef[functionName];
34+
// This can only happen if a module was picked.
35+
// Otherwise Commander verifies the user input via .choices()
36+
if (entryRef === undefined) {
37+
throw new FunctionReferenceError(functionName, moduleName);
38+
}
39+
40+
process.stdout.write(String(entryRef()));
41+
}
42+
43+
export function buildProgram(): Command {
44+
return new Command()
45+
.name('faker')
46+
.version(packageJson.version)
47+
.description(packageJson.description)
48+
.addOption(moduleOption)
49+
.addArgument(entryArgument)
50+
.action(actionHandler);
51+
}

src/errors.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export class FunctionReferenceError extends Error {
2+
constructor(functionName: string, moduleName: string | undefined) {
3+
super(
4+
`The function ${functionName} does not exist${moduleName ? ' within the module ' + moduleName : ''}. Try to omit the module option or choose a different module.`,
5+
);
6+
this.name = FunctionReferenceError.name;
7+
}
8+
}

src/errors/argument.error.ts

Lines changed: 0 additions & 8 deletions
This file was deleted.

src/errors/reference.error.ts

Lines changed: 0 additions & 5 deletions
This file was deleted.

src/index.ts

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,4 @@
1-
import { Command } from 'commander';
2-
import packageJson from '../package.json' with { type: 'json' };
3-
import { API } from './api.js';
4-
import { ArgumentError } from './errors/argument.error.js';
5-
import { ReferenceError } from './errors/reference.error.js';
6-
7-
function buildProgram(): Command {
8-
return new Command()
9-
.name('faker')
10-
.version(packageJson.version)
11-
.description(packageJson.description)
12-
.argument('<moduleName>', 'The name of the module to invoke.')
13-
.argument('<functionName>', 'The name of the function to invoke.')
14-
.action((moduleName, functionName) => {
15-
if (typeof moduleName !== 'string') {
16-
throw new ArgumentError('moduleName', moduleName);
17-
}
18-
19-
const moduleRef = API[moduleName];
20-
if (moduleRef === undefined) {
21-
throw new ReferenceError('module', moduleName);
22-
}
23-
24-
if (typeof functionName !== 'string') {
25-
throw new ArgumentError('functionName', functionName);
26-
}
27-
28-
const entryRef = moduleRef[functionName];
29-
if (entryRef === undefined) {
30-
throw new ReferenceError('function', moduleName);
31-
}
32-
33-
process.stdout.write(String(entryRef()));
34-
});
35-
}
1+
import { buildProgram } from './build-program.js';
362

373
export function cli(args: string[]) {
384
try {

tools/generate-cli.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ async function generateApi() {
110110
111111
import { faker } from '@faker-js/faker/locale/en';
112112
113-
export const API: Record<string, Record<string, () => unknown>> = ${apiObjectString};
113+
export const API = ${apiObjectString} satisfies Record<string, Record<string, () => unknown>>;
114114
`,
115115
// @ts-expect-error migrate to prettier js|ts config file to get type definitions correctly
116116
{ ...prettierConfig, parser: 'typescript' },

0 commit comments

Comments
 (0)