create-objectstack 4.0.3 → 4.0.5
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/dist/index.js +41 -10
- package/dist/templates/copilot-instructions.md +74 -0
- package/package.json +20 -3
- package/.turbo/turbo-build.log +0 -13
- package/CHANGELOG.md +0 -43
- package/src/index.ts +0 -720
- package/tsconfig.json +0 -23
- package/tsup.config.ts +0 -10
package/dist/index.js
CHANGED
|
@@ -4,6 +4,10 @@ import chalk from "chalk";
|
|
|
4
4
|
import fs from "fs";
|
|
5
5
|
import path from "path";
|
|
6
6
|
import { execSync } from "child_process";
|
|
7
|
+
import { fileURLToPath } from "url";
|
|
8
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
var __dirname = path.dirname(__filename);
|
|
10
|
+
var TEMPLATES_DIR = path.resolve(__dirname, "templates");
|
|
7
11
|
var TEMPLATES = {
|
|
8
12
|
"minimal-api": {
|
|
9
13
|
description: "Server + memory driver + 1 object + REST API",
|
|
@@ -66,7 +70,7 @@ export default defineStack({
|
|
|
66
70
|
include: ["*.ts", "src/**/*"],
|
|
67
71
|
exclude: ["dist", "node_modules"]
|
|
68
72
|
}, null, 2) + "\n",
|
|
69
|
-
"src/objects/task.ts": () => `import
|
|
73
|
+
"src/objects/task.ts": () => `import * as Data from '@objectstack/spec/data';
|
|
70
74
|
|
|
71
75
|
const task: Data.Object = {
|
|
72
76
|
name: 'task',
|
|
@@ -200,7 +204,7 @@ export default defineStack({
|
|
|
200
204
|
include: ["*.ts", "src/**/*"],
|
|
201
205
|
exclude: ["dist", "node_modules"]
|
|
202
206
|
}, null, 2) + "\n",
|
|
203
|
-
"src/objects/contact.ts": () => `import
|
|
207
|
+
"src/objects/contact.ts": () => `import * as Data from '@objectstack/spec/data';
|
|
204
208
|
|
|
205
209
|
const contact: Data.Object = {
|
|
206
210
|
name: 'contact',
|
|
@@ -235,7 +239,7 @@ const contact: Data.Object = {
|
|
|
235
239
|
|
|
236
240
|
export default contact;
|
|
237
241
|
`,
|
|
238
|
-
"src/objects/company.ts": () => `import
|
|
242
|
+
"src/objects/company.ts": () => `import * as Data from '@objectstack/spec/data';
|
|
239
243
|
|
|
240
244
|
const company: Data.Object = {
|
|
241
245
|
name: 'company',
|
|
@@ -266,7 +270,7 @@ const company: Data.Object = {
|
|
|
266
270
|
|
|
267
271
|
export default company;
|
|
268
272
|
`,
|
|
269
|
-
"src/objects/deal.ts": () => `import
|
|
273
|
+
"src/objects/deal.ts": () => `import * as Data from '@objectstack/spec/data';
|
|
270
274
|
|
|
271
275
|
const deal: Data.Object = {
|
|
272
276
|
name: 'deal',
|
|
@@ -317,7 +321,7 @@ export default deal;
|
|
|
317
321
|
export { default as company } from './company';
|
|
318
322
|
export { default as deal } from './deal';
|
|
319
323
|
`,
|
|
320
|
-
"src/views/contact_list.ts": () => `import
|
|
324
|
+
"src/views/contact_list.ts": () => `import * as UI from '@objectstack/spec/ui';
|
|
321
325
|
|
|
322
326
|
const contactList: UI.View = {
|
|
323
327
|
name: 'contact_list',
|
|
@@ -329,7 +333,7 @@ const contactList: UI.View = {
|
|
|
329
333
|
|
|
330
334
|
export default contactList;
|
|
331
335
|
`,
|
|
332
|
-
"src/views/company_list.ts": () => `import
|
|
336
|
+
"src/views/company_list.ts": () => `import * as UI from '@objectstack/spec/ui';
|
|
333
337
|
|
|
334
338
|
const companyList: UI.View = {
|
|
335
339
|
name: 'company_list',
|
|
@@ -341,7 +345,7 @@ const companyList: UI.View = {
|
|
|
341
345
|
|
|
342
346
|
export default companyList;
|
|
343
347
|
`,
|
|
344
|
-
"src/views/deal_list.ts": () => `import
|
|
348
|
+
"src/views/deal_list.ts": () => `import * as UI from '@objectstack/spec/ui';
|
|
345
349
|
|
|
346
350
|
const dealList: UI.View = {
|
|
347
351
|
name: 'deal_list',
|
|
@@ -353,7 +357,7 @@ const dealList: UI.View = {
|
|
|
353
357
|
|
|
354
358
|
export default dealList;
|
|
355
359
|
`,
|
|
356
|
-
"src/apps/crm.ts": () => `import
|
|
360
|
+
"src/apps/crm.ts": () => `import * as UI from '@objectstack/spec/ui';
|
|
357
361
|
|
|
358
362
|
const crm: UI.App = {
|
|
359
363
|
name: 'crm',
|
|
@@ -465,7 +469,7 @@ export default defineStack({
|
|
|
465
469
|
*/
|
|
466
470
|
export * as objects from './objects';
|
|
467
471
|
`,
|
|
468
|
-
"src/objects/sample.ts": (name) => `import
|
|
472
|
+
"src/objects/sample.ts": (name) => `import * as Data from '@objectstack/spec/data';
|
|
469
473
|
|
|
470
474
|
const sample: Data.Object = {
|
|
471
475
|
name: '${name}_sample',
|
|
@@ -541,6 +545,12 @@ MIT
|
|
|
541
545
|
}
|
|
542
546
|
}
|
|
543
547
|
};
|
|
548
|
+
function readTemplate(filename) {
|
|
549
|
+
return fs.readFileSync(path.join(TEMPLATES_DIR, filename), "utf-8");
|
|
550
|
+
}
|
|
551
|
+
var AI_CONFIG_FILES = {
|
|
552
|
+
".github/copilot-instructions.md": (name) => readTemplate("copilot-instructions.md").replaceAll("{{PROJECT_NAME}}", name).replaceAll("{{PROJECT_TITLE}}", toTitleCase(name))
|
|
553
|
+
};
|
|
544
554
|
function toTitleCase(str) {
|
|
545
555
|
return str.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
546
556
|
}
|
|
@@ -600,7 +610,8 @@ var program = new Command().name("create-objectstack").description("Create a new
|
|
|
600
610
|
if (!fs.existsSync(targetDir)) {
|
|
601
611
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
602
612
|
}
|
|
603
|
-
|
|
613
|
+
const allFiles = { ...template.files, ...AI_CONFIG_FILES };
|
|
614
|
+
for (const [filePath, contentFn] of Object.entries(allFiles)) {
|
|
604
615
|
const fullPath = path.join(targetDir, filePath);
|
|
605
616
|
const dir = path.dirname(fullPath);
|
|
606
617
|
if (!fs.existsSync(dir)) {
|
|
@@ -625,6 +636,21 @@ var program = new Command().name("create-objectstack").description("Create a new
|
|
|
625
636
|
console.log("");
|
|
626
637
|
}
|
|
627
638
|
}
|
|
639
|
+
if (!options.skipInstall) {
|
|
640
|
+
printStep("Installing AI skills for your coding agent...");
|
|
641
|
+
try {
|
|
642
|
+
execSync("npx -y skills add objectstack-ai/framework --all", {
|
|
643
|
+
stdio: "inherit",
|
|
644
|
+
cwd: targetDir
|
|
645
|
+
});
|
|
646
|
+
console.log("");
|
|
647
|
+
} catch {
|
|
648
|
+
printWarning(
|
|
649
|
+
"Skills installation skipped. Run manually:\n npx skills add objectstack-ai/framework"
|
|
650
|
+
);
|
|
651
|
+
console.log("");
|
|
652
|
+
}
|
|
653
|
+
}
|
|
628
654
|
printSuccess("Project created!");
|
|
629
655
|
console.log("");
|
|
630
656
|
console.log(chalk.bold(" Next steps:"));
|
|
@@ -636,6 +662,11 @@ var program = new Command().name("create-objectstack").description("Create a new
|
|
|
636
662
|
}
|
|
637
663
|
console.log(chalk.dim(" npm run dev # Start development server"));
|
|
638
664
|
console.log(chalk.dim(" npm run validate # Check configuration"));
|
|
665
|
+
if (options.skipInstall) {
|
|
666
|
+
console.log("");
|
|
667
|
+
console.log(chalk.bold(" AI Skills (recommended):"));
|
|
668
|
+
console.log(chalk.dim(" npx skills add objectstack-ai/framework"));
|
|
669
|
+
}
|
|
639
670
|
console.log("");
|
|
640
671
|
} catch (error) {
|
|
641
672
|
printError(error.message || String(error));
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# {{PROJECT_TITLE}} — Copilot Instructions
|
|
2
|
+
|
|
3
|
+
> Auto-generated by `create-objectstack`. Customise freely.
|
|
4
|
+
|
|
5
|
+
## Project Context
|
|
6
|
+
|
|
7
|
+
This is an **ObjectStack** application — a metadata-driven low-code project
|
|
8
|
+
that defines business objects, views, automations, and AI agents in TypeScript.
|
|
9
|
+
|
|
10
|
+
- **Entry point:** `objectstack.config.ts` (uses `defineStack()`)
|
|
11
|
+
- **Spec package:** `@objectstack/spec` (Zod-first schemas and types)
|
|
12
|
+
|
|
13
|
+
## Naming Conventions
|
|
14
|
+
|
|
15
|
+
| Context | Convention | Example |
|
|
16
|
+
|:--------|:-----------|:--------|
|
|
17
|
+
| Config keys (TS props) | `camelCase` | `maxLength`, `defaultValue` |
|
|
18
|
+
| Machine names (data values) | `snake_case` | `project_task`, `first_name` |
|
|
19
|
+
| Metadata type names | singular | `'agent'`, `'view'`, `'flow'` |
|
|
20
|
+
| File names | `{name}.{type}.ts` | `task.object.ts`, `main.app.ts` |
|
|
21
|
+
|
|
22
|
+
## Key Rules
|
|
23
|
+
|
|
24
|
+
1. **Zod First** — All schema definitions start with Zod. Types are derived via `z.infer<>`.
|
|
25
|
+
2. `defineStack()` is the single configuration entry point in `objectstack.config.ts`.
|
|
26
|
+
3. Use `Object.values()` barrel pattern for metadata arrays.
|
|
27
|
+
4. Import from `@objectstack/spec` — never use relative paths into the spec package.
|
|
28
|
+
|
|
29
|
+
## Project Structure
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
{{PROJECT_NAME}}/
|
|
33
|
+
├── objectstack.config.ts # defineStack() — the single entry point
|
|
34
|
+
├── src/
|
|
35
|
+
│ ├── objects/ # Business object definitions (snake_case names)
|
|
36
|
+
│ ├── views/ # UI view definitions (list, form, kanban, calendar)
|
|
37
|
+
│ ├── apps/ # App navigation & page structure
|
|
38
|
+
│ ├── flows/ # Automation flows & workflows
|
|
39
|
+
│ ├── actions/ # Custom actions (buttons, bulk ops)
|
|
40
|
+
│ ├── dashboards/ # BI dashboards
|
|
41
|
+
│ ├── reports/ # Analytics reports
|
|
42
|
+
│ ├── agents/ # AI agent definitions
|
|
43
|
+
│ ├── i18n/ # Translation bundles
|
|
44
|
+
│ └── handlers/ # Runtime hook handlers
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## AI Skills
|
|
48
|
+
|
|
49
|
+
This project uses ObjectStack skills from `objectstack-ai/framework`.
|
|
50
|
+
Install or update skills with the standard [skills CLI](https://skills.sh/):
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npx skills add objectstack-ai/framework
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Skills are triggered automatically based on task context:
|
|
57
|
+
|
|
58
|
+
| Skill | Trigger Context |
|
|
59
|
+
|:------|:----------------|
|
|
60
|
+
| **objectstack-schema** | Define objects, fields, relationships, validations, indexes |
|
|
61
|
+
| **objectstack-query** | Filters, sorting, pagination, aggregation, ObjectQL |
|
|
62
|
+
| **objectstack-ui** | Views, dashboards, apps, reports, actions, navigation |
|
|
63
|
+
| **objectstack-api** | REST endpoints, authentication, service contracts |
|
|
64
|
+
| **objectstack-plugin** | Plugin lifecycle, DI, services, hooks, events |
|
|
65
|
+
| **objectstack-automation** | Flows, workflows, triggers, approvals, state machines |
|
|
66
|
+
| **objectstack-ai** | Agents, tools, skills, RAG pipelines, LLM config |
|
|
67
|
+
| **objectstack-quickstart** | Project setup, defineStack(), driver selection |
|
|
68
|
+
| **objectstack-i18n** | Translation bundles, locale config, coverage detection |
|
|
69
|
+
|
|
70
|
+
## Learn More
|
|
71
|
+
|
|
72
|
+
- [ObjectStack Documentation](https://objectstack.com/docs)
|
|
73
|
+
- [GitHub: objectstack-ai/framework](https://github.com/objectstack-ai/framework)
|
|
74
|
+
- [Skills CLI](https://skills.sh/) — Manage AI skills across agents
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-objectstack",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.5",
|
|
4
4
|
"description": "Create a new ObjectStack project — npx create-objectstack",
|
|
5
5
|
"bin": {
|
|
6
6
|
"create-objectstack": "./bin/create-objectstack.js"
|
|
@@ -19,9 +19,26 @@
|
|
|
19
19
|
"commander": "^14.0.3"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
|
-
"@types/node": "^25.6.
|
|
22
|
+
"@types/node": "^25.6.2",
|
|
23
23
|
"tsup": "^8.5.1",
|
|
24
|
-
"typescript": "^6.0.
|
|
24
|
+
"typescript": "^6.0.3"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/objectstack-ai/framework.git",
|
|
29
|
+
"directory": "packages/create-objectstack"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://objectstack.ai/docs",
|
|
32
|
+
"bugs": "https://github.com/objectstack-ai/framework/issues",
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist",
|
|
38
|
+
"README.md"
|
|
39
|
+
],
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=18.0.0"
|
|
25
42
|
},
|
|
26
43
|
"scripts": {
|
|
27
44
|
"build": "tsup",
|
package/.turbo/turbo-build.log
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
> create-objectstack@4.0.3 build /home/runner/work/framework/framework/packages/create-objectstack
|
|
3
|
-
> tsup
|
|
4
|
-
|
|
5
|
-
[34mCLI[39m Building entry: src/index.ts
|
|
6
|
-
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
7
|
-
[34mCLI[39m tsup v8.5.1
|
|
8
|
-
[34mCLI[39m Using tsup config: /home/runner/work/framework/framework/packages/create-objectstack/tsup.config.ts
|
|
9
|
-
[34mCLI[39m Target: es2022
|
|
10
|
-
[34mCLI[39m Cleaning output folder
|
|
11
|
-
[34mESM[39m Build start
|
|
12
|
-
[32mESM[39m [1mdist/index.js [22m[32m16.79 KB[39m
|
|
13
|
-
[32mESM[39m ⚡️ Build success in 30ms
|
package/CHANGELOG.md
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
# create-objectstack
|
|
2
|
-
|
|
3
|
-
## 4.0.3
|
|
4
|
-
|
|
5
|
-
## 4.0.2
|
|
6
|
-
|
|
7
|
-
## 4.0.0
|
|
8
|
-
|
|
9
|
-
## 3.3.1
|
|
10
|
-
|
|
11
|
-
## 3.3.0
|
|
12
|
-
|
|
13
|
-
## 3.2.9
|
|
14
|
-
|
|
15
|
-
## 3.2.8
|
|
16
|
-
|
|
17
|
-
## 3.2.7
|
|
18
|
-
|
|
19
|
-
## 3.2.6
|
|
20
|
-
|
|
21
|
-
## 3.2.5
|
|
22
|
-
|
|
23
|
-
## 3.2.4
|
|
24
|
-
|
|
25
|
-
## 3.2.3
|
|
26
|
-
|
|
27
|
-
## 3.2.2
|
|
28
|
-
|
|
29
|
-
## 3.2.1
|
|
30
|
-
|
|
31
|
-
## 3.2.0
|
|
32
|
-
|
|
33
|
-
## 3.1.1
|
|
34
|
-
|
|
35
|
-
## 3.1.0
|
|
36
|
-
|
|
37
|
-
## 3.0.11
|
|
38
|
-
|
|
39
|
-
## 3.0.10
|
|
40
|
-
|
|
41
|
-
## 3.0.9
|
|
42
|
-
|
|
43
|
-
## 3.0.8
|
package/src/index.ts
DELETED
|
@@ -1,720 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
import { Command } from 'commander';
|
|
4
|
-
import chalk from 'chalk';
|
|
5
|
-
import fs from 'fs';
|
|
6
|
-
import path from 'path';
|
|
7
|
-
import { execSync } from 'child_process';
|
|
8
|
-
|
|
9
|
-
// ─── Template Registry ──────────────────────────────────────────────
|
|
10
|
-
|
|
11
|
-
type TemplateFiles = Record<string, (name: string) => string>;
|
|
12
|
-
|
|
13
|
-
interface Template {
|
|
14
|
-
description: string;
|
|
15
|
-
files: TemplateFiles;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const TEMPLATES: Record<string, Template> = {
|
|
19
|
-
'minimal-api': {
|
|
20
|
-
description: 'Server + memory driver + 1 object + REST API',
|
|
21
|
-
files: {
|
|
22
|
-
'objectstack.config.ts': (name) => `import { defineStack } from '@objectstack/spec';
|
|
23
|
-
import * as objects from './src/objects';
|
|
24
|
-
|
|
25
|
-
export default defineStack({
|
|
26
|
-
manifest: {
|
|
27
|
-
id: 'com.example.${name}',
|
|
28
|
-
namespace: '${name}',
|
|
29
|
-
version: '0.1.0',
|
|
30
|
-
type: 'app',
|
|
31
|
-
name: '${toTitleCase(name)}',
|
|
32
|
-
description: '${toTitleCase(name)} — built with ObjectStack',
|
|
33
|
-
},
|
|
34
|
-
|
|
35
|
-
objects: Object.values(objects),
|
|
36
|
-
|
|
37
|
-
api: {
|
|
38
|
-
rest: { enabled: true, basePath: '/api' },
|
|
39
|
-
},
|
|
40
|
-
});
|
|
41
|
-
`,
|
|
42
|
-
'package.json': (name) => JSON.stringify({
|
|
43
|
-
name,
|
|
44
|
-
version: '0.1.0',
|
|
45
|
-
private: true,
|
|
46
|
-
type: 'module',
|
|
47
|
-
scripts: {
|
|
48
|
-
dev: 'objectstack dev',
|
|
49
|
-
start: 'objectstack serve',
|
|
50
|
-
build: 'objectstack compile',
|
|
51
|
-
validate: 'objectstack validate',
|
|
52
|
-
typecheck: 'tsc --noEmit',
|
|
53
|
-
},
|
|
54
|
-
dependencies: {
|
|
55
|
-
'@objectstack/spec': '^3.0.0',
|
|
56
|
-
'@objectstack/runtime': '^3.0.0',
|
|
57
|
-
'@objectstack/driver-memory': '^3.0.0',
|
|
58
|
-
'@objectstack/plugin-hono-server': '^3.0.0',
|
|
59
|
-
},
|
|
60
|
-
devDependencies: {
|
|
61
|
-
'@objectstack/cli': '^3.0.0',
|
|
62
|
-
'typescript': '^5.3.0',
|
|
63
|
-
},
|
|
64
|
-
}, null, 2) + '\n',
|
|
65
|
-
'tsconfig.json': () => JSON.stringify({
|
|
66
|
-
compilerOptions: {
|
|
67
|
-
target: 'ES2022',
|
|
68
|
-
module: 'ESNext',
|
|
69
|
-
moduleResolution: 'bundler',
|
|
70
|
-
strict: true,
|
|
71
|
-
esModuleInterop: true,
|
|
72
|
-
skipLibCheck: true,
|
|
73
|
-
outDir: 'dist',
|
|
74
|
-
rootDir: '.',
|
|
75
|
-
declaration: true,
|
|
76
|
-
},
|
|
77
|
-
include: ['*.ts', 'src/**/*'],
|
|
78
|
-
exclude: ['dist', 'node_modules'],
|
|
79
|
-
}, null, 2) + '\n',
|
|
80
|
-
'src/objects/task.ts': () => `import { Data } from '@objectstack/spec';
|
|
81
|
-
|
|
82
|
-
const task: Data.Object = {
|
|
83
|
-
name: 'task',
|
|
84
|
-
label: 'Task',
|
|
85
|
-
ownership: 'own',
|
|
86
|
-
fields: {
|
|
87
|
-
title: {
|
|
88
|
-
type: 'text',
|
|
89
|
-
label: 'Title',
|
|
90
|
-
required: true,
|
|
91
|
-
},
|
|
92
|
-
description: {
|
|
93
|
-
type: 'textarea',
|
|
94
|
-
label: 'Description',
|
|
95
|
-
},
|
|
96
|
-
status: {
|
|
97
|
-
type: 'select',
|
|
98
|
-
label: 'Status',
|
|
99
|
-
options: [
|
|
100
|
-
{ label: 'Open', value: 'open' },
|
|
101
|
-
{ label: 'In Progress', value: 'in_progress' },
|
|
102
|
-
{ label: 'Done', value: 'done' },
|
|
103
|
-
],
|
|
104
|
-
defaultValue: 'open',
|
|
105
|
-
},
|
|
106
|
-
due_date: {
|
|
107
|
-
type: 'date',
|
|
108
|
-
label: 'Due Date',
|
|
109
|
-
},
|
|
110
|
-
},
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
export default task;
|
|
114
|
-
`,
|
|
115
|
-
'src/objects/index.ts': () => `export { default as task } from './task';
|
|
116
|
-
`,
|
|
117
|
-
'.gitignore': () => `node_modules/
|
|
118
|
-
dist/
|
|
119
|
-
*.tsbuildinfo
|
|
120
|
-
`,
|
|
121
|
-
'README.md': (name) => `# ${toTitleCase(name)}
|
|
122
|
-
|
|
123
|
-
Built with [ObjectStack](https://objectstack.com).
|
|
124
|
-
|
|
125
|
-
## Quick Start
|
|
126
|
-
|
|
127
|
-
\`\`\`bash
|
|
128
|
-
# Install dependencies
|
|
129
|
-
npm install
|
|
130
|
-
|
|
131
|
-
# Start development server
|
|
132
|
-
npm run dev
|
|
133
|
-
|
|
134
|
-
# Validate configuration
|
|
135
|
-
npm run validate
|
|
136
|
-
\`\`\`
|
|
137
|
-
|
|
138
|
-
## Project Structure
|
|
139
|
-
|
|
140
|
-
- \`objectstack.config.ts\` — Stack definition (objects, API, settings)
|
|
141
|
-
- \`src/objects/\` — Object definitions
|
|
142
|
-
- \`dist/\` — Compiled output
|
|
143
|
-
|
|
144
|
-
## Learn More
|
|
145
|
-
|
|
146
|
-
- [ObjectStack Documentation](https://objectstack.com/docs)
|
|
147
|
-
`,
|
|
148
|
-
},
|
|
149
|
-
},
|
|
150
|
-
|
|
151
|
-
'full-stack': {
|
|
152
|
-
description: 'Server + UI + auth + 3 CRM objects',
|
|
153
|
-
files: {
|
|
154
|
-
'objectstack.config.ts': (name) => `import { defineStack } from '@objectstack/spec';
|
|
155
|
-
import * as objects from './src/objects';
|
|
156
|
-
import * as apps from './src/apps';
|
|
157
|
-
|
|
158
|
-
export default defineStack({
|
|
159
|
-
manifest: {
|
|
160
|
-
id: 'com.example.${name}',
|
|
161
|
-
namespace: '${name}',
|
|
162
|
-
version: '0.1.0',
|
|
163
|
-
type: 'app',
|
|
164
|
-
name: '${toTitleCase(name)}',
|
|
165
|
-
description: '${toTitleCase(name)} CRM — built with ObjectStack',
|
|
166
|
-
},
|
|
167
|
-
|
|
168
|
-
objects: Object.values(objects),
|
|
169
|
-
apps: Object.values(apps),
|
|
170
|
-
|
|
171
|
-
api: {
|
|
172
|
-
rest: { enabled: true, basePath: '/api' },
|
|
173
|
-
},
|
|
174
|
-
});
|
|
175
|
-
`,
|
|
176
|
-
'package.json': (name) => JSON.stringify({
|
|
177
|
-
name,
|
|
178
|
-
version: '0.1.0',
|
|
179
|
-
private: true,
|
|
180
|
-
type: 'module',
|
|
181
|
-
scripts: {
|
|
182
|
-
dev: 'objectstack dev',
|
|
183
|
-
start: 'objectstack serve',
|
|
184
|
-
build: 'objectstack compile',
|
|
185
|
-
validate: 'objectstack validate',
|
|
186
|
-
typecheck: 'tsc --noEmit',
|
|
187
|
-
},
|
|
188
|
-
dependencies: {
|
|
189
|
-
'@objectstack/spec': '^3.0.0',
|
|
190
|
-
'@objectstack/runtime': '^3.0.0',
|
|
191
|
-
'@objectstack/driver-memory': '^3.0.0',
|
|
192
|
-
'@objectstack/plugin-hono-server': '^3.0.0',
|
|
193
|
-
'@objectstack/plugin-auth': '^3.0.0',
|
|
194
|
-
},
|
|
195
|
-
devDependencies: {
|
|
196
|
-
'@objectstack/cli': '^3.0.0',
|
|
197
|
-
'typescript': '^5.3.0',
|
|
198
|
-
},
|
|
199
|
-
}, null, 2) + '\n',
|
|
200
|
-
'tsconfig.json': () => JSON.stringify({
|
|
201
|
-
compilerOptions: {
|
|
202
|
-
target: 'ES2022',
|
|
203
|
-
module: 'ESNext',
|
|
204
|
-
moduleResolution: 'bundler',
|
|
205
|
-
strict: true,
|
|
206
|
-
esModuleInterop: true,
|
|
207
|
-
skipLibCheck: true,
|
|
208
|
-
outDir: 'dist',
|
|
209
|
-
rootDir: '.',
|
|
210
|
-
declaration: true,
|
|
211
|
-
},
|
|
212
|
-
include: ['*.ts', 'src/**/*'],
|
|
213
|
-
exclude: ['dist', 'node_modules'],
|
|
214
|
-
}, null, 2) + '\n',
|
|
215
|
-
'src/objects/contact.ts': () => `import { Data } from '@objectstack/spec';
|
|
216
|
-
|
|
217
|
-
const contact: Data.Object = {
|
|
218
|
-
name: 'contact',
|
|
219
|
-
label: 'Contact',
|
|
220
|
-
ownership: 'own',
|
|
221
|
-
fields: {
|
|
222
|
-
first_name: {
|
|
223
|
-
type: 'text',
|
|
224
|
-
label: 'First Name',
|
|
225
|
-
required: true,
|
|
226
|
-
},
|
|
227
|
-
last_name: {
|
|
228
|
-
type: 'text',
|
|
229
|
-
label: 'Last Name',
|
|
230
|
-
required: true,
|
|
231
|
-
},
|
|
232
|
-
email: {
|
|
233
|
-
type: 'text',
|
|
234
|
-
label: 'Email',
|
|
235
|
-
},
|
|
236
|
-
phone: {
|
|
237
|
-
type: 'text',
|
|
238
|
-
label: 'Phone',
|
|
239
|
-
},
|
|
240
|
-
company: {
|
|
241
|
-
type: 'lookup',
|
|
242
|
-
label: 'Company',
|
|
243
|
-
reference: 'company',
|
|
244
|
-
},
|
|
245
|
-
},
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
export default contact;
|
|
249
|
-
`,
|
|
250
|
-
'src/objects/company.ts': () => `import { Data } from '@objectstack/spec';
|
|
251
|
-
|
|
252
|
-
const company: Data.Object = {
|
|
253
|
-
name: 'company',
|
|
254
|
-
label: 'Company',
|
|
255
|
-
ownership: 'own',
|
|
256
|
-
fields: {
|
|
257
|
-
name: {
|
|
258
|
-
type: 'text',
|
|
259
|
-
label: 'Company Name',
|
|
260
|
-
required: true,
|
|
261
|
-
},
|
|
262
|
-
website: {
|
|
263
|
-
type: 'text',
|
|
264
|
-
label: 'Website',
|
|
265
|
-
},
|
|
266
|
-
industry: {
|
|
267
|
-
type: 'select',
|
|
268
|
-
label: 'Industry',
|
|
269
|
-
options: [
|
|
270
|
-
{ label: 'Technology', value: 'technology' },
|
|
271
|
-
{ label: 'Finance', value: 'finance' },
|
|
272
|
-
{ label: 'Healthcare', value: 'healthcare' },
|
|
273
|
-
{ label: 'Other', value: 'other' },
|
|
274
|
-
],
|
|
275
|
-
},
|
|
276
|
-
},
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
export default company;
|
|
280
|
-
`,
|
|
281
|
-
'src/objects/deal.ts': () => `import { Data } from '@objectstack/spec';
|
|
282
|
-
|
|
283
|
-
const deal: Data.Object = {
|
|
284
|
-
name: 'deal',
|
|
285
|
-
label: 'Deal',
|
|
286
|
-
ownership: 'own',
|
|
287
|
-
fields: {
|
|
288
|
-
name: {
|
|
289
|
-
type: 'text',
|
|
290
|
-
label: 'Deal Name',
|
|
291
|
-
required: true,
|
|
292
|
-
},
|
|
293
|
-
amount: {
|
|
294
|
-
type: 'number',
|
|
295
|
-
label: 'Amount',
|
|
296
|
-
},
|
|
297
|
-
stage: {
|
|
298
|
-
type: 'select',
|
|
299
|
-
label: 'Stage',
|
|
300
|
-
options: [
|
|
301
|
-
{ label: 'Prospecting', value: 'prospecting' },
|
|
302
|
-
{ label: 'Qualification', value: 'qualification' },
|
|
303
|
-
{ label: 'Proposal', value: 'proposal' },
|
|
304
|
-
{ label: 'Closed Won', value: 'closed_won' },
|
|
305
|
-
{ label: 'Closed Lost', value: 'closed_lost' },
|
|
306
|
-
],
|
|
307
|
-
defaultValue: 'prospecting',
|
|
308
|
-
},
|
|
309
|
-
contact: {
|
|
310
|
-
type: 'lookup',
|
|
311
|
-
label: 'Contact',
|
|
312
|
-
reference: 'contact',
|
|
313
|
-
},
|
|
314
|
-
company: {
|
|
315
|
-
type: 'lookup',
|
|
316
|
-
label: 'Company',
|
|
317
|
-
reference: 'company',
|
|
318
|
-
},
|
|
319
|
-
close_date: {
|
|
320
|
-
type: 'date',
|
|
321
|
-
label: 'Close Date',
|
|
322
|
-
},
|
|
323
|
-
},
|
|
324
|
-
};
|
|
325
|
-
|
|
326
|
-
export default deal;
|
|
327
|
-
`,
|
|
328
|
-
'src/objects/index.ts': () => `export { default as contact } from './contact';
|
|
329
|
-
export { default as company } from './company';
|
|
330
|
-
export { default as deal } from './deal';
|
|
331
|
-
`,
|
|
332
|
-
'src/views/contact_list.ts': () => `import { UI } from '@objectstack/spec';
|
|
333
|
-
|
|
334
|
-
const contactList: UI.View = {
|
|
335
|
-
name: 'contact_list',
|
|
336
|
-
label: 'All Contacts',
|
|
337
|
-
object: 'contact',
|
|
338
|
-
type: 'list',
|
|
339
|
-
columns: ['first_name', 'last_name', 'email', 'phone', 'company'],
|
|
340
|
-
};
|
|
341
|
-
|
|
342
|
-
export default contactList;
|
|
343
|
-
`,
|
|
344
|
-
'src/views/company_list.ts': () => `import { UI } from '@objectstack/spec';
|
|
345
|
-
|
|
346
|
-
const companyList: UI.View = {
|
|
347
|
-
name: 'company_list',
|
|
348
|
-
label: 'All Companies',
|
|
349
|
-
object: 'company',
|
|
350
|
-
type: 'list',
|
|
351
|
-
columns: ['name', 'website', 'industry'],
|
|
352
|
-
};
|
|
353
|
-
|
|
354
|
-
export default companyList;
|
|
355
|
-
`,
|
|
356
|
-
'src/views/deal_list.ts': () => `import { UI } from '@objectstack/spec';
|
|
357
|
-
|
|
358
|
-
const dealList: UI.View = {
|
|
359
|
-
name: 'deal_list',
|
|
360
|
-
label: 'All Deals',
|
|
361
|
-
object: 'deal',
|
|
362
|
-
type: 'list',
|
|
363
|
-
columns: ['name', 'amount', 'stage', 'contact', 'close_date'],
|
|
364
|
-
};
|
|
365
|
-
|
|
366
|
-
export default dealList;
|
|
367
|
-
`,
|
|
368
|
-
'src/apps/crm.ts': () => `import { UI } from '@objectstack/spec';
|
|
369
|
-
|
|
370
|
-
const crm: UI.App = {
|
|
371
|
-
name: 'crm',
|
|
372
|
-
label: 'CRM',
|
|
373
|
-
description: 'Customer Relationship Management',
|
|
374
|
-
navigation: [
|
|
375
|
-
{ type: 'object', object: 'contact', label: 'Contacts' },
|
|
376
|
-
{ type: 'object', object: 'company', label: 'Companies' },
|
|
377
|
-
{ type: 'object', object: 'deal', label: 'Deals' },
|
|
378
|
-
],
|
|
379
|
-
};
|
|
380
|
-
|
|
381
|
-
export default crm;
|
|
382
|
-
`,
|
|
383
|
-
'src/apps/index.ts': () => `export { default as crm } from './crm';
|
|
384
|
-
`,
|
|
385
|
-
'.gitignore': () => `node_modules/
|
|
386
|
-
dist/
|
|
387
|
-
*.tsbuildinfo
|
|
388
|
-
`,
|
|
389
|
-
'README.md': (name) => `# ${toTitleCase(name)}
|
|
390
|
-
|
|
391
|
-
A full-stack CRM application built with [ObjectStack](https://objectstack.com).
|
|
392
|
-
|
|
393
|
-
## Quick Start
|
|
394
|
-
|
|
395
|
-
\`\`\`bash
|
|
396
|
-
npm install
|
|
397
|
-
npm run dev
|
|
398
|
-
\`\`\`
|
|
399
|
-
|
|
400
|
-
## Project Structure
|
|
401
|
-
|
|
402
|
-
- \`objectstack.config.ts\` — Stack definition
|
|
403
|
-
- \`src/objects/\` — Data objects (Contact, Company, Deal)
|
|
404
|
-
- \`src/views/\` — List views
|
|
405
|
-
- \`src/apps/crm.ts\` — CRM app with navigation
|
|
406
|
-
|
|
407
|
-
## Learn More
|
|
408
|
-
|
|
409
|
-
- [ObjectStack Documentation](https://objectstack.com/docs)
|
|
410
|
-
`,
|
|
411
|
-
},
|
|
412
|
-
},
|
|
413
|
-
|
|
414
|
-
plugin: {
|
|
415
|
-
description: 'Plugin skeleton with test setup',
|
|
416
|
-
files: {
|
|
417
|
-
'objectstack.config.ts': (name) => `import { defineStack } from '@objectstack/spec';
|
|
418
|
-
import * as objects from './src/objects';
|
|
419
|
-
|
|
420
|
-
export default defineStack({
|
|
421
|
-
manifest: {
|
|
422
|
-
id: 'com.objectstack.plugin-${name}',
|
|
423
|
-
namespace: 'plugin_${name}',
|
|
424
|
-
version: '0.1.0',
|
|
425
|
-
type: 'plugin',
|
|
426
|
-
name: '${toTitleCase(name)} Plugin',
|
|
427
|
-
description: 'ObjectStack Plugin: ${toTitleCase(name)}',
|
|
428
|
-
},
|
|
429
|
-
|
|
430
|
-
objects: Object.values(objects),
|
|
431
|
-
});
|
|
432
|
-
`,
|
|
433
|
-
'package.json': (name) => JSON.stringify({
|
|
434
|
-
name: `@objectstack/plugin-${name}`,
|
|
435
|
-
version: '0.1.0',
|
|
436
|
-
description: `ObjectStack Plugin: ${toTitleCase(name)}`,
|
|
437
|
-
main: 'dist/index.js',
|
|
438
|
-
types: 'dist/index.d.ts',
|
|
439
|
-
type: 'module',
|
|
440
|
-
scripts: {
|
|
441
|
-
build: 'tsc',
|
|
442
|
-
dev: 'tsc --watch',
|
|
443
|
-
test: 'vitest run',
|
|
444
|
-
validate: 'objectstack validate',
|
|
445
|
-
typecheck: 'tsc --noEmit',
|
|
446
|
-
},
|
|
447
|
-
keywords: ['objectstack', 'plugin', name],
|
|
448
|
-
author: '',
|
|
449
|
-
license: 'MIT',
|
|
450
|
-
dependencies: {
|
|
451
|
-
'@objectstack/spec': '^3.0.0',
|
|
452
|
-
},
|
|
453
|
-
devDependencies: {
|
|
454
|
-
'@types/node': '^22.0.0',
|
|
455
|
-
'typescript': '^5.3.0',
|
|
456
|
-
'vitest': '^4.0.0',
|
|
457
|
-
},
|
|
458
|
-
}, null, 2) + '\n',
|
|
459
|
-
'tsconfig.json': () => JSON.stringify({
|
|
460
|
-
compilerOptions: {
|
|
461
|
-
target: 'ES2022',
|
|
462
|
-
module: 'ESNext',
|
|
463
|
-
moduleResolution: 'bundler',
|
|
464
|
-
strict: true,
|
|
465
|
-
esModuleInterop: true,
|
|
466
|
-
skipLibCheck: true,
|
|
467
|
-
outDir: 'dist',
|
|
468
|
-
rootDir: '.',
|
|
469
|
-
declaration: true,
|
|
470
|
-
},
|
|
471
|
-
include: ['*.ts', 'src/**/*'],
|
|
472
|
-
exclude: ['dist', 'node_modules'],
|
|
473
|
-
}, null, 2) + '\n',
|
|
474
|
-
'src/index.ts': (name) => `/**
|
|
475
|
-
* ${toTitleCase(name)} Plugin for ObjectStack
|
|
476
|
-
*
|
|
477
|
-
* Entry point — re-exports all plugin metadata.
|
|
478
|
-
*/
|
|
479
|
-
export * as objects from './objects';
|
|
480
|
-
`,
|
|
481
|
-
'src/objects/sample.ts': (name) => `import { Data } from '@objectstack/spec';
|
|
482
|
-
|
|
483
|
-
const sample: Data.Object = {
|
|
484
|
-
name: '${name}_sample',
|
|
485
|
-
label: '${toTitleCase(name)} Sample',
|
|
486
|
-
ownership: 'own',
|
|
487
|
-
fields: {
|
|
488
|
-
name: {
|
|
489
|
-
type: 'text',
|
|
490
|
-
label: 'Name',
|
|
491
|
-
required: true,
|
|
492
|
-
},
|
|
493
|
-
},
|
|
494
|
-
};
|
|
495
|
-
|
|
496
|
-
export default sample;
|
|
497
|
-
`,
|
|
498
|
-
'src/objects/index.ts': () => `export { default as sample } from './sample';
|
|
499
|
-
`,
|
|
500
|
-
'test/sample.test.ts': (name) => `import { describe, it, expect } from 'vitest';
|
|
501
|
-
import sample from '../src/objects/sample';
|
|
502
|
-
|
|
503
|
-
describe('${name} plugin', () => {
|
|
504
|
-
it('should export a valid sample object', () => {
|
|
505
|
-
expect(sample).toBeDefined();
|
|
506
|
-
expect(sample.name).toBe('${name}_sample');
|
|
507
|
-
expect(sample.fields).toHaveProperty('name');
|
|
508
|
-
});
|
|
509
|
-
});
|
|
510
|
-
`,
|
|
511
|
-
'.gitignore': () => `node_modules/
|
|
512
|
-
dist/
|
|
513
|
-
*.tsbuildinfo
|
|
514
|
-
`,
|
|
515
|
-
'README.md': (name) => `# @objectstack/plugin-${name}
|
|
516
|
-
|
|
517
|
-
ObjectStack Plugin: ${toTitleCase(name)}
|
|
518
|
-
|
|
519
|
-
## Installation
|
|
520
|
-
|
|
521
|
-
\`\`\`bash
|
|
522
|
-
npm install @objectstack/plugin-${name}
|
|
523
|
-
\`\`\`
|
|
524
|
-
|
|
525
|
-
## Usage
|
|
526
|
-
|
|
527
|
-
\`\`\`typescript
|
|
528
|
-
import { defineStack } from '@objectstack/spec';
|
|
529
|
-
|
|
530
|
-
export default defineStack({
|
|
531
|
-
plugins: [
|
|
532
|
-
'@objectstack/plugin-${name}',
|
|
533
|
-
],
|
|
534
|
-
});
|
|
535
|
-
\`\`\`
|
|
536
|
-
|
|
537
|
-
## Development
|
|
538
|
-
|
|
539
|
-
\`\`\`bash
|
|
540
|
-
# Run tests
|
|
541
|
-
npm test
|
|
542
|
-
|
|
543
|
-
# Build
|
|
544
|
-
npm run build
|
|
545
|
-
|
|
546
|
-
# Validate metadata
|
|
547
|
-
npm run validate
|
|
548
|
-
\`\`\`
|
|
549
|
-
|
|
550
|
-
## License
|
|
551
|
-
|
|
552
|
-
MIT
|
|
553
|
-
`,
|
|
554
|
-
},
|
|
555
|
-
},
|
|
556
|
-
};
|
|
557
|
-
|
|
558
|
-
// ─── Helpers ────────────────────────────────────────────────────────
|
|
559
|
-
|
|
560
|
-
function toCamelCase(str: string): string {
|
|
561
|
-
return str.replace(/[-_]([a-z])/g, (_, c) => c.toUpperCase());
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
function toTitleCase(str: string): string {
|
|
565
|
-
return str.replace(/[-_]/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
// ─── Formatting (matches @objectstack/cli style) ────────────────────
|
|
569
|
-
|
|
570
|
-
function printHeader(title: string) {
|
|
571
|
-
console.log(chalk.bold(`\n◆ ${title}`));
|
|
572
|
-
console.log(chalk.dim('─'.repeat(40)));
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
function printKV(key: string, value: string) {
|
|
576
|
-
console.log(` ${chalk.dim(key + ':')} ${chalk.white(value)}`);
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
function printSuccess(msg: string) {
|
|
580
|
-
console.log(chalk.green(` ✓ ${msg}`));
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
function printError(msg: string) {
|
|
584
|
-
console.log(chalk.red(` ✗ ${msg}`));
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
function printStep(msg: string) {
|
|
588
|
-
console.log(chalk.yellow(` → ${msg}`));
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
function printWarning(msg: string) {
|
|
592
|
-
console.log(chalk.yellow(` ⚠ ${msg}`));
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
// ─── CLI Program ────────────────────────────────────────────────────
|
|
596
|
-
|
|
597
|
-
const program = new Command()
|
|
598
|
-
.name('create-objectstack')
|
|
599
|
-
.description('Create a new ObjectStack project')
|
|
600
|
-
.version('3.0.0')
|
|
601
|
-
.argument('[name]', 'Project name (defaults to current directory name)')
|
|
602
|
-
.option(
|
|
603
|
-
'-t, --template <template>',
|
|
604
|
-
'Project template: minimal-api, full-stack, plugin',
|
|
605
|
-
'minimal-api',
|
|
606
|
-
)
|
|
607
|
-
.option('--skip-install', 'Skip dependency installation')
|
|
608
|
-
.action(async (name: string | undefined, options: { template: string; skipInstall?: boolean }) => {
|
|
609
|
-
// Banner
|
|
610
|
-
console.log('');
|
|
611
|
-
console.log(chalk.bold.cyan(' ╔═══════════════════════════════════╗'));
|
|
612
|
-
console.log(chalk.bold.cyan(' ║') + chalk.bold(' ◆ Create ObjectStack ') + chalk.dim('v3.0') + chalk.bold.cyan(' ║'));
|
|
613
|
-
console.log(chalk.bold.cyan(' ╚═══════════════════════════════════╝'));
|
|
614
|
-
|
|
615
|
-
printHeader('New Project');
|
|
616
|
-
|
|
617
|
-
// Resolve template
|
|
618
|
-
const template = TEMPLATES[options.template];
|
|
619
|
-
if (!template) {
|
|
620
|
-
printError(`Unknown template: ${options.template}`);
|
|
621
|
-
console.log(chalk.dim(` Available: ${Object.keys(TEMPLATES).join(', ')}`));
|
|
622
|
-
process.exit(1);
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
// Resolve project name and directory
|
|
626
|
-
const cwd = process.cwd();
|
|
627
|
-
const projectName = name || path.basename(cwd);
|
|
628
|
-
const targetDir = name ? path.resolve(cwd, name) : cwd;
|
|
629
|
-
const isCurrentDir = targetDir === cwd;
|
|
630
|
-
|
|
631
|
-
printKV('Project', projectName);
|
|
632
|
-
printKV('Template', `${options.template} — ${template.description}`);
|
|
633
|
-
printKV('Directory', targetDir);
|
|
634
|
-
console.log('');
|
|
635
|
-
|
|
636
|
-
// Guard: if creating in a sub-directory, check it doesn't already exist
|
|
637
|
-
if (!isCurrentDir && fs.existsSync(targetDir)) {
|
|
638
|
-
const existing = fs.readdirSync(targetDir);
|
|
639
|
-
if (existing.length > 0) {
|
|
640
|
-
printError(`Directory already exists and is not empty: ${targetDir}`);
|
|
641
|
-
process.exit(1);
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
const createdFiles: string[] = [];
|
|
646
|
-
|
|
647
|
-
try {
|
|
648
|
-
// Ensure target directory exists
|
|
649
|
-
if (!fs.existsSync(targetDir)) {
|
|
650
|
-
fs.mkdirSync(targetDir, { recursive: true });
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
// Write every file defined by the template
|
|
654
|
-
for (const [filePath, contentFn] of Object.entries(template.files)) {
|
|
655
|
-
const fullPath = path.join(targetDir, filePath);
|
|
656
|
-
const dir = path.dirname(fullPath);
|
|
657
|
-
|
|
658
|
-
if (!fs.existsSync(dir)) {
|
|
659
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
fs.writeFileSync(fullPath, contentFn(projectName));
|
|
663
|
-
createdFiles.push(filePath);
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
// Summary
|
|
667
|
-
console.log(chalk.bold(' Created files:'));
|
|
668
|
-
for (const f of createdFiles) {
|
|
669
|
-
console.log(chalk.green(` + ${f}`));
|
|
670
|
-
}
|
|
671
|
-
console.log('');
|
|
672
|
-
|
|
673
|
-
// Install dependencies
|
|
674
|
-
if (!options.skipInstall) {
|
|
675
|
-
printStep('Installing dependencies...');
|
|
676
|
-
try {
|
|
677
|
-
// Detect package manager — prefer pnpm, fall back to npm
|
|
678
|
-
const pm = detectPackageManager();
|
|
679
|
-
execSync(`${pm} install`, { stdio: 'inherit', cwd: targetDir });
|
|
680
|
-
console.log('');
|
|
681
|
-
} catch {
|
|
682
|
-
printWarning('Dependency installation failed. Run `npm install` manually.');
|
|
683
|
-
console.log('');
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
printSuccess('Project created!');
|
|
688
|
-
console.log('');
|
|
689
|
-
|
|
690
|
-
// Next steps
|
|
691
|
-
console.log(chalk.bold(' Next steps:'));
|
|
692
|
-
if (!isCurrentDir) {
|
|
693
|
-
console.log(chalk.dim(` cd ${name}`));
|
|
694
|
-
}
|
|
695
|
-
if (options.skipInstall) {
|
|
696
|
-
console.log(chalk.dim(' npm install'));
|
|
697
|
-
}
|
|
698
|
-
console.log(chalk.dim(' npm run dev # Start development server'));
|
|
699
|
-
console.log(chalk.dim(' npm run validate # Check configuration'));
|
|
700
|
-
console.log('');
|
|
701
|
-
|
|
702
|
-
} catch (error: any) {
|
|
703
|
-
printError(error.message || String(error));
|
|
704
|
-
process.exit(1);
|
|
705
|
-
}
|
|
706
|
-
});
|
|
707
|
-
|
|
708
|
-
/**
|
|
709
|
-
* Detect available package manager (pnpm > npm).
|
|
710
|
-
*/
|
|
711
|
-
function detectPackageManager(): string {
|
|
712
|
-
try {
|
|
713
|
-
execSync('pnpm --version', { stdio: 'ignore' });
|
|
714
|
-
return 'pnpm';
|
|
715
|
-
} catch {
|
|
716
|
-
return 'npm';
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
program.parse();
|
package/tsconfig.json
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "NodeNext",
|
|
5
|
-
"moduleResolution": "NodeNext",
|
|
6
|
-
"lib": [
|
|
7
|
-
"ES2022"
|
|
8
|
-
],
|
|
9
|
-
"strict": true,
|
|
10
|
-
"esModuleInterop": true,
|
|
11
|
-
"skipLibCheck": true,
|
|
12
|
-
"forceConsistentCasingInFileNames": true,
|
|
13
|
-
"outDir": "dist",
|
|
14
|
-
"rootDir": "src",
|
|
15
|
-
"types": [
|
|
16
|
-
"node"
|
|
17
|
-
],
|
|
18
|
-
"ignoreDeprecations": "6.0"
|
|
19
|
-
},
|
|
20
|
-
"include": [
|
|
21
|
-
"src"
|
|
22
|
-
]
|
|
23
|
-
}
|
package/tsup.config.ts
DELETED