@strav/cli 0.1.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/package.json +51 -0
- package/src/cli/bootstrap.ts +79 -0
- package/src/cli/command_loader.ts +180 -0
- package/src/cli/index.ts +3 -0
- package/src/cli/strav.ts +13 -0
- package/src/commands/db_seed.ts +77 -0
- package/src/commands/generate_api.ts +74 -0
- package/src/commands/generate_key.ts +47 -0
- package/src/commands/generate_models.ts +117 -0
- package/src/commands/generate_seeder.ts +68 -0
- package/src/commands/migration_compare.ts +153 -0
- package/src/commands/migration_fresh.ts +149 -0
- package/src/commands/migration_generate.ts +93 -0
- package/src/commands/migration_rollback.ts +66 -0
- package/src/commands/migration_run.ts +57 -0
- package/src/commands/package_install.ts +161 -0
- package/src/commands/queue_flush.ts +35 -0
- package/src/commands/queue_retry.ts +34 -0
- package/src/commands/queue_work.ts +40 -0
- package/src/commands/scheduler_work.ts +46 -0
- package/src/config/loader.ts +39 -0
- package/src/generators/api_generator.ts +1036 -0
- package/src/generators/config.ts +128 -0
- package/src/generators/doc_generator.ts +986 -0
- package/src/generators/index.ts +11 -0
- package/src/generators/model_generator.ts +621 -0
- package/src/generators/route_generator.ts +188 -0
- package/src/generators/test_generator.ts +1668 -0
- package/src/index.ts +2 -0
- package/tsconfig.json +5 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { resolve, relative } from 'node:path'
|
|
2
|
+
import type { GeneratedFile } from './model_generator.ts'
|
|
3
|
+
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Types
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
export interface GeneratorPaths {
|
|
9
|
+
models: string
|
|
10
|
+
enums: string
|
|
11
|
+
controllers: string
|
|
12
|
+
services: string
|
|
13
|
+
events: string
|
|
14
|
+
validators: string
|
|
15
|
+
policies: string
|
|
16
|
+
resources: string
|
|
17
|
+
routes: string
|
|
18
|
+
tests: string
|
|
19
|
+
docs: string
|
|
20
|
+
// Database paths
|
|
21
|
+
schemas: string
|
|
22
|
+
migrations: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ModelNaming {
|
|
26
|
+
[domain: string]: string | null | undefined
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface GeneratorConfig {
|
|
30
|
+
paths?: Partial<GeneratorPaths>
|
|
31
|
+
modelNaming?: ModelNaming
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Defaults
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
|
|
38
|
+
const DEFAULT_PATHS: GeneratorPaths = {
|
|
39
|
+
models: 'app/models',
|
|
40
|
+
enums: 'app/enums',
|
|
41
|
+
controllers: 'app/http/controllers',
|
|
42
|
+
services: 'app/services',
|
|
43
|
+
events: 'app/events',
|
|
44
|
+
validators: 'app/validators',
|
|
45
|
+
policies: 'app/policies',
|
|
46
|
+
resources: 'app/resources',
|
|
47
|
+
routes: 'start',
|
|
48
|
+
tests: 'tests/api',
|
|
49
|
+
docs: 'public/_docs',
|
|
50
|
+
// Database paths
|
|
51
|
+
schemas: 'database/schemas',
|
|
52
|
+
migrations: 'database/migrations',
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
// Helpers
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
|
|
59
|
+
/** Merge user config with defaults, returning fully resolved paths. */
|
|
60
|
+
export function resolvePaths(config?: GeneratorConfig, scope?: string): GeneratorPaths {
|
|
61
|
+
const basePaths = config?.paths ? { ...DEFAULT_PATHS, ...config.paths } : { ...DEFAULT_PATHS }
|
|
62
|
+
|
|
63
|
+
// If a domain is provided, append it to models and enums paths
|
|
64
|
+
if (scope) {
|
|
65
|
+
return {
|
|
66
|
+
...basePaths,
|
|
67
|
+
models: `${basePaths.models}/${scope}`,
|
|
68
|
+
enums: `${basePaths.enums}/${scope}`
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return basePaths
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Compute a relative import path from a file inside `fromDir` to a file
|
|
77
|
+
* inside `toDir`. Returns a path suitable for ES import statements
|
|
78
|
+
* (e.g. `'../../services'`, `'../enums'`).
|
|
79
|
+
*/
|
|
80
|
+
export function relativeImport(fromDir: string, toDir: string): string {
|
|
81
|
+
let rel = relative(fromDir, toDir)
|
|
82
|
+
// Normalize Windows separators for import paths
|
|
83
|
+
rel = rel.split('\\').join('/')
|
|
84
|
+
return rel.startsWith('.') ? rel : './' + rel
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Get the model name prefix for a given domain. Returns empty string if no prefix. */
|
|
88
|
+
export function getModelPrefix(config: GeneratorConfig | undefined, domain: string | undefined): string {
|
|
89
|
+
if (!domain) return ''
|
|
90
|
+
|
|
91
|
+
// Public domain has no prefix by default
|
|
92
|
+
if (domain === 'public') {
|
|
93
|
+
return config?.modelNaming?.[domain] ?? ''
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// For other domains, use configured prefix or default to PascalCase
|
|
97
|
+
if (config?.modelNaming?.[domain] !== undefined) {
|
|
98
|
+
return config.modelNaming[domain] || ''
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Default prefix: PascalCase the domain name
|
|
102
|
+
return domain.charAt(0).toUpperCase() + domain.slice(1).toLowerCase()
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Format generated files with Prettier and write them to disk.
|
|
107
|
+
* Resolves the Prettier config from each file's location (so the project's
|
|
108
|
+
* `.prettierrc` is picked up automatically). Falls back to writing
|
|
109
|
+
* unformatted content if Prettier is not installed.
|
|
110
|
+
*/
|
|
111
|
+
export async function formatAndWrite(files: GeneratedFile[]): Promise<void> {
|
|
112
|
+
let prettier: typeof import('prettier') | null = null
|
|
113
|
+
try {
|
|
114
|
+
prettier = await import('prettier')
|
|
115
|
+
} catch {
|
|
116
|
+
// Prettier not installed — write unformatted
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
for (const file of files) {
|
|
120
|
+
let content = file.content
|
|
121
|
+
if (prettier) {
|
|
122
|
+
const filePath = resolve(file.path)
|
|
123
|
+
const options = await prettier.resolveConfig(filePath)
|
|
124
|
+
content = await prettier.format(content, { ...options, filepath: filePath })
|
|
125
|
+
}
|
|
126
|
+
await Bun.write(file.path, content)
|
|
127
|
+
}
|
|
128
|
+
}
|