Skip to content

Commit 0269c5a

Browse files
piyushsinghgaur1Piyush Singh Gaur
andauthored
refactor(cli): update code structure to follow backend structure (#2379)
* refactor(cli): update code structure to follow backend structure * test(cli): Add tests to ensure scaffold commands require a project name and run when provided --------- Co-authored-by: Piyush Singh Gaur <[email protected]>
1 parent e8bf28a commit 0269c5a

File tree

13 files changed

+499
-275
lines changed

13 files changed

+499
-275
lines changed

packages/cli/README.md

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ To use the CLI as an MCP server, add this to your MCP client configuration:
5858
## Usage
5959

6060
<!-- usage -->
61-
6261
```sh-session
6362
$ npm install -g @sourceloop/cli
6463
$ sl COMMAND
@@ -70,23 +69,21 @@ USAGE
7069
$ sl COMMAND
7170
...
7271
```
73-
7472
<!-- usagestop -->
7573

7674
## Commands
7775

7876
<!-- commands -->
79-
80-
- [`sl autocomplete [SHELL]`](#sl-autocomplete-shell)
81-
- [`sl cdk`](#sl-cdk)
82-
- [`sl extension [NAME]`](#sl-extension-name)
83-
- [`sl help [COMMAND]`](#sl-help-command)
84-
- [`sl mcp`](#sl-mcp)
85-
- [`sl microservice [NAME]`](#sl-microservice-name)
86-
- [`sl update`](#sl-update)
87-
- [`sl scaffold [NAME]`](#sl-scaffold-name)
88-
- [`sl angular:scaffold [NAME]`](#sl-angularscaffold-name)
89-
- [`sl react:scaffold [NAME]`](#sl-reactscaffold-name)
77+
* [`sl angular:scaffold [NAME]`](#sl-angularscaffold-name)
78+
* [`sl autocomplete [SHELL]`](#sl-autocomplete-shell)
79+
* [`sl cdk`](#sl-cdk)
80+
* [`sl extension [NAME]`](#sl-extension-name)
81+
* [`sl help [COMMAND]`](#sl-help-command)
82+
* [`sl mcp`](#sl-mcp)
83+
* [`sl microservice [NAME]`](#sl-microservice-name)
84+
* [`sl react:scaffold [NAME]`](#sl-reactscaffold-name)
85+
* [`sl scaffold [NAME]`](#sl-scaffold-name)
86+
* [`sl update`](#sl-update)
9087

9188
## `sl angular:scaffold [NAME]`
9289

@@ -100,9 +97,14 @@ ARGUMENTS
10097
NAME Project name
10198
10299
OPTIONS
103-
--help Show manual pages
104-
--installDeps Install dependencies after scaffolding
105-
--templateRepo=templateRepo [default: sourcefuse/angular-boilerplate] Template repository (owner/repo or local path)
100+
--help Show manual pages
101+
--installDeps Install dependencies after scaffolding
102+
--localPath=localPath Local path to use instead of remote template
103+
104+
--templateRepo=templateRepo [default: sourcefuse/angular-boilerplate] Template repository (owner/repo or local
105+
path)
106+
107+
--templateVersion=templateVersion Template branch, tag, or version
106108
```
107109

108110
_See code: [src/commands/angular/scaffold.ts](https://github.com/sourcefuse/loopback4-microservice-catalog/blob/v12.0.0/src/commands/angular/scaffold.ts)_
@@ -191,7 +193,7 @@ _See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v3.2.1
191193

192194
## `sl mcp`
193195

194-
Command that runs an MCP server for the sourceloop CLI, this is not supposed to be run directly, but rather used by the MCP client to interact with the CLI commands.
196+
Command that runs an MCP server for the sourceloop CLI, this is not supposed to be run directly, but rather used by the MCP client to interact with the CLI commands.
195197

196198
```
197199
USAGE
@@ -201,8 +203,8 @@ OPTIONS
201203
--help show manual pages
202204
203205
DESCRIPTION
204-
Command that runs an MCP server for the sourceloop CLI, this is not supposed to be run directly, but rather used by
205-
the MCP client to interact with the CLI commands.
206+
Command that runs an MCP server for the sourceloop CLI, this is not supposed to be run directly, but rather used by
207+
the MCP client to interact with the CLI commands.
206208
You can use it using the following MCP server configuration:
207209
"sourceloop": {
208210
"command": "npx",
@@ -274,6 +276,7 @@ ARGUMENTS
274276
OPTIONS
275277
--help Show manual pages
276278
--installDeps Install dependencies after scaffolding
279+
--localPath=localPath Local path to template
277280
--templateRepo=templateRepo [default: sourcefuse/react-boilerplate-ts-ui] Template repository (org/repo)
278281
--templateVersion=templateVersion Template branch or version
279282
```
@@ -317,7 +320,6 @@ OPTIONS
317320
```
318321

319322
_See code: [src/commands/update.ts](https://github.com/sourcefuse/loopback4-microservice-catalog/blob/v12.0.0/src/commands/update.ts)_
320-
321323
<!-- commandsstop -->
322324

323325
---
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// sonarignore:start
2+
import {Config} from '@oclif/config';
3+
import {assert} from 'chai';
4+
import {createStubInstance, stub} from 'sinon';
5+
import Environment from 'yeoman-environment';
6+
import {AngularScaffold} from '../../commands/angular/scaffold';
7+
8+
const DEFAULT_FLAGS = [
9+
'--templateRepo',
10+
'sourcefuse/angular-boilerplate',
11+
'--templateVersion',
12+
'main',
13+
'--localPath',
14+
'./template',
15+
];
16+
17+
type RunOptions = Record<string, unknown>;
18+
19+
describe('angular scaffold', () => {
20+
it('throws an error when project name is not provided', async () => {
21+
const prompt = stub().resolves({name: ''});
22+
const env = createStubInstance(Environment);
23+
env.run.callsFake(
24+
async (_namespace: string | string[], options?: RunOptions) => {
25+
const name = (options?.name as string | undefined) ?? '';
26+
if (name.trim().length === 0) {
27+
throw new Error('Project name is required');
28+
}
29+
},
30+
);
31+
const command = new AngularScaffold(
32+
[...DEFAULT_FLAGS],
33+
new Config({root: ''}),
34+
prompt,
35+
env,
36+
);
37+
38+
let error: Error | undefined;
39+
try {
40+
await command.run();
41+
} catch (err) {
42+
error = err as Error;
43+
}
44+
45+
assert.isTrue(prompt.calledOnce);
46+
assert.isTrue(env.run.calledOnce);
47+
assert.exists(error);
48+
assert.include(error?.message ?? '', 'Project name is required');
49+
});
50+
51+
it('runs successfully when project name is provided', async () => {
52+
const prompt = stub().resolves({});
53+
const env = createStubInstance(Environment);
54+
env.run.callsFake(
55+
async (_namespace: string | string[], options?: RunOptions) => {
56+
const name = options?.name as string;
57+
assert.equal(name, 'angular-ui');
58+
},
59+
);
60+
const command = new AngularScaffold(
61+
['angular-ui', ...DEFAULT_FLAGS],
62+
new Config({root: ''}),
63+
prompt,
64+
env,
65+
);
66+
67+
await command.run();
68+
69+
assert.isFalse(prompt.called);
70+
assert.isTrue(env.run.calledOnce);
71+
});
72+
});
73+
// sonarignore:end
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// sonarignore:start
2+
import {Config} from '@oclif/config';
3+
import {assert, expect} from 'chai';
4+
import {createStubInstance, stub} from 'sinon';
5+
import Environment from 'yeoman-environment';
6+
import {ReactScaffold} from '../../commands/react/scaffold';
7+
8+
const DEFAULT_FLAGS = [
9+
'--templateRepo',
10+
'sourcefuse/react-boilerplate-ts-ui',
11+
'--templateVersion',
12+
'main',
13+
'--localPath',
14+
'./template',
15+
];
16+
17+
type RunOptions = Record<string, unknown>;
18+
19+
describe('react scaffold', () => {
20+
it('throws an error when project name is not provided', async () => {
21+
const prompt = stub().resolves({name: ''});
22+
const env = createStubInstance(Environment);
23+
env.run.callsFake(
24+
async (_namespace: string | string[], options?: RunOptions) => {
25+
const name = (options?.name as string | undefined) ?? '';
26+
if (name.trim().length === 0) {
27+
throw new Error('Project name is required');
28+
}
29+
},
30+
);
31+
const command = new ReactScaffold(
32+
[...DEFAULT_FLAGS],
33+
new Config({root: ''}),
34+
prompt,
35+
env,
36+
);
37+
38+
let error: Error | undefined;
39+
try {
40+
await command.run();
41+
} catch (err) {
42+
error = err as Error;
43+
}
44+
45+
assert.isTrue(prompt.calledOnce);
46+
assert.isTrue(env.run.calledOnce);
47+
assert.exists(error);
48+
assert.include(error?.message ?? '', 'Project name is required');
49+
});
50+
51+
it('runs successfully when project name is provided', async () => {
52+
const prompt = stub().resolves({});
53+
const env = createStubInstance(Environment);
54+
env.run.callsFake(
55+
async (_namespace: string | string[], options?: RunOptions) => {
56+
const name = options?.name as string;
57+
expect(name).to.equal('react-ui');
58+
},
59+
);
60+
const command = new ReactScaffold(
61+
['react-ui', ...DEFAULT_FLAGS],
62+
new Config({root: ''}),
63+
prompt,
64+
env,
65+
);
66+
67+
await command.run();
68+
69+
assert.isFalse(prompt.called);
70+
assert.isTrue(env.run.calledOnce);
71+
});
72+
});
73+
// sonarignore:end

0 commit comments

Comments
 (0)