@objectstack/cli 0.6.1 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/dist/bin.js +496 -2
- package/package.json +4 -2
- package/src/bin.ts +11 -2
- package/src/commands/create.ts +265 -0
- package/src/commands/dev.ts +40 -0
- package/src/commands/doctor.ts +175 -0
- package/src/commands/serve.ts +101 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @objectstack/cli
|
|
2
2
|
|
|
3
|
+
## 0.7.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- fb41cc0: Patch release: Updated documentation and JSON schemas
|
|
8
|
+
- Updated dependencies [fb41cc0]
|
|
9
|
+
- @objectstack/spec@0.7.2
|
|
10
|
+
- @objectstack/core@0.7.2
|
|
11
|
+
- @objectstack/plugin-hono-server@0.7.2
|
|
12
|
+
|
|
13
|
+
## 0.7.1
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Patch release for maintenance and stability improvements
|
|
18
|
+
- Updated dependencies
|
|
19
|
+
- @objectstack/spec@0.7.1
|
|
20
|
+
|
|
3
21
|
## 0.6.1
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/dist/bin.js
CHANGED
|
@@ -4,8 +4,502 @@ import {
|
|
|
4
4
|
} from "./chunk-RQRAZ23Z.js";
|
|
5
5
|
|
|
6
6
|
// src/bin.ts
|
|
7
|
+
import { Command as Command5 } from "commander";
|
|
8
|
+
|
|
9
|
+
// src/commands/dev.ts
|
|
7
10
|
import { Command } from "commander";
|
|
8
|
-
|
|
9
|
-
|
|
11
|
+
import chalk from "chalk";
|
|
12
|
+
import { execSync } from "child_process";
|
|
13
|
+
var devCommand = new Command("dev").description("Start development mode for a package").argument("[package]", "Package name (without @objectstack/ prefix)", "all").option("-w, --watch", "Enable watch mode (default)", true).option("-v, --verbose", "Verbose output").action(async (packageName, options) => {
|
|
14
|
+
console.log(chalk.bold(`
|
|
15
|
+
\u{1F680} ObjectStack Development Mode`));
|
|
16
|
+
console.log(chalk.dim(`-------------------------------`));
|
|
17
|
+
try {
|
|
18
|
+
const cwd = process.cwd();
|
|
19
|
+
const filter = packageName === "all" ? "" : `--filter @objectstack/${packageName}`;
|
|
20
|
+
console.log(`\u{1F4E6} Package: ${chalk.blue(packageName === "all" ? "All packages" : `@objectstack/${packageName}`)}`);
|
|
21
|
+
console.log(`\u{1F504} Watch mode: ${chalk.green("enabled")}`);
|
|
22
|
+
console.log("");
|
|
23
|
+
const command = `pnpm ${filter} dev`.trim();
|
|
24
|
+
console.log(chalk.dim(`$ ${command}`));
|
|
25
|
+
console.log("");
|
|
26
|
+
execSync(command, {
|
|
27
|
+
stdio: "inherit",
|
|
28
|
+
cwd
|
|
29
|
+
});
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error(chalk.red(`
|
|
32
|
+
\u274C Development mode failed:`));
|
|
33
|
+
console.error(error.message || error);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// src/commands/doctor.ts
|
|
39
|
+
import { Command as Command2 } from "commander";
|
|
40
|
+
import chalk2 from "chalk";
|
|
41
|
+
import { execSync as execSync2 } from "child_process";
|
|
42
|
+
import fs from "fs";
|
|
43
|
+
import path from "path";
|
|
44
|
+
var doctorCommand = new Command2("doctor").description("Check development environment health").option("-v, --verbose", "Show detailed information").action(async (options) => {
|
|
45
|
+
console.log(chalk2.bold(`
|
|
46
|
+
\u{1F3E5} ObjectStack Environment Health Check`));
|
|
47
|
+
console.log(chalk2.dim(`-----------------------------------------`));
|
|
48
|
+
console.log("");
|
|
49
|
+
const results = [];
|
|
50
|
+
try {
|
|
51
|
+
const nodeVersion = process.version;
|
|
52
|
+
const majorVersion = parseInt(nodeVersion.slice(1).split(".")[0]);
|
|
53
|
+
if (majorVersion >= 18) {
|
|
54
|
+
results.push({
|
|
55
|
+
name: "Node.js",
|
|
56
|
+
status: "ok",
|
|
57
|
+
message: `Version ${nodeVersion}`
|
|
58
|
+
});
|
|
59
|
+
} else {
|
|
60
|
+
results.push({
|
|
61
|
+
name: "Node.js",
|
|
62
|
+
status: "error",
|
|
63
|
+
message: `Version ${nodeVersion} (requires >= 18.0.0)`,
|
|
64
|
+
fix: "Upgrade Node.js: https://nodejs.org"
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
} catch (error) {
|
|
68
|
+
results.push({
|
|
69
|
+
name: "Node.js",
|
|
70
|
+
status: "error",
|
|
71
|
+
message: "Not found",
|
|
72
|
+
fix: "Install Node.js: https://nodejs.org"
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
const pnpmVersion = execSync2("pnpm -v", { encoding: "utf-8" }).trim();
|
|
77
|
+
results.push({
|
|
78
|
+
name: "pnpm",
|
|
79
|
+
status: "ok",
|
|
80
|
+
message: `Version ${pnpmVersion}`
|
|
81
|
+
});
|
|
82
|
+
} catch (error) {
|
|
83
|
+
results.push({
|
|
84
|
+
name: "pnpm",
|
|
85
|
+
status: "error",
|
|
86
|
+
message: "Not found",
|
|
87
|
+
fix: "Install pnpm: npm install -g pnpm@10.28.1"
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const tscVersion = execSync2("tsc -v", { encoding: "utf-8" }).trim();
|
|
92
|
+
results.push({
|
|
93
|
+
name: "TypeScript",
|
|
94
|
+
status: "ok",
|
|
95
|
+
message: tscVersion
|
|
96
|
+
});
|
|
97
|
+
} catch (error) {
|
|
98
|
+
results.push({
|
|
99
|
+
name: "TypeScript",
|
|
100
|
+
status: "warning",
|
|
101
|
+
message: "Not found in PATH",
|
|
102
|
+
fix: "Installed locally via pnpm"
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
const cwd = process.cwd();
|
|
106
|
+
const nodeModulesPath = path.join(cwd, "node_modules");
|
|
107
|
+
if (fs.existsSync(nodeModulesPath)) {
|
|
108
|
+
results.push({
|
|
109
|
+
name: "Dependencies",
|
|
110
|
+
status: "ok",
|
|
111
|
+
message: "Installed"
|
|
112
|
+
});
|
|
113
|
+
} else {
|
|
114
|
+
results.push({
|
|
115
|
+
name: "Dependencies",
|
|
116
|
+
status: "error",
|
|
117
|
+
message: "Not installed",
|
|
118
|
+
fix: "Run: pnpm install"
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
const specDistPath = path.join(cwd, "packages/spec/dist");
|
|
122
|
+
if (fs.existsSync(specDistPath)) {
|
|
123
|
+
results.push({
|
|
124
|
+
name: "@objectstack/spec",
|
|
125
|
+
status: "ok",
|
|
126
|
+
message: "Built"
|
|
127
|
+
});
|
|
128
|
+
} else {
|
|
129
|
+
results.push({
|
|
130
|
+
name: "@objectstack/spec",
|
|
131
|
+
status: "warning",
|
|
132
|
+
message: "Not built",
|
|
133
|
+
fix: "Run: pnpm --filter @objectstack/spec build"
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
const gitVersion = execSync2("git --version", { encoding: "utf-8" }).trim();
|
|
138
|
+
results.push({
|
|
139
|
+
name: "Git",
|
|
140
|
+
status: "ok",
|
|
141
|
+
message: gitVersion
|
|
142
|
+
});
|
|
143
|
+
} catch (error) {
|
|
144
|
+
results.push({
|
|
145
|
+
name: "Git",
|
|
146
|
+
status: "warning",
|
|
147
|
+
message: "Not found",
|
|
148
|
+
fix: "Install Git for version control"
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
let hasErrors = false;
|
|
152
|
+
let hasWarnings = false;
|
|
153
|
+
results.forEach((result) => {
|
|
154
|
+
const icon = result.status === "ok" ? "\u2713" : result.status === "warning" ? "\u26A0" : "\u2717";
|
|
155
|
+
const color = result.status === "ok" ? chalk2.green : result.status === "warning" ? chalk2.yellow : chalk2.red;
|
|
156
|
+
console.log(color(`${icon} ${result.name.padEnd(20)} ${result.message}`));
|
|
157
|
+
if (result.fix && options.verbose) {
|
|
158
|
+
console.log(chalk2.dim(` \u2192 ${result.fix}`));
|
|
159
|
+
}
|
|
160
|
+
if (result.status === "error") hasErrors = true;
|
|
161
|
+
if (result.status === "warning") hasWarnings = true;
|
|
162
|
+
});
|
|
163
|
+
console.log("");
|
|
164
|
+
if (hasErrors) {
|
|
165
|
+
console.log(chalk2.red("\u274C Some critical issues found. Please fix them before continuing."));
|
|
166
|
+
results.filter((r) => r.status === "error" && r.fix).forEach((r) => console.log(chalk2.dim(` ${r.fix}`)));
|
|
167
|
+
process.exit(1);
|
|
168
|
+
} else if (hasWarnings) {
|
|
169
|
+
console.log(chalk2.yellow("\u26A0\uFE0F Environment is functional but has some warnings."));
|
|
170
|
+
console.log(chalk2.dim(" Run with --verbose to see fix suggestions."));
|
|
171
|
+
} else {
|
|
172
|
+
console.log(chalk2.green("\u2705 Environment is healthy and ready for development!"));
|
|
173
|
+
}
|
|
174
|
+
console.log("");
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// src/commands/create.ts
|
|
178
|
+
import { Command as Command3 } from "commander";
|
|
179
|
+
import chalk3 from "chalk";
|
|
180
|
+
import fs2 from "fs";
|
|
181
|
+
import path2 from "path";
|
|
182
|
+
var templates = {
|
|
183
|
+
plugin: {
|
|
184
|
+
description: "Create a new ObjectStack plugin",
|
|
185
|
+
files: {
|
|
186
|
+
"package.json": (name) => ({
|
|
187
|
+
name: `@objectstack/plugin-${name}`,
|
|
188
|
+
version: "0.1.0",
|
|
189
|
+
description: `ObjectStack Plugin: ${name}`,
|
|
190
|
+
main: "dist/index.js",
|
|
191
|
+
types: "dist/index.d.ts",
|
|
192
|
+
scripts: {
|
|
193
|
+
build: "tsc",
|
|
194
|
+
dev: "tsc --watch",
|
|
195
|
+
test: "vitest"
|
|
196
|
+
},
|
|
197
|
+
keywords: ["objectstack", "plugin", name],
|
|
198
|
+
author: "",
|
|
199
|
+
license: "MIT",
|
|
200
|
+
dependencies: {
|
|
201
|
+
"@objectstack/spec": "workspace:*",
|
|
202
|
+
zod: "^3.22.4"
|
|
203
|
+
},
|
|
204
|
+
devDependencies: {
|
|
205
|
+
"@types/node": "^20.10.0",
|
|
206
|
+
typescript: "^5.3.0",
|
|
207
|
+
vitest: "^2.1.8"
|
|
208
|
+
}
|
|
209
|
+
}),
|
|
210
|
+
"tsconfig.json": () => ({
|
|
211
|
+
extends: "../../tsconfig.json",
|
|
212
|
+
compilerOptions: {
|
|
213
|
+
outDir: "dist",
|
|
214
|
+
rootDir: "src"
|
|
215
|
+
},
|
|
216
|
+
include: ["src/**/*"]
|
|
217
|
+
}),
|
|
218
|
+
"src/index.ts": (name) => `import type { Plugin } from '@objectstack/spec';
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* ${name} Plugin for ObjectStack
|
|
222
|
+
*/
|
|
223
|
+
export const ${toCamelCase(name)}Plugin: Plugin = {
|
|
224
|
+
name: '${name}',
|
|
225
|
+
version: '0.1.0',
|
|
226
|
+
|
|
227
|
+
async initialize(context) {
|
|
228
|
+
console.log('Initializing ${name} plugin...');
|
|
229
|
+
// Plugin initialization logic
|
|
230
|
+
},
|
|
231
|
+
|
|
232
|
+
async destroy() {
|
|
233
|
+
console.log('Destroying ${name} plugin...');
|
|
234
|
+
// Plugin cleanup logic
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
export default ${toCamelCase(name)}Plugin;
|
|
239
|
+
`,
|
|
240
|
+
"README.md": (name) => `# @objectstack/plugin-${name}
|
|
241
|
+
|
|
242
|
+
ObjectStack Plugin: ${name}
|
|
243
|
+
|
|
244
|
+
## Installation
|
|
245
|
+
|
|
246
|
+
\`\`\`bash
|
|
247
|
+
pnpm add @objectstack/plugin-${name}
|
|
248
|
+
\`\`\`
|
|
249
|
+
|
|
250
|
+
## Usage
|
|
251
|
+
|
|
252
|
+
\`\`\`typescript
|
|
253
|
+
import { ${toCamelCase(name)}Plugin } from '@objectstack/plugin-${name}';
|
|
254
|
+
|
|
255
|
+
// Use the plugin in your ObjectStack configuration
|
|
256
|
+
export default {
|
|
257
|
+
plugins: [
|
|
258
|
+
${toCamelCase(name)}Plugin,
|
|
259
|
+
],
|
|
260
|
+
};
|
|
261
|
+
\`\`\`
|
|
262
|
+
|
|
263
|
+
## License
|
|
264
|
+
|
|
265
|
+
MIT
|
|
266
|
+
`
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
example: {
|
|
270
|
+
description: "Create a new ObjectStack example application",
|
|
271
|
+
files: {
|
|
272
|
+
"package.json": (name) => ({
|
|
273
|
+
name: `@objectstack/example-${name}`,
|
|
274
|
+
version: "0.1.0",
|
|
275
|
+
private: true,
|
|
276
|
+
description: `ObjectStack Example: ${name}`,
|
|
277
|
+
scripts: {
|
|
278
|
+
build: "objectstack compile",
|
|
279
|
+
dev: "tsx watch objectstack.config.ts",
|
|
280
|
+
test: "vitest"
|
|
281
|
+
},
|
|
282
|
+
dependencies: {
|
|
283
|
+
"@objectstack/spec": "workspace:*",
|
|
284
|
+
"@objectstack/cli": "workspace:*",
|
|
285
|
+
zod: "^3.22.4"
|
|
286
|
+
},
|
|
287
|
+
devDependencies: {
|
|
288
|
+
"@types/node": "^20.10.0",
|
|
289
|
+
tsx: "^4.21.0",
|
|
290
|
+
typescript: "^5.3.0",
|
|
291
|
+
vitest: "^2.1.8"
|
|
292
|
+
}
|
|
293
|
+
}),
|
|
294
|
+
"objectstack.config.ts": (name) => `import { defineStack } from '@objectstack/spec';
|
|
295
|
+
|
|
296
|
+
export default defineStack({
|
|
297
|
+
metadata: {
|
|
298
|
+
name: '${name}',
|
|
299
|
+
version: '0.1.0',
|
|
300
|
+
description: '${name} example application',
|
|
301
|
+
},
|
|
302
|
+
|
|
303
|
+
objects: {
|
|
304
|
+
// Define your data objects here
|
|
305
|
+
},
|
|
306
|
+
|
|
307
|
+
ui: {
|
|
308
|
+
apps: [],
|
|
309
|
+
views: [],
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
`,
|
|
313
|
+
"README.md": (name) => `# ${name} Example
|
|
314
|
+
|
|
315
|
+
ObjectStack example application: ${name}
|
|
316
|
+
|
|
317
|
+
## Quick Start
|
|
318
|
+
|
|
319
|
+
\`\`\`bash
|
|
320
|
+
# Build the configuration
|
|
321
|
+
pnpm build
|
|
322
|
+
|
|
323
|
+
# Run in development mode
|
|
324
|
+
pnpm dev
|
|
325
|
+
\`\`\`
|
|
326
|
+
|
|
327
|
+
## Structure
|
|
328
|
+
|
|
329
|
+
- \`objectstack.config.ts\` - Main configuration file
|
|
330
|
+
- \`dist/objectstack.json\` - Compiled artifact
|
|
331
|
+
|
|
332
|
+
## Learn More
|
|
333
|
+
|
|
334
|
+
- [ObjectStack Documentation](../../content/docs)
|
|
335
|
+
- [Examples](../)
|
|
336
|
+
`,
|
|
337
|
+
"tsconfig.json": () => ({
|
|
338
|
+
extends: "../../tsconfig.json",
|
|
339
|
+
compilerOptions: {
|
|
340
|
+
outDir: "dist",
|
|
341
|
+
rootDir: "."
|
|
342
|
+
},
|
|
343
|
+
include: ["*.ts", "src/**/*"]
|
|
344
|
+
})
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
function toCamelCase(str) {
|
|
349
|
+
return str.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
|
|
350
|
+
}
|
|
351
|
+
var createCommand = new Command3("create").description("Create a new package, plugin, or example from template").argument("<type>", "Type of project to create (plugin, example)").argument("[name]", "Name of the project").option("-d, --dir <directory>", "Target directory").action(async (type, name, options) => {
|
|
352
|
+
console.log(chalk3.bold(`
|
|
353
|
+
\u{1F4E6} ObjectStack Project Creator`));
|
|
354
|
+
console.log(chalk3.dim(`-------------------------------`));
|
|
355
|
+
if (!templates[type]) {
|
|
356
|
+
console.error(chalk3.red(`
|
|
357
|
+
\u274C Unknown type: ${type}`));
|
|
358
|
+
console.log(chalk3.dim("Available types: plugin, example"));
|
|
359
|
+
process.exit(1);
|
|
360
|
+
}
|
|
361
|
+
if (!name) {
|
|
362
|
+
console.error(chalk3.red("\n\u274C Project name is required"));
|
|
363
|
+
console.log(chalk3.dim(`Usage: objectstack create ${type} <name>`));
|
|
364
|
+
process.exit(1);
|
|
365
|
+
}
|
|
366
|
+
const template = templates[type];
|
|
367
|
+
const cwd = process.cwd();
|
|
368
|
+
let targetDir;
|
|
369
|
+
if (options?.dir) {
|
|
370
|
+
targetDir = path2.resolve(cwd, options.dir);
|
|
371
|
+
} else {
|
|
372
|
+
const baseDir = type === "plugin" ? "packages/plugins" : "examples";
|
|
373
|
+
const projectName = type === "plugin" ? `plugin-${name}` : name;
|
|
374
|
+
targetDir = path2.join(cwd, baseDir, projectName);
|
|
375
|
+
}
|
|
376
|
+
if (fs2.existsSync(targetDir)) {
|
|
377
|
+
console.error(chalk3.red(`
|
|
378
|
+
\u274C Directory already exists: ${targetDir}`));
|
|
379
|
+
process.exit(1);
|
|
380
|
+
}
|
|
381
|
+
console.log(`\u{1F4C1} Creating ${type}: ${chalk3.blue(name)}`);
|
|
382
|
+
console.log(`\u{1F4C2} Location: ${chalk3.dim(targetDir)}`);
|
|
383
|
+
console.log("");
|
|
384
|
+
try {
|
|
385
|
+
fs2.mkdirSync(targetDir, { recursive: true });
|
|
386
|
+
for (const [filePath, contentFn] of Object.entries(template.files)) {
|
|
387
|
+
const fullPath = path2.join(targetDir, filePath);
|
|
388
|
+
const dir = path2.dirname(fullPath);
|
|
389
|
+
if (!fs2.existsSync(dir)) {
|
|
390
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
391
|
+
}
|
|
392
|
+
const content = contentFn(name);
|
|
393
|
+
const fileContent = typeof content === "string" ? content : JSON.stringify(content, null, 2);
|
|
394
|
+
fs2.writeFileSync(fullPath, fileContent);
|
|
395
|
+
console.log(chalk3.green(`\u2713 Created ${filePath}`));
|
|
396
|
+
}
|
|
397
|
+
console.log("");
|
|
398
|
+
console.log(chalk3.green("\u2705 Project created successfully!"));
|
|
399
|
+
console.log("");
|
|
400
|
+
console.log(chalk3.bold("Next steps:"));
|
|
401
|
+
console.log(chalk3.dim(` cd ${path2.relative(cwd, targetDir)}`));
|
|
402
|
+
console.log(chalk3.dim(" pnpm install"));
|
|
403
|
+
console.log(chalk3.dim(" pnpm build"));
|
|
404
|
+
console.log("");
|
|
405
|
+
} catch (error) {
|
|
406
|
+
console.error(chalk3.red("\n\u274C Failed to create project:"));
|
|
407
|
+
console.error(error.message || error);
|
|
408
|
+
if (fs2.existsSync(targetDir)) {
|
|
409
|
+
fs2.rmSync(targetDir, { recursive: true });
|
|
410
|
+
}
|
|
411
|
+
process.exit(1);
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
// src/commands/serve.ts
|
|
416
|
+
import { Command as Command4 } from "commander";
|
|
417
|
+
import path3 from "path";
|
|
418
|
+
import fs3 from "fs";
|
|
419
|
+
import chalk4 from "chalk";
|
|
420
|
+
import { bundleRequire } from "bundle-require";
|
|
421
|
+
var serveCommand = new Command4("serve").description("Start ObjectStack server with plugins from configuration").argument("[config]", "Configuration file path", "objectstack.config.ts").option("-p, --port <port>", "Server port", "3000").option("--no-server", "Skip starting HTTP server plugin").action(async (configPath, options) => {
|
|
422
|
+
console.log(chalk4.bold(`
|
|
423
|
+
\u{1F680} ObjectStack Server`));
|
|
424
|
+
console.log(chalk4.dim(`------------------------`));
|
|
425
|
+
console.log(`\u{1F4C2} Config: ${chalk4.blue(configPath)}`);
|
|
426
|
+
console.log(`\u{1F310} Port: ${chalk4.blue(options.port)}`);
|
|
427
|
+
console.log("");
|
|
428
|
+
const absolutePath = path3.resolve(process.cwd(), configPath);
|
|
429
|
+
if (!fs3.existsSync(absolutePath)) {
|
|
430
|
+
console.error(chalk4.red(`
|
|
431
|
+
\u274C Configuration file not found: ${absolutePath}`));
|
|
432
|
+
process.exit(1);
|
|
433
|
+
}
|
|
434
|
+
try {
|
|
435
|
+
console.log(chalk4.yellow(`\u{1F4E6} Loading configuration...`));
|
|
436
|
+
const { mod } = await bundleRequire({
|
|
437
|
+
filepath: absolutePath
|
|
438
|
+
});
|
|
439
|
+
const config = mod.default || mod;
|
|
440
|
+
if (!config) {
|
|
441
|
+
throw new Error(`Default export not found in ${configPath}`);
|
|
442
|
+
}
|
|
443
|
+
console.log(chalk4.green(`\u2713 Configuration loaded`));
|
|
444
|
+
const { ObjectStackKernel } = await import("@objectstack/core");
|
|
445
|
+
console.log(chalk4.yellow(`\u{1F527} Initializing ObjectStack kernel...`));
|
|
446
|
+
const kernel = new ObjectStackKernel({
|
|
447
|
+
metadata: config.metadata || {},
|
|
448
|
+
objects: config.objects || {}
|
|
449
|
+
});
|
|
450
|
+
const plugins = config.plugins || [];
|
|
451
|
+
if (plugins.length > 0) {
|
|
452
|
+
console.log(chalk4.yellow(`\u{1F4E6} Loading ${plugins.length} plugin(s)...`));
|
|
453
|
+
for (const plugin of plugins) {
|
|
454
|
+
try {
|
|
455
|
+
kernel.registerPlugin(plugin);
|
|
456
|
+
const pluginName = plugin.name || plugin.constructor?.name || "unnamed";
|
|
457
|
+
console.log(chalk4.green(` \u2713 Registered plugin: ${pluginName}`));
|
|
458
|
+
} catch (e) {
|
|
459
|
+
console.error(chalk4.red(` \u2717 Failed to register plugin: ${e.message}`));
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
if (options.server !== false) {
|
|
464
|
+
try {
|
|
465
|
+
const { HonoServerPlugin } = await import("@objectstack/plugin-hono-server");
|
|
466
|
+
const serverPlugin = new HonoServerPlugin({ port: parseInt(options.port) });
|
|
467
|
+
kernel.registerPlugin(serverPlugin);
|
|
468
|
+
console.log(chalk4.green(` \u2713 Registered HTTP server plugin (port: ${options.port})`));
|
|
469
|
+
} catch (e) {
|
|
470
|
+
console.warn(chalk4.yellow(` \u26A0 HTTP server plugin not available: ${e.message}`));
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
console.log(chalk4.yellow(`
|
|
474
|
+
\u{1F680} Starting ObjectStack...`));
|
|
475
|
+
await kernel.boot();
|
|
476
|
+
console.log(chalk4.green(`
|
|
477
|
+
\u2705 ObjectStack server is running!`));
|
|
478
|
+
console.log(chalk4.dim(` Press Ctrl+C to stop
|
|
479
|
+
`));
|
|
480
|
+
process.on("SIGINT", async () => {
|
|
481
|
+
console.log(chalk4.yellow(`
|
|
482
|
+
|
|
483
|
+
\u23F9 Stopping server...`));
|
|
484
|
+
await kernel.shutdown();
|
|
485
|
+
console.log(chalk4.green(`\u2705 Server stopped`));
|
|
486
|
+
process.exit(0);
|
|
487
|
+
});
|
|
488
|
+
} catch (error) {
|
|
489
|
+
console.error(chalk4.red(`
|
|
490
|
+
\u274C Server Error:`));
|
|
491
|
+
console.error(error.message || error);
|
|
492
|
+
console.error(error.stack);
|
|
493
|
+
process.exit(1);
|
|
494
|
+
}
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
// src/bin.ts
|
|
498
|
+
var program = new Command5();
|
|
499
|
+
program.name("objectstack").description("CLI for ObjectStack Protocol - Development Tools for Microkernel Architecture").version("0.7.1");
|
|
10
500
|
program.addCommand(compileCommand);
|
|
501
|
+
program.addCommand(serveCommand);
|
|
502
|
+
program.addCommand(devCommand);
|
|
503
|
+
program.addCommand(doctorCommand);
|
|
504
|
+
program.addCommand(createCommand);
|
|
11
505
|
program.parse(process.argv);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "Command Line Interface for ObjectStack Protocol",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -22,7 +22,9 @@
|
|
|
22
22
|
"commander": "^11.1.0",
|
|
23
23
|
"tsx": "^4.7.1",
|
|
24
24
|
"zod": "^3.22.4",
|
|
25
|
-
"@objectstack/spec": "0.
|
|
25
|
+
"@objectstack/spec": "0.7.2",
|
|
26
|
+
"@objectstack/core": "0.7.2",
|
|
27
|
+
"@objectstack/plugin-hono-server": "0.7.2"
|
|
26
28
|
},
|
|
27
29
|
"devDependencies": {
|
|
28
30
|
"@types/node": "^20.11.19",
|
package/src/bin.ts
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { compileCommand } from './commands/compile.js';
|
|
3
|
+
import { devCommand } from './commands/dev.js';
|
|
4
|
+
import { doctorCommand } from './commands/doctor.js';
|
|
5
|
+
import { createCommand } from './commands/create.js';
|
|
6
|
+
import { serveCommand } from './commands/serve.js';
|
|
3
7
|
|
|
4
8
|
const program = new Command();
|
|
5
9
|
|
|
6
10
|
program
|
|
7
11
|
.name('objectstack')
|
|
8
|
-
.description('CLI for ObjectStack Protocol')
|
|
9
|
-
.version('0.1
|
|
12
|
+
.description('CLI for ObjectStack Protocol - Development Tools for Microkernel Architecture')
|
|
13
|
+
.version('0.7.1');
|
|
10
14
|
|
|
15
|
+
// Add all commands
|
|
11
16
|
program.addCommand(compileCommand);
|
|
17
|
+
program.addCommand(serveCommand);
|
|
18
|
+
program.addCommand(devCommand);
|
|
19
|
+
program.addCommand(doctorCommand);
|
|
20
|
+
program.addCommand(createCommand);
|
|
12
21
|
|
|
13
22
|
program.parse(process.argv);
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { execSync } from 'child_process';
|
|
6
|
+
|
|
7
|
+
const templates = {
|
|
8
|
+
plugin: {
|
|
9
|
+
description: 'Create a new ObjectStack plugin',
|
|
10
|
+
files: {
|
|
11
|
+
'package.json': (name: string) => ({
|
|
12
|
+
name: `@objectstack/plugin-${name}`,
|
|
13
|
+
version: '0.1.0',
|
|
14
|
+
description: `ObjectStack Plugin: ${name}`,
|
|
15
|
+
main: 'dist/index.js',
|
|
16
|
+
types: 'dist/index.d.ts',
|
|
17
|
+
scripts: {
|
|
18
|
+
build: 'tsc',
|
|
19
|
+
dev: 'tsc --watch',
|
|
20
|
+
test: 'vitest',
|
|
21
|
+
},
|
|
22
|
+
keywords: ['objectstack', 'plugin', name],
|
|
23
|
+
author: '',
|
|
24
|
+
license: 'MIT',
|
|
25
|
+
dependencies: {
|
|
26
|
+
'@objectstack/spec': 'workspace:*',
|
|
27
|
+
zod: '^3.22.4',
|
|
28
|
+
},
|
|
29
|
+
devDependencies: {
|
|
30
|
+
'@types/node': '^20.10.0',
|
|
31
|
+
typescript: '^5.3.0',
|
|
32
|
+
vitest: '^2.1.8',
|
|
33
|
+
},
|
|
34
|
+
}),
|
|
35
|
+
'tsconfig.json': () => ({
|
|
36
|
+
extends: '../../tsconfig.json',
|
|
37
|
+
compilerOptions: {
|
|
38
|
+
outDir: 'dist',
|
|
39
|
+
rootDir: 'src',
|
|
40
|
+
},
|
|
41
|
+
include: ['src/**/*'],
|
|
42
|
+
}),
|
|
43
|
+
'src/index.ts': (name: string) => `import type { Plugin } from '@objectstack/spec';
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* ${name} Plugin for ObjectStack
|
|
47
|
+
*/
|
|
48
|
+
export const ${toCamelCase(name)}Plugin: Plugin = {
|
|
49
|
+
name: '${name}',
|
|
50
|
+
version: '0.1.0',
|
|
51
|
+
|
|
52
|
+
async initialize(context) {
|
|
53
|
+
console.log('Initializing ${name} plugin...');
|
|
54
|
+
// Plugin initialization logic
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
async destroy() {
|
|
58
|
+
console.log('Destroying ${name} plugin...');
|
|
59
|
+
// Plugin cleanup logic
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export default ${toCamelCase(name)}Plugin;
|
|
64
|
+
`,
|
|
65
|
+
'README.md': (name: string) => `# @objectstack/plugin-${name}
|
|
66
|
+
|
|
67
|
+
ObjectStack Plugin: ${name}
|
|
68
|
+
|
|
69
|
+
## Installation
|
|
70
|
+
|
|
71
|
+
\`\`\`bash
|
|
72
|
+
pnpm add @objectstack/plugin-${name}
|
|
73
|
+
\`\`\`
|
|
74
|
+
|
|
75
|
+
## Usage
|
|
76
|
+
|
|
77
|
+
\`\`\`typescript
|
|
78
|
+
import { ${toCamelCase(name)}Plugin } from '@objectstack/plugin-${name}';
|
|
79
|
+
|
|
80
|
+
// Use the plugin in your ObjectStack configuration
|
|
81
|
+
export default {
|
|
82
|
+
plugins: [
|
|
83
|
+
${toCamelCase(name)}Plugin,
|
|
84
|
+
],
|
|
85
|
+
};
|
|
86
|
+
\`\`\`
|
|
87
|
+
|
|
88
|
+
## License
|
|
89
|
+
|
|
90
|
+
MIT
|
|
91
|
+
`,
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
example: {
|
|
96
|
+
description: 'Create a new ObjectStack example application',
|
|
97
|
+
files: {
|
|
98
|
+
'package.json': (name: string) => ({
|
|
99
|
+
name: `@objectstack/example-${name}`,
|
|
100
|
+
version: '0.1.0',
|
|
101
|
+
private: true,
|
|
102
|
+
description: `ObjectStack Example: ${name}`,
|
|
103
|
+
scripts: {
|
|
104
|
+
build: 'objectstack compile',
|
|
105
|
+
dev: 'tsx watch objectstack.config.ts',
|
|
106
|
+
test: 'vitest',
|
|
107
|
+
},
|
|
108
|
+
dependencies: {
|
|
109
|
+
'@objectstack/spec': 'workspace:*',
|
|
110
|
+
'@objectstack/cli': 'workspace:*',
|
|
111
|
+
zod: '^3.22.4',
|
|
112
|
+
},
|
|
113
|
+
devDependencies: {
|
|
114
|
+
'@types/node': '^20.10.0',
|
|
115
|
+
tsx: '^4.21.0',
|
|
116
|
+
typescript: '^5.3.0',
|
|
117
|
+
vitest: '^2.1.8',
|
|
118
|
+
},
|
|
119
|
+
}),
|
|
120
|
+
'objectstack.config.ts': (name: string) => `import { defineStack } from '@objectstack/spec';
|
|
121
|
+
|
|
122
|
+
export default defineStack({
|
|
123
|
+
metadata: {
|
|
124
|
+
name: '${name}',
|
|
125
|
+
version: '0.1.0',
|
|
126
|
+
description: '${name} example application',
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
objects: {
|
|
130
|
+
// Define your data objects here
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
ui: {
|
|
134
|
+
apps: [],
|
|
135
|
+
views: [],
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
`,
|
|
139
|
+
'README.md': (name: string) => `# ${name} Example
|
|
140
|
+
|
|
141
|
+
ObjectStack example application: ${name}
|
|
142
|
+
|
|
143
|
+
## Quick Start
|
|
144
|
+
|
|
145
|
+
\`\`\`bash
|
|
146
|
+
# Build the configuration
|
|
147
|
+
pnpm build
|
|
148
|
+
|
|
149
|
+
# Run in development mode
|
|
150
|
+
pnpm dev
|
|
151
|
+
\`\`\`
|
|
152
|
+
|
|
153
|
+
## Structure
|
|
154
|
+
|
|
155
|
+
- \`objectstack.config.ts\` - Main configuration file
|
|
156
|
+
- \`dist/objectstack.json\` - Compiled artifact
|
|
157
|
+
|
|
158
|
+
## Learn More
|
|
159
|
+
|
|
160
|
+
- [ObjectStack Documentation](../../content/docs)
|
|
161
|
+
- [Examples](../)
|
|
162
|
+
`,
|
|
163
|
+
'tsconfig.json': () => ({
|
|
164
|
+
extends: '../../tsconfig.json',
|
|
165
|
+
compilerOptions: {
|
|
166
|
+
outDir: 'dist',
|
|
167
|
+
rootDir: '.',
|
|
168
|
+
},
|
|
169
|
+
include: ['*.ts', 'src/**/*'],
|
|
170
|
+
}),
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
function toCamelCase(str: string): string {
|
|
176
|
+
return str.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export const createCommand = new Command('create')
|
|
180
|
+
.description('Create a new package, plugin, or example from template')
|
|
181
|
+
.argument('<type>', 'Type of project to create (plugin, example)')
|
|
182
|
+
.argument('[name]', 'Name of the project')
|
|
183
|
+
.option('-d, --dir <directory>', 'Target directory')
|
|
184
|
+
.action(async (type: string, name?: string, options?: { dir?: string }) => {
|
|
185
|
+
console.log(chalk.bold(`\n📦 ObjectStack Project Creator`));
|
|
186
|
+
console.log(chalk.dim(`-------------------------------`));
|
|
187
|
+
|
|
188
|
+
if (!templates[type as keyof typeof templates]) {
|
|
189
|
+
console.error(chalk.red(`\n❌ Unknown type: ${type}`));
|
|
190
|
+
console.log(chalk.dim('Available types: plugin, example'));
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (!name) {
|
|
195
|
+
console.error(chalk.red('\n❌ Project name is required'));
|
|
196
|
+
console.log(chalk.dim(`Usage: objectstack create ${type} <name>`));
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const template = templates[type as keyof typeof templates];
|
|
201
|
+
const cwd = process.cwd();
|
|
202
|
+
|
|
203
|
+
// Determine target directory
|
|
204
|
+
let targetDir: string;
|
|
205
|
+
if (options?.dir) {
|
|
206
|
+
targetDir = path.resolve(cwd, options.dir);
|
|
207
|
+
} else {
|
|
208
|
+
const baseDir = type === 'plugin' ? 'packages/plugins' : 'examples';
|
|
209
|
+
const projectName = type === 'plugin' ? `plugin-${name}` : name;
|
|
210
|
+
targetDir = path.join(cwd, baseDir, projectName);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Check if directory already exists
|
|
214
|
+
if (fs.existsSync(targetDir)) {
|
|
215
|
+
console.error(chalk.red(`\n❌ Directory already exists: ${targetDir}`));
|
|
216
|
+
process.exit(1);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
console.log(`📁 Creating ${type}: ${chalk.blue(name)}`);
|
|
220
|
+
console.log(`📂 Location: ${chalk.dim(targetDir)}`);
|
|
221
|
+
console.log('');
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
// Create directory
|
|
225
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
226
|
+
|
|
227
|
+
// Create files from template
|
|
228
|
+
for (const [filePath, contentFn] of Object.entries(template.files)) {
|
|
229
|
+
const fullPath = path.join(targetDir, filePath);
|
|
230
|
+
const dir = path.dirname(fullPath);
|
|
231
|
+
|
|
232
|
+
if (!fs.existsSync(dir)) {
|
|
233
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const content = contentFn(name);
|
|
237
|
+
const fileContent = typeof content === 'string'
|
|
238
|
+
? content
|
|
239
|
+
: JSON.stringify(content, null, 2);
|
|
240
|
+
|
|
241
|
+
fs.writeFileSync(fullPath, fileContent);
|
|
242
|
+
console.log(chalk.green(`✓ Created ${filePath}`));
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
console.log('');
|
|
246
|
+
console.log(chalk.green('✅ Project created successfully!'));
|
|
247
|
+
console.log('');
|
|
248
|
+
console.log(chalk.bold('Next steps:'));
|
|
249
|
+
console.log(chalk.dim(` cd ${path.relative(cwd, targetDir)}`));
|
|
250
|
+
console.log(chalk.dim(' pnpm install'));
|
|
251
|
+
console.log(chalk.dim(' pnpm build'));
|
|
252
|
+
console.log('');
|
|
253
|
+
|
|
254
|
+
} catch (error: any) {
|
|
255
|
+
console.error(chalk.red('\n❌ Failed to create project:'));
|
|
256
|
+
console.error(error.message || error);
|
|
257
|
+
|
|
258
|
+
// Clean up on error
|
|
259
|
+
if (fs.existsSync(targetDir)) {
|
|
260
|
+
fs.rmSync(targetDir, { recursive: true });
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
process.exit(1);
|
|
264
|
+
}
|
|
265
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { execSync } from 'child_process';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
|
|
8
|
+
export const devCommand = new Command('dev')
|
|
9
|
+
.description('Start development mode for a package')
|
|
10
|
+
.argument('[package]', 'Package name (without @objectstack/ prefix)', 'all')
|
|
11
|
+
.option('-w, --watch', 'Enable watch mode (default)', true)
|
|
12
|
+
.option('-v, --verbose', 'Verbose output')
|
|
13
|
+
.action(async (packageName, options) => {
|
|
14
|
+
console.log(chalk.bold(`\n🚀 ObjectStack Development Mode`));
|
|
15
|
+
console.log(chalk.dim(`-------------------------------`));
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
const cwd = process.cwd();
|
|
19
|
+
const filter = packageName === 'all' ? '' : `--filter @objectstack/${packageName}`;
|
|
20
|
+
|
|
21
|
+
console.log(`📦 Package: ${chalk.blue(packageName === 'all' ? 'All packages' : `@objectstack/${packageName}`)}`);
|
|
22
|
+
console.log(`🔄 Watch mode: ${chalk.green('enabled')}`);
|
|
23
|
+
console.log('');
|
|
24
|
+
|
|
25
|
+
// Start dev mode
|
|
26
|
+
const command = `pnpm ${filter} dev`.trim();
|
|
27
|
+
console.log(chalk.dim(`$ ${command}`));
|
|
28
|
+
console.log('');
|
|
29
|
+
|
|
30
|
+
execSync(command, {
|
|
31
|
+
stdio: 'inherit',
|
|
32
|
+
cwd
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
} catch (error: any) {
|
|
36
|
+
console.error(chalk.red(`\n❌ Development mode failed:`));
|
|
37
|
+
console.error(error.message || error);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { execSync } from 'child_process';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
|
|
7
|
+
interface HealthCheckResult {
|
|
8
|
+
name: string;
|
|
9
|
+
status: 'ok' | 'warning' | 'error';
|
|
10
|
+
message: string;
|
|
11
|
+
fix?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const doctorCommand = new Command('doctor')
|
|
15
|
+
.description('Check development environment health')
|
|
16
|
+
.option('-v, --verbose', 'Show detailed information')
|
|
17
|
+
.action(async (options) => {
|
|
18
|
+
console.log(chalk.bold(`\n🏥 ObjectStack Environment Health Check`));
|
|
19
|
+
console.log(chalk.dim(`-----------------------------------------`));
|
|
20
|
+
console.log('');
|
|
21
|
+
|
|
22
|
+
const results: HealthCheckResult[] = [];
|
|
23
|
+
|
|
24
|
+
// Check Node.js version
|
|
25
|
+
try {
|
|
26
|
+
const nodeVersion = process.version;
|
|
27
|
+
const majorVersion = parseInt(nodeVersion.slice(1).split('.')[0]);
|
|
28
|
+
|
|
29
|
+
if (majorVersion >= 18) {
|
|
30
|
+
results.push({
|
|
31
|
+
name: 'Node.js',
|
|
32
|
+
status: 'ok',
|
|
33
|
+
message: `Version ${nodeVersion}`,
|
|
34
|
+
});
|
|
35
|
+
} else {
|
|
36
|
+
results.push({
|
|
37
|
+
name: 'Node.js',
|
|
38
|
+
status: 'error',
|
|
39
|
+
message: `Version ${nodeVersion} (requires >= 18.0.0)`,
|
|
40
|
+
fix: 'Upgrade Node.js: https://nodejs.org',
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
} catch (error) {
|
|
44
|
+
results.push({
|
|
45
|
+
name: 'Node.js',
|
|
46
|
+
status: 'error',
|
|
47
|
+
message: 'Not found',
|
|
48
|
+
fix: 'Install Node.js: https://nodejs.org',
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Check pnpm
|
|
53
|
+
try {
|
|
54
|
+
const pnpmVersion = execSync('pnpm -v', { encoding: 'utf-8' }).trim();
|
|
55
|
+
results.push({
|
|
56
|
+
name: 'pnpm',
|
|
57
|
+
status: 'ok',
|
|
58
|
+
message: `Version ${pnpmVersion}`,
|
|
59
|
+
});
|
|
60
|
+
} catch (error) {
|
|
61
|
+
results.push({
|
|
62
|
+
name: 'pnpm',
|
|
63
|
+
status: 'error',
|
|
64
|
+
message: 'Not found',
|
|
65
|
+
fix: 'Install pnpm: npm install -g pnpm@10.28.1',
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Check TypeScript
|
|
70
|
+
try {
|
|
71
|
+
const tscVersion = execSync('tsc -v', { encoding: 'utf-8' }).trim();
|
|
72
|
+
results.push({
|
|
73
|
+
name: 'TypeScript',
|
|
74
|
+
status: 'ok',
|
|
75
|
+
message: tscVersion,
|
|
76
|
+
});
|
|
77
|
+
} catch (error) {
|
|
78
|
+
results.push({
|
|
79
|
+
name: 'TypeScript',
|
|
80
|
+
status: 'warning',
|
|
81
|
+
message: 'Not found in PATH',
|
|
82
|
+
fix: 'Installed locally via pnpm',
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Check if dependencies are installed
|
|
87
|
+
const cwd = process.cwd();
|
|
88
|
+
const nodeModulesPath = path.join(cwd, 'node_modules');
|
|
89
|
+
|
|
90
|
+
if (fs.existsSync(nodeModulesPath)) {
|
|
91
|
+
results.push({
|
|
92
|
+
name: 'Dependencies',
|
|
93
|
+
status: 'ok',
|
|
94
|
+
message: 'Installed',
|
|
95
|
+
});
|
|
96
|
+
} else {
|
|
97
|
+
results.push({
|
|
98
|
+
name: 'Dependencies',
|
|
99
|
+
status: 'error',
|
|
100
|
+
message: 'Not installed',
|
|
101
|
+
fix: 'Run: pnpm install',
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Check if spec package is built
|
|
106
|
+
const specDistPath = path.join(cwd, 'packages/spec/dist');
|
|
107
|
+
|
|
108
|
+
if (fs.existsSync(specDistPath)) {
|
|
109
|
+
results.push({
|
|
110
|
+
name: '@objectstack/spec',
|
|
111
|
+
status: 'ok',
|
|
112
|
+
message: 'Built',
|
|
113
|
+
});
|
|
114
|
+
} else {
|
|
115
|
+
results.push({
|
|
116
|
+
name: '@objectstack/spec',
|
|
117
|
+
status: 'warning',
|
|
118
|
+
message: 'Not built',
|
|
119
|
+
fix: 'Run: pnpm --filter @objectstack/spec build',
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Check Git
|
|
124
|
+
try {
|
|
125
|
+
const gitVersion = execSync('git --version', { encoding: 'utf-8' }).trim();
|
|
126
|
+
results.push({
|
|
127
|
+
name: 'Git',
|
|
128
|
+
status: 'ok',
|
|
129
|
+
message: gitVersion,
|
|
130
|
+
});
|
|
131
|
+
} catch (error) {
|
|
132
|
+
results.push({
|
|
133
|
+
name: 'Git',
|
|
134
|
+
status: 'warning',
|
|
135
|
+
message: 'Not found',
|
|
136
|
+
fix: 'Install Git for version control',
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Display results
|
|
141
|
+
let hasErrors = false;
|
|
142
|
+
let hasWarnings = false;
|
|
143
|
+
|
|
144
|
+
results.forEach((result) => {
|
|
145
|
+
const icon = result.status === 'ok' ? '✓' : result.status === 'warning' ? '⚠' : '✗';
|
|
146
|
+
const color = result.status === 'ok' ? chalk.green : result.status === 'warning' ? chalk.yellow : chalk.red;
|
|
147
|
+
|
|
148
|
+
console.log(color(`${icon} ${result.name.padEnd(20)} ${result.message}`));
|
|
149
|
+
|
|
150
|
+
if (result.fix && options.verbose) {
|
|
151
|
+
console.log(chalk.dim(` → ${result.fix}`));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (result.status === 'error') hasErrors = true;
|
|
155
|
+
if (result.status === 'warning') hasWarnings = true;
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
console.log('');
|
|
159
|
+
|
|
160
|
+
// Summary
|
|
161
|
+
if (hasErrors) {
|
|
162
|
+
console.log(chalk.red('❌ Some critical issues found. Please fix them before continuing.'));
|
|
163
|
+
results
|
|
164
|
+
.filter(r => r.status === 'error' && r.fix)
|
|
165
|
+
.forEach(r => console.log(chalk.dim(` ${r.fix}`)));
|
|
166
|
+
process.exit(1);
|
|
167
|
+
} else if (hasWarnings) {
|
|
168
|
+
console.log(chalk.yellow('⚠️ Environment is functional but has some warnings.'));
|
|
169
|
+
console.log(chalk.dim(' Run with --verbose to see fix suggestions.'));
|
|
170
|
+
} else {
|
|
171
|
+
console.log(chalk.green('✅ Environment is healthy and ready for development!'));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
console.log('');
|
|
175
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { bundleRequire } from 'bundle-require';
|
|
6
|
+
|
|
7
|
+
export const serveCommand = new Command('serve')
|
|
8
|
+
.description('Start ObjectStack server with plugins from configuration')
|
|
9
|
+
.argument('[config]', 'Configuration file path', 'objectstack.config.ts')
|
|
10
|
+
.option('-p, --port <port>', 'Server port', '3000')
|
|
11
|
+
.option('--no-server', 'Skip starting HTTP server plugin')
|
|
12
|
+
.action(async (configPath, options) => {
|
|
13
|
+
console.log(chalk.bold(`\n🚀 ObjectStack Server`));
|
|
14
|
+
console.log(chalk.dim(`------------------------`));
|
|
15
|
+
console.log(`📂 Config: ${chalk.blue(configPath)}`);
|
|
16
|
+
console.log(`🌐 Port: ${chalk.blue(options.port)}`);
|
|
17
|
+
console.log('');
|
|
18
|
+
|
|
19
|
+
const absolutePath = path.resolve(process.cwd(), configPath);
|
|
20
|
+
|
|
21
|
+
if (!fs.existsSync(absolutePath)) {
|
|
22
|
+
console.error(chalk.red(`\n❌ Configuration file not found: ${absolutePath}`));
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
// Load configuration
|
|
28
|
+
console.log(chalk.yellow(`📦 Loading configuration...`));
|
|
29
|
+
const { mod } = await bundleRequire({
|
|
30
|
+
filepath: absolutePath,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const config = mod.default || mod;
|
|
34
|
+
|
|
35
|
+
if (!config) {
|
|
36
|
+
throw new Error(`Default export not found in ${configPath}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
console.log(chalk.green(`✓ Configuration loaded`));
|
|
40
|
+
|
|
41
|
+
// Import ObjectStack runtime
|
|
42
|
+
const { ObjectStackKernel } = await import('@objectstack/core');
|
|
43
|
+
|
|
44
|
+
// Create kernel instance
|
|
45
|
+
console.log(chalk.yellow(`🔧 Initializing ObjectStack kernel...`));
|
|
46
|
+
const kernel = new ObjectStackKernel({
|
|
47
|
+
metadata: config.metadata || {},
|
|
48
|
+
objects: config.objects || {},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Load plugins from configuration
|
|
52
|
+
const plugins = config.plugins || [];
|
|
53
|
+
|
|
54
|
+
if (plugins.length > 0) {
|
|
55
|
+
console.log(chalk.yellow(`📦 Loading ${plugins.length} plugin(s)...`));
|
|
56
|
+
|
|
57
|
+
for (const plugin of plugins) {
|
|
58
|
+
try {
|
|
59
|
+
kernel.registerPlugin(plugin);
|
|
60
|
+
const pluginName = plugin.name || plugin.constructor?.name || 'unnamed';
|
|
61
|
+
console.log(chalk.green(` ✓ Registered plugin: ${pluginName}`));
|
|
62
|
+
} catch (e: any) {
|
|
63
|
+
console.error(chalk.red(` ✗ Failed to register plugin: ${e.message}`));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Add HTTP server plugin if not disabled
|
|
69
|
+
if (options.server !== false) {
|
|
70
|
+
try {
|
|
71
|
+
const { HonoServerPlugin } = await import('@objectstack/plugin-hono-server');
|
|
72
|
+
const serverPlugin = new HonoServerPlugin({ port: parseInt(options.port) });
|
|
73
|
+
kernel.registerPlugin(serverPlugin);
|
|
74
|
+
console.log(chalk.green(` ✓ Registered HTTP server plugin (port: ${options.port})`));
|
|
75
|
+
} catch (e: any) {
|
|
76
|
+
console.warn(chalk.yellow(` ⚠ HTTP server plugin not available: ${e.message}`));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Boot the kernel
|
|
81
|
+
console.log(chalk.yellow(`\n🚀 Starting ObjectStack...`));
|
|
82
|
+
await kernel.boot();
|
|
83
|
+
|
|
84
|
+
console.log(chalk.green(`\n✅ ObjectStack server is running!`));
|
|
85
|
+
console.log(chalk.dim(` Press Ctrl+C to stop\n`));
|
|
86
|
+
|
|
87
|
+
// Keep process alive
|
|
88
|
+
process.on('SIGINT', async () => {
|
|
89
|
+
console.log(chalk.yellow(`\n\n⏹ Stopping server...`));
|
|
90
|
+
await kernel.shutdown();
|
|
91
|
+
console.log(chalk.green(`✅ Server stopped`));
|
|
92
|
+
process.exit(0);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
} catch (error: any) {
|
|
96
|
+
console.error(chalk.red(`\n❌ Server Error:`));
|
|
97
|
+
console.error(error.message || error);
|
|
98
|
+
console.error(error.stack);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
});
|