Skip to content

Commit 5f82b1c

Browse files
Copilothuangyiirene
andcommitted
Add lint and test commands to CLI
Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com>
1 parent ae76583 commit 5f82b1c

4 files changed

Lines changed: 204 additions & 2 deletions

File tree

packages/cli/README.md

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,38 @@ objectui serve my-schema.json --port 8080
100100
- `-p, --port <port>` - Port to run the server on (default: `3000`)
101101
- `-h, --host <host>` - Host to bind the server to (default: `localhost`)
102102

103+
### `objectui lint`
104+
105+
Lint the generated application code using ESLint.
106+
107+
```bash
108+
objectui lint
109+
objectui lint --fix
110+
```
111+
112+
**Options:**
113+
- `--fix` - Automatically fix linting issues
114+
115+
**Note:** Run `objectui dev` first to generate the application before linting.
116+
117+
### `objectui test`
118+
119+
Run tests for the application using Vitest.
120+
121+
```bash
122+
objectui test
123+
objectui test --watch
124+
objectui test --coverage
125+
objectui test --ui
126+
```
127+
128+
**Options:**
129+
- `-w, --watch` - Run tests in watch mode
130+
- `-c, --coverage` - Generate test coverage report
131+
- `--ui` - Run tests with Vitest UI
132+
133+
**Note:** Run `objectui dev` first to generate the application before testing.
134+
103135
## Quick Start
104136

105137
1. Create a new application:
@@ -113,12 +145,22 @@ objectui serve my-schema.json --port 8080
113145
objectui dev app.json
114146
```
115147

116-
3. Build for production:
148+
3. Lint your code (optional):
149+
```bash
150+
objectui lint
151+
```
152+
153+
4. Run tests (optional):
154+
```bash
155+
objectui test
156+
```
157+
158+
5. Build for production:
117159
```bash
118160
objectui build app.json
119161
```
120162

121-
4. Serve the production build:
163+
6. Serve the production build:
122164
```bash
123165
objectui start
124166
```

packages/cli/src/cli.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { init } from './commands/init.js';
66
import { dev } from './commands/dev.js';
77
import { buildApp } from './commands/build.js';
88
import { start } from './commands/start.js';
9+
import { lint } from './commands/lint.js';
10+
import { test } from './commands/test.js';
911
import { readFileSync } from 'fs';
1012
import { fileURLToPath } from 'url';
1113
import { dirname, join } from 'path';
@@ -100,4 +102,32 @@ program
100102
}
101103
});
102104

105+
program
106+
.command('lint')
107+
.description('Lint the generated application code')
108+
.option('--fix', 'Automatically fix linting issues')
109+
.action(async (options) => {
110+
try {
111+
await lint(options);
112+
} catch (error) {
113+
console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
114+
process.exit(1);
115+
}
116+
});
117+
118+
program
119+
.command('test')
120+
.description('Run tests for the application')
121+
.option('-w, --watch', 'Run tests in watch mode')
122+
.option('-c, --coverage', 'Generate test coverage report')
123+
.option('--ui', 'Run tests with Vitest UI')
124+
.action(async (options) => {
125+
try {
126+
await test(options);
127+
} catch (error) {
128+
console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
129+
process.exit(1);
130+
}
131+
});
132+
103133
program.parse();

packages/cli/src/commands/lint.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { execSync } from 'child_process';
2+
import { existsSync } from 'fs';
3+
import { join } from 'path';
4+
import chalk from 'chalk';
5+
6+
interface LintOptions {
7+
fix?: boolean;
8+
}
9+
10+
export async function lint(options: LintOptions) {
11+
const cwd = process.cwd();
12+
13+
console.log(chalk.blue('🔍 Running linter...\n'));
14+
15+
// Check if the generated temp app exists
16+
const tmpDir = join(cwd, '.objectui-tmp');
17+
const hasTempApp = existsSync(tmpDir);
18+
19+
if (!hasTempApp) {
20+
throw new Error(
21+
'No Object UI application found. Run \'objectui dev\' first to generate the application.'
22+
);
23+
}
24+
25+
// Check if package.json and node_modules exist
26+
const packageJsonPath = join(tmpDir, 'package.json');
27+
const nodeModulesPath = join(tmpDir, 'node_modules');
28+
29+
if (!existsSync(packageJsonPath) || !existsSync(nodeModulesPath)) {
30+
throw new Error(
31+
'Dependencies not installed. Run \'objectui dev\' first to set up the application.'
32+
);
33+
}
34+
35+
try {
36+
const fixFlag = options.fix ? '--fix' : '';
37+
const command = `npx eslint "src/**/*.{js,jsx,ts,tsx}" ${fixFlag}`.trim();
38+
39+
console.log(chalk.dim(` Running: ${command}\n`));
40+
41+
execSync(command, {
42+
cwd: tmpDir,
43+
stdio: 'inherit',
44+
});
45+
46+
console.log();
47+
console.log(chalk.green('✓ Linting completed successfully!'));
48+
console.log();
49+
} catch {
50+
// ESLint returns non-zero exit code when there are linting errors
51+
console.log();
52+
console.log(chalk.yellow('⚠ Linting found issues.'));
53+
if (!options.fix) {
54+
console.log(chalk.dim(' Run \'objectui lint --fix\' to automatically fix some issues.'));
55+
}
56+
console.log();
57+
process.exit(1);
58+
}
59+
}

packages/cli/src/commands/test.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { execSync } from 'child_process';
2+
import { existsSync } from 'fs';
3+
import { join } from 'path';
4+
import chalk from 'chalk';
5+
6+
interface TestOptions {
7+
watch?: boolean;
8+
coverage?: boolean;
9+
ui?: boolean;
10+
}
11+
12+
export async function test(options: TestOptions) {
13+
const cwd = process.cwd();
14+
15+
console.log(chalk.blue('🧪 Running tests...\n'));
16+
17+
// Check if the generated temp app exists
18+
const tmpDir = join(cwd, '.objectui-tmp');
19+
const hasTempApp = existsSync(tmpDir);
20+
21+
if (!hasTempApp) {
22+
throw new Error(
23+
'No Object UI application found. Run \'objectui dev\' first to generate the application.'
24+
);
25+
}
26+
27+
// Check if package.json and node_modules exist
28+
const packageJsonPath = join(tmpDir, 'package.json');
29+
const nodeModulesPath = join(tmpDir, 'node_modules');
30+
31+
if (!existsSync(packageJsonPath) || !existsSync(nodeModulesPath)) {
32+
throw new Error(
33+
'Dependencies not installed. Run \'objectui dev\' first to set up the application.'
34+
);
35+
}
36+
37+
try {
38+
let command = 'npx vitest';
39+
40+
if (options.watch) {
41+
command += ' --watch';
42+
} else if (options.ui) {
43+
command += ' --ui';
44+
} else {
45+
command += ' run';
46+
}
47+
48+
if (options.coverage) {
49+
command += ' --coverage';
50+
}
51+
52+
console.log(chalk.dim(` Running: ${command}\n`));
53+
54+
execSync(command, {
55+
cwd: tmpDir,
56+
stdio: 'inherit',
57+
});
58+
59+
if (!options.watch && !options.ui) {
60+
console.log();
61+
console.log(chalk.green('✓ Tests completed successfully!'));
62+
console.log();
63+
}
64+
} catch {
65+
// Vitest returns non-zero exit code when tests fail
66+
console.log();
67+
console.log(chalk.red('✗ Some tests failed.'));
68+
console.log();
69+
process.exit(1);
70+
}
71+
}

0 commit comments

Comments
 (0)