@stonecrop/nuxt 0.7.3 → 0.7.5
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/README.md +105 -1
- package/bin/init.mjs +104 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +84 -5
- package/package.json +25 -10
- package/src/cli/detect.ts +125 -0
- package/src/cli/index.ts +184 -0
- package/src/cli/installers/casl.ts +51 -0
- package/src/cli/installers/doctypes.ts +206 -0
- package/src/cli/installers/frontend.ts +68 -0
- package/src/cli/installers/grafserv.ts +308 -0
- package/src/cli/installers/graphql-client.ts +36 -0
- package/src/cli/installers/index.ts +10 -0
- package/src/cli/installers/rockfoil.ts +51 -0
- package/src/cli/prompts.ts +204 -0
- package/src/cli/utils/config.ts +260 -0
- package/src/cli/utils/index.ts +15 -0
- package/src/cli/utils/package.ts +128 -0
- package/src/cli/utils/plugin.ts +107 -0
- package/templates/Example.json +85 -0
- package/templates/example-table.json +60 -0
- package/templates/plugins.ts +81 -0
- package/templates/resolvers.ts +256 -0
- package/templates/schema.graphql +129 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphQL server installer
|
|
3
|
+
* Installs @stonecrop/nuxt-grafserv and scaffolds server files
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { existsSync } from 'node:fs'
|
|
7
|
+
import { mkdir, writeFile, readFile } from 'node:fs/promises'
|
|
8
|
+
import { join, dirname } from 'pathe'
|
|
9
|
+
import { fileURLToPath } from 'node:url'
|
|
10
|
+
import consola from 'consola'
|
|
11
|
+
import { addDependencies } from '../utils/package'
|
|
12
|
+
import { updateNuxtConfig } from '../utils/config'
|
|
13
|
+
|
|
14
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
15
|
+
|
|
16
|
+
export interface GrafservInstallerOptions {
|
|
17
|
+
cwd: string
|
|
18
|
+
schemaPath?: string
|
|
19
|
+
resolversPath?: string
|
|
20
|
+
endpoint?: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Install the @stonecrop/nuxt-grafserv GraphQL server
|
|
25
|
+
*/
|
|
26
|
+
export async function installGrafserv(options: GrafservInstallerOptions): Promise<boolean> {
|
|
27
|
+
const {
|
|
28
|
+
cwd,
|
|
29
|
+
schemaPath = 'server/schema.graphql',
|
|
30
|
+
resolversPath = 'server/resolvers.ts',
|
|
31
|
+
endpoint = '/graphql/',
|
|
32
|
+
} = options
|
|
33
|
+
|
|
34
|
+
consola.start('Installing @stonecrop/nuxt-grafserv GraphQL server...')
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
// Scaffold server files first
|
|
38
|
+
await scaffoldServerFiles(cwd)
|
|
39
|
+
|
|
40
|
+
// Add dependencies - use latest published versions
|
|
41
|
+
await addDependencies(cwd, {
|
|
42
|
+
'@stonecrop/nuxt-grafserv': 'latest',
|
|
43
|
+
'@stonecrop/graphql-middleware': 'latest',
|
|
44
|
+
graphql: '^16.11.0',
|
|
45
|
+
grafast: '^1.0.0-rc.4',
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
// Update nuxt.config.ts
|
|
49
|
+
const configUpdated = await updateNuxtConfig(cwd, {
|
|
50
|
+
import: "import type { ModuleOptions as GrafservOptions } from '@stonecrop/nuxt-grafserv'",
|
|
51
|
+
module: "'@stonecrop/nuxt-grafserv'",
|
|
52
|
+
moduleOptions: {
|
|
53
|
+
key: 'grafserv',
|
|
54
|
+
value: `{
|
|
55
|
+
// GraphQL schema and resolvers
|
|
56
|
+
schema: '${schemaPath}',
|
|
57
|
+
resolvers: '${resolversPath}',
|
|
58
|
+
|
|
59
|
+
// GraphQL endpoint
|
|
60
|
+
url: '${endpoint}',
|
|
61
|
+
|
|
62
|
+
// Enable GraphiQL in development
|
|
63
|
+
graphiql: true,
|
|
64
|
+
|
|
65
|
+
// Graphile preset with grafserv options
|
|
66
|
+
preset: {
|
|
67
|
+
grafserv: {
|
|
68
|
+
websockets: false,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
} as GrafservOptions`,
|
|
72
|
+
},
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
if (!configUpdated) {
|
|
76
|
+
consola.warn('Could not automatically update nuxt.config.ts')
|
|
77
|
+
consola.info('Please add @stonecrop/nuxt-grafserv to your modules array manually')
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
consola.success('@stonecrop/nuxt-grafserv installed successfully')
|
|
81
|
+
return true
|
|
82
|
+
} catch (error) {
|
|
83
|
+
consola.error('Failed to install @stonecrop/nuxt-grafserv:', error)
|
|
84
|
+
return false
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Scaffold the server directory with GraphQL files
|
|
90
|
+
*/
|
|
91
|
+
async function scaffoldServerFiles(cwd: string): Promise<void> {
|
|
92
|
+
const serverDir = join(cwd, 'server')
|
|
93
|
+
|
|
94
|
+
// Create server directory if it doesn't exist
|
|
95
|
+
if (!existsSync(serverDir)) {
|
|
96
|
+
await mkdir(serverDir, { recursive: true })
|
|
97
|
+
consola.info('Created server/ directory')
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Scaffold schema.graphql
|
|
101
|
+
const schemaPath = join(serverDir, 'schema.graphql')
|
|
102
|
+
if (!existsSync(schemaPath)) {
|
|
103
|
+
const schemaTemplate = await loadTemplate('schema.graphql')
|
|
104
|
+
await writeFile(schemaPath, schemaTemplate, 'utf-8')
|
|
105
|
+
consola.info('Created server/schema.graphql')
|
|
106
|
+
} else {
|
|
107
|
+
consola.info('server/schema.graphql already exists, skipping')
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Scaffold resolvers.ts
|
|
111
|
+
const resolversPath = join(serverDir, 'resolvers.ts')
|
|
112
|
+
if (!existsSync(resolversPath)) {
|
|
113
|
+
const resolversTemplate = await loadTemplate('resolvers.ts')
|
|
114
|
+
await writeFile(resolversPath, resolversTemplate, 'utf-8')
|
|
115
|
+
consola.info('Created server/resolvers.ts')
|
|
116
|
+
} else {
|
|
117
|
+
consola.info('server/resolvers.ts already exists, skipping')
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Scaffold plugins.ts
|
|
121
|
+
const pluginsPath = join(serverDir, 'plugins.ts')
|
|
122
|
+
if (!existsSync(pluginsPath)) {
|
|
123
|
+
const pluginsTemplate = await loadTemplate('plugins.ts')
|
|
124
|
+
await writeFile(pluginsPath, pluginsTemplate, 'utf-8')
|
|
125
|
+
consola.info('Created server/plugins.ts')
|
|
126
|
+
} else {
|
|
127
|
+
consola.info('server/plugins.ts already exists, skipping')
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Load a template file
|
|
133
|
+
*/
|
|
134
|
+
async function loadTemplate(filename: string): Promise<string> {
|
|
135
|
+
// Try to load from templates directory
|
|
136
|
+
const templatePath = join(__dirname, '..', '..', '..', 'templates', filename)
|
|
137
|
+
|
|
138
|
+
if (existsSync(templatePath)) {
|
|
139
|
+
return readFile(templatePath, 'utf-8')
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Fallback to inline templates
|
|
143
|
+
return getInlineTemplate(filename)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Get inline template content as fallback
|
|
148
|
+
*/
|
|
149
|
+
function getInlineTemplate(filename: string): string {
|
|
150
|
+
const templates: Record<string, string> = {
|
|
151
|
+
'schema.graphql': `# Stonecrop GraphQL Schema
|
|
152
|
+
# Add your type definitions here
|
|
153
|
+
|
|
154
|
+
scalar JSON
|
|
155
|
+
|
|
156
|
+
type Query {
|
|
157
|
+
"""
|
|
158
|
+
Health check endpoint
|
|
159
|
+
"""
|
|
160
|
+
healthCheck: HealthStatus!
|
|
161
|
+
|
|
162
|
+
"""
|
|
163
|
+
Get metadata for a doctype
|
|
164
|
+
"""
|
|
165
|
+
getMeta(doctype: String!): JSON
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
type Mutation {
|
|
169
|
+
"""
|
|
170
|
+
Execute a doctype action
|
|
171
|
+
"""
|
|
172
|
+
stonecropAction(doctype: String!, action: String!, args: JSON): ActionResult!
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
type HealthStatus {
|
|
176
|
+
status: String!
|
|
177
|
+
timestamp: String!
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
type ActionResult {
|
|
181
|
+
success: Boolean!
|
|
182
|
+
data: JSON
|
|
183
|
+
error: String
|
|
184
|
+
}
|
|
185
|
+
`,
|
|
186
|
+
'resolvers.ts': `/**
|
|
187
|
+
* GraphQL Resolvers
|
|
188
|
+
* Add your resolver implementations here
|
|
189
|
+
*/
|
|
190
|
+
|
|
191
|
+
export const resolvers = {
|
|
192
|
+
Query: {
|
|
193
|
+
healthCheck: () => ({
|
|
194
|
+
status: 'healthy',
|
|
195
|
+
timestamp: new Date().toISOString(),
|
|
196
|
+
}),
|
|
197
|
+
|
|
198
|
+
getMeta: (_: unknown, { doctype }: { doctype: string }) => {
|
|
199
|
+
// TODO: Implement doctype metadata lookup
|
|
200
|
+
console.log('getMeta called for:', doctype)
|
|
201
|
+
return null
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
Mutation: {
|
|
206
|
+
stonecropAction: async (
|
|
207
|
+
_: unknown,
|
|
208
|
+
{ doctype, action, args }: { doctype: string; action: string; args?: unknown }
|
|
209
|
+
) => {
|
|
210
|
+
// TODO: Implement action execution
|
|
211
|
+
console.log('stonecropAction called:', { doctype, action, args })
|
|
212
|
+
return {
|
|
213
|
+
success: true,
|
|
214
|
+
data: null,
|
|
215
|
+
error: null,
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export default resolvers
|
|
222
|
+
`,
|
|
223
|
+
'plugins.ts': `/**
|
|
224
|
+
* Grafserv Plugins
|
|
225
|
+
* Add custom middleware and hooks via Grafserv plugins
|
|
226
|
+
*
|
|
227
|
+
* @see https://grafast.org/grafserv/plugins
|
|
228
|
+
*/
|
|
229
|
+
|
|
230
|
+
import type { GraphileConfig } from 'graphile-config'
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Example: Request logging plugin
|
|
234
|
+
*/
|
|
235
|
+
const loggingPlugin: GraphileConfig.Plugin = {
|
|
236
|
+
name: 'request-logging',
|
|
237
|
+
version: '1.0.0',
|
|
238
|
+
grafserv: {
|
|
239
|
+
middleware: {
|
|
240
|
+
processGraphQLRequestBody: async (next, event) => {
|
|
241
|
+
const start = Date.now()
|
|
242
|
+
console.log('[GraphQL] Request started:', {
|
|
243
|
+
path: event.request.url,
|
|
244
|
+
method: event.request.method,
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
const result = await next()
|
|
248
|
+
|
|
249
|
+
const duration = Date.now() - start
|
|
250
|
+
console.log(\`[GraphQL] Request completed in \${duration}ms\`)
|
|
251
|
+
|
|
252
|
+
return result
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Example: Authentication plugin
|
|
260
|
+
*/
|
|
261
|
+
const authPlugin: GraphileConfig.Plugin = {
|
|
262
|
+
name: 'authentication',
|
|
263
|
+
version: '1.0.0',
|
|
264
|
+
grafserv: {
|
|
265
|
+
middleware: {
|
|
266
|
+
processGraphQLRequestBody: async (next, event) => {
|
|
267
|
+
// Extract authentication from headers
|
|
268
|
+
const authHeader = event.request.headers.get('authorization')
|
|
269
|
+
|
|
270
|
+
if (authHeader?.startsWith('Bearer ')) {
|
|
271
|
+
const token = authHeader.slice(7)
|
|
272
|
+
// TODO: Validate token and set user context
|
|
273
|
+
console.log('[Auth] Token received:', token)
|
|
274
|
+
} else {
|
|
275
|
+
console.log('[Auth] Anonymous request')
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return next()
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Export all plugins
|
|
286
|
+
* Import these in your nuxt.config.ts:
|
|
287
|
+
*
|
|
288
|
+
* import plugins from './server/plugins'
|
|
289
|
+
*
|
|
290
|
+
* export default defineNuxtConfig({
|
|
291
|
+
* grafserv: {
|
|
292
|
+
* preset: {
|
|
293
|
+
* plugins
|
|
294
|
+
* }
|
|
295
|
+
* }
|
|
296
|
+
* })
|
|
297
|
+
*/
|
|
298
|
+
export const plugins: GraphileConfig.Plugin[] = [
|
|
299
|
+
loggingPlugin,
|
|
300
|
+
// authPlugin, // Uncomment to enable authentication
|
|
301
|
+
]
|
|
302
|
+
|
|
303
|
+
export default plugins
|
|
304
|
+
`,
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return templates[filename] || ''
|
|
308
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphQL client installer
|
|
3
|
+
* Installs @stonecrop/graphql-client for frontend GraphQL operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import consola from 'consola'
|
|
7
|
+
import { addDependencies } from '../utils/package'
|
|
8
|
+
|
|
9
|
+
export interface GraphqlClientInstallerOptions {
|
|
10
|
+
cwd: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Install the @stonecrop/graphql-client package
|
|
15
|
+
*/
|
|
16
|
+
export async function installGraphqlClient(options: GraphqlClientInstallerOptions): Promise<boolean> {
|
|
17
|
+
const { cwd } = options
|
|
18
|
+
|
|
19
|
+
consola.start('Installing @stonecrop/graphql-client...')
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
// Add dependencies - use latest published version
|
|
23
|
+
await addDependencies(cwd, {
|
|
24
|
+
'@stonecrop/graphql-client': 'latest',
|
|
25
|
+
graphql: '^16.11.0',
|
|
26
|
+
'graphql-request': '~6.0.0',
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
consola.success('@stonecrop/graphql-client installed successfully')
|
|
30
|
+
consola.info('Usage: import { createClient } from "@stonecrop/graphql-client"')
|
|
31
|
+
return true
|
|
32
|
+
} catch (error) {
|
|
33
|
+
consola.error('Failed to install @stonecrop/graphql-client:', error)
|
|
34
|
+
return false
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feature installers barrel export
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { installFrontend, type FrontendInstallerOptions } from './frontend'
|
|
6
|
+
export { installGrafserv, type GrafservInstallerOptions } from './grafserv'
|
|
7
|
+
export { installGraphqlClient, type GraphqlClientInstallerOptions } from './graphql-client'
|
|
8
|
+
export { installCasl, type CaslInstallerOptions } from './casl'
|
|
9
|
+
export { installRockfoil, type RockfoilInstallerOptions } from './rockfoil'
|
|
10
|
+
export { installDoctypes, type DoctypesInstallerOptions } from './doctypes'
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rockfoil installer
|
|
3
|
+
* Installs @stonecrop/rockfoil PostGraphile middleware
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import consola from 'consola'
|
|
7
|
+
|
|
8
|
+
import { updateNuxtConfig } from '../utils/config'
|
|
9
|
+
import { addDependencies } from '../utils/package'
|
|
10
|
+
import { addPluginToGrafservConfig } from '../utils/plugin'
|
|
11
|
+
|
|
12
|
+
export interface RockfoilInstallerOptions {
|
|
13
|
+
cwd: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Install the @stonecrop/rockfoil PostGraphile middleware
|
|
18
|
+
*/
|
|
19
|
+
export async function installRockfoil(options: RockfoilInstallerOptions): Promise<boolean> {
|
|
20
|
+
const { cwd } = options
|
|
21
|
+
|
|
22
|
+
consola.start('Installing @stonecrop/rockfoil PostGraphile middleware...')
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
// Add dependencies - use latest published version
|
|
26
|
+
await addDependencies(cwd, {
|
|
27
|
+
'@stonecrop/rockfoil': 'latest',
|
|
28
|
+
postgraphile: '^5.0.0-rc.4',
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
// Add import to nuxt.config.ts
|
|
32
|
+
await updateNuxtConfig(cwd, {
|
|
33
|
+
import: "import { createPglRockfoilPlugin } from '@stonecrop/rockfoil'",
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
// Add plugin to grafserv preset configuration
|
|
37
|
+
const pluginAdded = await addPluginToGrafservConfig(cwd, 'createPglRockfoilPlugin({})')
|
|
38
|
+
|
|
39
|
+
if (pluginAdded) {
|
|
40
|
+
consola.success('@stonecrop/rockfoil installed and configured successfully')
|
|
41
|
+
} else {
|
|
42
|
+
consola.success('@stonecrop/rockfoil installed successfully')
|
|
43
|
+
consola.info('Add createPglRockfoilPlugin({}) to your grafserv preset plugins array manually')
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return true
|
|
47
|
+
} catch (error) {
|
|
48
|
+
consola.error('Failed to install @stonecrop/rockfoil:', error)
|
|
49
|
+
return false
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive prompts for feature selection
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import prompts from 'prompts'
|
|
6
|
+
import consola from 'consola'
|
|
7
|
+
import type { DetectedFeatures } from './detect'
|
|
8
|
+
|
|
9
|
+
export interface SelectedFeatures {
|
|
10
|
+
frontend: boolean
|
|
11
|
+
graphql: boolean
|
|
12
|
+
graphqlClient: boolean
|
|
13
|
+
casl: boolean
|
|
14
|
+
rockfoil: boolean
|
|
15
|
+
doctypes: boolean
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface PromptOptions {
|
|
19
|
+
/** Pre-selected features from CLI flags */
|
|
20
|
+
preselected?: Partial<SelectedFeatures>
|
|
21
|
+
/** Already detected features in the project */
|
|
22
|
+
detected?: DetectedFeatures
|
|
23
|
+
/** Skip confirmation prompts */
|
|
24
|
+
skipConfirm?: boolean
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Prompt the user to select which features to install
|
|
29
|
+
*/
|
|
30
|
+
export async function promptFeatures(options: PromptOptions = {}): Promise<SelectedFeatures | null> {
|
|
31
|
+
const { preselected = {}, detected, skipConfirm } = options
|
|
32
|
+
|
|
33
|
+
// If any features are pre-selected via flags, use those
|
|
34
|
+
const hasPreselected = Object.values(preselected).some(Boolean)
|
|
35
|
+
if (hasPreselected) {
|
|
36
|
+
return {
|
|
37
|
+
frontend: preselected.frontend ?? false,
|
|
38
|
+
graphql: preselected.graphql ?? false,
|
|
39
|
+
graphqlClient: preselected.graphqlClient ?? false,
|
|
40
|
+
casl: preselected.casl ?? false,
|
|
41
|
+
rockfoil: preselected.rockfoil ?? false,
|
|
42
|
+
doctypes: preselected.doctypes ?? false,
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Build choices based on what's already installed
|
|
47
|
+
const choices = [
|
|
48
|
+
{
|
|
49
|
+
title: '@stonecrop/nuxt - Frontend module',
|
|
50
|
+
value: 'frontend',
|
|
51
|
+
description: 'Schema-driven UI components and routing',
|
|
52
|
+
disabled: detected?.hasFrontend,
|
|
53
|
+
selected: !detected?.hasFrontend,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
title: '@stonecrop/graphql-client - GraphQL client',
|
|
57
|
+
value: 'graphqlClient',
|
|
58
|
+
description: 'GraphQL client with Stonecrop integration',
|
|
59
|
+
disabled: detected?.hasGraphqlClient,
|
|
60
|
+
selected: false,
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
title: '@stonecrop/nuxt-grafserv - GraphQL server',
|
|
64
|
+
value: 'graphql',
|
|
65
|
+
description: 'Grafserv-based GraphQL API with middleware support',
|
|
66
|
+
disabled: detected?.hasGraphql,
|
|
67
|
+
selected: false,
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
title: '@stonecrop/casl-middleware - Authorization',
|
|
71
|
+
value: 'casl',
|
|
72
|
+
description: 'CASL-based permission system for GraphQL',
|
|
73
|
+
disabled: detected?.hasCasl,
|
|
74
|
+
selected: false,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
title: '@stonecrop/rockfoil - PostGraphile middleware',
|
|
78
|
+
value: 'rockfoil',
|
|
79
|
+
description: 'PostGraphile integration for database-driven GraphQL',
|
|
80
|
+
disabled: detected?.hasRockfoil,
|
|
81
|
+
selected: false,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
title: 'Sample doctypes',
|
|
85
|
+
value: 'doctypes',
|
|
86
|
+
description: 'Example doctype JSON files to get started',
|
|
87
|
+
disabled: detected?.hasDoctypes,
|
|
88
|
+
selected: false,
|
|
89
|
+
},
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
// Filter out disabled choices for the hint
|
|
93
|
+
const availableCount = choices.filter(c => !c.disabled).length
|
|
94
|
+
if (availableCount === 0) {
|
|
95
|
+
consola.info('All Stonecrop features are already installed!')
|
|
96
|
+
return null
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Show what's already installed
|
|
100
|
+
if (detected) {
|
|
101
|
+
const installed = choices.filter(c => c.disabled)
|
|
102
|
+
if (installed.length > 0) {
|
|
103
|
+
consola.info('Already installed:')
|
|
104
|
+
for (const item of installed) {
|
|
105
|
+
consola.info(` ✓ ${item.title}`)
|
|
106
|
+
}
|
|
107
|
+
console.log()
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const response = await prompts(
|
|
112
|
+
{
|
|
113
|
+
type: 'multiselect',
|
|
114
|
+
name: 'features',
|
|
115
|
+
message: 'Select features to install',
|
|
116
|
+
choices: choices.filter(c => !c.disabled),
|
|
117
|
+
hint: '- Space to select. Return to submit',
|
|
118
|
+
instructions: false,
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
onCancel: () => {
|
|
122
|
+
consola.info('Installation cancelled')
|
|
123
|
+
process.exit(0)
|
|
124
|
+
},
|
|
125
|
+
}
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
if (!response.features || response.features.length === 0) {
|
|
129
|
+
consola.info('No features selected')
|
|
130
|
+
return null
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const selected: SelectedFeatures = {
|
|
134
|
+
frontend: response.features.includes('frontend'),
|
|
135
|
+
graphql: response.features.includes('graphql'),
|
|
136
|
+
graphqlClient: response.features.includes('graphqlClient'),
|
|
137
|
+
casl: response.features.includes('casl'),
|
|
138
|
+
rockfoil: response.features.includes('rockfoil'),
|
|
139
|
+
doctypes: response.features.includes('doctypes'),
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Confirm selection
|
|
143
|
+
if (!skipConfirm) {
|
|
144
|
+
console.log()
|
|
145
|
+
consola.info('Selected features:')
|
|
146
|
+
if (selected.frontend) consola.info(' • @stonecrop/nuxt')
|
|
147
|
+
if (selected.graphqlClient) consola.info(' • @stonecrop/graphql-client')
|
|
148
|
+
if (selected.graphql) consola.info(' • @stonecrop/nuxt-grafserv')
|
|
149
|
+
if (selected.casl) consola.info(' • @stonecrop/casl-middleware')
|
|
150
|
+
if (selected.rockfoil) consola.info(' • @stonecrop/rockfoil')
|
|
151
|
+
if (selected.doctypes) consola.info(' • Sample doctypes')
|
|
152
|
+
console.log()
|
|
153
|
+
|
|
154
|
+
const confirm = await prompts({
|
|
155
|
+
type: 'confirm',
|
|
156
|
+
name: 'proceed',
|
|
157
|
+
message: 'Proceed with installation?',
|
|
158
|
+
initial: true,
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
if (!confirm.proceed) {
|
|
162
|
+
consola.info('Installation cancelled')
|
|
163
|
+
return null
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return selected
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Prompt for GraphQL server configuration
|
|
172
|
+
*/
|
|
173
|
+
export async function promptGraphqlConfig(): Promise<{
|
|
174
|
+
schemaPath: string
|
|
175
|
+
resolversPath: string
|
|
176
|
+
endpoint: string
|
|
177
|
+
} | null> {
|
|
178
|
+
const response = await prompts([
|
|
179
|
+
{
|
|
180
|
+
type: 'text',
|
|
181
|
+
name: 'schemaPath',
|
|
182
|
+
message: 'GraphQL schema path',
|
|
183
|
+
initial: './server/schema.graphql',
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
type: 'text',
|
|
187
|
+
name: 'resolversPath',
|
|
188
|
+
message: 'Resolvers file path',
|
|
189
|
+
initial: './server/resolvers.ts',
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
type: 'text',
|
|
193
|
+
name: 'endpoint',
|
|
194
|
+
message: 'GraphQL endpoint URL',
|
|
195
|
+
initial: '/graphql/',
|
|
196
|
+
},
|
|
197
|
+
])
|
|
198
|
+
|
|
199
|
+
if (!response.schemaPath) {
|
|
200
|
+
return null
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return response
|
|
204
|
+
}
|