Skip to content

Commit 4a73cd1

Browse files
committed
feat: 更新开发命令,添加类型生成和自动端口检测功能,重构示例代码
1 parent 7b19944 commit 4a73cd1

File tree

10 files changed

+296
-22
lines changed

10 files changed

+296
-22
lines changed

examples/showcase/enterprise-erp/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
],
2727
"scripts": {
2828
"start": "ts-node src/index.ts",
29-
"codegen": "objectql generate -s src -o src/types",
29+
"codegen": "objectql types -s src -o src/types",
3030
"build": "npm run codegen && tsc && rsync -a --include '*/' --include '*.yml' --exclude '*' src/ dist/",
3131
"test": "jest"
3232
},

examples/showcase/project-tracker/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
"scripts": {
2727
"start": "objectql serve --dir src",
2828
"seed": "ts-node src/seed.ts",
29-
"codegen": "objectql generate -s src -o src/types",
29+
"codegen": "objectql types -s src -o src/types",
3030
"build": "npm run codegen && tsc && rsync -a --include '*/' --include '*.yml' --exclude '*' src/ dist/",
3131
"repl": "objectql repl",
3232
"test": "jest"

package.json

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,27 @@
22
"name": "objectql-monorepo",
33
"private": true,
44
"scripts": {
5+
"dev": "tsc -b -w",
56
"build": "tsc -b && pnpm -r run build",
6-
"check-versions": "node scripts/check-versions.js",
7+
"clean": "rm -rf dist packages/*/dist packages/*/node_modules node_modules",
78
"test": "pnpm run check-versions && pnpm -r run test",
9+
"lint": "eslint packages --ext .ts,.tsx",
10+
"format": "prettier --write \"packages/**/*.{ts,tsx,json,md}\"",
11+
812
"objectql": "node packages/tools/cli/dist/index.js",
9-
"changeset": "changeset",
10-
"version": "changeset version",
11-
"release": "pnpm run build && changeset publish",
13+
"cli:dev": "ts-node packages/tools/cli/src/index.ts",
14+
15+
"example:tracker": "pnpm objectql dev -d examples/showcase/project-tracker/src",
16+
"example:erp": "pnpm objectql dev -d examples/showcase/enterprise-erp/src",
17+
18+
"check-versions": "node scripts/check-versions.js",
1219
"docs:dev": "vitepress dev docs",
1320
"docs:build": "vitepress build docs",
14-
"docs:preview": "vitepress preview docs"
21+
"docs:preview": "vitepress preview docs",
22+
23+
"changeset": "changeset",
24+
"version": "changeset version",
25+
"release": "pnpm run build && changeset publish"
1526
},
1627
"devDependencies": {
1728
"@changesets/cli": "^2.29.8",
Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import { serve } from './serve';
2+
import { generateTypes } from './generate';
23
import chalk from 'chalk';
4+
import * as fs from 'fs';
5+
import * as path from 'path';
36

47
/**
58
* Start development server with hot reload
@@ -12,19 +15,68 @@ export async function dev(options: {
1215
modules?: string;
1316
watch?: boolean;
1417
}) {
15-
console.log(chalk.cyan('🚀 Starting ObjectQL Development Server...\n'));
18+
console.log(chalk.cyan('🚀 Starting ObjectQL Development Server...'));
19+
20+
const sourceDir = path.resolve(process.cwd(), options.dir || '.');
21+
// Default output for types in dev mode
22+
const typeOutputDir = path.resolve(process.cwd(), 'src/generated');
23+
24+
// 1. Initial Type Generation
25+
try {
26+
await generateTypes(sourceDir, typeOutputDir);
27+
} catch (e) {
28+
console.error(chalk.red('Initial type generation failed:'), e);
29+
// Continue anyway, maybe it's just a starting project
30+
}
1631

17-
// For now, delegate to serve command
18-
// In future, can add file watching and auto-reload
32+
// 2. Setup Watcher (Before serve, in case serve blocks)
33+
if (options.watch !== false) {
34+
startWatcher(sourceDir, typeOutputDir);
35+
}
36+
37+
console.log(chalk.blue(`\n🌐 Server context: ${sourceDir}`));
38+
39+
// 3. Start Server
1940
await serve({
2041
port: options.port,
2142
dir: options.dir,
2243
config: options.config,
2344
modules: options.modules
2445
});
46+
}
47+
48+
let debounceTimer: NodeJS.Timeout | null = null;
49+
50+
function startWatcher(sourceDir: string, outputDir: string) {
51+
console.log(chalk.yellow('👀 Watching for metadata changes...'));
2552

26-
if (options.watch !== false) {
27-
console.log(chalk.yellow('\n👀 Watching for file changes... (Not yet implemented)'));
28-
console.log(chalk.gray(' Tip: Use --no-watch to disable file watching'));
53+
try {
54+
// Recursive watch if supported (macOS/Windows support strict recursive)
55+
// Linux might need fallback or libraries like chokidar, but we use fs.watch for zero-dep strictness
56+
const watcher = fs.watch(sourceDir, { recursive: true }, (eventType, filename) => {
57+
if (!filename) return;
58+
59+
// Only care about YAML
60+
if (filename.endsWith('.yml') || filename.endsWith('.yaml')) {
61+
// Debounce
62+
if (debounceTimer) clearTimeout(debounceTimer);
63+
64+
debounceTimer = setTimeout(async () => {
65+
console.log(chalk.gray(`\n📝 Change detected: ${filename}`));
66+
try {
67+
console.log(chalk.blue('⚡️ Regenerating types...'));
68+
await generateTypes(sourceDir, outputDir);
69+
} catch (e) {
70+
console.error(chalk.red('Type generation failed:'), e);
71+
}
72+
}, 500); // 500ms debounce
73+
}
74+
});
75+
76+
watcher.on('error', (e) => console.error(chalk.red('Watcher error:'), e));
77+
78+
} catch (e) {
79+
console.warn(chalk.yellow('Native recursive watch not supported or failed. Watching root only.'));
80+
// Fallback logic could go here
2981
}
3082
}

packages/tools/cli/src/commands/doctor.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import chalk from 'chalk';
2-
import { loadObjectQLInstance } from '../utils/loader'; // Assuming utils exist or will be needed
3-
// Note: We might need to abstract loader if not exists, but for now let's focus on structure
42

53
export async function doctorCommand() {
64
console.log(chalk.blue('🩺 ObjectQL Doctor'));

packages/tools/cli/src/commands/serve.ts

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,38 @@ export async function serve(options: {
143143
await internalHandler(req, res);
144144
});
145145

146-
server.listen(options.port, () => {
147-
console.log(chalk.green(`\n🚀 Server ready at http://localhost:${options.port}`));
148-
console.log(chalk.green(`📚 Swagger UI: http://localhost:${options.port}/swagger`));
149-
console.log(chalk.blue(`📖 OpenAPI Spec: http://localhost:${options.port}/openapi.json`));
150-
console.log(chalk.gray('\nTry a curl command:'));
151-
console.log(`curl -X POST http://localhost:${options.port} -H "Content-Type: application/json" -d '{"op": "find", "object": "YourObject", "args": {}}'`);
152-
});
146+
// Auto-port detection logic
147+
const startPort = options.port;
148+
const tryListen = (port: number, attempt = 0) => {
149+
if (attempt > 10) {
150+
console.error(chalk.red(`❌ Unable to find a free port after 10 attempts.`));
151+
process.exit(1);
152+
}
153+
154+
const onError = (e: any) => {
155+
if (e.code === 'EADDRINUSE') {
156+
console.log(chalk.yellow(`⚠️ Port ${port} is in use, trying ${port + 1}...`));
157+
// Wait a tick before retrying to let the socket cleanup if needed, though usually not needed for EADDRINUSE on listen
158+
setTimeout(() => {
159+
server.close(); // Ensure previous attempt is closed
160+
tryListen(port + 1, attempt + 1);
161+
}, 100);
162+
} else {
163+
console.error(chalk.red('❌ Server error:'), e);
164+
}
165+
};
166+
167+
server.once('error', onError);
168+
169+
server.listen(port, () => {
170+
server.removeListener('error', onError);
171+
console.log(chalk.green(`\n🚀 Server ready at http://localhost:${port}`));
172+
console.log(chalk.green(`📚 Swagger UI: http://localhost:${port}/swagger`));
173+
console.log(chalk.blue(`📖 OpenAPI Spec: http://localhost:${port}/openapi.json`));
174+
console.log(chalk.gray('\nTry a curl command:'));
175+
console.log(`curl -X POST http://localhost:${port} -H "Content-Type: application/json" -d '{"op": "find", "object": "YourObject", "args": {}}'`);
176+
});
177+
};
178+
179+
tryListen(startPort);
153180
}

src/generated/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from './kitchen_sink';
2+
export * from './projects';
3+
export * from './tasks';

src/generated/kitchen_sink.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Auto-generated by ObjectQL. DO NOT EDIT.
2+
import { ObjectDoc } from '@objectql/types';
3+
4+
export interface KitchenSink extends ObjectDoc {
5+
/**
6+
* Simple Text
7+
*/
8+
simple_text: string;
9+
/**
10+
* Multi-line Text
11+
*/
12+
long_text?: string;
13+
/**
14+
* Markdown Content
15+
*/
16+
rich_content?: string;
17+
/**
18+
* HTML Content
19+
*/
20+
raw_html?: string;
21+
/**
22+
* Integer Count
23+
*/
24+
count?: number;
25+
/**
26+
* Price
27+
*/
28+
price?: number;
29+
/**
30+
* Progress
31+
*/
32+
progress?: number;
33+
/**
34+
* Is Active
35+
*/
36+
is_active?: boolean;
37+
/**
38+
* Due Date
39+
*/
40+
due_date?: Date | string;
41+
/**
42+
* Meeting (Date & Time)
43+
*/
44+
meeting_time?: Date | string;
45+
/**
46+
* Alarm Time
47+
*/
48+
alarm?: Date | string;
49+
/**
50+
* Status
51+
*/
52+
status?: string;
53+
/**
54+
* Tags
55+
*/
56+
tags?: string;
57+
/**
58+
* Email
59+
*/
60+
email_address?: string;
61+
/**
62+
* Phone
63+
*/
64+
phone_number?: string;
65+
/**
66+
* Website URL
67+
*/
68+
website?: string;
69+
/**
70+
* Avatar
71+
*/
72+
profile_pic?: any;
73+
/**
74+
* Attachment document
75+
*/
76+
attachment?: any;
77+
/**
78+
* Image Gallery
79+
*/
80+
gallery?: any[];
81+
/**
82+
* Secret Code
83+
*/
84+
secret_code?: string;
85+
/**
86+
* GPS Coordinates
87+
*/
88+
coords?: any;
89+
/**
90+
* JSON Metadata
91+
*/
92+
metadata?: any;
93+
/**
94+
* Vector Embedding
95+
*/
96+
embedding?: number[];
97+
/**
98+
* Related Project
99+
*/
100+
related_project?: string | number;
101+
}

src/generated/projects.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Auto-generated by ObjectQL. DO NOT EDIT.
2+
import { ObjectDoc } from '@objectql/types';
3+
4+
export interface Projects extends ObjectDoc {
5+
/**
6+
* Name
7+
*/
8+
name: string;
9+
/**
10+
* Status
11+
*/
12+
status?: string;
13+
/**
14+
* Priority
15+
*/
16+
priority?: string;
17+
/**
18+
* Description
19+
*/
20+
description?: string;
21+
/**
22+
* Owner
23+
*/
24+
owner?: string;
25+
/**
26+
* Budget
27+
*/
28+
budget?: number;
29+
/**
30+
* Start Date
31+
*/
32+
start_date?: Date | string;
33+
/**
34+
* End Date
35+
*/
36+
end_date?: Date | string;
37+
/**
38+
* Approved By
39+
*/
40+
approved_by?: string;
41+
/**
42+
* Approved At
43+
*/
44+
approved_at?: Date | string;
45+
/**
46+
* Approval Comment
47+
*/
48+
approval_comment?: string;
49+
}

src/generated/tasks.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Auto-generated by ObjectQL. DO NOT EDIT.
2+
import { ObjectDoc } from '@objectql/types';
3+
4+
export interface Tasks extends ObjectDoc {
5+
/**
6+
* Name
7+
*/
8+
name: string;
9+
/**
10+
* Project
11+
*/
12+
project?: string | number;
13+
/**
14+
* Due Date
15+
*/
16+
due_date?: Date | string;
17+
/**
18+
* Completed
19+
*/
20+
completed?: boolean;
21+
/**
22+
* Priority
23+
*/
24+
priority?: string;
25+
/**
26+
* Assignee
27+
*/
28+
assigned_to?: string;
29+
/**
30+
* Estimated Hours
31+
*/
32+
estimated_hours?: number;
33+
}

0 commit comments

Comments
 (0)