@vobs/cli 0.3.0 → 1.2.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.
Files changed (143) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +116 -0
  3. package/bin/vobs.js +12 -0
  4. package/dist/banner.cjs +37 -0
  5. package/dist/banner.cjs.map +1 -0
  6. package/dist/banner.d.cts +18 -0
  7. package/dist/banner.d.ts +18 -0
  8. package/dist/banner.js +33 -0
  9. package/dist/banner.js.map +1 -0
  10. package/dist/cli.cjs +416 -0
  11. package/dist/cli.cjs.map +1 -0
  12. package/dist/cli.d.cts +5 -0
  13. package/dist/cli.d.ts +5 -0
  14. package/dist/cli.js +389 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/commands/add.cjs +113 -0
  17. package/dist/commands/add.cjs.map +1 -0
  18. package/dist/commands/add.d.cts +5 -0
  19. package/dist/commands/add.d.ts +5 -0
  20. package/dist/commands/add.js +88 -0
  21. package/dist/commands/add.js.map +1 -0
  22. package/dist/commands/build.cjs +55 -0
  23. package/dist/commands/build.cjs.map +1 -0
  24. package/dist/commands/build.d.cts +5 -0
  25. package/dist/commands/build.d.ts +5 -0
  26. package/dist/commands/build.js +49 -0
  27. package/dist/commands/build.js.map +1 -0
  28. package/dist/commands/dev.cjs +57 -0
  29. package/dist/commands/dev.cjs.map +1 -0
  30. package/dist/commands/dev.d.cts +5 -0
  31. package/dist/commands/dev.d.ts +5 -0
  32. package/dist/commands/dev.js +51 -0
  33. package/dist/commands/dev.js.map +1 -0
  34. package/dist/commands/generate.cjs +153 -0
  35. package/dist/commands/generate.cjs.map +1 -0
  36. package/dist/commands/generate.d.cts +5 -0
  37. package/dist/commands/generate.d.ts +5 -0
  38. package/dist/commands/generate.js +126 -0
  39. package/dist/commands/generate.js.map +1 -0
  40. package/dist/commands/index.cjs +331 -0
  41. package/dist/commands/index.cjs.map +1 -0
  42. package/dist/commands/index.d.cts +6 -0
  43. package/dist/commands/index.d.ts +6 -0
  44. package/dist/commands/index.js +300 -0
  45. package/dist/commands/index.js.map +1 -0
  46. package/dist/commands/init.cjs +187 -0
  47. package/dist/commands/init.cjs.map +1 -0
  48. package/dist/commands/init.d.cts +5 -0
  49. package/dist/commands/init.d.ts +5 -0
  50. package/dist/commands/init.js +160 -0
  51. package/dist/commands/init.js.map +1 -0
  52. package/dist/index.cjs +561 -0
  53. package/dist/index.cjs.map +1 -0
  54. package/dist/index.d.cts +14 -0
  55. package/dist/index.d.ts +14 -18
  56. package/dist/index.js +489 -486
  57. package/dist/index.js.map +1 -0
  58. package/dist/repl.cjs +436 -0
  59. package/dist/repl.cjs.map +1 -0
  60. package/dist/repl.d.cts +8 -0
  61. package/dist/repl.d.ts +8 -0
  62. package/dist/repl.js +409 -0
  63. package/dist/repl.js.map +1 -0
  64. package/dist/start.cjs +543 -0
  65. package/dist/start.cjs.map +1 -0
  66. package/dist/start.d.cts +4 -0
  67. package/dist/start.d.ts +4 -0
  68. package/dist/start.js +516 -0
  69. package/dist/start.js.map +1 -0
  70. package/dist/templates/basic/.gitignore.hbs +4 -0
  71. package/dist/templates/basic/index.html.hbs +12 -0
  72. package/dist/templates/basic/package.json.hbs +25 -0
  73. package/dist/templates/basic/src/App.tsx.hbs +8 -0
  74. package/dist/templates/basic/src/app.css.hbs +32 -0
  75. package/dist/templates/basic/src/main.ts.hbs +20 -0
  76. package/dist/templates/basic/tsconfig.json.hbs +19 -0
  77. package/dist/templates/basic/vite.config.ts.hbs +10 -0
  78. package/dist/templates/component.hbs +7 -0
  79. package/dist/templates/page.hbs +7 -0
  80. package/dist/types.cjs +4 -0
  81. package/dist/types.cjs.map +1 -0
  82. package/dist/types.d.cts +71 -0
  83. package/dist/types.d.ts +71 -0
  84. package/dist/types.js +3 -0
  85. package/dist/types.js.map +1 -0
  86. package/dist/utils/file.cjs +79 -0
  87. package/dist/utils/file.cjs.map +1 -0
  88. package/dist/utils/file.d.cts +14 -0
  89. package/dist/utils/file.d.ts +14 -0
  90. package/dist/utils/file.js +67 -0
  91. package/dist/utils/file.js.map +1 -0
  92. package/dist/utils/logger.cjs +45 -0
  93. package/dist/utils/logger.cjs.map +1 -0
  94. package/dist/utils/logger.d.cts +14 -0
  95. package/dist/utils/logger.d.ts +14 -0
  96. package/dist/utils/logger.js +34 -0
  97. package/dist/utils/logger.js.map +1 -0
  98. package/dist/utils/pm.cjs +38 -0
  99. package/dist/utils/pm.cjs.map +1 -0
  100. package/dist/utils/pm.d.cts +9 -0
  101. package/dist/utils/pm.d.ts +9 -0
  102. package/dist/utils/pm.js +34 -0
  103. package/dist/utils/pm.js.map +1 -0
  104. package/package.json +30 -40
  105. package/src/banner.ts +41 -0
  106. package/src/cli.ts +108 -0
  107. package/src/commands/add.ts +37 -0
  108. package/src/commands/build.ts +17 -0
  109. package/src/commands/dev.ts +19 -0
  110. package/src/commands/generate.ts +87 -0
  111. package/src/commands/index.ts +5 -0
  112. package/src/commands/init.ts +84 -0
  113. package/src/index.ts +13 -0
  114. package/src/repl.ts +140 -0
  115. package/src/start.ts +20 -0
  116. package/src/templates/basic/.gitignore.hbs +4 -0
  117. package/src/templates/basic/index.html.hbs +12 -0
  118. package/src/templates/basic/package.json.hbs +25 -0
  119. package/src/templates/basic/src/App.tsx.hbs +8 -0
  120. package/src/templates/basic/src/app.css.hbs +32 -0
  121. package/src/templates/basic/src/main.ts.hbs +20 -0
  122. package/src/templates/basic/tsconfig.json.hbs +19 -0
  123. package/src/templates/basic/vite.config.ts.hbs +10 -0
  124. package/src/templates/component.hbs +7 -0
  125. package/src/templates/page.hbs +7 -0
  126. package/src/types.ts +78 -0
  127. package/src/utils/file.ts +77 -0
  128. package/src/utils/logger.ts +27 -0
  129. package/src/utils/pm.ts +42 -0
  130. package/dist/ai-server.d.ts +0 -11
  131. package/dist/ai-server.js +0 -212
  132. package/dist/context.d.ts +0 -12
  133. package/dist/context.js +0 -301
  134. package/dist/diagnose.d.ts +0 -12
  135. package/dist/diagnose.js +0 -122
  136. package/dist/enterprise-template.d.ts +0 -83
  137. package/dist/enterprise-template.js +0 -638
  138. package/dist/inspect.d.ts +0 -11
  139. package/dist/inspect.js +0 -576
  140. package/dist/logo/cli-logo.d.ts +0 -5
  141. package/dist/logo/cli-logo.js +0 -16
  142. package/dist/scaffold.d.ts +0 -23
  143. package/dist/scaffold.js +0 -448
package/dist/index.js CHANGED
@@ -1,513 +1,516 @@
1
- #!/usr/bin/env node
2
- /** @license MIT
3
- * Copyright (c) 2026 vobsjs
4
- * @vobs/cli
5
- */
6
- import { existsSync } from 'node:fs';
7
- import { realpathSync } from 'node:fs';
8
- import * as path from 'node:path';
9
- import process from 'node:process';
10
- import { createInterface } from 'node:readline/promises';
11
- import { fileURLToPath } from 'node:url';
12
- import { checkViewProject } from '@vobs/compiler-dom';
13
- import { VOBS_AI_TOOL_CATALOG } from '@vobs/ai-contracts';
14
- import { createVobsProjectContext } from './context.js';
15
- import { createVobsDiagnosticReport } from './diagnose.js';
16
- import { inspectVobsProject, writeInspectOutput } from './inspect.js';
17
- import { runVobsAiServer } from './ai-server.js';
18
- export { runVobsAiServer } from './ai-server.js';
19
- import { renderCliLogo } from './logo/cli-logo.js';
20
- import { createScaffold, isScaffoldTemplate, scaffoldSuccessText, } from './scaffold.js';
21
- export function runVobsCli(args, streams = {
22
- stdin: process.stdin,
23
- stdout: process.stdout,
24
- stderr: process.stderr,
25
- }) {
26
- const { args: commandArgs, shouldShowLogo } = readLogoOption(args);
27
- if (shouldShowLogo) {
28
- streams.stdout.write(`${renderCliLogo()}\n\n`);
29
- }
30
- const parsed = parseArgs(commandArgs);
31
- if (parsed.command === 'help') {
32
- streams.stdout.write(helpText());
33
- return Promise.resolve(0);
34
- }
35
- if (parsed.command === 'new') {
36
- return runNewCommand(parsed, streams);
37
- }
38
- if (parsed.command === 'inspect') {
39
- return runInspectCommand(parsed, streams);
40
- }
41
- if (parsed.command === 'ai-tools') {
42
- streams.stdout.write(`${JSON.stringify(VOBS_AI_TOOL_CATALOG, null, 2)}\n`);
43
- return Promise.resolve(0);
44
- }
45
- if (parsed.command === 'ai-serve') {
46
- return runAiServeCommand(streams);
47
- }
48
- if (parsed.command === 'context') {
49
- streams.stdout.write(`${JSON.stringify(createVobsProjectContext(parsed.options), null, 2)}\n`);
50
- return Promise.resolve(0);
51
- }
52
- if (parsed.command === 'diagnose') {
53
- streams.stdout.write(`${JSON.stringify(createVobsDiagnosticReport(parsed.options), null, 2)}\n`);
54
- return Promise.resolve(0);
55
- }
56
- const result = checkViewProject({
57
- tsconfigPath: parsed.tsconfigPath,
58
- ...(parsed.viewFiles === undefined ? {} : { viewFiles: parsed.viewFiles }),
59
- });
60
- const errors = result.diagnostics.filter((diagnostic) => diagnostic.severity === 'error');
61
- const warnings = result.diagnostics.filter((diagnostic) => diagnostic.severity === 'warning');
62
- if (errors.length === 0) {
63
- if (warnings.length > 0) {
64
- streams.stderr.write(`vobs check warnings\n${formatDiagnostics(warnings)}\n`);
65
- }
66
- streams.stdout.write(`vobs check passed (${result.units.length} view unit(s)).\n`);
67
- return Promise.resolve(0);
68
- }
69
- streams.stderr.write(`vobs check failed\n${formatDiagnostics(errors)}${warnings.length === 0 ? '' : `\nvobs check warnings\n${formatDiagnostics(warnings)}`}\n`);
70
- return Promise.resolve(1);
1
+ import { cac } from 'cac';
2
+ import { dirname, resolve } from 'path';
3
+ import { execSync } from 'child_process';
4
+ import * as p from '@clack/prompts';
5
+ import pc2 from 'picocolors';
6
+ import { readFileSync, mkdirSync, writeFileSync, existsSync } from 'fs';
7
+ import { fileURLToPath } from 'url';
8
+ import Handlebars from 'handlebars';
9
+ import { createServer, build } from 'vite';
10
+ import { createInterface } from 'readline/promises';
11
+ import { stdout, stdin } from 'process';
12
+
13
+ var __defProp = Object.defineProperty;
14
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
15
+ function timestamp() {
16
+ return pc2.dim(`[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}]`);
71
17
  }
72
- function readLogoOption(args) {
73
- const commandArgs = args.filter((arg) => arg !== '--no-logo');
74
- const isAiServer = commandArgs[0] === 'ai-serve' || (commandArgs[0] === 'ai' && commandArgs[1] === 'serve');
75
- return {
76
- args: commandArgs,
77
- shouldShowLogo: !args.includes('--no-logo') &&
78
- !args.includes('--json') &&
79
- !args.includes('inspect') &&
80
- !isAiServer,
81
- };
18
+ __name(timestamp, "timestamp");
19
+ function info(msg) {
20
+ console.log(`${timestamp()} ${pc2.cyan("\u2139")} ${msg}`);
82
21
  }
83
- function parseArgs(args) {
84
- const [command, ...rest] = args;
85
- if (command === undefined || command === '--help' || command === '-h')
86
- return { command: 'help' };
87
- if (command === 'new' || command === 'create')
88
- return parseNewArgs(rest, command === 'create');
89
- if (command === 'inspect')
90
- return parseInspectArgs(rest);
91
- if (command === 'ai' && rest[0] === 'tools')
92
- return parseAiToolsArgs(rest.slice(1));
93
- if (command === 'ai' && rest[0] === 'serve')
94
- return parseAiServeArgs(rest.slice(1));
95
- if (command === 'ai' && rest[0] === 'context')
96
- return parseContextArgs(rest.slice(1));
97
- if (command === 'ai' && rest[0] === 'diagnose')
98
- return parseDiagnoseArgs(rest.slice(1));
99
- if (command === 'ai-tools')
100
- return parseAiToolsArgs(rest);
101
- if (command === 'ai-serve')
102
- return parseAiServeArgs(rest);
103
- if (command === 'context')
104
- return parseContextArgs(rest);
105
- if (command === 'diagnose')
106
- return parseDiagnoseArgs(rest);
107
- if (command !== 'check')
108
- throw new Error(`Unknown command: ${command}`);
109
- let tsconfigPath = path.join(process.cwd(), 'tsconfig.json');
110
- const viewFiles = [];
111
- for (let index = 0; index < rest.length; index += 1) {
112
- const arg = rest[index];
113
- if (arg === '--help' || arg === '-h')
114
- return { command: 'help' };
115
- if (arg === '--tsconfig' || arg === '-p') {
116
- const value = rest[index + 1];
117
- if (value === undefined)
118
- throw new Error(`${arg} requires a path`);
119
- tsconfigPath = path.resolve(value);
120
- index += 1;
121
- continue;
122
- }
123
- if (arg === '--view') {
124
- const value = rest[index + 1];
125
- if (value === undefined)
126
- throw new Error('--view requires a path');
127
- viewFiles.push(path.resolve(value));
128
- index += 1;
129
- continue;
130
- }
131
- throw new Error(`Unknown option: ${arg ?? ''}`);
132
- }
133
- return {
134
- command: 'check',
135
- tsconfigPath,
136
- viewFiles: viewFiles.length === 0 ? undefined : viewFiles,
137
- };
22
+ __name(info, "info");
23
+ function success(msg) {
24
+ console.log(`${timestamp()} ${pc2.green("\u2714")} ${msg}`);
138
25
  }
139
- function parseAiServeArgs(args) {
140
- for (const arg of args) {
141
- if (arg === '--help' || arg === '-h')
142
- return { command: 'help' };
143
- throw new Error(`Unknown option: ${arg}`);
144
- }
145
- return { command: 'ai-serve' };
26
+ __name(success, "success");
27
+ function warn(msg) {
28
+ console.log(`${timestamp()} ${pc2.yellow("\u26A0")} ${msg}`);
146
29
  }
147
- function parseAiToolsArgs(args) {
148
- for (const arg of args) {
149
- if (arg === '--json')
150
- continue;
151
- if (arg === '--help' || arg === '-h')
152
- return { command: 'help' };
153
- throw new Error(`Unknown option: ${arg}`);
154
- }
155
- return { command: 'ai-tools' };
30
+ __name(warn, "warn");
31
+ function error(msg) {
32
+ console.error(`${timestamp()} ${pc2.red("\u2716")} ${msg}`);
156
33
  }
157
- function parseContextArgs(args) {
158
- let tsconfigPath = path.join(process.cwd(), 'tsconfig.json');
159
- let route;
160
- let file;
161
- let depth;
162
- for (let index = 0; index < args.length; index += 1) {
163
- const arg = args[index];
164
- if (arg === '--json')
165
- continue;
166
- if (arg === '--help' || arg === '-h')
167
- return { command: 'help' };
168
- if (arg === '--tsconfig' || arg === '-p') {
169
- const value = args[index + 1];
170
- if (value === undefined)
171
- throw new Error(`${arg} requires a path`);
172
- tsconfigPath = path.resolve(value);
173
- index += 1;
174
- continue;
175
- }
176
- if (arg === '--route') {
177
- const value = args[index + 1];
178
- if (value === undefined)
179
- throw new Error('--route requires a path');
180
- route = value;
181
- index += 1;
182
- continue;
183
- }
184
- if (arg === '--file') {
185
- const value = args[index + 1];
186
- if (value === undefined)
187
- throw new Error('--file requires a path');
188
- file = value;
189
- index += 1;
190
- continue;
191
- }
192
- if (arg === '--depth') {
193
- const value = args[index + 1];
194
- if (value === undefined)
195
- throw new Error('--depth requires a number');
196
- depth = Number(value);
197
- index += 1;
198
- continue;
199
- }
200
- throw new Error(`Unknown option: ${arg ?? ''}`);
201
- }
202
- return {
203
- command: 'context',
204
- options: {
205
- tsconfigPath,
206
- ...(route === undefined ? {} : { route }),
207
- ...(file === undefined ? {} : { file }),
208
- ...(depth === undefined ? {} : { depth }),
209
- },
210
- };
34
+ __name(error, "error");
35
+ function step(msg) {
36
+ console.log(`
37
+ ${pc2.bold(pc2.cyan(msg))}`);
211
38
  }
212
- function parseDiagnoseArgs(args) {
213
- let tsconfigPath = path.join(process.cwd(), 'tsconfig.json');
214
- let route;
215
- let file;
216
- let depth;
217
- for (let index = 0; index < args.length; index += 1) {
218
- const arg = args[index];
219
- if (arg === '--json')
220
- continue;
221
- if (arg === '--help' || arg === '-h')
222
- return { command: 'help' };
223
- if (arg === '--tsconfig' || arg === '-p') {
224
- const value = args[index + 1];
225
- if (value === undefined)
226
- throw new Error(`${arg} requires a path`);
227
- tsconfigPath = path.resolve(value);
228
- index += 1;
229
- continue;
230
- }
231
- if (arg === '--route') {
232
- const value = args[index + 1];
233
- if (value === undefined)
234
- throw new Error('--route requires a path');
235
- route = value;
236
- index += 1;
237
- continue;
238
- }
239
- if (arg === '--file') {
240
- const value = args[index + 1];
241
- if (value === undefined)
242
- throw new Error('--file requires a path');
243
- file = value;
244
- index += 1;
245
- continue;
246
- }
247
- if (arg === '--depth') {
248
- const value = args[index + 1];
249
- if (value === undefined)
250
- throw new Error('--depth requires a number');
251
- depth = Number(value);
252
- index += 1;
253
- continue;
254
- }
255
- throw new Error(`Unknown option: ${arg ?? ''}`);
256
- }
257
- return {
258
- command: 'diagnose',
259
- options: {
260
- tsconfigPath,
261
- ...(route === undefined ? {} : { route }),
262
- ...(file === undefined ? {} : { file }),
263
- ...(depth === undefined ? {} : { depth }),
264
- },
265
- };
39
+ __name(step, "step");
40
+ var logger = { info, success, warn, error, step };
41
+ var __dirname$1 = dirname(fileURLToPath(import.meta.url));
42
+ var TEMPLATES_ROOT = resolve(__dirname$1, "..", "templates");
43
+ function getTemplatesDir() {
44
+ return TEMPLATES_ROOT;
266
45
  }
267
- function parseInspectArgs(args) {
268
- let tsconfigPath = path.join(process.cwd(), 'tsconfig.json');
269
- let out;
270
- for (let index = 0; index < args.length; index += 1) {
271
- const arg = args[index];
272
- if (arg === '--help' || arg === '-h')
273
- return { command: 'help' };
274
- if (arg === '--json')
275
- continue;
276
- if (arg === '--tsconfig' || arg === '-p') {
277
- const value = args[index + 1];
278
- if (value === undefined)
279
- throw new Error(`${arg} requires a path`);
280
- tsconfigPath = path.resolve(value);
281
- index += 1;
282
- continue;
283
- }
284
- if (arg === '--out' || arg === '-o') {
285
- const value = args[index + 1];
286
- if (value === undefined)
287
- throw new Error(`${arg} requires a path`);
288
- out = value;
289
- index += 1;
290
- continue;
291
- }
292
- throw new Error(`Unknown option: ${arg ?? ''}`);
293
- }
294
- return { command: 'inspect', options: { tsconfigPath, ...(out === undefined ? {} : { out }) } };
46
+ __name(getTemplatesDir, "getTemplatesDir");
47
+ function renderTemplate(templatePath, context) {
48
+ const source = readFileSync(templatePath, "utf-8");
49
+ const template = Handlebars.compile(source);
50
+ return template(context);
295
51
  }
296
- function runInspectCommand(command, streams) {
297
- const manifest = inspectVobsProject(command.options);
298
- const output = `${JSON.stringify(manifest, null, 2)}\n`;
299
- if (command.options.out === undefined) {
300
- streams.stdout.write(output);
301
- }
302
- else {
303
- writeInspectOutput(manifest, command.options.out);
304
- streams.stdout.write(`Wrote vobs inspect manifest to ${path.resolve(process.cwd(), command.options.out)}\n`);
305
- }
306
- return Promise.resolve(0);
52
+ __name(renderTemplate, "renderTemplate");
53
+ function writeFile(dir, filename, content) {
54
+ const filePath = resolve(dir, filename);
55
+ mkdirSync(dirname(filePath), { recursive: true });
56
+ writeFileSync(filePath, content, "utf-8");
307
57
  }
308
- async function runAiServeCommand(streams) {
309
- if (streams.stdin === undefined)
310
- throw new Error('vobs ai serve requires stdin');
311
- const input = createInterface({ input: streams.stdin, crlfDelay: Infinity });
312
- const serverStreams = {
313
- input,
314
- stdout: streams.stdout,
315
- ...(streams.stderr === undefined ? {} : { stderr: streams.stderr }),
316
- };
317
- try {
318
- await runVobsAiServer(serverStreams);
319
- }
320
- finally {
321
- input.close();
322
- }
323
- return 0;
58
+ __name(writeFile, "writeFile");
59
+ function ensureDir(dir) {
60
+ if (!existsSync(dir)) {
61
+ mkdirSync(dir, { recursive: true });
62
+ }
324
63
  }
325
- function parseNewArgs(args, deprecatedAlias) {
326
- let type;
327
- let name;
328
- let template;
329
- let withDi;
330
- let force = false;
331
- for (let index = 0; index < args.length; index += 1) {
332
- const arg = args[index];
333
- if (arg === undefined)
334
- continue;
335
- if (arg === '--help' || arg === '-h')
336
- return { command: 'help' };
337
- if (arg === '--template' || arg === '-t') {
338
- const value = args[index + 1];
339
- if (value === undefined)
340
- throw new Error(`${arg} requires a template`);
341
- if (!isScaffoldTemplate(value))
342
- throw new Error(`Unknown template: ${value}`);
343
- template = value;
344
- index += 1;
345
- continue;
346
- }
347
- if (arg === '--with-di') {
348
- withDi = true;
349
- continue;
350
- }
351
- if (arg === '--force') {
352
- force = true;
353
- continue;
354
- }
355
- if (arg.startsWith('-'))
356
- throw new Error(`Unknown option: ${arg}`);
357
- if (type === undefined) {
358
- type = arg;
359
- continue;
360
- }
361
- if (name === undefined) {
362
- name = arg;
363
- continue;
364
- }
365
- throw new Error(`Unexpected argument: ${arg ?? ''}`);
64
+ __name(ensureDir, "ensureDir");
65
+ async function copyDirRecursive(srcDir, destDir, context) {
66
+ const { readdirSync } = await import('fs');
67
+ const { join } = await import('path');
68
+ const entries = readdirSync(srcDir, { withFileTypes: true });
69
+ for (const entry of entries) {
70
+ const srcPath = join(srcDir, entry.name);
71
+ const isHbs = entry.name.endsWith(".hbs");
72
+ const destName = isHbs ? entry.name.slice(0, -4) : entry.name;
73
+ const destPath = join(destDir, destName);
74
+ if (entry.isDirectory()) {
75
+ ensureDir(destPath);
76
+ await copyDirRecursive(srcPath, destPath, context);
77
+ } else if (isHbs) {
78
+ const content = renderTemplate(srcPath, context);
79
+ writeFile(destDir, destName, content);
80
+ } else {
81
+ const content = readFileSync(srcPath, "utf-8");
82
+ writeFile(destDir, destName, content);
366
83
  }
367
- const resolvedType = template === undefined ? type : type === template ? undefined : type;
368
- const resolvedName = template === undefined ? name : (name ?? type);
369
- return {
370
- command: 'new',
371
- type: resolvedType,
372
- name: resolvedName,
373
- options: {
374
- ...(template === undefined ? {} : { template }),
375
- ...(withDi === undefined ? {} : { withDi }),
376
- ...(force ? { force } : {}),
377
- },
378
- deprecatedAlias,
379
- };
84
+ }
380
85
  }
381
- async function runNewCommand(command, streams) {
382
- if (command.deprecatedAlias) {
383
- streams.stderr.write('Warning: "vobs create" is deprecated. Use "vobs new" instead.\n');
384
- }
385
- const completed = await completeNewCommand(command, streams);
386
- if (completed.name !== undefined &&
387
- completed.options.force === false &&
388
- existsSync(path.resolve(process.cwd(), completed.name))) {
389
- streams.stdout.write('Cancelled.\n');
390
- return 0;
391
- }
392
- const result = createScaffold(completed.type, completed.name, completed.options);
393
- streams.stdout.write(scaffoldSuccessText(result));
394
- return 0;
86
+ __name(copyDirRecursive, "copyDirRecursive");
87
+ async function scaffoldProject(targetDir, context) {
88
+ const templateDir = resolve(TEMPLATES_ROOT, "basic");
89
+ ensureDir(targetDir);
90
+ await copyDirRecursive(templateDir, targetDir, context);
91
+ }
92
+ __name(scaffoldProject, "scaffoldProject");
93
+ function detectPackageManager(targetDir) {
94
+ if (targetDir) {
95
+ if (existsSync(resolve(targetDir, "pnpm-lock.yaml"))) return "pnpm";
96
+ if (existsSync(resolve(targetDir, "yarn.lock"))) return "yarn";
97
+ if (existsSync(resolve(targetDir, "package-lock.json"))) return "npm";
98
+ }
99
+ const userAgent = process.env.npm_config_user_agent || "";
100
+ if (userAgent.startsWith("pnpm")) return "pnpm";
101
+ if (userAgent.startsWith("yarn")) return "yarn";
102
+ if (userAgent.startsWith("npm")) return "npm";
103
+ return "pnpm";
104
+ }
105
+ __name(detectPackageManager, "detectPackageManager");
106
+ function installDependencies(targetDir, pm) {
107
+ const cmd = pm === "yarn" ? "yarn" : `${pm} install`;
108
+ execSync(cmd, { cwd: targetDir, stdio: "inherit" });
109
+ }
110
+ __name(installDependencies, "installDependencies");
111
+ function addPackage(targetDir, pm, pkg, dev) {
112
+ const installFlag = dev ? "-D" : "";
113
+ const cmd = pm === "npm" ? `npm install ${installFlag} ${pkg}` : pm === "yarn" ? `yarn add ${installFlag} ${pkg}` : `pnpm add ${installFlag} ${pkg}`;
114
+ execSync(cmd, { cwd: targetDir, stdio: "inherit" });
395
115
  }
396
- async function completeNewCommand(command, streams) {
397
- const needsPrompt = command.type === undefined || command.name === undefined;
398
- if (!needsPrompt)
399
- return command;
400
- const prompt = streams.prompt ?? createNodePrompt();
116
+ __name(addPackage, "addPackage");
117
+
118
+ // packages/cli/src/commands/init.ts
119
+ async function initCommand(options) {
120
+ const projectName = options.name || await p.text({
121
+ message: "Project name:",
122
+ placeholder: "my-vobs-app",
123
+ validate: /* @__PURE__ */ __name((v) => !v ? "Project name is required" : void 0, "validate")
124
+ });
125
+ if (p.isCancel(projectName)) {
126
+ p.cancel("Cancelled");
127
+ return;
128
+ }
129
+ const targetDir = options.targetDir || resolve(process.cwd(), projectName);
130
+ const packageManager = options.packageManager || await p.select({
131
+ message: "Package manager:",
132
+ options: [
133
+ { value: "pnpm", label: "pnpm" },
134
+ { value: "npm", label: "npm" },
135
+ { value: "yarn", label: "yarn" }
136
+ ]
137
+ });
138
+ if (p.isCancel(packageManager)) {
139
+ p.cancel("Cancelled");
140
+ return;
141
+ }
142
+ const useTypescript = options.typescript ?? await p.confirm({
143
+ message: "Use TypeScript?",
144
+ initialValue: true
145
+ });
146
+ if (p.isCancel(useTypescript)) {
147
+ p.cancel("Cancelled");
148
+ return;
149
+ }
150
+ const initGit = options.git ?? await p.confirm({
151
+ message: "Initialize a git repository?",
152
+ initialValue: true
153
+ });
154
+ if (p.isCancel(initGit)) {
155
+ p.cancel("Cancelled");
156
+ return;
157
+ }
158
+ logger.step(`Creating project ${projectName}...`);
159
+ await scaffoldProject(targetDir, {
160
+ name: projectName,
161
+ packageName: projectName.toLowerCase().replace(/\s+/g, "-"),
162
+ typescript: useTypescript,
163
+ vobsVersion: "1.0.0"
164
+ });
165
+ logger.success("Project scaffolded");
166
+ logger.step("Installing dependencies...");
167
+ installDependencies(targetDir, packageManager);
168
+ logger.success("Dependencies installed");
169
+ if (initGit) {
170
+ logger.step("Initializing git repository...");
401
171
  try {
402
- const type = command.options.template ?? command.type ?? (await askTemplate(prompt));
403
- const name = command.name ?? (await askProjectName(prompt));
404
- const withDi = command.options.withDi ??
405
- (type === 'app' || type === 'lib'
406
- ? await prompt.confirm('Include @vobs/di integration?', false)
407
- : undefined);
408
- const force = command.options.force === true
409
- ? true
410
- : existsSync(path.resolve(process.cwd(), name))
411
- ? await prompt.confirm(`Directory "${name}" already exists. Overwrite?`, false)
412
- : undefined;
413
- return {
414
- ...command,
415
- type,
416
- name,
417
- options: {
418
- ...command.options,
419
- ...(withDi === undefined ? {} : { withDi }),
420
- ...(force === undefined ? {} : { force }),
421
- },
422
- };
423
- }
424
- finally {
425
- if (streams.prompt === undefined)
426
- prompt.close?.();
172
+ execSync("git init", { cwd: targetDir, stdio: "ignore" });
173
+ logger.success("Git repository initialized");
174
+ } catch {
175
+ logger.warn("Git not found, skipping git init");
427
176
  }
177
+ }
178
+ logger.step("Done!");
179
+ console.log(`
180
+ ${packageManager === "pnpm" ? "pnpm" : packageManager === "yarn" ? "yarn" : "npm run"} dev
181
+ `);
428
182
  }
429
- async function askTemplate(prompt) {
430
- while (true) {
431
- const value = await prompt.text('Template (app, app-di, enterprise, lib, lib-di)', 'app');
432
- if (isScaffoldTemplate(value))
433
- return value;
183
+ __name(initCommand, "initCommand");
184
+ async function devCommand(options) {
185
+ logger.info("Starting dev server...");
186
+ const server = await createServer({
187
+ configFile: options.config,
188
+ server: {
189
+ port: options.port,
190
+ open: options.open
434
191
  }
192
+ });
193
+ await server.listen();
194
+ server.printUrls();
195
+ logger.success("Dev server is running");
435
196
  }
436
- async function askProjectName(prompt) {
437
- while (true) {
438
- const value = await prompt.text('Project name');
439
- if (/^[a-z0-9][a-z0-9-]*$/u.test(value))
440
- return value;
441
- }
197
+ __name(devCommand, "devCommand");
198
+ async function buildCommand(options) {
199
+ logger.info("Building for production...");
200
+ await build({
201
+ configFile: options.config,
202
+ build: {
203
+ outDir: options.outDir
204
+ },
205
+ mode: options.mode || "production"
206
+ });
207
+ logger.success("Build complete");
442
208
  }
443
- function createNodePrompt() {
444
- const terminal = createInterface({
445
- input: process.stdin,
446
- output: process.stdout,
447
- });
448
- return {
449
- async text(question, initial) {
450
- const suffix = initial === undefined ? ': ' : ` (${initial}): `;
451
- const answer = (await terminal.question(`${question}${suffix}`)).trim();
452
- return answer === '' && initial !== undefined ? initial : answer;
453
- },
454
- async confirm(question, initial = false) {
455
- const suffix = initial ? ' (Y/n): ' : ' (y/N): ';
456
- const answer = (await terminal.question(`${question}${suffix}`)).trim().toLowerCase();
457
- if (answer === '')
458
- return initial;
459
- return answer === 'y' || answer === 'yes';
460
- },
461
- close() {
462
- terminal.close();
463
- },
209
+ __name(buildCommand, "buildCommand");
210
+ async function generateCommand(options) {
211
+ const type = options.type || await p.select({
212
+ message: "What to generate?",
213
+ options: [
214
+ { value: "component", label: "Component" },
215
+ { value: "page", label: "Page" }
216
+ ]
217
+ });
218
+ if (p.isCancel(type)) {
219
+ p.cancel("Cancelled");
220
+ return;
221
+ }
222
+ const name = options.name || await p.text({
223
+ message: "Name:",
224
+ placeholder: type === "component" ? "Button" : "AboutPage",
225
+ validate: /* @__PURE__ */ __name((v) => !v ? "Name is required" : void 0, "validate")
226
+ });
227
+ if (p.isCancel(name)) {
228
+ p.cancel("Cancelled");
229
+ return;
230
+ }
231
+ const templatesDir = getTemplatesDir();
232
+ if (type === "component") {
233
+ const targetDir = options.targetDir || resolve(process.cwd(), "src/components");
234
+ ensureDir(targetDir);
235
+ const context = {
236
+ name: toPascalCase(name),
237
+ kebabName: toKebabCase(name)
464
238
  };
239
+ const content = renderTemplate(
240
+ resolve(templatesDir, "component.hbs"),
241
+ context
242
+ );
243
+ const filename = `${context.name}.tsx`;
244
+ writeFile(targetDir, filename, content);
245
+ logger.success(`Created ${type} at ${resolve(targetDir, filename)}`);
246
+ } else {
247
+ const targetDir = options.targetDir || resolve(process.cwd(), "src/pages");
248
+ ensureDir(targetDir);
249
+ const context = {
250
+ name: toPascalCase(name),
251
+ path: `/${toKebabCase(name)}`
252
+ };
253
+ const content = renderTemplate(
254
+ resolve(templatesDir, "page.hbs"),
255
+ context
256
+ );
257
+ const filename = `${context.name}.tsx`;
258
+ writeFile(targetDir, filename, content);
259
+ logger.success(`Created ${type} at ${resolve(targetDir, filename)}`);
260
+ }
261
+ }
262
+ __name(generateCommand, "generateCommand");
263
+ function toPascalCase(str) {
264
+ return str.replace(/[^a-zA-Z0-9]/g, " ").split(/\s+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
265
+ }
266
+ __name(toPascalCase, "toPascalCase");
267
+ function toKebabCase(str) {
268
+ return str.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "").replace(/[^a-z0-9-]/g, "-").replace(/--+/g, "-").replace(/^-|-$/g, "");
465
269
  }
466
- function formatDiagnostics(diagnostics) {
467
- return diagnostics.map(formatDiagnostic).join('\n');
270
+ __name(toKebabCase, "toKebabCase");
271
+ async function addCommand(options) {
272
+ const pkg = options.package || await p.text({
273
+ message: "Package to add:",
274
+ placeholder: "@vobs/ui",
275
+ validate: /* @__PURE__ */ __name((v) => !v ? "Package name is required" : void 0, "validate")
276
+ });
277
+ if (p.isCancel(pkg)) {
278
+ p.cancel("Cancelled");
279
+ return;
280
+ }
281
+ const isDev = options.dev ?? await p.confirm({
282
+ message: "Save as dev dependency?",
283
+ initialValue: false
284
+ });
285
+ if (p.isCancel(isDev)) {
286
+ p.cancel("Cancelled");
287
+ return;
288
+ }
289
+ const pm = detectPackageManager(process.cwd());
290
+ logger.step(`Installing ${pkg}...`);
291
+ addPackage(process.cwd(), pm, pkg, isDev);
292
+ logger.success(`Installed ${pkg}`);
293
+ console.log(`
294
+ Import it in your code:
295
+ `);
296
+ console.log(` import { ... } from '${pkg}'`);
297
+ console.log();
468
298
  }
469
- function formatDiagnostic(diagnostic) {
470
- const source = diagnostic.sourceId ?? 'project';
471
- const position = diagnostic.start === undefined ? '' : `:${diagnostic.start.line}:${diagnostic.start.column}`;
472
- return `${source}${position} ${diagnostic.severity.toUpperCase()} ${diagnostic.code}: ${diagnostic.message}`;
299
+ __name(addCommand, "addCommand");
300
+
301
+ // packages/cli/src/banner.ts
302
+ var BANNER_LINE1 = "\u{1D5E9}\u{1D5E2}\u{1D5D5}\u{1D5E6} \u{1D5D6}\u{1D5DF}\u{1D5DC} \u{1D603}\u{1D7ED}.\u{1D7EC}";
303
+ var BANNER_TAGLINE = "Signals First \xB7 Zero Re-renders \xB7 Ultra Lightweight";
304
+ var INDENT = " ";
305
+ var DIM = "\x1B[2m";
306
+ var RESET = "\x1B[0m";
307
+ function showLogo() {
308
+ console.log();
309
+ console.log();
310
+ console.log(`${INDENT}${BANNER_LINE1}`);
311
+ console.log();
312
+ console.log(`${INDENT}${DIM}${BANNER_TAGLINE}${RESET}`);
313
+ console.log();
314
+ console.log();
473
315
  }
474
- function helpText() {
475
- return [
476
- 'Usage:',
477
- ' vobs check [--tsconfig tsconfig.json] [--view view.html]',
478
- ' vobs new [app|app-di|enterprise|lib|lib-di] [name] [--with-di] [--force]',
479
- ' vobs inspect --json [--tsconfig tsconfig.json] [--out manifest.json]',
480
- ' vobs ai tools --json',
481
- ' vobs ai serve JSON-RPC stdio bridge for read-only AI tools',
482
- ' vobs context --json (--route path | --file source) [--depth 0..4]',
483
- ' vobs diagnose --json [--route path | --file source] [--depth 0..4]',
484
- '',
485
- 'Check options:',
486
- ' -p, --tsconfig <path> TypeScript project file. Defaults to ./tsconfig.json.',
487
- ' --view <path> Require a specific HTML view to have exactly one owner.',
488
- '',
489
- 'New options:',
490
- ' -t, --template <name> Template: app, app-di, enterprise, lib, lib-di.',
491
- ' --with-di Include DI integration for app/lib templates.',
492
- ' --force Overwrite an existing project directory.',
493
- '',
494
- 'Global options:',
495
- ' --no-logo Hide the startup logo.',
496
- ' -h, --help Show this help.',
497
- '',
498
- 'Example:',
499
- ' vobs new enterprise my-admin',
500
- '',
501
- ].join('\n');
316
+ __name(showLogo, "showLogo");
317
+ function showBanner() {
318
+ console.log(`${INDENT}${BANNER_LINE1}`);
502
319
  }
503
- try {
504
- if (process.argv[1] !== undefined &&
505
- realpathSync.native(fileURLToPath(import.meta.url)) ===
506
- realpathSync.native(path.resolve(process.argv[1]))) {
507
- process.exitCode = await runVobsCli(process.argv.slice(2));
320
+ __name(showBanner, "showBanner");
321
+ function showDevBanner() {
322
+ console.log();
323
+ console.log(`${INDENT}${BANNER_LINE1}`);
324
+ console.log();
325
+ }
326
+ __name(showDevBanner, "showDevBanner");
327
+
328
+ // packages/cli/src/cli.ts
329
+ function createCLI() {
330
+ const cli = cac("vobs");
331
+ cli.command("init [name]", "Create a new vobs project").option("--dir <dir>", "Target directory").option("--pm <pm>", "Package manager (pnpm/npm/yarn)").option("--no-typescript", "Do not use TypeScript").option("--no-git", "Do not initialize git").action(async (name, options) => {
332
+ await initCommand({
333
+ name,
334
+ targetDir: options.dir,
335
+ packageManager: options.pm,
336
+ typescript: options.typescript !== false,
337
+ git: options.git !== false
338
+ });
339
+ });
340
+ cli.command("create [name]", "Alias for init").option("--dir <dir>", "Target directory").option("--pm <pm>", "Package manager (pnpm/npm/yarn)").option("--no-typescript", "Do not use TypeScript").option("--no-git", "Do not initialize git").action(async (name, options) => {
341
+ await initCommand({
342
+ name,
343
+ targetDir: options.dir,
344
+ packageManager: options.pm,
345
+ typescript: options.typescript !== false,
346
+ git: options.git !== false
347
+ });
348
+ });
349
+ cli.command("dev", "Start the dev server").option("--config <path>", "Vite config file").option("--port <port>", "Port to listen on").option("--open", "Open browser on start").action(async (options) => {
350
+ showDevBanner();
351
+ await devCommand({
352
+ config: options.config,
353
+ port: options.port,
354
+ open: options.open
355
+ });
356
+ });
357
+ cli.command("build", "Build for production").option("--config <path>", "Vite config file").option("--outDir <dir>", "Output directory").option("--mode <mode>", "Build mode").action(async (options) => {
358
+ await buildCommand({
359
+ config: options.config,
360
+ outDir: options.outDir,
361
+ mode: options.mode
362
+ });
363
+ });
364
+ cli.command("generate [name]", "Generate code (component/page)").alias("g").option("--type <type>", "Type: component or page").option("--dir <dir>", "Target directory").action(async (name, options) => {
365
+ await generateCommand({
366
+ name,
367
+ type: options.type || void 0,
368
+ targetDir: options.dir
369
+ });
370
+ });
371
+ cli.command("add [package]", "Add a package").option("-D, --dev", "Save as dev dependency").action(async (pkg, options) => {
372
+ await addCommand({
373
+ package: pkg,
374
+ dev: options.dev
375
+ });
376
+ });
377
+ cli.command("logo", "Show the vobs CLI logo and version info").action(() => {
378
+ showLogo();
379
+ });
380
+ cli.on("--help", () => {
381
+ showBanner();
382
+ });
383
+ cli.help();
384
+ cli.version("1.0.0");
385
+ return cli;
386
+ }
387
+ __name(createCLI, "createCLI");
388
+ async function startRepl() {
389
+ showLogo();
390
+ const rl = createInterface({
391
+ input: stdin,
392
+ output: stdout,
393
+ prompt: ` ${pc2.magenta("\u276F")} `,
394
+ terminal: true
395
+ });
396
+ rl.prompt();
397
+ for await (const rawLine of rl) {
398
+ const line = rawLine.trim();
399
+ if (!line) {
400
+ rl.prompt();
401
+ continue;
402
+ }
403
+ const args = splitArgs(line);
404
+ try {
405
+ await dispatch(args);
406
+ } catch (err) {
407
+ console.error(` ${pc2.red("\u2716")} ${err instanceof Error ? err.message : String(err)}`);
508
408
  }
409
+ rl.prompt();
410
+ }
411
+ }
412
+ __name(startRepl, "startRepl");
413
+ var TABLE = {
414
+ help: {
415
+ cmd: /* @__PURE__ */ __name(async () => {
416
+ console.log(` ${pc2.bold("Available commands:")}`);
417
+ console.log(` ${pc2.dim("exit|quit")} Leave the interactive CLI`);
418
+ console.log(` ${pc2.dim("clear")} Clear the screen`);
419
+ console.log(` ${pc2.dim("logo")} Re-print the banner`);
420
+ console.log(` ${pc2.dim("version")} Show version`);
421
+ console.log(` ${pc2.dim("init <name>")} Create a new vobs project`);
422
+ console.log(` ${pc2.dim("generate <name>")} Generate a component/page`);
423
+ console.log(` ${pc2.dim("add <package>")} Add a package`);
424
+ console.log(` ${pc2.dim("dev|build")} Use \`vobs dev\` / \`vobs build\` in a terminal`);
425
+ }, "cmd"),
426
+ help: ""
427
+ },
428
+ logo: { cmd: /* @__PURE__ */ __name(async () => showLogo(), "cmd"), help: "" },
429
+ version: { cmd: /* @__PURE__ */ __name(async () => showBanner(), "cmd"), help: "" },
430
+ clear: {
431
+ cmd: /* @__PURE__ */ __name(async () => {
432
+ process.stdout.write("\x1Bc");
433
+ }, "cmd"),
434
+ help: ""
435
+ },
436
+ exit: { cmd: /* @__PURE__ */ __name(async () => process.exit(0), "cmd"), help: "" },
437
+ quit: { cmd: /* @__PURE__ */ __name(async () => process.exit(0), "cmd"), help: "" },
438
+ init: {
439
+ cmd: /* @__PURE__ */ __name(async (args) => {
440
+ await initCommand({
441
+ name: args[0],
442
+ targetDir: void 0,
443
+ packageManager: void 0,
444
+ typescript: true,
445
+ git: true
446
+ });
447
+ }, "cmd"),
448
+ help: ""
449
+ },
450
+ generate: {
451
+ cmd: /* @__PURE__ */ __name(async (args) => {
452
+ await generateCommand({
453
+ name: args[0],
454
+ type: void 0,
455
+ targetDir: void 0
456
+ });
457
+ }, "cmd"),
458
+ help: ""
459
+ },
460
+ g: {
461
+ cmd: /* @__PURE__ */ __name(async (args) => {
462
+ await generateCommand({
463
+ name: args[0],
464
+ type: void 0,
465
+ targetDir: void 0
466
+ });
467
+ }, "cmd"),
468
+ help: ""
469
+ },
470
+ add: {
471
+ cmd: /* @__PURE__ */ __name(async (args) => {
472
+ await addCommand({ package: args[0], dev: args.includes("-D") || args.includes("--dev") });
473
+ }, "cmd"),
474
+ help: ""
475
+ }
476
+ };
477
+ async function dispatch(args) {
478
+ const name = args[0];
479
+ const rest = args.slice(1);
480
+ if (!name) return;
481
+ const entry = TABLE[name];
482
+ if (entry) {
483
+ await entry.cmd(rest);
484
+ return;
485
+ }
486
+ console.log(` ${pc2.yellow("\u26A0")} Unknown command: ${pc2.bold(String(name))}. Type ${pc2.cyan("help")} to see options.`);
487
+ }
488
+ __name(dispatch, "dispatch");
489
+ function splitArgs(line) {
490
+ const out = [];
491
+ const re = /[^\s"']+|"([^"]*)"|'([^']*)'/g;
492
+ let m;
493
+ while ((m = re.exec(line)) !== null) {
494
+ out.push(m[1] ?? m[2] ?? m[0]);
495
+ }
496
+ return out;
509
497
  }
510
- catch (error) {
511
- console.error(error instanceof Error ? error.message : String(error));
512
- process.exitCode = 1;
498
+ __name(splitArgs, "splitArgs");
499
+
500
+ // packages/cli/src/start.ts
501
+ async function start(args) {
502
+ if (args.length === 0 || args[0] === "cli") {
503
+ await startRepl();
504
+ return;
505
+ }
506
+ if (args.includes("--version") || args.includes("-V")) {
507
+ showBanner();
508
+ return;
509
+ }
510
+ createCLI().parse();
513
511
  }
512
+ __name(start, "start");
513
+
514
+ export { addCommand, addPackage, buildCommand, createCLI, detectPackageManager, devCommand, ensureDir, generateCommand, initCommand, installDependencies, logger, renderTemplate, scaffoldProject, showBanner, showDevBanner, showLogo, start, startRepl, writeFile };
515
+ //# sourceMappingURL=index.js.map
516
+ //# sourceMappingURL=index.js.map