@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/inspect.js DELETED
@@ -1,576 +0,0 @@
1
- /** @license MIT
2
- * Copyright (c) 2026 vobsjs
3
- * @vobs/cli
4
- */
5
- import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
6
- import * as path from 'node:path';
7
- import * as ts from 'typescript';
8
- import { analyzeView, checkViewProject } from '@vobs/compiler-dom';
9
- const ignoredDirectories = new Set(['.git', '.cache', '.tmp', 'coverage', 'dist', 'node_modules']);
10
- export function inspectVobsProject(options) {
11
- const tsconfigPath = path.resolve(options.tsconfigPath);
12
- const projectRoot = path.dirname(tsconfigPath);
13
- const workspaceRoot = findWorkspaceRoot(projectRoot);
14
- const rootPackage = readJsonFile(path.join(workspaceRoot, 'package.json'));
15
- const projectPackage = readJsonFile(path.join(projectRoot, 'package.json'));
16
- const config = readTsConfig(tsconfigPath, projectRoot);
17
- const files = collectProjectFiles(projectRoot);
18
- const routeRoot = path.join(projectRoot, 'src', 'pages');
19
- const routes = collectRoutes(routeRoot, projectRoot);
20
- const checkResult = checkViewProject({ tsconfigPath });
21
- return {
22
- schemaVersion: '0.1',
23
- generatedBy: 'vobs inspect',
24
- workspace: {
25
- root: manifestPath(workspaceRoot, workspaceRoot),
26
- ...(typeof rootPackage?.packageManager === 'string'
27
- ? { packageManager: rootPackage.packageManager }
28
- : {}),
29
- },
30
- project: {
31
- root: manifestPath(projectRoot, workspaceRoot),
32
- ...(typeof projectPackage?.name === 'string' ? { name: projectPackage.name } : {}),
33
- ...(typeof projectPackage?.name === 'string' ? { packageName: projectPackage.name } : {}),
34
- ...(typeof projectPackage?.version === 'string'
35
- ? { packageVersion: projectPackage.version }
36
- : {}),
37
- },
38
- tsconfig: {
39
- path: manifestPath(tsconfigPath, workspaceRoot),
40
- rootNames: config.fileNames.map((fileName) => manifestPath(fileName, workspaceRoot)),
41
- references: (config.projectReferences ?? []).map((reference) => manifestPath(reference.path, workspaceRoot)),
42
- },
43
- packages: collectPackages(workspaceRoot),
44
- files: {
45
- views: files.views.map((view) => manifestPath(view.file, workspaceRoot)),
46
- routes: routes.map((route) => toManifestRoute(route, workspaceRoot)),
47
- components: files.components.map((component) => toManifestComponent(component, workspaceRoot)),
48
- globalComponents: checkResult.globalComponentTags ?? [],
49
- viewContracts: files.views.map((view) => toManifestView(view, workspaceRoot)),
50
- authorization: files.authorization.map((file) => toManifestPermission(file, workspaceRoot)),
51
- },
52
- diagnostics: checkResult.diagnostics.map((diagnostic) => toManifestDiagnostic(diagnostic, workspaceRoot)),
53
- };
54
- }
55
- export function writeInspectOutput(manifest, outputPath) {
56
- const workingDirectory = path.resolve(process.cwd());
57
- const target = path.resolve(workingDirectory, outputPath);
58
- const relativeTarget = path.relative(workingDirectory, target);
59
- if (relativeTarget === '' ||
60
- relativeTarget.startsWith(`..${path.sep}`) ||
61
- path.isAbsolute(relativeTarget)) {
62
- throw new Error(`Refusing to write inspect output outside the current directory: ${target}`);
63
- }
64
- const fileName = path.basename(target).toLowerCase();
65
- if (fileName === '' || /(?:^|[._-])(?:env|secret|token|credential|password)/u.test(fileName)) {
66
- throw new Error(`Refusing to write inspect output to a sensitive filename: ${target}`);
67
- }
68
- mkdirSync(path.dirname(target), { recursive: true });
69
- writeFileSync(target, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
70
- }
71
- function collectProjectFiles(root) {
72
- const views = [];
73
- const components = [];
74
- const authorization = [];
75
- for (const fileName of walkFiles(root)) {
76
- if (fileName.endsWith('.html')) {
77
- views.push({ file: fileName, ...analyzeView(readFileSync(fileName, 'utf8'), fileName) });
78
- }
79
- if (!isTypeScriptFile(fileName))
80
- continue;
81
- const source = readFileSync(fileName, 'utf8');
82
- const component = readComponentFile(fileName, source);
83
- if (component !== undefined)
84
- components.push(component);
85
- const permission = readPermissionFile(fileName, source);
86
- if (permission !== undefined)
87
- authorization.push(permission);
88
- }
89
- views.sort((left, right) => left.file.localeCompare(right.file));
90
- components.sort((left, right) => left.file.localeCompare(right.file));
91
- authorization.sort((left, right) => left.file.localeCompare(right.file));
92
- return { views, components, authorization };
93
- }
94
- function readComponentFile(fileName, source) {
95
- const sourceFile = ts.createSourceFile(fileName, source, ts.ScriptTarget.Latest, true);
96
- const definition = findDefinitionCall(sourceFile);
97
- if (definition === undefined)
98
- return undefined;
99
- const viewRequest = readHtmlImport(sourceFile);
100
- const view = viewRequest === undefined ? undefined : path.resolve(path.dirname(fileName), viewRequest);
101
- return {
102
- kind: definition.kind,
103
- file: fileName,
104
- ...(view === undefined ? {} : { view }),
105
- exports: readExportNames(source),
106
- props: readStaticArrayProperty(definition.options, 'props'),
107
- emits: readStaticArrayProperty(definition.options, 'emits'),
108
- exposed: readSetupExposedNames(definition.options, sourceFile),
109
- registeredComponents: readStaticObjectKeys(definition.options, 'components'),
110
- };
111
- }
112
- function findDefinitionCall(sourceFile) {
113
- let result;
114
- const visit = (node) => {
115
- if (result === undefined &&
116
- ts.isCallExpression(node) &&
117
- ts.isIdentifier(node.expression) &&
118
- (node.expression.text === 'definePage' || node.expression.text === 'defineComponent')) {
119
- const options = node.arguments[0];
120
- if (options !== undefined && ts.isObjectLiteralExpression(options)) {
121
- result = {
122
- kind: node.expression.text === 'definePage' ? 'page' : 'component',
123
- options,
124
- };
125
- return;
126
- }
127
- }
128
- ts.forEachChild(node, visit);
129
- };
130
- visit(sourceFile);
131
- return result;
132
- }
133
- function readHtmlImport(sourceFile) {
134
- for (const statement of sourceFile.statements) {
135
- if (ts.isImportDeclaration(statement) &&
136
- ts.isStringLiteral(statement.moduleSpecifier) &&
137
- statement.moduleSpecifier.text.endsWith('.html')) {
138
- return statement.moduleSpecifier.text;
139
- }
140
- }
141
- return undefined;
142
- }
143
- function readStaticArrayProperty(options, name) {
144
- const expression = readPropertyExpression(options, name);
145
- const array = expression === undefined ? undefined : unwrapExpression(expression);
146
- if (array === undefined || !ts.isArrayLiteralExpression(array))
147
- return [];
148
- return array.elements
149
- .map((element) => (ts.isStringLiteral(element) ? element.text : undefined))
150
- .filter((value) => value !== undefined)
151
- .sort();
152
- }
153
- function readStaticObjectKeys(options, name) {
154
- const expression = readPropertyExpression(options, name);
155
- const object = expression === undefined ? undefined : unwrapExpression(expression);
156
- if (object === undefined || !ts.isObjectLiteralExpression(object))
157
- return [];
158
- return object.properties
159
- .map(readObjectElementName)
160
- .filter((value) => value !== undefined)
161
- .sort();
162
- }
163
- function readSetupExposedNames(options, sourceFile) {
164
- const setup = findSetupFunction(options, sourceFile);
165
- const body = setup !== undefined && 'body' in setup ? setup.body : undefined;
166
- if (body === undefined)
167
- return [];
168
- const names = new Set();
169
- if (!ts.isBlock(body)) {
170
- const expression = unwrapExpression(body);
171
- if (ts.isObjectLiteralExpression(expression))
172
- collectObjectKeys(expression, names);
173
- }
174
- const visit = (node) => {
175
- if (node !== body && ts.isFunctionLike(node))
176
- return;
177
- if (ts.isReturnStatement(node) && node.expression !== undefined) {
178
- const expression = unwrapExpression(node.expression);
179
- if (ts.isObjectLiteralExpression(expression))
180
- collectObjectKeys(expression, names);
181
- }
182
- if (ts.isCallExpression(node) && isExposeCall(node.expression)) {
183
- const argument = node.arguments[0];
184
- if (argument !== undefined) {
185
- const expression = unwrapExpression(argument);
186
- if (ts.isObjectLiteralExpression(expression))
187
- collectObjectKeys(expression, names);
188
- }
189
- }
190
- ts.forEachChild(node, visit);
191
- };
192
- if (ts.isBlock(body))
193
- visit(body);
194
- return [...names].sort();
195
- }
196
- function findSetupFunction(options, sourceFile) {
197
- for (const property of options.properties) {
198
- if (readPropertyName(property.name) !== 'setup')
199
- continue;
200
- if (ts.isMethodDeclaration(property))
201
- return property;
202
- if (ts.isShorthandPropertyAssignment(property)) {
203
- return findTopLevelFunction(sourceFile, property.name.text);
204
- }
205
- if (ts.isPropertyAssignment(property)) {
206
- const initializer = unwrapExpression(property.initializer);
207
- if (ts.isArrowFunction(initializer) || ts.isFunctionExpression(initializer)) {
208
- return initializer;
209
- }
210
- if (ts.isIdentifier(initializer))
211
- return findTopLevelFunction(sourceFile, initializer.text);
212
- }
213
- }
214
- return undefined;
215
- }
216
- function findTopLevelFunction(sourceFile, name) {
217
- for (const statement of sourceFile.statements) {
218
- if (ts.isFunctionDeclaration(statement) && statement.name?.text === name)
219
- return statement;
220
- if (!ts.isVariableStatement(statement))
221
- continue;
222
- for (const declaration of statement.declarationList.declarations) {
223
- if (!ts.isIdentifier(declaration.name) || declaration.name.text !== name)
224
- continue;
225
- const initializer = declaration.initializer === undefined
226
- ? undefined
227
- : unwrapExpression(declaration.initializer);
228
- if (initializer !== undefined &&
229
- (ts.isArrowFunction(initializer) || ts.isFunctionExpression(initializer))) {
230
- return initializer;
231
- }
232
- }
233
- }
234
- return undefined;
235
- }
236
- function isExposeCall(expression) {
237
- return ((ts.isIdentifier(expression) && expression.text === 'expose') ||
238
- (ts.isPropertyAccessExpression(expression) && expression.name.text === 'expose'));
239
- }
240
- function collectObjectKeys(object, names) {
241
- for (const property of object.properties) {
242
- const name = readObjectElementName(property);
243
- if (name !== undefined)
244
- names.add(name);
245
- }
246
- }
247
- function readPropertyExpression(object, name) {
248
- for (const property of object.properties) {
249
- if (readPropertyName(property.name) !== name)
250
- continue;
251
- if (ts.isPropertyAssignment(property))
252
- return property.initializer;
253
- }
254
- return undefined;
255
- }
256
- function readPropertyName(name) {
257
- if (name === undefined)
258
- return undefined;
259
- if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) {
260
- return name.text;
261
- }
262
- return undefined;
263
- }
264
- function readObjectElementName(property) {
265
- return ts.isSpreadAssignment(property) ? undefined : readPropertyName(property.name);
266
- }
267
- function unwrapExpression(expression) {
268
- let current = expression;
269
- while (ts.isParenthesizedExpression(current) ||
270
- ts.isAsExpression(current) ||
271
- ts.isSatisfiesExpression(current) ||
272
- ts.isNonNullExpression(current)) {
273
- current = current.expression;
274
- }
275
- return current;
276
- }
277
- function readPermissionFile(fileName, source) {
278
- const exportedSymbols = readExportNames(source);
279
- const kinds = [];
280
- const authorizerOptions = readAuthorizerOptionNames(fileName, source);
281
- if (authorizerOptions !== undefined ||
282
- exportedSymbols.some((name) => /authorization|authorizer|permission/iu.test(name))) {
283
- kinds.push('authorization');
284
- }
285
- if (authorizerOptions?.has('roles') === true ||
286
- exportedSymbols.some((name) => /^roles?$/iu.test(name) || /^AuthorizationRole/u.test(name))) {
287
- kinds.push('role');
288
- }
289
- if (authorizerOptions?.has('policies') === true ||
290
- exportedSymbols.some((name) => /^polic(?:y|ies)$/iu.test(name) || /^AuthorizationPolicy/u.test(name))) {
291
- kinds.push('policy');
292
- }
293
- if (kinds.length === 0)
294
- return undefined;
295
- return {
296
- file: fileName,
297
- kinds,
298
- symbols: exportedSymbols.filter((name) => /authorization|authorizer|permission|roles?|polic(?:y|ies)/iu.test(name)),
299
- };
300
- }
301
- function readAuthorizerOptionNames(fileName, source) {
302
- const sourceFile = ts.createSourceFile(fileName, source, ts.ScriptTarget.Latest, true);
303
- let options;
304
- const visit = (node) => {
305
- if (options === undefined &&
306
- ts.isCallExpression(node) &&
307
- ts.isIdentifier(node.expression) &&
308
- node.expression.text === 'createAuthorizer') {
309
- options = new Set();
310
- const argument = node.arguments[0];
311
- if (argument !== undefined && ts.isObjectLiteralExpression(argument)) {
312
- for (const property of argument.properties) {
313
- if ((ts.isPropertyAssignment(property) || ts.isShorthandPropertyAssignment(property)) &&
314
- ts.isIdentifier(property.name)) {
315
- options.add(property.name.text);
316
- }
317
- }
318
- }
319
- }
320
- ts.forEachChild(node, visit);
321
- };
322
- visit(sourceFile);
323
- return options;
324
- }
325
- function readExportNames(source) {
326
- const names = new Set();
327
- if (/\bexport\s+default\b/u.test(source))
328
- names.add('default');
329
- for (const match of source.matchAll(/\bexport\s+(?:const|let|var|function|class|interface|type)\s+([\w$]+)/gu)) {
330
- const name = match[1];
331
- if (name !== undefined)
332
- names.add(name);
333
- }
334
- for (const match of source.matchAll(/\bexport\s*\{([^}]*)\}/gu)) {
335
- for (const entry of (match[1] ?? '').split(',')) {
336
- const exported = /\bas\s+([\w$]+)/u.exec(entry)?.[1] ?? entry.trim();
337
- if (exported !== '')
338
- names.add(exported);
339
- }
340
- }
341
- return [...names].sort();
342
- }
343
- function collectRoutes(root, projectRoot) {
344
- if (!existsSync(root) || !statSync(root).isDirectory())
345
- return [];
346
- const routes = [];
347
- walkRouteDirectory(root, root, [], undefined, routes);
348
- const notFound = path.join(root, '_404.ts');
349
- if (existsSync(notFound)) {
350
- routes.push({
351
- kind: 'not-found',
352
- path: '*',
353
- file: notFound,
354
- layouts: [],
355
- hasGuard: false,
356
- hasLoader: false,
357
- });
358
- }
359
- const error = path.join(root, '_error.ts');
360
- if (existsSync(error)) {
361
- routes.push({
362
- kind: 'error',
363
- path: 'error',
364
- file: error,
365
- layouts: [],
366
- hasGuard: false,
367
- hasLoader: false,
368
- });
369
- }
370
- return routes
371
- .filter((route) => route.file.startsWith(projectRoot))
372
- .sort((left, right) => left.path.localeCompare(right.path) || left.file.localeCompare(right.file));
373
- }
374
- function walkRouteDirectory(directory, pagesRoot, parentLayouts, parentError, routes) {
375
- const layout = path.join(directory, '_layout.ts');
376
- const layouts = existsSync(layout) ? [...parentLayouts, layout] : parentLayouts;
377
- const localError = path.join(directory, '_error.ts');
378
- const error = existsSync(localError) ? localError : parentError;
379
- for (const entry of readdirSync(directory, { withFileTypes: true })) {
380
- const entryPath = path.join(directory, entry.name);
381
- if (entry.isDirectory()) {
382
- walkRouteDirectory(entryPath, pagesRoot, layouts, error, routes);
383
- continue;
384
- }
385
- if (!entry.name.endsWith('.ts') || entry.name.endsWith('.d.ts') || entry.name.startsWith('_')) {
386
- continue;
387
- }
388
- const source = readFileSync(entryPath, 'utf8');
389
- routes.push({
390
- kind: 'page',
391
- path: routePathForFile(entryPath, pagesRoot),
392
- file: entryPath,
393
- layouts,
394
- ...(error === undefined ? {} : { error }),
395
- hasGuard: hasExportedRouteMember(source, 'guard'),
396
- hasLoader: hasExportedRouteMember(source, 'loader'),
397
- });
398
- }
399
- }
400
- function routePathForFile(fileName, pagesRoot) {
401
- const relative = path.relative(pagesRoot, fileName).replaceAll(path.sep, '/');
402
- const segments = relative
403
- .slice(0, -'.ts'.length)
404
- .split('/')
405
- .flatMap((segment) => {
406
- if (segment === 'index')
407
- return [];
408
- if (segment.startsWith('(') && segment.endsWith(')'))
409
- return [];
410
- if (segment.startsWith('[...') && segment.endsWith(']'))
411
- return [`*${segment.slice(4, -1)}`];
412
- if (segment.startsWith('[') && segment.endsWith(']'))
413
- return [`:${segment.slice(1, -1)}`];
414
- return [segment];
415
- });
416
- return segments.length === 0 ? '/' : `/${segments.join('/')}`;
417
- }
418
- function hasExportedRouteMember(source, name) {
419
- return new RegExp(`^\\s*export\\s+(?:(?:async\\s+)?function|const|let|var)\\s+${name}\\b`, 'mu').test(source);
420
- }
421
- function walkFiles(root) {
422
- const files = [];
423
- walk(root, files);
424
- return files;
425
- }
426
- function walk(directory, files) {
427
- for (const entry of readdirSync(directory, { withFileTypes: true })) {
428
- if (entry.isDirectory()) {
429
- if (!ignoredDirectories.has(entry.name) && entry.name !== '__tests__') {
430
- walk(path.join(directory, entry.name), files);
431
- }
432
- continue;
433
- }
434
- const fileName = path.join(directory, entry.name);
435
- if (!entry.name.endsWith('.test.ts') &&
436
- !entry.name.endsWith('.spec.ts') &&
437
- (entry.name.endsWith('.html') || isTypeScriptFile(fileName))) {
438
- files.push(fileName);
439
- }
440
- }
441
- }
442
- function isTypeScriptFile(fileName) {
443
- return (fileName.endsWith('.ts') || fileName.endsWith('.tsx')) && !fileName.endsWith('.d.ts');
444
- }
445
- function collectPackages(workspaceRoot) {
446
- const roots = [];
447
- for (const groupName of ['packages', 'plugins']) {
448
- const group = path.join(workspaceRoot, groupName);
449
- if (!existsSync(group) || !statSync(group).isDirectory())
450
- continue;
451
- for (const entry of readdirSync(group, { withFileTypes: true })) {
452
- if (!entry.isDirectory())
453
- continue;
454
- const direct = path.join(group, entry.name);
455
- if (existsSync(path.join(direct, 'package.json'))) {
456
- roots.push(direct);
457
- continue;
458
- }
459
- for (const nested of readdirSync(direct, { withFileTypes: true })) {
460
- if (nested.isDirectory() && existsSync(path.join(direct, nested.name, 'package.json'))) {
461
- roots.push(path.join(direct, nested.name));
462
- }
463
- }
464
- }
465
- }
466
- return roots
467
- .map((root) => {
468
- const packageJson = readJsonFile(path.join(root, 'package.json'));
469
- return {
470
- name: typeof packageJson?.name === 'string'
471
- ? packageJson.name
472
- : manifestPath(root, workspaceRoot),
473
- ...(typeof packageJson?.version === 'string' ? { version: packageJson.version } : {}),
474
- path: manifestPath(root, workspaceRoot),
475
- ...(typeof packageJson?.private === 'boolean' ? { private: packageJson.private } : {}),
476
- };
477
- })
478
- .sort((left, right) => left.path.localeCompare(right.path));
479
- }
480
- function readTsConfig(tsconfigPath, projectRoot) {
481
- const read = ts.readConfigFile(tsconfigPath, (fileName) => ts.sys.readFile(fileName));
482
- if (read.error !== undefined)
483
- throw new Error(formatTsDiagnostic(read.error));
484
- return ts.parseJsonConfigFileContent(read.config, ts.sys, projectRoot, undefined, tsconfigPath);
485
- }
486
- function readJsonFile(fileName) {
487
- try {
488
- const value = JSON.parse(readFileSync(fileName, 'utf8'));
489
- return typeof value === 'object' && value !== null && !Array.isArray(value)
490
- ? value
491
- : undefined;
492
- }
493
- catch {
494
- return undefined;
495
- }
496
- }
497
- function findWorkspaceRoot(start) {
498
- let current = path.resolve(start);
499
- while (true) {
500
- if (existsSync(path.join(current, 'pnpm-workspace.yaml')))
501
- return current;
502
- const parent = path.dirname(current);
503
- if (parent === current)
504
- return path.resolve(start);
505
- current = parent;
506
- }
507
- }
508
- function manifestPath(fileName, workspaceRoot) {
509
- const relative = path.relative(workspaceRoot, fileName).replaceAll(path.sep, '/');
510
- return relative === '' ? '.' : relative;
511
- }
512
- function toManifestRoute(route, workspaceRoot) {
513
- return {
514
- kind: route.kind,
515
- path: route.path,
516
- file: manifestPath(route.file, workspaceRoot),
517
- layouts: route.layouts.map((fileName) => manifestPath(fileName, workspaceRoot)),
518
- ...(route.error === undefined ? {} : { error: manifestPath(route.error, workspaceRoot) }),
519
- hasGuard: route.hasGuard,
520
- hasLoader: route.hasLoader,
521
- };
522
- }
523
- function toManifestComponent(component, workspaceRoot) {
524
- return {
525
- kind: component.kind,
526
- file: manifestPath(component.file, workspaceRoot),
527
- ...(component.view === undefined ? {} : { view: manifestPath(component.view, workspaceRoot) }),
528
- exports: component.exports,
529
- props: component.props,
530
- emits: component.emits,
531
- exposed: component.exposed,
532
- registeredComponents: component.registeredComponents,
533
- };
534
- }
535
- function toManifestView(view, workspaceRoot) {
536
- return {
537
- file: manifestPath(view.file, workspaceRoot),
538
- bindings: view.bindings,
539
- events: view.events,
540
- directives: view.directives,
541
- components: view.components,
542
- includes: view.includes
543
- .map((request) => path.resolve(path.dirname(view.file), request))
544
- .filter((fileName) => isPathInside(workspaceRoot, fileName))
545
- .map((fileName) => manifestPath(fileName, workspaceRoot)),
546
- refs: view.refs,
547
- slots: view.slots,
548
- };
549
- }
550
- function toManifestPermission(file, workspaceRoot) {
551
- return {
552
- file: manifestPath(file.file, workspaceRoot),
553
- kinds: file.kinds,
554
- symbols: file.symbols,
555
- };
556
- }
557
- function toManifestDiagnostic(diagnostic, workspaceRoot) {
558
- return {
559
- code: diagnostic.code,
560
- message: diagnostic.message,
561
- severity: diagnostic.severity,
562
- phase: diagnostic.phase,
563
- ...(diagnostic.sourceId === undefined
564
- ? {}
565
- : { source: manifestPath(diagnostic.sourceId, workspaceRoot) }),
566
- ...(diagnostic.start === undefined ? {} : { start: diagnostic.start }),
567
- ...(diagnostic.end === undefined ? {} : { end: diagnostic.end }),
568
- };
569
- }
570
- function formatTsDiagnostic(diagnostic) {
571
- return ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
572
- }
573
- function isPathInside(root, fileName) {
574
- const relative = path.relative(root, fileName);
575
- return relative === '' || (!relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));
576
- }
@@ -1,5 +0,0 @@
1
- /** @license MIT
2
- * Copyright (c) 2026 vobsjs
3
- * @vobs/cli
4
- */
5
- export declare function renderCliLogo(): string;
@@ -1,16 +0,0 @@
1
- /** @license MIT
2
- * Copyright (c) 2026 vobsjs
3
- * @vobs/cli
4
- */
5
- import chalk from 'chalk';
6
- const BRAND_COLOR = '#2DD4BF';
7
- const LOGO_MARK = ['╭──────╮', '│ vobs │', '╰──────╯'];
8
- const PRODUCT_NAME = 'vobs';
9
- export function renderCliLogo() {
10
- const brand = chalk.hex(BRAND_COLOR);
11
- return [
12
- brand(LOGO_MARK[0]),
13
- `${brand(LOGO_MARK[1])} ${brand.bold(PRODUCT_NAME)}`,
14
- brand(LOGO_MARK[2]),
15
- ].join('\n');
16
- }
@@ -1,23 +0,0 @@
1
- /** @license MIT
2
- * Copyright (c) 2026 vobsjs
3
- * @vobs/cli
4
- */
5
- export type ScaffoldTemplate = 'app' | 'app-di' | 'enterprise' | 'lib' | 'lib-di';
6
- export interface ScaffoldOptions {
7
- readonly template?: ScaffoldTemplate;
8
- readonly withDi?: boolean;
9
- readonly force?: boolean;
10
- }
11
- export interface ScaffoldResult {
12
- readonly name: string;
13
- readonly targetDir: string;
14
- readonly template: ScaffoldTemplate;
15
- }
16
- export interface TemplateFile {
17
- readonly path: string;
18
- readonly content: string;
19
- }
20
- export declare function createScaffold(type: string | undefined, name: string | undefined, options?: ScaffoldOptions, cwd?: string): ScaffoldResult;
21
- export declare function resolveScaffoldTemplate(type: string | undefined, options?: ScaffoldOptions): ScaffoldTemplate;
22
- export declare function isScaffoldTemplate(value: string): value is ScaffoldTemplate;
23
- export declare function scaffoldSuccessText(result: ScaffoldResult): string;