netlify-cli 8.12.0 → 8.13.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/README.md +11 -0
- package/npm-shrinkwrap.json +612 -1243
- package/package.json +5 -2
- package/src/commands/dev/dev.js +31 -6
- package/src/commands/graph/graph-edit.js +91 -0
- package/src/commands/graph/graph-pull.js +95 -0
- package/src/commands/graph/graph.js +30 -0
- package/src/commands/graph/index.js +5 -0
- package/src/commands/main.js +2 -0
- package/src/lib/one-graph/cli-client.js +278 -0
- package/src/lib/one-graph/cli-netlify-graph.js +278 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const process = require('process')
|
|
4
|
+
|
|
5
|
+
const { GraphQL, InternalConsole, NetlifyGraph } = require('netlify-onegraph-internal')
|
|
6
|
+
|
|
7
|
+
const { detectServerSettings, error, getFunctionsDir, log, warn } = require('../../utils')
|
|
8
|
+
|
|
9
|
+
const { printSchema } = GraphQL
|
|
10
|
+
|
|
11
|
+
const internalConsole = {
|
|
12
|
+
log,
|
|
13
|
+
warn,
|
|
14
|
+
error,
|
|
15
|
+
debug: console.debug,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
InternalConsole.registerConsole(internalConsole)
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Remove any relative path components from the given path
|
|
22
|
+
* @param {string[]} items Filesystem path items to filter
|
|
23
|
+
* @return {string[]} Filtered filesystem path items
|
|
24
|
+
*/
|
|
25
|
+
const filterRelativePathItems = (items) => items.filter((part) => part !== '')
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Return a full NetlifyGraph config with any defaults overridden by netlify.toml
|
|
29
|
+
* @param {import('../base-command').BaseCommand} command
|
|
30
|
+
* @return {NetlifyGraphConfig} NetlifyGraphConfig
|
|
31
|
+
*/
|
|
32
|
+
const getNetlifyGraphConfig = async ({ command, options }) => {
|
|
33
|
+
const { config, site } = command.netlify
|
|
34
|
+
config.dev = { ...config.dev }
|
|
35
|
+
config.build = { ...config.build }
|
|
36
|
+
const userSpecifiedConfig = (config && config.graph) || {}
|
|
37
|
+
/** @type {import('./types').DevConfig} */
|
|
38
|
+
const devConfig = {
|
|
39
|
+
framework: '#auto',
|
|
40
|
+
...(config.functionsDirectory && { functions: config.functionsDirectory }),
|
|
41
|
+
...(config.build.publish && { publish: config.build.publish }),
|
|
42
|
+
...config.dev,
|
|
43
|
+
...options,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** @type {Partial<import('../../utils/types').ServerSettings>} */
|
|
47
|
+
let settings = {}
|
|
48
|
+
try {
|
|
49
|
+
settings = await detectServerSettings(devConfig, options, site.root)
|
|
50
|
+
} catch (detectServerSettingsError) {
|
|
51
|
+
error(detectServerSettingsError)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const siteRoot = [path.sep, ...filterRelativePathItems(site.root.split(path.sep))]
|
|
55
|
+
|
|
56
|
+
const tsConfig = 'tsconfig.json'
|
|
57
|
+
const autodetectedLanguage = fs.existsSync(tsConfig) ? 'typescript' : 'javascript'
|
|
58
|
+
|
|
59
|
+
const framework = settings.framework || userSpecifiedConfig.framework
|
|
60
|
+
const isNextjs = framework === 'Next.js'
|
|
61
|
+
const detectedFunctionsPathString = getFunctionsDir({ config, options })
|
|
62
|
+
const detectedFunctionsPath = detectedFunctionsPathString ? detectedFunctionsPathString.split(path.sep) : null
|
|
63
|
+
const functionsPath = filterRelativePathItems(isNextjs ? [...siteRoot, 'pages', 'api'] : [...detectedFunctionsPath])
|
|
64
|
+
const netlifyGraphPath = filterRelativePathItems(
|
|
65
|
+
isNextjs
|
|
66
|
+
? [...siteRoot, 'lib', 'netlifyGraph']
|
|
67
|
+
: [...siteRoot, ...NetlifyGraph.defaultNetlifyGraphConfig.netlifyGraphPath],
|
|
68
|
+
)
|
|
69
|
+
const baseConfig = { ...NetlifyGraph.defaultNetlifyGraphConfig, ...userSpecifiedConfig }
|
|
70
|
+
const netlifyGraphImplementationFilename = [...netlifyGraphPath, `index.${baseConfig.extension}`]
|
|
71
|
+
const netlifyGraphTypeDefinitionsFilename = [...netlifyGraphPath, `index.d.ts`]
|
|
72
|
+
const graphQLOperationsSourceFilename = [...netlifyGraphPath, NetlifyGraph.defaultSourceOperationsFilename]
|
|
73
|
+
const graphQLSchemaFilename = [...netlifyGraphPath, NetlifyGraph.defaultGraphQLSchemaFilename]
|
|
74
|
+
const netlifyGraphRequirePath = isNextjs ? ['..', '..', 'lib', 'netlifyGraph'] : [`./netlifyGraph`]
|
|
75
|
+
const language = userSpecifiedConfig.language || autodetectedLanguage
|
|
76
|
+
const moduleType = baseConfig.moduleType || isNextjs ? 'esm' : 'commonjs'
|
|
77
|
+
const fullConfig = {
|
|
78
|
+
...baseConfig,
|
|
79
|
+
functionsPath,
|
|
80
|
+
netlifyGraphPath,
|
|
81
|
+
netlifyGraphImplementationFilename,
|
|
82
|
+
netlifyGraphTypeDefinitionsFilename,
|
|
83
|
+
graphQLOperationsSourceFilename,
|
|
84
|
+
graphQLSchemaFilename,
|
|
85
|
+
netlifyGraphRequirePath,
|
|
86
|
+
framework,
|
|
87
|
+
language,
|
|
88
|
+
moduleType,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return fullConfig
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Given a NetlifyGraphConfig, ensure that the netlifyGraphPath exists
|
|
96
|
+
* @param {NetlifyGraphConfig} netlifyGraphConfig
|
|
97
|
+
*/
|
|
98
|
+
const ensureNetlifyGraphPath = (netlifyGraphConfig) => {
|
|
99
|
+
fs.mkdirSync(path.resolve(...netlifyGraphConfig.netlifyGraphPath), { recursive: true })
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Given a NetlifyGraphConfig, ensure that the functionsPath exists
|
|
104
|
+
* @param {NetlifyGraphConfig} netlifyGraphConfig
|
|
105
|
+
*/
|
|
106
|
+
const ensureFunctionsPath = (netlifyGraphConfig) => {
|
|
107
|
+
fs.mkdirSync(path.resolve(...netlifyGraphConfig.functionsPath), { recursive: true })
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Generate a library file with type definitions for a given NetlifyGraphConfig, operationsDoc, and schema, writing them to the filesystem
|
|
112
|
+
* @param {NetlifyGraphConfig} netlifyGraphConfig
|
|
113
|
+
* @param {GraphQLSchema} schema The schema to use when generating the functions and their types
|
|
114
|
+
* @param {string} operationsDoc The GraphQL operations doc to use when generating the functions
|
|
115
|
+
* @param {NetlifyGraph.ParsedFunction} queries The parsed queries with metadata to use when generating library functions
|
|
116
|
+
*/
|
|
117
|
+
const generateFunctionsFile = (netlifyGraphConfig, schema, operationsDoc, queries) => {
|
|
118
|
+
const { clientSource, typeDefinitionsSource } = NetlifyGraph.generateFunctionsSource(
|
|
119
|
+
netlifyGraphConfig,
|
|
120
|
+
schema,
|
|
121
|
+
operationsDoc,
|
|
122
|
+
queries,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
ensureNetlifyGraphPath(netlifyGraphConfig)
|
|
126
|
+
fs.writeFileSync(path.resolve(...netlifyGraphConfig.netlifyGraphImplementationFilename), clientSource, 'utf8')
|
|
127
|
+
fs.writeFileSync(
|
|
128
|
+
path.resolve(...netlifyGraphConfig.netlifyGraphTypeDefinitionsFilename),
|
|
129
|
+
typeDefinitionsSource,
|
|
130
|
+
'utf8',
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Using the given NetlifyGraphConfig, read the GraphQL operations file and return the _unparsed_ GraphQL operations doc
|
|
136
|
+
* @param {NetlifyGraphConfig} netlifyGraphConfig
|
|
137
|
+
* @returns {string} GraphQL operations doc
|
|
138
|
+
*/
|
|
139
|
+
const readGraphQLOperationsSourceFile = (netlifyGraphConfig) => {
|
|
140
|
+
ensureNetlifyGraphPath(netlifyGraphConfig)
|
|
141
|
+
|
|
142
|
+
const fullFilename = path.resolve(...netlifyGraphConfig.graphQLOperationsSourceFilename)
|
|
143
|
+
if (!fs.existsSync(fullFilename)) {
|
|
144
|
+
fs.writeFileSync(fullFilename, '')
|
|
145
|
+
fs.closeSync(fs.openSync(fullFilename, 'w'))
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const source = fs.readFileSync(fullFilename, 'utf8')
|
|
149
|
+
|
|
150
|
+
return source
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Write an operations doc to the filesystem using the given NetlifyGraphConfig
|
|
155
|
+
* @param {NetlifyGraphConfig} netlifyGraphConfig
|
|
156
|
+
* @param {string} operationsDoc The GraphQL operations doc to write
|
|
157
|
+
*/
|
|
158
|
+
const writeGraphQLOperationsSourceFile = (netlifyGraphConfig, operationDocString) => {
|
|
159
|
+
const graphqlSource = operationDocString
|
|
160
|
+
|
|
161
|
+
ensureNetlifyGraphPath(netlifyGraphConfig)
|
|
162
|
+
fs.writeFileSync(path.resolve(...netlifyGraphConfig.graphQLOperationsSourceFilename), graphqlSource, 'utf8')
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Write a GraphQL Schema printed in SDL format to the filesystem using the given NetlifyGraphConfig
|
|
167
|
+
* @param {NetlifyGraphConfig} netlifyGraphConfig
|
|
168
|
+
* @param {GraphQLSchema} schema The GraphQL schema to print and write to the filesystem
|
|
169
|
+
*/
|
|
170
|
+
const writeGraphQLSchemaFile = (netlifyGraphConfig, schema) => {
|
|
171
|
+
const graphqlSource = printSchema(schema)
|
|
172
|
+
|
|
173
|
+
ensureNetlifyGraphPath(netlifyGraphConfig)
|
|
174
|
+
fs.writeFileSync(path.resolve(...netlifyGraphConfig.graphQLSchemaFilename), graphqlSource, 'utf8')
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Using the given NetlifyGraphConfig, read the GraphQL schema file and return it _unparsed_
|
|
179
|
+
* @param {NetlifyGraphConfig} netlifyGraphConfig
|
|
180
|
+
* @returns {string} GraphQL schema
|
|
181
|
+
*/
|
|
182
|
+
const readGraphQLSchemaFile = (netlifyGraphConfig) => {
|
|
183
|
+
ensureNetlifyGraphPath(netlifyGraphConfig)
|
|
184
|
+
return fs.readFileSync(path.resolve(...netlifyGraphConfig.graphQLSchemaFilename), 'utf8')
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Given a NetlifyGraphConfig, read the appropriate files and write a handler for the given operationId to the filesystem
|
|
189
|
+
* @param {NetlifyGraphConfig} netlifyGraphConfig
|
|
190
|
+
* @param {GraphQLSchema} schema The GraphQL schema to use when generating the handler
|
|
191
|
+
* @param {string} operationId The operationId to use when generating the handler
|
|
192
|
+
* @param {object} handlerOptions The options to use when generating the handler
|
|
193
|
+
* @returns
|
|
194
|
+
*/
|
|
195
|
+
const generateHandler = (netlifyGraphConfig, schema, operationId, handlerOptions) => {
|
|
196
|
+
let currentOperationsDoc = readGraphQLOperationsSourceFile(netlifyGraphConfig)
|
|
197
|
+
if (currentOperationsDoc.trim().length === 0) {
|
|
198
|
+
currentOperationsDoc = NetlifyGraph.defaultExampleOperationsDoc
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const result = NetlifyGraph.generateHandlerSource({
|
|
202
|
+
handlerOptions,
|
|
203
|
+
schema,
|
|
204
|
+
netlifyGraphConfig,
|
|
205
|
+
operationId,
|
|
206
|
+
operationsDoc: currentOperationsDoc,
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
if (!result) {
|
|
210
|
+
warn(`No handler was generated for operationId ${operationId}`)
|
|
211
|
+
return
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const { exportedFiles, operation } = result
|
|
215
|
+
|
|
216
|
+
ensureFunctionsPath(netlifyGraphConfig)
|
|
217
|
+
|
|
218
|
+
if (!exportedFiles) {
|
|
219
|
+
return
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
exportedFiles.forEach((exportedFile) => {
|
|
223
|
+
const { content } = exportedFile
|
|
224
|
+
const isNamed = exportedFile.kind === 'NamedExportedFile'
|
|
225
|
+
|
|
226
|
+
let filenameArr
|
|
227
|
+
|
|
228
|
+
if (isNamed) {
|
|
229
|
+
filenameArr = [...exportedFile.name]
|
|
230
|
+
} else {
|
|
231
|
+
const operationName = (operation.name && operation.name.value) || 'Unnamed'
|
|
232
|
+
const fileExtension = netlifyGraphConfig.language === 'typescript' ? 'ts' : netlifyGraphConfig.extension
|
|
233
|
+
const defaultBaseFilename = `${operationName}.${fileExtension}`
|
|
234
|
+
const baseFilename = defaultBaseFilename
|
|
235
|
+
|
|
236
|
+
filenameArr = [path.sep, ...netlifyGraphConfig.functionsPath, baseFilename]
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const absoluteFilename = path.resolve(...filenameArr)
|
|
240
|
+
|
|
241
|
+
fs.writeFileSync(absoluteFilename, content)
|
|
242
|
+
})
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Export the minimal set of functions that are required for Netlify Graph
|
|
246
|
+
const { buildSchema, parse } = GraphQL
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
*
|
|
250
|
+
* @param {object} options
|
|
251
|
+
* @param {string} options.siteName The name of the site as used in the Netlify UI url scheme
|
|
252
|
+
* @param {string} options.oneGraphSessionId The oneGraph session id to use when generating the graph-edit link
|
|
253
|
+
* @returns {string} The url to the Netlify Graph UI for the current session
|
|
254
|
+
*/
|
|
255
|
+
const getGraphEditUrlBySiteName = ({ oneGraphSessionId, siteName }) => {
|
|
256
|
+
const host = 'app.netlify.com' || process.env.NETLIFY_APP_HOST
|
|
257
|
+
// http because app.netlify.com will redirect to https, and localhost will still work for development
|
|
258
|
+
const url = `http://${host}/sites/${siteName}/graph/explorer?cliSessionId=${oneGraphSessionId}`
|
|
259
|
+
|
|
260
|
+
return url
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
module.exports = {
|
|
264
|
+
buildSchema,
|
|
265
|
+
defaultExampleOperationsDoc: NetlifyGraph.defaultExampleOperationsDoc,
|
|
266
|
+
extractFunctionsFromOperationDoc: NetlifyGraph.extractFunctionsFromOperationDoc,
|
|
267
|
+
generateFunctionsSource: NetlifyGraph.generateFunctionsSource,
|
|
268
|
+
generateFunctionsFile,
|
|
269
|
+
generateHandlerSource: NetlifyGraph.generateHandlerSource,
|
|
270
|
+
generateHandler,
|
|
271
|
+
getGraphEditUrlBySiteName,
|
|
272
|
+
getNetlifyGraphConfig,
|
|
273
|
+
parse,
|
|
274
|
+
readGraphQLOperationsSourceFile,
|
|
275
|
+
readGraphQLSchemaFile,
|
|
276
|
+
writeGraphQLOperationsSourceFile,
|
|
277
|
+
writeGraphQLSchemaFile,
|
|
278
|
+
}
|