create-objectstack 3.0.0
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/.turbo/turbo-build.log +13 -0
- package/LICENSE +202 -0
- package/README.md +100 -0
- package/bin/create-objectstack.js +2 -0
- package/dist/index.js +653 -0
- package/package.json +30 -0
- package/src/index.ts +720 -0
- package/tsconfig.json +15 -0
- package/tsup.config.ts +10 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,653 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { execSync } from "child_process";
|
|
7
|
+
var TEMPLATES = {
|
|
8
|
+
"minimal-api": {
|
|
9
|
+
description: "Server + memory driver + 1 object + REST API",
|
|
10
|
+
files: {
|
|
11
|
+
"objectstack.config.ts": (name) => `import { defineStack } from '@objectstack/spec';
|
|
12
|
+
import * as objects from './src/objects';
|
|
13
|
+
|
|
14
|
+
export default defineStack({
|
|
15
|
+
manifest: {
|
|
16
|
+
id: 'com.example.${name}',
|
|
17
|
+
namespace: '${name}',
|
|
18
|
+
version: '0.1.0',
|
|
19
|
+
type: 'app',
|
|
20
|
+
name: '${toTitleCase(name)}',
|
|
21
|
+
description: '${toTitleCase(name)} \u2014 built with ObjectStack',
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
objects: Object.values(objects),
|
|
25
|
+
|
|
26
|
+
api: {
|
|
27
|
+
rest: { enabled: true, basePath: '/api' },
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
`,
|
|
31
|
+
"package.json": (name) => JSON.stringify({
|
|
32
|
+
name,
|
|
33
|
+
version: "0.1.0",
|
|
34
|
+
private: true,
|
|
35
|
+
type: "module",
|
|
36
|
+
scripts: {
|
|
37
|
+
dev: "objectstack dev",
|
|
38
|
+
start: "objectstack serve",
|
|
39
|
+
build: "objectstack compile",
|
|
40
|
+
validate: "objectstack validate",
|
|
41
|
+
typecheck: "tsc --noEmit"
|
|
42
|
+
},
|
|
43
|
+
dependencies: {
|
|
44
|
+
"@objectstack/spec": "^3.0.0",
|
|
45
|
+
"@objectstack/runtime": "^3.0.0",
|
|
46
|
+
"@objectstack/driver-memory": "^3.0.0",
|
|
47
|
+
"@objectstack/plugin-hono-server": "^3.0.0"
|
|
48
|
+
},
|
|
49
|
+
devDependencies: {
|
|
50
|
+
"@objectstack/cli": "^3.0.0",
|
|
51
|
+
"typescript": "^5.3.0"
|
|
52
|
+
}
|
|
53
|
+
}, null, 2) + "\n",
|
|
54
|
+
"tsconfig.json": () => JSON.stringify({
|
|
55
|
+
compilerOptions: {
|
|
56
|
+
target: "ES2022",
|
|
57
|
+
module: "ESNext",
|
|
58
|
+
moduleResolution: "bundler",
|
|
59
|
+
strict: true,
|
|
60
|
+
esModuleInterop: true,
|
|
61
|
+
skipLibCheck: true,
|
|
62
|
+
outDir: "dist",
|
|
63
|
+
rootDir: ".",
|
|
64
|
+
declaration: true
|
|
65
|
+
},
|
|
66
|
+
include: ["*.ts", "src/**/*"],
|
|
67
|
+
exclude: ["dist", "node_modules"]
|
|
68
|
+
}, null, 2) + "\n",
|
|
69
|
+
"src/objects/task.ts": () => `import { Data } from '@objectstack/spec';
|
|
70
|
+
|
|
71
|
+
const task: Data.Object = {
|
|
72
|
+
name: 'task',
|
|
73
|
+
label: 'Task',
|
|
74
|
+
ownership: 'own',
|
|
75
|
+
fields: {
|
|
76
|
+
title: {
|
|
77
|
+
type: 'text',
|
|
78
|
+
label: 'Title',
|
|
79
|
+
required: true,
|
|
80
|
+
},
|
|
81
|
+
description: {
|
|
82
|
+
type: 'textarea',
|
|
83
|
+
label: 'Description',
|
|
84
|
+
},
|
|
85
|
+
status: {
|
|
86
|
+
type: 'select',
|
|
87
|
+
label: 'Status',
|
|
88
|
+
options: [
|
|
89
|
+
{ label: 'Open', value: 'open' },
|
|
90
|
+
{ label: 'In Progress', value: 'in_progress' },
|
|
91
|
+
{ label: 'Done', value: 'done' },
|
|
92
|
+
],
|
|
93
|
+
defaultValue: 'open',
|
|
94
|
+
},
|
|
95
|
+
due_date: {
|
|
96
|
+
type: 'date',
|
|
97
|
+
label: 'Due Date',
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export default task;
|
|
103
|
+
`,
|
|
104
|
+
"src/objects/index.ts": () => `export { default as task } from './task';
|
|
105
|
+
`,
|
|
106
|
+
".gitignore": () => `node_modules/
|
|
107
|
+
dist/
|
|
108
|
+
*.tsbuildinfo
|
|
109
|
+
`,
|
|
110
|
+
"README.md": (name) => `# ${toTitleCase(name)}
|
|
111
|
+
|
|
112
|
+
Built with [ObjectStack](https://objectstack.com).
|
|
113
|
+
|
|
114
|
+
## Quick Start
|
|
115
|
+
|
|
116
|
+
\`\`\`bash
|
|
117
|
+
# Install dependencies
|
|
118
|
+
npm install
|
|
119
|
+
|
|
120
|
+
# Start development server
|
|
121
|
+
npm run dev
|
|
122
|
+
|
|
123
|
+
# Validate configuration
|
|
124
|
+
npm run validate
|
|
125
|
+
\`\`\`
|
|
126
|
+
|
|
127
|
+
## Project Structure
|
|
128
|
+
|
|
129
|
+
- \`objectstack.config.ts\` \u2014 Stack definition (objects, API, settings)
|
|
130
|
+
- \`src/objects/\` \u2014 Object definitions
|
|
131
|
+
- \`dist/\` \u2014 Compiled output
|
|
132
|
+
|
|
133
|
+
## Learn More
|
|
134
|
+
|
|
135
|
+
- [ObjectStack Documentation](https://objectstack.com/docs)
|
|
136
|
+
`
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
"full-stack": {
|
|
140
|
+
description: "Server + UI + auth + 3 CRM objects",
|
|
141
|
+
files: {
|
|
142
|
+
"objectstack.config.ts": (name) => `import { defineStack } from '@objectstack/spec';
|
|
143
|
+
import * as objects from './src/objects';
|
|
144
|
+
import * as apps from './src/apps';
|
|
145
|
+
|
|
146
|
+
export default defineStack({
|
|
147
|
+
manifest: {
|
|
148
|
+
id: 'com.example.${name}',
|
|
149
|
+
namespace: '${name}',
|
|
150
|
+
version: '0.1.0',
|
|
151
|
+
type: 'app',
|
|
152
|
+
name: '${toTitleCase(name)}',
|
|
153
|
+
description: '${toTitleCase(name)} CRM \u2014 built with ObjectStack',
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
objects: Object.values(objects),
|
|
157
|
+
apps: Object.values(apps),
|
|
158
|
+
|
|
159
|
+
api: {
|
|
160
|
+
rest: { enabled: true, basePath: '/api' },
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
`,
|
|
164
|
+
"package.json": (name) => JSON.stringify({
|
|
165
|
+
name,
|
|
166
|
+
version: "0.1.0",
|
|
167
|
+
private: true,
|
|
168
|
+
type: "module",
|
|
169
|
+
scripts: {
|
|
170
|
+
dev: "objectstack dev",
|
|
171
|
+
start: "objectstack serve",
|
|
172
|
+
build: "objectstack compile",
|
|
173
|
+
validate: "objectstack validate",
|
|
174
|
+
typecheck: "tsc --noEmit"
|
|
175
|
+
},
|
|
176
|
+
dependencies: {
|
|
177
|
+
"@objectstack/spec": "^3.0.0",
|
|
178
|
+
"@objectstack/runtime": "^3.0.0",
|
|
179
|
+
"@objectstack/driver-memory": "^3.0.0",
|
|
180
|
+
"@objectstack/plugin-hono-server": "^3.0.0",
|
|
181
|
+
"@objectstack/plugin-auth": "^3.0.0"
|
|
182
|
+
},
|
|
183
|
+
devDependencies: {
|
|
184
|
+
"@objectstack/cli": "^3.0.0",
|
|
185
|
+
"typescript": "^5.3.0"
|
|
186
|
+
}
|
|
187
|
+
}, null, 2) + "\n",
|
|
188
|
+
"tsconfig.json": () => JSON.stringify({
|
|
189
|
+
compilerOptions: {
|
|
190
|
+
target: "ES2022",
|
|
191
|
+
module: "ESNext",
|
|
192
|
+
moduleResolution: "bundler",
|
|
193
|
+
strict: true,
|
|
194
|
+
esModuleInterop: true,
|
|
195
|
+
skipLibCheck: true,
|
|
196
|
+
outDir: "dist",
|
|
197
|
+
rootDir: ".",
|
|
198
|
+
declaration: true
|
|
199
|
+
},
|
|
200
|
+
include: ["*.ts", "src/**/*"],
|
|
201
|
+
exclude: ["dist", "node_modules"]
|
|
202
|
+
}, null, 2) + "\n",
|
|
203
|
+
"src/objects/contact.ts": () => `import { Data } from '@objectstack/spec';
|
|
204
|
+
|
|
205
|
+
const contact: Data.Object = {
|
|
206
|
+
name: 'contact',
|
|
207
|
+
label: 'Contact',
|
|
208
|
+
ownership: 'own',
|
|
209
|
+
fields: {
|
|
210
|
+
first_name: {
|
|
211
|
+
type: 'text',
|
|
212
|
+
label: 'First Name',
|
|
213
|
+
required: true,
|
|
214
|
+
},
|
|
215
|
+
last_name: {
|
|
216
|
+
type: 'text',
|
|
217
|
+
label: 'Last Name',
|
|
218
|
+
required: true,
|
|
219
|
+
},
|
|
220
|
+
email: {
|
|
221
|
+
type: 'text',
|
|
222
|
+
label: 'Email',
|
|
223
|
+
},
|
|
224
|
+
phone: {
|
|
225
|
+
type: 'text',
|
|
226
|
+
label: 'Phone',
|
|
227
|
+
},
|
|
228
|
+
company: {
|
|
229
|
+
type: 'lookup',
|
|
230
|
+
label: 'Company',
|
|
231
|
+
reference: 'company',
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
export default contact;
|
|
237
|
+
`,
|
|
238
|
+
"src/objects/company.ts": () => `import { Data } from '@objectstack/spec';
|
|
239
|
+
|
|
240
|
+
const company: Data.Object = {
|
|
241
|
+
name: 'company',
|
|
242
|
+
label: 'Company',
|
|
243
|
+
ownership: 'own',
|
|
244
|
+
fields: {
|
|
245
|
+
name: {
|
|
246
|
+
type: 'text',
|
|
247
|
+
label: 'Company Name',
|
|
248
|
+
required: true,
|
|
249
|
+
},
|
|
250
|
+
website: {
|
|
251
|
+
type: 'text',
|
|
252
|
+
label: 'Website',
|
|
253
|
+
},
|
|
254
|
+
industry: {
|
|
255
|
+
type: 'select',
|
|
256
|
+
label: 'Industry',
|
|
257
|
+
options: [
|
|
258
|
+
{ label: 'Technology', value: 'technology' },
|
|
259
|
+
{ label: 'Finance', value: 'finance' },
|
|
260
|
+
{ label: 'Healthcare', value: 'healthcare' },
|
|
261
|
+
{ label: 'Other', value: 'other' },
|
|
262
|
+
],
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
export default company;
|
|
268
|
+
`,
|
|
269
|
+
"src/objects/deal.ts": () => `import { Data } from '@objectstack/spec';
|
|
270
|
+
|
|
271
|
+
const deal: Data.Object = {
|
|
272
|
+
name: 'deal',
|
|
273
|
+
label: 'Deal',
|
|
274
|
+
ownership: 'own',
|
|
275
|
+
fields: {
|
|
276
|
+
name: {
|
|
277
|
+
type: 'text',
|
|
278
|
+
label: 'Deal Name',
|
|
279
|
+
required: true,
|
|
280
|
+
},
|
|
281
|
+
amount: {
|
|
282
|
+
type: 'number',
|
|
283
|
+
label: 'Amount',
|
|
284
|
+
},
|
|
285
|
+
stage: {
|
|
286
|
+
type: 'select',
|
|
287
|
+
label: 'Stage',
|
|
288
|
+
options: [
|
|
289
|
+
{ label: 'Prospecting', value: 'prospecting' },
|
|
290
|
+
{ label: 'Qualification', value: 'qualification' },
|
|
291
|
+
{ label: 'Proposal', value: 'proposal' },
|
|
292
|
+
{ label: 'Closed Won', value: 'closed_won' },
|
|
293
|
+
{ label: 'Closed Lost', value: 'closed_lost' },
|
|
294
|
+
],
|
|
295
|
+
defaultValue: 'prospecting',
|
|
296
|
+
},
|
|
297
|
+
contact: {
|
|
298
|
+
type: 'lookup',
|
|
299
|
+
label: 'Contact',
|
|
300
|
+
reference: 'contact',
|
|
301
|
+
},
|
|
302
|
+
company: {
|
|
303
|
+
type: 'lookup',
|
|
304
|
+
label: 'Company',
|
|
305
|
+
reference: 'company',
|
|
306
|
+
},
|
|
307
|
+
close_date: {
|
|
308
|
+
type: 'date',
|
|
309
|
+
label: 'Close Date',
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
export default deal;
|
|
315
|
+
`,
|
|
316
|
+
"src/objects/index.ts": () => `export { default as contact } from './contact';
|
|
317
|
+
export { default as company } from './company';
|
|
318
|
+
export { default as deal } from './deal';
|
|
319
|
+
`,
|
|
320
|
+
"src/views/contact_list.ts": () => `import { UI } from '@objectstack/spec';
|
|
321
|
+
|
|
322
|
+
const contactList: UI.View = {
|
|
323
|
+
name: 'contact_list',
|
|
324
|
+
label: 'All Contacts',
|
|
325
|
+
object: 'contact',
|
|
326
|
+
type: 'list',
|
|
327
|
+
columns: ['first_name', 'last_name', 'email', 'phone', 'company'],
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
export default contactList;
|
|
331
|
+
`,
|
|
332
|
+
"src/views/company_list.ts": () => `import { UI } from '@objectstack/spec';
|
|
333
|
+
|
|
334
|
+
const companyList: UI.View = {
|
|
335
|
+
name: 'company_list',
|
|
336
|
+
label: 'All Companies',
|
|
337
|
+
object: 'company',
|
|
338
|
+
type: 'list',
|
|
339
|
+
columns: ['name', 'website', 'industry'],
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
export default companyList;
|
|
343
|
+
`,
|
|
344
|
+
"src/views/deal_list.ts": () => `import { UI } from '@objectstack/spec';
|
|
345
|
+
|
|
346
|
+
const dealList: UI.View = {
|
|
347
|
+
name: 'deal_list',
|
|
348
|
+
label: 'All Deals',
|
|
349
|
+
object: 'deal',
|
|
350
|
+
type: 'list',
|
|
351
|
+
columns: ['name', 'amount', 'stage', 'contact', 'close_date'],
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
export default dealList;
|
|
355
|
+
`,
|
|
356
|
+
"src/apps/crm.ts": () => `import { UI } from '@objectstack/spec';
|
|
357
|
+
|
|
358
|
+
const crm: UI.App = {
|
|
359
|
+
name: 'crm',
|
|
360
|
+
label: 'CRM',
|
|
361
|
+
description: 'Customer Relationship Management',
|
|
362
|
+
navigation: [
|
|
363
|
+
{ type: 'object', object: 'contact', label: 'Contacts' },
|
|
364
|
+
{ type: 'object', object: 'company', label: 'Companies' },
|
|
365
|
+
{ type: 'object', object: 'deal', label: 'Deals' },
|
|
366
|
+
],
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
export default crm;
|
|
370
|
+
`,
|
|
371
|
+
"src/apps/index.ts": () => `export { default as crm } from './crm';
|
|
372
|
+
`,
|
|
373
|
+
".gitignore": () => `node_modules/
|
|
374
|
+
dist/
|
|
375
|
+
*.tsbuildinfo
|
|
376
|
+
`,
|
|
377
|
+
"README.md": (name) => `# ${toTitleCase(name)}
|
|
378
|
+
|
|
379
|
+
A full-stack CRM application built with [ObjectStack](https://objectstack.com).
|
|
380
|
+
|
|
381
|
+
## Quick Start
|
|
382
|
+
|
|
383
|
+
\`\`\`bash
|
|
384
|
+
npm install
|
|
385
|
+
npm run dev
|
|
386
|
+
\`\`\`
|
|
387
|
+
|
|
388
|
+
## Project Structure
|
|
389
|
+
|
|
390
|
+
- \`objectstack.config.ts\` \u2014 Stack definition
|
|
391
|
+
- \`src/objects/\` \u2014 Data objects (Contact, Company, Deal)
|
|
392
|
+
- \`src/views/\` \u2014 List views
|
|
393
|
+
- \`src/apps/crm.ts\` \u2014 CRM app with navigation
|
|
394
|
+
|
|
395
|
+
## Learn More
|
|
396
|
+
|
|
397
|
+
- [ObjectStack Documentation](https://objectstack.com/docs)
|
|
398
|
+
`
|
|
399
|
+
}
|
|
400
|
+
},
|
|
401
|
+
plugin: {
|
|
402
|
+
description: "Plugin skeleton with test setup",
|
|
403
|
+
files: {
|
|
404
|
+
"objectstack.config.ts": (name) => `import { defineStack } from '@objectstack/spec';
|
|
405
|
+
import * as objects from './src/objects';
|
|
406
|
+
|
|
407
|
+
export default defineStack({
|
|
408
|
+
manifest: {
|
|
409
|
+
id: 'com.objectstack.plugin-${name}',
|
|
410
|
+
namespace: 'plugin_${name}',
|
|
411
|
+
version: '0.1.0',
|
|
412
|
+
type: 'plugin',
|
|
413
|
+
name: '${toTitleCase(name)} Plugin',
|
|
414
|
+
description: 'ObjectStack Plugin: ${toTitleCase(name)}',
|
|
415
|
+
},
|
|
416
|
+
|
|
417
|
+
objects: Object.values(objects),
|
|
418
|
+
});
|
|
419
|
+
`,
|
|
420
|
+
"package.json": (name) => JSON.stringify({
|
|
421
|
+
name: `@objectstack/plugin-${name}`,
|
|
422
|
+
version: "0.1.0",
|
|
423
|
+
description: `ObjectStack Plugin: ${toTitleCase(name)}`,
|
|
424
|
+
main: "dist/index.js",
|
|
425
|
+
types: "dist/index.d.ts",
|
|
426
|
+
type: "module",
|
|
427
|
+
scripts: {
|
|
428
|
+
build: "tsc",
|
|
429
|
+
dev: "tsc --watch",
|
|
430
|
+
test: "vitest run",
|
|
431
|
+
validate: "objectstack validate",
|
|
432
|
+
typecheck: "tsc --noEmit"
|
|
433
|
+
},
|
|
434
|
+
keywords: ["objectstack", "plugin", name],
|
|
435
|
+
author: "",
|
|
436
|
+
license: "MIT",
|
|
437
|
+
dependencies: {
|
|
438
|
+
"@objectstack/spec": "^3.0.0"
|
|
439
|
+
},
|
|
440
|
+
devDependencies: {
|
|
441
|
+
"@types/node": "^22.0.0",
|
|
442
|
+
"typescript": "^5.3.0",
|
|
443
|
+
"vitest": "^4.0.0"
|
|
444
|
+
}
|
|
445
|
+
}, null, 2) + "\n",
|
|
446
|
+
"tsconfig.json": () => JSON.stringify({
|
|
447
|
+
compilerOptions: {
|
|
448
|
+
target: "ES2022",
|
|
449
|
+
module: "ESNext",
|
|
450
|
+
moduleResolution: "bundler",
|
|
451
|
+
strict: true,
|
|
452
|
+
esModuleInterop: true,
|
|
453
|
+
skipLibCheck: true,
|
|
454
|
+
outDir: "dist",
|
|
455
|
+
rootDir: ".",
|
|
456
|
+
declaration: true
|
|
457
|
+
},
|
|
458
|
+
include: ["*.ts", "src/**/*"],
|
|
459
|
+
exclude: ["dist", "node_modules"]
|
|
460
|
+
}, null, 2) + "\n",
|
|
461
|
+
"src/index.ts": (name) => `/**
|
|
462
|
+
* ${toTitleCase(name)} Plugin for ObjectStack
|
|
463
|
+
*
|
|
464
|
+
* Entry point \u2014 re-exports all plugin metadata.
|
|
465
|
+
*/
|
|
466
|
+
export * as objects from './objects';
|
|
467
|
+
`,
|
|
468
|
+
"src/objects/sample.ts": (name) => `import { Data } from '@objectstack/spec';
|
|
469
|
+
|
|
470
|
+
const sample: Data.Object = {
|
|
471
|
+
name: '${name}_sample',
|
|
472
|
+
label: '${toTitleCase(name)} Sample',
|
|
473
|
+
ownership: 'own',
|
|
474
|
+
fields: {
|
|
475
|
+
name: {
|
|
476
|
+
type: 'text',
|
|
477
|
+
label: 'Name',
|
|
478
|
+
required: true,
|
|
479
|
+
},
|
|
480
|
+
},
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
export default sample;
|
|
484
|
+
`,
|
|
485
|
+
"src/objects/index.ts": () => `export { default as sample } from './sample';
|
|
486
|
+
`,
|
|
487
|
+
"test/sample.test.ts": (name) => `import { describe, it, expect } from 'vitest';
|
|
488
|
+
import sample from '../src/objects/sample';
|
|
489
|
+
|
|
490
|
+
describe('${name} plugin', () => {
|
|
491
|
+
it('should export a valid sample object', () => {
|
|
492
|
+
expect(sample).toBeDefined();
|
|
493
|
+
expect(sample.name).toBe('${name}_sample');
|
|
494
|
+
expect(sample.fields).toHaveProperty('name');
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
`,
|
|
498
|
+
".gitignore": () => `node_modules/
|
|
499
|
+
dist/
|
|
500
|
+
*.tsbuildinfo
|
|
501
|
+
`,
|
|
502
|
+
"README.md": (name) => `# @objectstack/plugin-${name}
|
|
503
|
+
|
|
504
|
+
ObjectStack Plugin: ${toTitleCase(name)}
|
|
505
|
+
|
|
506
|
+
## Installation
|
|
507
|
+
|
|
508
|
+
\`\`\`bash
|
|
509
|
+
npm install @objectstack/plugin-${name}
|
|
510
|
+
\`\`\`
|
|
511
|
+
|
|
512
|
+
## Usage
|
|
513
|
+
|
|
514
|
+
\`\`\`typescript
|
|
515
|
+
import { defineStack } from '@objectstack/spec';
|
|
516
|
+
|
|
517
|
+
export default defineStack({
|
|
518
|
+
plugins: [
|
|
519
|
+
'@objectstack/plugin-${name}',
|
|
520
|
+
],
|
|
521
|
+
});
|
|
522
|
+
\`\`\`
|
|
523
|
+
|
|
524
|
+
## Development
|
|
525
|
+
|
|
526
|
+
\`\`\`bash
|
|
527
|
+
# Run tests
|
|
528
|
+
npm test
|
|
529
|
+
|
|
530
|
+
# Build
|
|
531
|
+
npm run build
|
|
532
|
+
|
|
533
|
+
# Validate metadata
|
|
534
|
+
npm run validate
|
|
535
|
+
\`\`\`
|
|
536
|
+
|
|
537
|
+
## License
|
|
538
|
+
|
|
539
|
+
MIT
|
|
540
|
+
`
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
};
|
|
544
|
+
function toTitleCase(str) {
|
|
545
|
+
return str.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
546
|
+
}
|
|
547
|
+
function printHeader(title) {
|
|
548
|
+
console.log(chalk.bold(`
|
|
549
|
+
\u25C6 ${title}`));
|
|
550
|
+
console.log(chalk.dim("\u2500".repeat(40)));
|
|
551
|
+
}
|
|
552
|
+
function printKV(key, value) {
|
|
553
|
+
console.log(` ${chalk.dim(key + ":")} ${chalk.white(value)}`);
|
|
554
|
+
}
|
|
555
|
+
function printSuccess(msg) {
|
|
556
|
+
console.log(chalk.green(` \u2713 ${msg}`));
|
|
557
|
+
}
|
|
558
|
+
function printError(msg) {
|
|
559
|
+
console.log(chalk.red(` \u2717 ${msg}`));
|
|
560
|
+
}
|
|
561
|
+
function printStep(msg) {
|
|
562
|
+
console.log(chalk.yellow(` \u2192 ${msg}`));
|
|
563
|
+
}
|
|
564
|
+
function printWarning(msg) {
|
|
565
|
+
console.log(chalk.yellow(` \u26A0 ${msg}`));
|
|
566
|
+
}
|
|
567
|
+
var program = new Command().name("create-objectstack").description("Create a new ObjectStack project").version("3.0.0").argument("[name]", "Project name (defaults to current directory name)").option(
|
|
568
|
+
"-t, --template <template>",
|
|
569
|
+
"Project template: minimal-api, full-stack, plugin",
|
|
570
|
+
"minimal-api"
|
|
571
|
+
).option("--skip-install", "Skip dependency installation").action(async (name, options) => {
|
|
572
|
+
console.log("");
|
|
573
|
+
console.log(chalk.bold.cyan(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
574
|
+
console.log(chalk.bold.cyan(" \u2551") + chalk.bold(" \u25C6 Create ObjectStack ") + chalk.dim("v3.0") + chalk.bold.cyan(" \u2551"));
|
|
575
|
+
console.log(chalk.bold.cyan(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
576
|
+
printHeader("New Project");
|
|
577
|
+
const template = TEMPLATES[options.template];
|
|
578
|
+
if (!template) {
|
|
579
|
+
printError(`Unknown template: ${options.template}`);
|
|
580
|
+
console.log(chalk.dim(` Available: ${Object.keys(TEMPLATES).join(", ")}`));
|
|
581
|
+
process.exit(1);
|
|
582
|
+
}
|
|
583
|
+
const cwd = process.cwd();
|
|
584
|
+
const projectName = name || path.basename(cwd);
|
|
585
|
+
const targetDir = name ? path.resolve(cwd, name) : cwd;
|
|
586
|
+
const isCurrentDir = targetDir === cwd;
|
|
587
|
+
printKV("Project", projectName);
|
|
588
|
+
printKV("Template", `${options.template} \u2014 ${template.description}`);
|
|
589
|
+
printKV("Directory", targetDir);
|
|
590
|
+
console.log("");
|
|
591
|
+
if (!isCurrentDir && fs.existsSync(targetDir)) {
|
|
592
|
+
const existing = fs.readdirSync(targetDir);
|
|
593
|
+
if (existing.length > 0) {
|
|
594
|
+
printError(`Directory already exists and is not empty: ${targetDir}`);
|
|
595
|
+
process.exit(1);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
const createdFiles = [];
|
|
599
|
+
try {
|
|
600
|
+
if (!fs.existsSync(targetDir)) {
|
|
601
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
602
|
+
}
|
|
603
|
+
for (const [filePath, contentFn] of Object.entries(template.files)) {
|
|
604
|
+
const fullPath = path.join(targetDir, filePath);
|
|
605
|
+
const dir = path.dirname(fullPath);
|
|
606
|
+
if (!fs.existsSync(dir)) {
|
|
607
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
608
|
+
}
|
|
609
|
+
fs.writeFileSync(fullPath, contentFn(projectName));
|
|
610
|
+
createdFiles.push(filePath);
|
|
611
|
+
}
|
|
612
|
+
console.log(chalk.bold(" Created files:"));
|
|
613
|
+
for (const f of createdFiles) {
|
|
614
|
+
console.log(chalk.green(` + ${f}`));
|
|
615
|
+
}
|
|
616
|
+
console.log("");
|
|
617
|
+
if (!options.skipInstall) {
|
|
618
|
+
printStep("Installing dependencies...");
|
|
619
|
+
try {
|
|
620
|
+
const pm = detectPackageManager();
|
|
621
|
+
execSync(`${pm} install`, { stdio: "inherit", cwd: targetDir });
|
|
622
|
+
console.log("");
|
|
623
|
+
} catch {
|
|
624
|
+
printWarning("Dependency installation failed. Run `npm install` manually.");
|
|
625
|
+
console.log("");
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
printSuccess("Project created!");
|
|
629
|
+
console.log("");
|
|
630
|
+
console.log(chalk.bold(" Next steps:"));
|
|
631
|
+
if (!isCurrentDir) {
|
|
632
|
+
console.log(chalk.dim(` cd ${name}`));
|
|
633
|
+
}
|
|
634
|
+
if (options.skipInstall) {
|
|
635
|
+
console.log(chalk.dim(" npm install"));
|
|
636
|
+
}
|
|
637
|
+
console.log(chalk.dim(" npm run dev # Start development server"));
|
|
638
|
+
console.log(chalk.dim(" npm run validate # Check configuration"));
|
|
639
|
+
console.log("");
|
|
640
|
+
} catch (error) {
|
|
641
|
+
printError(error.message || String(error));
|
|
642
|
+
process.exit(1);
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
function detectPackageManager() {
|
|
646
|
+
try {
|
|
647
|
+
execSync("pnpm --version", { stdio: "ignore" });
|
|
648
|
+
return "pnpm";
|
|
649
|
+
} catch {
|
|
650
|
+
return "npm";
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-objectstack",
|
|
3
|
+
"version": "3.0.0",
|
|
4
|
+
"description": "Create a new ObjectStack project — npx create-objectstack",
|
|
5
|
+
"bin": {
|
|
6
|
+
"create-objectstack": "./bin/create-objectstack.js"
|
|
7
|
+
},
|
|
8
|
+
"keywords": [
|
|
9
|
+
"objectstack",
|
|
10
|
+
"create",
|
|
11
|
+
"scaffold",
|
|
12
|
+
"cli"
|
|
13
|
+
],
|
|
14
|
+
"type": "module",
|
|
15
|
+
"author": "Steedos",
|
|
16
|
+
"license": "Apache-2.0",
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"chalk": "^5.3.0",
|
|
19
|
+
"commander": "^14.0.3"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/node": "^25.2.2",
|
|
23
|
+
"tsup": "^8.0.2",
|
|
24
|
+
"typescript": "^5.3.3"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsup",
|
|
28
|
+
"dev": "tsup --watch"
|
|
29
|
+
}
|
|
30
|
+
}
|