@opensaas/stack-cli 0.1.7 → 0.4.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 +1 -1
- package/CHANGELOG.md +348 -0
- package/CLAUDE.md +60 -12
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +13 -13
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/mcp.d.ts +6 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +116 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/generator/context.d.ts.map +1 -1
- package/dist/generator/context.js +40 -7
- package/dist/generator/context.js.map +1 -1
- package/dist/generator/index.d.ts +4 -1
- package/dist/generator/index.d.ts.map +1 -1
- package/dist/generator/index.js +4 -1
- package/dist/generator/index.js.map +1 -1
- package/dist/generator/lists.d.ts +31 -0
- package/dist/generator/lists.d.ts.map +1 -0
- package/dist/generator/lists.js +123 -0
- package/dist/generator/lists.js.map +1 -0
- package/dist/generator/plugin-types.d.ts +10 -0
- package/dist/generator/plugin-types.d.ts.map +1 -0
- package/dist/generator/plugin-types.js +122 -0
- package/dist/generator/plugin-types.js.map +1 -0
- package/dist/generator/prisma-config.d.ts +17 -0
- package/dist/generator/prisma-config.d.ts.map +1 -0
- package/dist/generator/prisma-config.js +40 -0
- package/dist/generator/prisma-config.js.map +1 -0
- package/dist/generator/prisma-extensions.d.ts +11 -0
- package/dist/generator/prisma-extensions.d.ts.map +1 -0
- package/dist/generator/prisma-extensions.js +134 -0
- package/dist/generator/prisma-extensions.js.map +1 -0
- package/dist/generator/prisma.d.ts.map +1 -1
- package/dist/generator/prisma.js +5 -2
- package/dist/generator/prisma.js.map +1 -1
- package/dist/generator/types.d.ts.map +1 -1
- package/dist/generator/types.js +201 -17
- package/dist/generator/types.js.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/lib/documentation-provider.d.ts +43 -0
- package/dist/mcp/lib/documentation-provider.d.ts.map +1 -0
- package/dist/mcp/lib/documentation-provider.js +163 -0
- package/dist/mcp/lib/documentation-provider.js.map +1 -0
- package/dist/mcp/lib/features/catalog.d.ts +26 -0
- package/dist/mcp/lib/features/catalog.d.ts.map +1 -0
- package/dist/mcp/lib/features/catalog.js +291 -0
- package/dist/mcp/lib/features/catalog.js.map +1 -0
- package/dist/mcp/lib/generators/feature-generator.d.ts +35 -0
- package/dist/mcp/lib/generators/feature-generator.d.ts.map +1 -0
- package/dist/mcp/lib/generators/feature-generator.js +546 -0
- package/dist/mcp/lib/generators/feature-generator.js.map +1 -0
- package/dist/mcp/lib/types.d.ts +80 -0
- package/dist/mcp/lib/types.d.ts.map +1 -0
- package/dist/mcp/lib/types.js +5 -0
- package/dist/mcp/lib/types.js.map +1 -0
- package/dist/mcp/lib/wizards/wizard-engine.d.ts +71 -0
- package/dist/mcp/lib/wizards/wizard-engine.d.ts.map +1 -0
- package/dist/mcp/lib/wizards/wizard-engine.js +356 -0
- package/dist/mcp/lib/wizards/wizard-engine.js.map +1 -0
- package/dist/mcp/server/index.d.ts +8 -0
- package/dist/mcp/server/index.d.ts.map +1 -0
- package/dist/mcp/server/index.js +202 -0
- package/dist/mcp/server/index.js.map +1 -0
- package/dist/mcp/server/stack-mcp-server.d.ts +92 -0
- package/dist/mcp/server/stack-mcp-server.d.ts.map +1 -0
- package/dist/mcp/server/stack-mcp-server.js +265 -0
- package/dist/mcp/server/stack-mcp-server.js.map +1 -0
- package/package.json +10 -8
- package/src/commands/__snapshots__/generate.test.ts.snap +145 -38
- package/src/commands/dev.test.ts +0 -1
- package/src/commands/generate.test.ts +18 -8
- package/src/commands/generate.ts +20 -19
- package/src/commands/mcp.ts +135 -0
- package/src/generator/__snapshots__/context.test.ts.snap +63 -18
- package/src/generator/__snapshots__/prisma.test.ts.snap +8 -16
- package/src/generator/__snapshots__/types.test.ts.snap +1267 -95
- package/src/generator/context.test.ts +15 -8
- package/src/generator/context.ts +40 -7
- package/src/generator/index.ts +4 -1
- package/src/generator/lists.test.ts +335 -0
- package/src/generator/lists.ts +140 -0
- package/src/generator/plugin-types.ts +147 -0
- package/src/generator/prisma-config.ts +46 -0
- package/src/generator/prisma-extensions.ts +159 -0
- package/src/generator/prisma.test.ts +0 -10
- package/src/generator/prisma.ts +6 -2
- package/src/generator/types.test.ts +0 -12
- package/src/generator/types.ts +257 -17
- package/src/index.ts +4 -0
- package/src/mcp/lib/documentation-provider.ts +203 -0
- package/src/mcp/lib/features/catalog.ts +301 -0
- package/src/mcp/lib/generators/feature-generator.ts +598 -0
- package/src/mcp/lib/types.ts +89 -0
- package/src/mcp/lib/wizards/wizard-engine.ts +427 -0
- package/src/mcp/server/index.ts +240 -0
- package/src/mcp/server/stack-mcp-server.ts +301 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/generator/type-patcher.d.ts +0 -13
- package/dist/generator/type-patcher.d.ts.map +0 -1
- package/dist/generator/type-patcher.js +0 -68
- package/dist/generator/type-patcher.js.map +0 -1
- package/src/generator/type-patcher.ts +0 -93
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import type { OpenSaasConfig } from '@opensaas/stack-core'
|
|
2
|
+
import * as fs from 'fs'
|
|
3
|
+
import * as path from 'path'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generate TypeScript declaration for plugin data types
|
|
7
|
+
* Creates type-safe access to config._pluginData
|
|
8
|
+
*/
|
|
9
|
+
function generatePluginDataInterface(config: OpenSaasConfig): string {
|
|
10
|
+
const lines: string[] = []
|
|
11
|
+
|
|
12
|
+
lines.push('/**')
|
|
13
|
+
lines.push(' * Plugin data storage types')
|
|
14
|
+
lines.push(' * Provides type-safe access to config._pluginData')
|
|
15
|
+
lines.push(' */')
|
|
16
|
+
lines.push('export interface PluginData {')
|
|
17
|
+
|
|
18
|
+
// Check if we have plugin data types to generate
|
|
19
|
+
if (config._pluginData && Object.keys(config._pluginData).length > 0) {
|
|
20
|
+
for (const pluginName of Object.keys(config._pluginData)) {
|
|
21
|
+
// Skip internal keys
|
|
22
|
+
if (pluginName.startsWith('__')) continue
|
|
23
|
+
|
|
24
|
+
// For each plugin, we'll add a Record<string, unknown> entry
|
|
25
|
+
// TODO: In the future, plugins could export their data types for proper typing
|
|
26
|
+
lines.push(` ${pluginName}?: Record<string, unknown>`)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
lines.push('}')
|
|
31
|
+
|
|
32
|
+
return lines.join('\n')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Generate TypeScript declaration for plugin runtime services
|
|
37
|
+
* Creates type-safe access to context.plugins
|
|
38
|
+
*/
|
|
39
|
+
function generatePluginServicesInterface(config: OpenSaasConfig): string {
|
|
40
|
+
const lines: string[] = []
|
|
41
|
+
const imports: string[] = []
|
|
42
|
+
|
|
43
|
+
// Check if we have plugins with runtime functions
|
|
44
|
+
const pluginsWithRuntime = (config._plugins || config.plugins || []).filter((p) => p.runtime)
|
|
45
|
+
|
|
46
|
+
// Collect imports from plugins that provide runtime service types
|
|
47
|
+
if (pluginsWithRuntime.length > 0) {
|
|
48
|
+
for (const plugin of pluginsWithRuntime) {
|
|
49
|
+
if (plugin.runtimeServiceTypes) {
|
|
50
|
+
imports.push(plugin.runtimeServiceTypes.import)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Add imports at the top if any exist
|
|
56
|
+
if (imports.length > 0) {
|
|
57
|
+
lines.push(...imports)
|
|
58
|
+
lines.push('')
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
lines.push('/**')
|
|
62
|
+
lines.push(' * Plugin runtime services')
|
|
63
|
+
lines.push(' * Provides type-safe access to context.plugins')
|
|
64
|
+
lines.push(' * Extends Record to allow compatibility with base AccessContext type')
|
|
65
|
+
lines.push(' */')
|
|
66
|
+
lines.push(
|
|
67
|
+
'export interface PluginServices extends Record<string, Record<string, any> | undefined> {',
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
if (pluginsWithRuntime.length > 0) {
|
|
71
|
+
for (const plugin of pluginsWithRuntime) {
|
|
72
|
+
if (plugin.runtimeServiceTypes) {
|
|
73
|
+
// Use typed runtime service from plugin
|
|
74
|
+
lines.push(` ${plugin.name}?: ${plugin.runtimeServiceTypes.typeName}`)
|
|
75
|
+
} else {
|
|
76
|
+
// Fallback to Record<string, any> for plugins without type metadata
|
|
77
|
+
lines.push(` ${plugin.name}?: Record<string, any>`)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
lines.push('}')
|
|
83
|
+
|
|
84
|
+
return lines.join('\n')
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Generate module augmentation for OpenSaaS core types
|
|
89
|
+
* Note: We cannot augment _pluginData or plugins properties directly due to type constraints
|
|
90
|
+
* Instead, users should cast to PluginServices when accessing context.plugins
|
|
91
|
+
*/
|
|
92
|
+
function generateModuleAugmentation(): string {
|
|
93
|
+
const lines: string[] = []
|
|
94
|
+
|
|
95
|
+
lines.push('')
|
|
96
|
+
lines.push('/**')
|
|
97
|
+
lines.push(' * Declare this module to make interfaces available globally')
|
|
98
|
+
lines.push(' * Import this file to get typed plugin access')
|
|
99
|
+
lines.push(' */')
|
|
100
|
+
lines.push('declare global {')
|
|
101
|
+
lines.push(' // Plugin types are available as PluginServices interface')
|
|
102
|
+
lines.push('}')
|
|
103
|
+
|
|
104
|
+
return lines.join('\n')
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Generate complete plugin types file
|
|
109
|
+
*/
|
|
110
|
+
export function generatePluginTypes(config: OpenSaasConfig): string {
|
|
111
|
+
const lines: string[] = []
|
|
112
|
+
|
|
113
|
+
// Add header comment
|
|
114
|
+
lines.push('/**')
|
|
115
|
+
lines.push(' * Generated plugin types from OpenSaas configuration')
|
|
116
|
+
lines.push(' * DO NOT EDIT - This file is automatically generated')
|
|
117
|
+
lines.push(' *')
|
|
118
|
+
lines.push(' * This file provides type-safe access to:')
|
|
119
|
+
lines.push(' * - config._pluginData - Plugin configuration data')
|
|
120
|
+
lines.push(' * - context.plugins - Plugin runtime services')
|
|
121
|
+
lines.push(' */')
|
|
122
|
+
lines.push('')
|
|
123
|
+
|
|
124
|
+
// Generate interfaces
|
|
125
|
+
lines.push(generatePluginDataInterface(config))
|
|
126
|
+
lines.push(generatePluginServicesInterface(config))
|
|
127
|
+
|
|
128
|
+
// Generate module augmentation
|
|
129
|
+
lines.push(generateModuleAugmentation())
|
|
130
|
+
|
|
131
|
+
return lines.join('\n')
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Write plugin types to file
|
|
136
|
+
*/
|
|
137
|
+
export function writePluginTypes(config: OpenSaasConfig, outputPath: string): void {
|
|
138
|
+
const pluginTypes = generatePluginTypes(config)
|
|
139
|
+
|
|
140
|
+
// Ensure directory exists
|
|
141
|
+
const dir = path.dirname(outputPath)
|
|
142
|
+
if (!fs.existsSync(dir)) {
|
|
143
|
+
fs.mkdirSync(dir, { recursive: true })
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
fs.writeFileSync(outputPath, pluginTypes, 'utf-8')
|
|
147
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { OpenSaasConfig } from '@opensaas/stack-core'
|
|
2
|
+
import * as fs from 'fs'
|
|
3
|
+
import * as path from 'path'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generate Prisma config file for CLI commands
|
|
7
|
+
*
|
|
8
|
+
* Prisma 7 requires a prisma.config.ts file at the project root for CLI commands
|
|
9
|
+
* like `prisma db push` and `prisma migrate dev`. This is separate from the
|
|
10
|
+
* runtime configuration (which uses adapters in opensaas.config.ts).
|
|
11
|
+
*
|
|
12
|
+
* The CLI config provides the database URL for schema operations, while the
|
|
13
|
+
* runtime config provides adapters for actual query execution.
|
|
14
|
+
*/
|
|
15
|
+
export function generatePrismaConfig(_config: OpenSaasConfig): string {
|
|
16
|
+
const lines: string[] = []
|
|
17
|
+
|
|
18
|
+
// Import dotenv for environment variable loading
|
|
19
|
+
lines.push("import 'dotenv/config'")
|
|
20
|
+
lines.push("import { defineConfig, env } from 'prisma/config'")
|
|
21
|
+
lines.push('')
|
|
22
|
+
lines.push('export default defineConfig({')
|
|
23
|
+
lines.push(" schema: 'prisma/schema.prisma',")
|
|
24
|
+
lines.push(' datasource: {')
|
|
25
|
+
lines.push(" url: env('DATABASE_URL'),")
|
|
26
|
+
lines.push(' },')
|
|
27
|
+
lines.push('})')
|
|
28
|
+
lines.push('')
|
|
29
|
+
|
|
30
|
+
return lines.join('\n')
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Write Prisma config to file
|
|
35
|
+
*/
|
|
36
|
+
export function writePrismaConfig(config: OpenSaasConfig, outputPath: string): void {
|
|
37
|
+
const prismaConfig = generatePrismaConfig(config)
|
|
38
|
+
|
|
39
|
+
// Ensure directory exists
|
|
40
|
+
const dir = path.dirname(outputPath)
|
|
41
|
+
if (!fs.existsSync(dir)) {
|
|
42
|
+
fs.mkdirSync(dir, { recursive: true })
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
fs.writeFileSync(outputPath, prismaConfig, 'utf-8')
|
|
46
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import type { OpenSaasConfig, FieldConfig } from '@opensaas/stack-core'
|
|
2
|
+
import * as fs from 'fs'
|
|
3
|
+
import * as path from 'path'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generate Prisma result extensions configuration
|
|
7
|
+
* This creates a Prisma client extension that calls field resolveOutput hooks
|
|
8
|
+
*/
|
|
9
|
+
export function generatePrismaExtensions(config: OpenSaasConfig): string {
|
|
10
|
+
const lines: string[] = []
|
|
11
|
+
|
|
12
|
+
// Add header comment
|
|
13
|
+
lines.push('/**')
|
|
14
|
+
lines.push(' * Generated Prisma result extensions from OpenSaas configuration')
|
|
15
|
+
lines.push(' * DO NOT EDIT - This file is automatically generated')
|
|
16
|
+
lines.push(' */')
|
|
17
|
+
lines.push('')
|
|
18
|
+
|
|
19
|
+
// Add imports
|
|
20
|
+
lines.push("import { Prisma } from './prisma-client/client'")
|
|
21
|
+
lines.push("import configOrPromise from '../opensaas.config'")
|
|
22
|
+
lines.push('')
|
|
23
|
+
|
|
24
|
+
// Resolve config synchronously if possible (will be resolved by context.ts anyway)
|
|
25
|
+
lines.push('// Resolve config - may be a promise if plugins are present')
|
|
26
|
+
lines.push('let resolvedConfig: any = null')
|
|
27
|
+
lines.push('const configPromise = Promise.resolve(configOrPromise)')
|
|
28
|
+
lines.push('configPromise.then(cfg => { resolvedConfig = cfg })')
|
|
29
|
+
lines.push('')
|
|
30
|
+
|
|
31
|
+
// Check if any fields have result extensions or are virtual
|
|
32
|
+
let hasExtensions = false
|
|
33
|
+
for (const listConfig of Object.values(config.lists)) {
|
|
34
|
+
for (const fieldConfig of Object.values(listConfig.fields)) {
|
|
35
|
+
if (fieldConfig.resultExtension || fieldConfig.virtual) {
|
|
36
|
+
hasExtensions = true
|
|
37
|
+
break
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (hasExtensions) break
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!hasExtensions) {
|
|
44
|
+
// No extensions needed - export a no-op
|
|
45
|
+
lines.push('/**')
|
|
46
|
+
lines.push(' * No result extensions configured')
|
|
47
|
+
lines.push(' */')
|
|
48
|
+
lines.push('export const prismaExtensions = {}')
|
|
49
|
+
lines.push('')
|
|
50
|
+
return lines.join('\n')
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Generate result extensions
|
|
54
|
+
lines.push('/**')
|
|
55
|
+
lines.push(' * Prisma result extensions for field transformations and virtual fields')
|
|
56
|
+
lines.push(' * Delegates to field resolveOutput hooks from config for runtime transformations')
|
|
57
|
+
lines.push(' */')
|
|
58
|
+
lines.push('export const prismaExtensions = Prisma.defineExtension({')
|
|
59
|
+
lines.push(' result: {')
|
|
60
|
+
|
|
61
|
+
// Generate extensions for each list
|
|
62
|
+
for (const [listName, listConfig] of Object.entries(config.lists)) {
|
|
63
|
+
// Include both fields with resultExtension AND virtual fields
|
|
64
|
+
const fieldsWithExtensions: Array<[string, FieldConfig]> = Object.entries(
|
|
65
|
+
listConfig.fields,
|
|
66
|
+
).filter(([_, config]) => config.resultExtension || config.virtual)
|
|
67
|
+
|
|
68
|
+
if (fieldsWithExtensions.length === 0) continue
|
|
69
|
+
|
|
70
|
+
const modelKey = listName.charAt(0).toLowerCase() + listName.slice(1) // camelCase
|
|
71
|
+
|
|
72
|
+
lines.push(` ${modelKey}: {`)
|
|
73
|
+
|
|
74
|
+
for (const [fieldName, fieldConfig] of fieldsWithExtensions) {
|
|
75
|
+
const isVirtual = fieldConfig.virtual
|
|
76
|
+
|
|
77
|
+
lines.push(` ${fieldName}: {`)
|
|
78
|
+
|
|
79
|
+
if (isVirtual) {
|
|
80
|
+
// Virtual fields don't need database fields - they compute from the full item
|
|
81
|
+
lines.push(` needs: {},`)
|
|
82
|
+
} else {
|
|
83
|
+
// Non-virtual fields need their database value
|
|
84
|
+
lines.push(` needs: { ${fieldName}: true },`)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
lines.push(` compute: (${modelKey}) => {`)
|
|
88
|
+
|
|
89
|
+
if (!isVirtual) {
|
|
90
|
+
// For non-virtual fields, get the database value and check nullability
|
|
91
|
+
lines.push(` const value = ${modelKey}.${fieldName}`)
|
|
92
|
+
lines.push(` if (value === null || value === undefined) {`)
|
|
93
|
+
lines.push(` return undefined`)
|
|
94
|
+
lines.push(` }`)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
lines.push(` // Call field's resolveOutput hook if available (synchronously)`)
|
|
98
|
+
lines.push(` if (!resolvedConfig) {`)
|
|
99
|
+
lines.push(
|
|
100
|
+
` // Config not yet resolved - return undefined for virtual, value for regular`,
|
|
101
|
+
)
|
|
102
|
+
if (isVirtual) {
|
|
103
|
+
lines.push(` return undefined`)
|
|
104
|
+
} else {
|
|
105
|
+
lines.push(` return value`)
|
|
106
|
+
}
|
|
107
|
+
lines.push(` }`)
|
|
108
|
+
lines.push(
|
|
109
|
+
` const fieldConfig = resolvedConfig.lists['${listName}'].fields['${fieldName}']`,
|
|
110
|
+
)
|
|
111
|
+
lines.push(` if (fieldConfig.hooks?.resolveOutput) {`)
|
|
112
|
+
lines.push(` return fieldConfig.hooks.resolveOutput({`)
|
|
113
|
+
lines.push(` operation: 'query',`)
|
|
114
|
+
if (isVirtual) {
|
|
115
|
+
lines.push(` value: undefined, // Virtual fields have no stored value`)
|
|
116
|
+
} else {
|
|
117
|
+
lines.push(` value,`)
|
|
118
|
+
}
|
|
119
|
+
lines.push(` item: ${modelKey},`)
|
|
120
|
+
lines.push(` listKey: '${listName}',`)
|
|
121
|
+
lines.push(` fieldName: '${fieldName}',`)
|
|
122
|
+
lines.push(
|
|
123
|
+
` context: null as any, // Extension context doesn't have full context`,
|
|
124
|
+
)
|
|
125
|
+
lines.push(` })`)
|
|
126
|
+
lines.push(` }`)
|
|
127
|
+
if (isVirtual) {
|
|
128
|
+
lines.push(` return undefined`)
|
|
129
|
+
} else {
|
|
130
|
+
lines.push(` return value`)
|
|
131
|
+
}
|
|
132
|
+
lines.push(` },`)
|
|
133
|
+
lines.push(` },`)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
lines.push(` },`)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
lines.push(' },')
|
|
140
|
+
lines.push('})')
|
|
141
|
+
lines.push('')
|
|
142
|
+
|
|
143
|
+
return lines.join('\n')
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Write Prisma extensions configuration to file
|
|
148
|
+
*/
|
|
149
|
+
export function writePrismaExtensions(config: OpenSaasConfig, outputPath: string): void {
|
|
150
|
+
const extensions = generatePrismaExtensions(config)
|
|
151
|
+
|
|
152
|
+
// Ensure directory exists
|
|
153
|
+
const dir = path.dirname(outputPath)
|
|
154
|
+
if (!fs.existsSync(dir)) {
|
|
155
|
+
fs.mkdirSync(dir, { recursive: true })
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
fs.writeFileSync(outputPath, extensions, 'utf-8')
|
|
159
|
+
}
|
|
@@ -9,7 +9,6 @@ describe('Prisma Schema Generator', () => {
|
|
|
9
9
|
const config: OpenSaasConfig = {
|
|
10
10
|
db: {
|
|
11
11
|
provider: 'sqlite',
|
|
12
|
-
url: 'file:./dev.db',
|
|
13
12
|
},
|
|
14
13
|
lists: {},
|
|
15
14
|
}
|
|
@@ -23,7 +22,6 @@ describe('Prisma Schema Generator', () => {
|
|
|
23
22
|
const config: OpenSaasConfig = {
|
|
24
23
|
db: {
|
|
25
24
|
provider: 'sqlite',
|
|
26
|
-
url: 'file:./dev.db',
|
|
27
25
|
},
|
|
28
26
|
opensaasPath: '.custom-path',
|
|
29
27
|
lists: {},
|
|
@@ -38,7 +36,6 @@ describe('Prisma Schema Generator', () => {
|
|
|
38
36
|
const config: OpenSaasConfig = {
|
|
39
37
|
db: {
|
|
40
38
|
provider: 'sqlite',
|
|
41
|
-
url: 'file:./dev.db',
|
|
42
39
|
},
|
|
43
40
|
lists: {
|
|
44
41
|
User: {
|
|
@@ -60,7 +57,6 @@ describe('Prisma Schema Generator', () => {
|
|
|
60
57
|
const config: OpenSaasConfig = {
|
|
61
58
|
db: {
|
|
62
59
|
provider: 'sqlite',
|
|
63
|
-
url: 'file:./dev.db',
|
|
64
60
|
},
|
|
65
61
|
lists: {
|
|
66
62
|
Post: {
|
|
@@ -81,7 +77,6 @@ describe('Prisma Schema Generator', () => {
|
|
|
81
77
|
const config: OpenSaasConfig = {
|
|
82
78
|
db: {
|
|
83
79
|
provider: 'sqlite',
|
|
84
|
-
url: 'file:./dev.db',
|
|
85
80
|
},
|
|
86
81
|
lists: {
|
|
87
82
|
Post: {
|
|
@@ -102,7 +97,6 @@ describe('Prisma Schema Generator', () => {
|
|
|
102
97
|
const config: OpenSaasConfig = {
|
|
103
98
|
db: {
|
|
104
99
|
provider: 'sqlite',
|
|
105
|
-
url: 'file:./dev.db',
|
|
106
100
|
},
|
|
107
101
|
lists: {
|
|
108
102
|
User: {
|
|
@@ -128,7 +122,6 @@ describe('Prisma Schema Generator', () => {
|
|
|
128
122
|
const config: OpenSaasConfig = {
|
|
129
123
|
db: {
|
|
130
124
|
provider: 'sqlite',
|
|
131
|
-
url: 'file:./dev.db',
|
|
132
125
|
},
|
|
133
126
|
lists: {
|
|
134
127
|
User: {
|
|
@@ -154,7 +147,6 @@ describe('Prisma Schema Generator', () => {
|
|
|
154
147
|
const config: OpenSaasConfig = {
|
|
155
148
|
db: {
|
|
156
149
|
provider: 'postgresql',
|
|
157
|
-
url: process.env.DATABASE_URL || 'postgresql://localhost:5432/db',
|
|
158
150
|
},
|
|
159
151
|
lists: {
|
|
160
152
|
User: {
|
|
@@ -184,7 +176,6 @@ describe('Prisma Schema Generator', () => {
|
|
|
184
176
|
const config: OpenSaasConfig = {
|
|
185
177
|
db: {
|
|
186
178
|
provider: 'sqlite',
|
|
187
|
-
url: 'file:./dev.db',
|
|
188
179
|
},
|
|
189
180
|
lists: {
|
|
190
181
|
User: {
|
|
@@ -206,7 +197,6 @@ describe('Prisma Schema Generator', () => {
|
|
|
206
197
|
const config: OpenSaasConfig = {
|
|
207
198
|
db: {
|
|
208
199
|
provider: 'sqlite',
|
|
209
|
-
url: 'file:./dev.db',
|
|
210
200
|
},
|
|
211
201
|
lists: {},
|
|
212
202
|
}
|
package/src/generator/prisma.ts
CHANGED
|
@@ -66,13 +66,12 @@ export function generatePrismaSchema(config: OpenSaasConfig): string {
|
|
|
66
66
|
|
|
67
67
|
// Generator and datasource
|
|
68
68
|
lines.push('generator client {')
|
|
69
|
-
lines.push(' provider = "prisma-client
|
|
69
|
+
lines.push(' provider = "prisma-client"')
|
|
70
70
|
lines.push(` output = "../${opensaasPath}/prisma-client"`)
|
|
71
71
|
lines.push('}')
|
|
72
72
|
lines.push('')
|
|
73
73
|
lines.push('datasource db {')
|
|
74
74
|
lines.push(` provider = "${config.db.provider}"`)
|
|
75
|
-
lines.push(' url = env("DATABASE_URL")')
|
|
76
75
|
lines.push('}')
|
|
77
76
|
lines.push('')
|
|
78
77
|
|
|
@@ -91,6 +90,11 @@ export function generatePrismaSchema(config: OpenSaasConfig): string {
|
|
|
91
90
|
|
|
92
91
|
// Add regular fields
|
|
93
92
|
for (const [fieldName, fieldConfig] of Object.entries(listConfig.fields)) {
|
|
93
|
+
// Skip virtual fields - they don't create database columns
|
|
94
|
+
if (fieldConfig.virtual) {
|
|
95
|
+
continue
|
|
96
|
+
}
|
|
97
|
+
|
|
94
98
|
if (fieldConfig.type === 'relationship') {
|
|
95
99
|
relationshipFields.push({
|
|
96
100
|
name: fieldName,
|
|
@@ -9,7 +9,6 @@ describe('Types Generator', () => {
|
|
|
9
9
|
const config: OpenSaasConfig = {
|
|
10
10
|
db: {
|
|
11
11
|
provider: 'sqlite',
|
|
12
|
-
url: 'file:./dev.db',
|
|
13
12
|
},
|
|
14
13
|
lists: {
|
|
15
14
|
User: {
|
|
@@ -30,7 +29,6 @@ describe('Types Generator', () => {
|
|
|
30
29
|
const config: OpenSaasConfig = {
|
|
31
30
|
db: {
|
|
32
31
|
provider: 'sqlite',
|
|
33
|
-
url: 'file:./dev.db',
|
|
34
32
|
},
|
|
35
33
|
lists: {
|
|
36
34
|
Post: {
|
|
@@ -51,7 +49,6 @@ describe('Types Generator', () => {
|
|
|
51
49
|
const config: OpenSaasConfig = {
|
|
52
50
|
db: {
|
|
53
51
|
provider: 'sqlite',
|
|
54
|
-
url: 'file:./dev.db',
|
|
55
52
|
},
|
|
56
53
|
lists: {
|
|
57
54
|
Post: {
|
|
@@ -72,7 +69,6 @@ describe('Types Generator', () => {
|
|
|
72
69
|
const config: OpenSaasConfig = {
|
|
73
70
|
db: {
|
|
74
71
|
provider: 'sqlite',
|
|
75
|
-
url: 'file:./dev.db',
|
|
76
72
|
},
|
|
77
73
|
lists: {
|
|
78
74
|
User: {
|
|
@@ -92,7 +88,6 @@ describe('Types Generator', () => {
|
|
|
92
88
|
const config: OpenSaasConfig = {
|
|
93
89
|
db: {
|
|
94
90
|
provider: 'sqlite',
|
|
95
|
-
url: 'file:./dev.db',
|
|
96
91
|
},
|
|
97
92
|
lists: {
|
|
98
93
|
User: {
|
|
@@ -112,7 +107,6 @@ describe('Types Generator', () => {
|
|
|
112
107
|
const config: OpenSaasConfig = {
|
|
113
108
|
db: {
|
|
114
109
|
provider: 'sqlite',
|
|
115
|
-
url: 'file:./dev.db',
|
|
116
110
|
},
|
|
117
111
|
lists: {
|
|
118
112
|
User: {
|
|
@@ -139,7 +133,6 @@ describe('Types Generator', () => {
|
|
|
139
133
|
const config: OpenSaasConfig = {
|
|
140
134
|
db: {
|
|
141
135
|
provider: 'sqlite',
|
|
142
|
-
url: 'file:./dev.db',
|
|
143
136
|
},
|
|
144
137
|
lists: {
|
|
145
138
|
Post: {
|
|
@@ -165,7 +158,6 @@ describe('Types Generator', () => {
|
|
|
165
158
|
const config: OpenSaasConfig = {
|
|
166
159
|
db: {
|
|
167
160
|
provider: 'sqlite',
|
|
168
|
-
url: 'file:./dev.db',
|
|
169
161
|
},
|
|
170
162
|
lists: {
|
|
171
163
|
Post: {
|
|
@@ -191,7 +183,6 @@ describe('Types Generator', () => {
|
|
|
191
183
|
const config: OpenSaasConfig = {
|
|
192
184
|
db: {
|
|
193
185
|
provider: 'sqlite',
|
|
194
|
-
url: 'file:./dev.db',
|
|
195
186
|
},
|
|
196
187
|
lists: {
|
|
197
188
|
User: {
|
|
@@ -221,7 +212,6 @@ describe('Types Generator', () => {
|
|
|
221
212
|
const config: OpenSaasConfig = {
|
|
222
213
|
db: {
|
|
223
214
|
provider: 'sqlite',
|
|
224
|
-
url: 'file:./dev.db',
|
|
225
215
|
},
|
|
226
216
|
lists: {
|
|
227
217
|
Product: {
|
|
@@ -243,7 +233,6 @@ describe('Types Generator', () => {
|
|
|
243
233
|
const config: OpenSaasConfig = {
|
|
244
234
|
db: {
|
|
245
235
|
provider: 'sqlite',
|
|
246
|
-
url: 'file:./dev.db',
|
|
247
236
|
},
|
|
248
237
|
lists: {
|
|
249
238
|
Post: {
|
|
@@ -265,7 +254,6 @@ describe('Types Generator', () => {
|
|
|
265
254
|
const config: OpenSaasConfig = {
|
|
266
255
|
db: {
|
|
267
256
|
provider: 'sqlite',
|
|
268
|
-
url: 'file:./dev.db',
|
|
269
257
|
},
|
|
270
258
|
lists: {},
|
|
271
259
|
}
|