netlify-cli 10.15.0 → 10.17.1
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/npm-shrinkwrap.json +812 -1571
- package/package.json +7 -6
- package/src/commands/dev/dev.js +31 -4
- package/src/commands/env/env-get.js +31 -4
- package/src/commands/env/env-list.js +59 -16
- package/src/commands/graph/graph-edit.js +3 -2
- package/src/commands/graph/graph-handler.js +41 -63
- package/src/commands/graph/graph-init.js +2 -1
- package/src/commands/graph/graph-library.js +20 -12
- package/src/commands/graph/graph-operations.js +6 -1
- package/src/commands/graph/graph-pull.js +72 -16
- package/src/commands/init/init.js +1 -1
- package/src/functions-templates/go/hello-world/go.mod +1 -1
- package/src/lib/edge-functions/proxy.js +4 -0
- package/src/lib/edge-functions/registry.js +29 -3
- package/src/lib/one-graph/cli-client.js +555 -108
- package/src/lib/one-graph/cli-netlify-graph.js +519 -50
- package/src/utils/dev.js +2 -2
- package/src/utils/env/index.js +156 -1
- package/src/utils/proxy.js +2 -0
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "netlify-cli",
|
|
3
3
|
"description": "Netlify command line tool",
|
|
4
|
-
"version": "10.
|
|
4
|
+
"version": "10.17.1",
|
|
5
5
|
"author": "Netlify Inc.",
|
|
6
6
|
"contributors": [
|
|
7
|
+
"@whitep4nth3r (https://twitter.com/whitep4nth3r)",
|
|
7
8
|
"Abraham Schilling <AbrahamSchilling@gmail.com> (https://gitlab.com/n4bb12)",
|
|
8
9
|
"Alberto De Agostini (https://twitter.com/albertodeago88)",
|
|
9
10
|
"Alejandro Ñáñez Ortiz <git@alejandro.dev> (https://twitter.com/alejandronanez)",
|
|
@@ -222,11 +223,11 @@
|
|
|
222
223
|
"dependencies": {
|
|
223
224
|
"@netlify/build": "^27.8.1",
|
|
224
225
|
"@netlify/config": "^18.1.2",
|
|
225
|
-
"@netlify/edge-bundler": "^1.
|
|
226
|
-
"@netlify/framework-info": "^9.
|
|
226
|
+
"@netlify/edge-bundler": "^1.12.1",
|
|
227
|
+
"@netlify/framework-info": "^9.2.0",
|
|
227
228
|
"@netlify/local-functions-proxy": "^1.1.1",
|
|
228
|
-
"@netlify/plugins-list": "^6.
|
|
229
|
-
"@netlify/zip-it-and-ship-it": "^5.13.
|
|
229
|
+
"@netlify/plugins-list": "^6.38.0",
|
|
230
|
+
"@netlify/zip-it-and-ship-it": "^5.13.4",
|
|
230
231
|
"@octokit/rest": "^18.0.0",
|
|
231
232
|
"@sindresorhus/slugify": "^1.1.0",
|
|
232
233
|
"ansi-escapes": "^5.0.0",
|
|
@@ -291,7 +292,7 @@
|
|
|
291
292
|
"multiparty": "^4.2.1",
|
|
292
293
|
"netlify": "^12.0.0",
|
|
293
294
|
"netlify-headers-parser": "^6.0.2",
|
|
294
|
-
"netlify-onegraph-internal": "0.4
|
|
295
|
+
"netlify-onegraph-internal": "0.8.4",
|
|
295
296
|
"netlify-redirect-parser": "^13.0.5",
|
|
296
297
|
"netlify-redirector": "^0.2.1",
|
|
297
298
|
"node-fetch": "^2.6.0",
|
package/src/commands/dev/dev.js
CHANGED
|
@@ -38,6 +38,7 @@ const {
|
|
|
38
38
|
error,
|
|
39
39
|
exit,
|
|
40
40
|
generateNetlifyGraphJWT,
|
|
41
|
+
getEnvelopeEnv,
|
|
41
42
|
getSiteInformation,
|
|
42
43
|
getToken,
|
|
43
44
|
injectEnvVariables,
|
|
@@ -233,6 +234,7 @@ const FRAMEWORK_PORT_TIMEOUT = 6e5
|
|
|
233
234
|
* @param {object} params
|
|
234
235
|
* @param {*} params.addonsUrls
|
|
235
236
|
* @param {import('../base-command').NetlifyOptions["config"]} params.config
|
|
237
|
+
* @param {import('../base-command').NetlifyOptions["cachedConfig"]['env']} params.env
|
|
236
238
|
* @param {InspectSettings} params.inspectSettings
|
|
237
239
|
* @param {() => Promise<object>} params.getUpdatedConfig
|
|
238
240
|
* @param {string} params.geolocationMode
|
|
@@ -247,6 +249,7 @@ const FRAMEWORK_PORT_TIMEOUT = 6e5
|
|
|
247
249
|
const startProxyServer = async ({
|
|
248
250
|
addonsUrls,
|
|
249
251
|
config,
|
|
252
|
+
env,
|
|
250
253
|
geoCountry,
|
|
251
254
|
geolocationMode,
|
|
252
255
|
getUpdatedConfig,
|
|
@@ -261,6 +264,7 @@ const startProxyServer = async ({
|
|
|
261
264
|
addonsUrls,
|
|
262
265
|
config,
|
|
263
266
|
configPath: site.configPath,
|
|
267
|
+
env,
|
|
264
268
|
geolocationMode,
|
|
265
269
|
geoCountry,
|
|
266
270
|
getUpdatedConfig,
|
|
@@ -425,7 +429,12 @@ const dev = async (options, command) => {
|
|
|
425
429
|
...options,
|
|
426
430
|
}
|
|
427
431
|
|
|
428
|
-
|
|
432
|
+
let { env } = command.netlify.cachedConfig
|
|
433
|
+
if (siteInfo.use_envelope) {
|
|
434
|
+
env = await getEnvelopeEnv({ api, context: options.context, env, siteInfo })
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
await injectEnvVariables({ devConfig, env, site })
|
|
429
438
|
await promptEditorHelper({ chalk, config, log, NETLIFYDEVLOG, repositoryRoot, state })
|
|
430
439
|
|
|
431
440
|
const { addonsUrls, capabilities, siteUrl, timeouts } = await getSiteInformation({
|
|
@@ -479,6 +488,7 @@ const dev = async (options, command) => {
|
|
|
479
488
|
let url = await startProxyServer({
|
|
480
489
|
addonsUrls,
|
|
481
490
|
config,
|
|
491
|
+
env: command.netlify.cachedConfig.env,
|
|
482
492
|
geolocationMode: options.geo,
|
|
483
493
|
geoCountry: options.country,
|
|
484
494
|
getUpdatedConfig,
|
|
@@ -514,6 +524,9 @@ const dev = async (options, command) => {
|
|
|
514
524
|
|
|
515
525
|
let stopWatchingCLISessions
|
|
516
526
|
|
|
527
|
+
let liveConfig = { ...config }
|
|
528
|
+
let isRestartingSession = false
|
|
529
|
+
|
|
517
530
|
const createOrResumeSession = async function () {
|
|
518
531
|
const netlifyGraphConfig = await getNetlifyGraphConfig({ command, options, settings })
|
|
519
532
|
|
|
@@ -524,6 +537,7 @@ const dev = async (options, command) => {
|
|
|
524
537
|
}
|
|
525
538
|
|
|
526
539
|
stopWatchingCLISessions = await startOneGraphCLISession({
|
|
540
|
+
config: liveConfig,
|
|
527
541
|
netlifyGraphConfig,
|
|
528
542
|
netlifyToken,
|
|
529
543
|
site,
|
|
@@ -535,6 +549,7 @@ const dev = async (options, command) => {
|
|
|
535
549
|
const oneGraphSessionId = loadCLISession(state)
|
|
536
550
|
|
|
537
551
|
await persistNewOperationsDocForSession({
|
|
552
|
+
config: liveConfig,
|
|
538
553
|
netlifyGraphConfig,
|
|
539
554
|
netlifyToken,
|
|
540
555
|
oneGraphSessionId,
|
|
@@ -570,10 +585,16 @@ const dev = async (options, command) => {
|
|
|
570
585
|
}
|
|
571
586
|
|
|
572
587
|
// Set up a handler for config changes.
|
|
573
|
-
configWatcher.on('change', (newConfig) => {
|
|
588
|
+
configWatcher.on('change', async (newConfig) => {
|
|
574
589
|
command.netlify.config = newConfig
|
|
575
|
-
|
|
576
|
-
|
|
590
|
+
liveConfig = newConfig
|
|
591
|
+
if (isRestartingSession) {
|
|
592
|
+
return
|
|
593
|
+
}
|
|
594
|
+
stopWatchingCLISessions && stopWatchingCLISessions()
|
|
595
|
+
isRestartingSession = true
|
|
596
|
+
await createOrResumeSession()
|
|
597
|
+
isRestartingSession = false
|
|
577
598
|
})
|
|
578
599
|
|
|
579
600
|
const oneGraphSessionId = await createOrResumeSession()
|
|
@@ -606,6 +627,11 @@ const createDevCommand = (program) => {
|
|
|
606
627
|
`Local dev server\nThe dev command will run a local dev server with Netlify's proxy and redirect rules`,
|
|
607
628
|
)
|
|
608
629
|
.option('-c ,--command <command>', 'command to run')
|
|
630
|
+
.addOption(
|
|
631
|
+
new Option('--context <context>', 'Specify a deploy context for environment variables')
|
|
632
|
+
.choices(['production', 'deploy-preview', 'branch-deploy', 'dev'])
|
|
633
|
+
.default('dev'),
|
|
634
|
+
)
|
|
609
635
|
.option('-p ,--port <port>', 'port of netlify dev', (value) => Number.parseInt(value))
|
|
610
636
|
.option('--targetPort <port>', 'port of target app server', (value) => Number.parseInt(value))
|
|
611
637
|
.option('--framework <name>', 'framework to use. Defaults to #auto which automatically detects a framework')
|
|
@@ -655,6 +681,7 @@ const createDevCommand = (program) => {
|
|
|
655
681
|
'netlify dev',
|
|
656
682
|
'netlify dev -d public',
|
|
657
683
|
'netlify dev -c "hugo server -w" --targetPort 1313',
|
|
684
|
+
'netlify dev --context production',
|
|
658
685
|
'netlify dev --graph',
|
|
659
686
|
'netlify dev --edgeInspect',
|
|
660
687
|
'netlify dev --edgeInspect=127.0.0.1:9229',
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
const {
|
|
2
|
+
const { Option } = require('commander')
|
|
3
|
+
|
|
4
|
+
const { chalk, error, getEnvelopeEnv, log, logJson } = require('../../utils')
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* The env:get command
|
|
@@ -8,6 +10,7 @@ const { log, logJson } = require('../../utils')
|
|
|
8
10
|
* @param {import('../base-command').BaseCommand} command
|
|
9
11
|
*/
|
|
10
12
|
const envGet = async (name, options, command) => {
|
|
13
|
+
const { context, scope } = options
|
|
11
14
|
const { api, cachedConfig, site } = command.netlify
|
|
12
15
|
const siteId = site.id
|
|
13
16
|
|
|
@@ -16,9 +19,21 @@ const envGet = async (name, options, command) => {
|
|
|
16
19
|
return false
|
|
17
20
|
}
|
|
18
21
|
|
|
19
|
-
const
|
|
22
|
+
const { siteInfo } = cachedConfig
|
|
23
|
+
let { env } = cachedConfig
|
|
24
|
+
|
|
25
|
+
if (siteInfo.use_envelope) {
|
|
26
|
+
env = await getEnvelopeEnv({ api, context, env, key: name, scope, siteInfo })
|
|
27
|
+
} else if (context !== 'dev' || scope !== 'any') {
|
|
28
|
+
error(
|
|
29
|
+
`To specify a context or scope, please run ${chalk.yellowBright(
|
|
30
|
+
'netlify open:admin',
|
|
31
|
+
)} to open the Netlify UI and opt in to the new environment variables experience from Site settings`,
|
|
32
|
+
)
|
|
33
|
+
return false
|
|
34
|
+
}
|
|
20
35
|
|
|
21
|
-
const { value } =
|
|
36
|
+
const { value } = env[name] || {}
|
|
22
37
|
|
|
23
38
|
// Return json response for piping commands
|
|
24
39
|
if (options.json) {
|
|
@@ -27,7 +42,9 @@ const envGet = async (name, options, command) => {
|
|
|
27
42
|
}
|
|
28
43
|
|
|
29
44
|
if (!value) {
|
|
30
|
-
|
|
45
|
+
const withContext = `in the ${chalk.magentaBright(context)} context`
|
|
46
|
+
const withScope = scope === 'any' ? '' : ` in the ${chalk.magentaBright(context)} scope`
|
|
47
|
+
log(`No value set ${withContext}${withScope} for environment variable ${chalk.yellowBright(name)}`)
|
|
31
48
|
return false
|
|
32
49
|
}
|
|
33
50
|
|
|
@@ -43,6 +60,16 @@ const createEnvGetCommand = (program) =>
|
|
|
43
60
|
program
|
|
44
61
|
.command('env:get')
|
|
45
62
|
.argument('<name>', 'Environment variable name')
|
|
63
|
+
.addOption(
|
|
64
|
+
new Option('-c, --context <context>', 'Specify a deploy context')
|
|
65
|
+
.choices(['production', 'deploy-preview', 'branch-deploy', 'dev'])
|
|
66
|
+
.default('dev'),
|
|
67
|
+
)
|
|
68
|
+
.addOption(
|
|
69
|
+
new Option('-s, --scope <scope>', 'Specify a scope')
|
|
70
|
+
.choices(['builds', 'functions', 'post_processing', 'runtime', 'any'])
|
|
71
|
+
.default('any'),
|
|
72
|
+
)
|
|
46
73
|
.description('Get resolved value of specified environment variable (includes netlify.toml)')
|
|
47
74
|
.action(async (name, options, command) => {
|
|
48
75
|
await envGet(name, options, command)
|
|
@@ -1,20 +1,33 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
const AsciiTable = require('ascii-table')
|
|
3
3
|
const { isCI } = require('ci-info')
|
|
4
|
+
const { Option } = require('commander')
|
|
4
5
|
const inquirer = require('inquirer')
|
|
5
6
|
const isEmpty = require('lodash/isEmpty')
|
|
6
7
|
|
|
7
|
-
const { chalk, log, logJson } = require('../../utils')
|
|
8
|
+
const { chalk, error, getEnvelopeEnv, getHumanReadableScopes, log, logJson } = require('../../utils')
|
|
8
9
|
|
|
9
10
|
const [logUpdatePromise, ansiEscapesPromise] = [import('log-update'), import('ansi-escapes')]
|
|
10
11
|
|
|
11
12
|
const MASK_LENGTH = 50
|
|
12
13
|
const MASK = '*'.repeat(MASK_LENGTH)
|
|
13
14
|
|
|
14
|
-
const getTable = ({ environment, hideValues }) => {
|
|
15
|
+
const getTable = ({ environment, hideValues, scopesColumn }) => {
|
|
15
16
|
const table = new AsciiTable(`Environment variables`)
|
|
16
|
-
|
|
17
|
-
table.
|
|
17
|
+
const headings = ['Key', 'Value', scopesColumn && 'Scope'].filter(Boolean)
|
|
18
|
+
table.setHeading(...headings)
|
|
19
|
+
table.addRowMatrix(
|
|
20
|
+
Object.entries(environment).map(([key, variable]) =>
|
|
21
|
+
[
|
|
22
|
+
// Key
|
|
23
|
+
key,
|
|
24
|
+
// Value
|
|
25
|
+
hideValues ? MASK : variable.value,
|
|
26
|
+
// Scope
|
|
27
|
+
scopesColumn && getHumanReadableScopes(variable.scopes),
|
|
28
|
+
].filter(Boolean),
|
|
29
|
+
),
|
|
30
|
+
)
|
|
18
31
|
return table.toString()
|
|
19
32
|
}
|
|
20
33
|
|
|
@@ -25,6 +38,7 @@ const getTable = ({ environment, hideValues }) => {
|
|
|
25
38
|
* @returns {Promise<boolean>}
|
|
26
39
|
*/
|
|
27
40
|
const envList = async (options, command) => {
|
|
41
|
+
const { context, scope } = options
|
|
28
42
|
const { api, cachedConfig, site } = command.netlify
|
|
29
43
|
const siteId = site.id
|
|
30
44
|
|
|
@@ -33,36 +47,55 @@ const envList = async (options, command) => {
|
|
|
33
47
|
return false
|
|
34
48
|
}
|
|
35
49
|
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
50
|
+
const { env, siteInfo } = cachedConfig
|
|
51
|
+
const isUsingEnvelope = siteInfo.use_envelope
|
|
52
|
+
let environment = env
|
|
53
|
+
|
|
54
|
+
if (isUsingEnvelope) {
|
|
55
|
+
environment = await getEnvelopeEnv({ api, context, env, scope, siteInfo })
|
|
56
|
+
} else if (context !== 'dev' || scope !== 'any') {
|
|
57
|
+
error(
|
|
58
|
+
`To specify a context or scope, please run ${chalk.yellowBright(
|
|
59
|
+
'netlify open:admin',
|
|
60
|
+
)} to open the Netlify UI and opt in to the new environment variables experience from Site settings`,
|
|
61
|
+
)
|
|
62
|
+
return false
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// filter out general sources
|
|
66
|
+
environment = Object.fromEntries(
|
|
67
|
+
Object.entries(environment).filter(([, variable]) => variable.sources[0] !== 'general'),
|
|
42
68
|
)
|
|
43
69
|
|
|
44
70
|
// Return json response for piping commands
|
|
45
71
|
if (options.json) {
|
|
46
|
-
|
|
72
|
+
const envDictionary = Object.fromEntries(
|
|
73
|
+
Object.entries(environment).map(([key, variable]) => [key, variable.value]),
|
|
74
|
+
)
|
|
75
|
+
logJson(envDictionary)
|
|
47
76
|
return false
|
|
48
77
|
}
|
|
49
78
|
|
|
79
|
+
const forSite = `for site ${chalk.greenBright(siteInfo.name)}`
|
|
80
|
+
const withContext = isUsingEnvelope ? `in the ${chalk.magentaBright(options.context)} context` : ''
|
|
81
|
+
const withScope = isUsingEnvelope && scope !== 'any' ? `and ${chalk.yellowBright(options.scope)} scope` : ''
|
|
50
82
|
if (isEmpty(environment)) {
|
|
51
|
-
log(`No environment variables set
|
|
83
|
+
log(`No environment variables set ${forSite} ${withContext} ${withScope}`)
|
|
52
84
|
return false
|
|
53
85
|
}
|
|
54
86
|
|
|
55
87
|
// List environment in a table
|
|
56
|
-
|
|
88
|
+
const count = Object.keys(environment).length
|
|
89
|
+
log(`${count} environment variable${count === 1 ? '' : 's'} ${forSite} ${withContext} ${withScope}`)
|
|
57
90
|
|
|
58
91
|
if (isCI) {
|
|
59
|
-
log(getTable({ environment, hideValues: false }))
|
|
92
|
+
log(getTable({ environment, hideValues: false, scopesColumn: isUsingEnvelope }))
|
|
60
93
|
return false
|
|
61
94
|
}
|
|
62
95
|
|
|
63
96
|
const { default: logUpdate } = await logUpdatePromise
|
|
64
97
|
|
|
65
|
-
logUpdate(getTable({ environment, hideValues: true }))
|
|
98
|
+
logUpdate(getTable({ environment, hideValues: true, scopesColumn: isUsingEnvelope }))
|
|
66
99
|
const { showValues } = await inquirer.prompt([
|
|
67
100
|
{
|
|
68
101
|
type: 'confirm',
|
|
@@ -76,7 +109,7 @@ const envList = async (options, command) => {
|
|
|
76
109
|
const { default: ansiEscapes } = await ansiEscapesPromise
|
|
77
110
|
// since inquirer adds a prompt, we need to account for it when printing the table again
|
|
78
111
|
log(ansiEscapes.eraseLines(3))
|
|
79
|
-
logUpdate(getTable({ environment, hideValues: false }))
|
|
112
|
+
logUpdate(getTable({ environment, hideValues: false, scopesColumn: isUsingEnvelope }))
|
|
80
113
|
log(`${chalk.cyan('?')} Show values? ${chalk.cyan('Yes')}`)
|
|
81
114
|
}
|
|
82
115
|
}
|
|
@@ -89,6 +122,16 @@ const envList = async (options, command) => {
|
|
|
89
122
|
const createEnvListCommand = (program) =>
|
|
90
123
|
program
|
|
91
124
|
.command('env:list')
|
|
125
|
+
.addOption(
|
|
126
|
+
new Option('-c, --context <context>', 'Specify a deploy context')
|
|
127
|
+
.choices(['production', 'deploy-preview', 'branch-deploy', 'dev'])
|
|
128
|
+
.default('dev'),
|
|
129
|
+
)
|
|
130
|
+
.addOption(
|
|
131
|
+
new Option('-s, --scope <scope>', 'Specify a scope')
|
|
132
|
+
.choices(['builds', 'functions', 'post_processing', 'runtime', 'any'])
|
|
133
|
+
.default('any'),
|
|
134
|
+
)
|
|
92
135
|
.description('Lists resolved environment variables for site (includes netlify.toml)')
|
|
93
136
|
.action(async (options, command) => {
|
|
94
137
|
await envList(options, command)
|
|
@@ -21,7 +21,7 @@ const { ensureAppForSite, executeCreatePersistedQueryMutation } = OneGraphCliCli
|
|
|
21
21
|
* @returns
|
|
22
22
|
*/
|
|
23
23
|
const graphEdit = async (options, command) => {
|
|
24
|
-
const { site, state } = command.netlify
|
|
24
|
+
const { config, site, state } = command.netlify
|
|
25
25
|
const siteId = site.id
|
|
26
26
|
|
|
27
27
|
if (!site.id) {
|
|
@@ -44,6 +44,7 @@ const graphEdit = async (options, command) => {
|
|
|
44
44
|
await ensureAppForSite(netlifyToken, siteId)
|
|
45
45
|
|
|
46
46
|
const oneGraphSessionId = await ensureCLISession({
|
|
47
|
+
config,
|
|
47
48
|
metadata: {},
|
|
48
49
|
netlifyToken,
|
|
49
50
|
site,
|
|
@@ -77,7 +78,7 @@ const graphEdit = async (options, command) => {
|
|
|
77
78
|
|
|
78
79
|
const { jwt } = await OneGraphClient.getGraphJwtForSite({ siteId, nfToken: netlifyToken })
|
|
79
80
|
await upsertMergeCLISessionMetadata({
|
|
80
|
-
|
|
81
|
+
config,
|
|
81
82
|
jwt,
|
|
82
83
|
siteId,
|
|
83
84
|
siteRoot: site.root,
|
|
@@ -1,29 +1,27 @@
|
|
|
1
|
+
/* eslint-disable eslint-comments/disable-enable-pair */
|
|
1
2
|
// @ts-check
|
|
2
|
-
const inquirer = require('inquirer')
|
|
3
|
-
const { GraphQL } = require('netlify-onegraph-internal')
|
|
4
3
|
|
|
5
4
|
const {
|
|
5
|
+
autocompleteCodegenModules,
|
|
6
|
+
autocompleteOperationNames,
|
|
6
7
|
buildSchema,
|
|
7
|
-
defaultExampleOperationsDoc,
|
|
8
|
-
extractFunctionsFromOperationDoc,
|
|
9
8
|
generateHandlerByOperationName,
|
|
9
|
+
getCodegenFunctionById,
|
|
10
|
+
getCodegenModule,
|
|
10
11
|
getNetlifyGraphConfig,
|
|
11
|
-
readGraphQLOperationsSourceFile,
|
|
12
12
|
readGraphQLSchemaFile,
|
|
13
13
|
} = require('../../lib/one-graph/cli-netlify-graph')
|
|
14
14
|
const { error, log } = require('../../utils')
|
|
15
15
|
|
|
16
|
-
const { parse } = GraphQL
|
|
17
|
-
|
|
18
16
|
/**
|
|
19
17
|
* Creates the `netlify graph:handler` command
|
|
20
|
-
* @param {string} userOperationName
|
|
21
18
|
* @param {import('commander').OptionValues} options
|
|
22
19
|
* @param {import('../base-command').BaseCommand} command
|
|
23
20
|
* @returns
|
|
24
21
|
*/
|
|
25
|
-
const graphHandler = async (
|
|
22
|
+
const graphHandler = async (args, options, command) => {
|
|
26
23
|
const netlifyGraphConfig = await getNetlifyGraphConfig({ command, options })
|
|
24
|
+
const { config } = command.netlify
|
|
27
25
|
|
|
28
26
|
const schemaString = readGraphQLSchemaFile(netlifyGraphConfig)
|
|
29
27
|
|
|
@@ -35,69 +33,48 @@ const graphHandler = async (userOperationName, options, command) => {
|
|
|
35
33
|
error(`Error parsing schema: ${buildSchemaError}`)
|
|
36
34
|
}
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
36
|
+
const userOperationName = args.operationName
|
|
37
|
+
const userCodegenId = options.codegen
|
|
41
38
|
|
|
42
39
|
let operationName = userOperationName
|
|
43
40
|
if (!operationName) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (currentOperationsDoc.trim().length === 0) {
|
|
47
|
-
currentOperationsDoc = defaultExampleOperationsDoc
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const parsedDoc = parse(currentOperationsDoc)
|
|
51
|
-
const { functions } = extractFunctionsFromOperationDoc(parsedDoc)
|
|
52
|
-
|
|
53
|
-
const sorted = Object.values(functions).sort((aItem, bItem) =>
|
|
54
|
-
aItem.operationName.localeCompare(bItem.operationName),
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
const perPage = 50
|
|
58
|
-
|
|
59
|
-
const allOperationChoices = sorted.map((operation) => ({
|
|
60
|
-
name: `${operation.operationName} (${operation.kind})`,
|
|
61
|
-
value: operation.operationName,
|
|
62
|
-
}))
|
|
63
|
-
|
|
64
|
-
const filterOperationNames = (operationChoices, input) =>
|
|
65
|
-
operationChoices.filter((operation) => operation.value.toLowerCase().match(input.toLowerCase()))
|
|
66
|
-
|
|
67
|
-
// eslint-disable-next-line n/global-require
|
|
68
|
-
const inquirerAutocompletePrompt = require('inquirer-autocomplete-prompt')
|
|
69
|
-
/** multiple matching detectors, make the user choose */
|
|
70
|
-
inquirer.registerPrompt('autocomplete', inquirerAutocompletePrompt)
|
|
41
|
+
operationName = await autocompleteOperationNames({ netlifyGraphConfig })
|
|
42
|
+
}
|
|
71
43
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
type: 'autocomplete',
|
|
76
|
-
pageSize: perPage,
|
|
77
|
-
source(_, input) {
|
|
78
|
-
if (!input || input === '') {
|
|
79
|
-
return allOperationChoices
|
|
80
|
-
}
|
|
44
|
+
if (!operationName) {
|
|
45
|
+
error(`No operation name provided`)
|
|
46
|
+
}
|
|
81
47
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
48
|
+
const codegenModule = await getCodegenModule({ config })
|
|
49
|
+
if (!codegenModule) {
|
|
50
|
+
error(
|
|
51
|
+
`No Netlify Graph codegen module specified in netlify.toml under the [graph] header. Please specify 'codeGenerator' field and try again.`,
|
|
52
|
+
)
|
|
53
|
+
return
|
|
54
|
+
}
|
|
87
55
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
} catch (parseError) {
|
|
92
|
-
parseError(`Error parsing operations library: ${parseError}`)
|
|
93
|
-
}
|
|
56
|
+
let codeGenerator = userCodegenId ? await getCodegenFunctionById({ config, id: userCodegenId }) : null
|
|
57
|
+
if (!codeGenerator) {
|
|
58
|
+
codeGenerator = await autocompleteCodegenModules({ config })
|
|
94
59
|
}
|
|
95
60
|
|
|
96
|
-
if (!
|
|
97
|
-
error(`
|
|
61
|
+
if (!codeGenerator) {
|
|
62
|
+
error(`Unable to select appropriate Netlify Graph code generator`)
|
|
63
|
+
return
|
|
98
64
|
}
|
|
99
65
|
|
|
100
|
-
|
|
66
|
+
if (schema) {
|
|
67
|
+
generateHandlerByOperationName({
|
|
68
|
+
generate: codeGenerator.generateHandler,
|
|
69
|
+
logger: log,
|
|
70
|
+
netlifyGraphConfig,
|
|
71
|
+
schema,
|
|
72
|
+
operationName,
|
|
73
|
+
handlerOptions: {},
|
|
74
|
+
})
|
|
75
|
+
} else {
|
|
76
|
+
error(`Failed to parse Netlify GraphQL schema`)
|
|
77
|
+
}
|
|
101
78
|
}
|
|
102
79
|
|
|
103
80
|
/**
|
|
@@ -109,11 +86,12 @@ const createGraphHandlerCommand = (program) =>
|
|
|
109
86
|
program
|
|
110
87
|
.command('graph:handler')
|
|
111
88
|
.argument('[name]', 'Operation name')
|
|
89
|
+
.option('-c, --codegen <id>', 'The id of the specific code generator to use')
|
|
112
90
|
.description(
|
|
113
91
|
'Generate a handler for a Graph operation given its name. See `graph:operations` for a list of operations.',
|
|
114
92
|
)
|
|
115
93
|
.action(async (operationName, options, command) => {
|
|
116
|
-
await graphHandler(operationName, options, command)
|
|
94
|
+
await graphHandler({ operationName }, options, command)
|
|
117
95
|
})
|
|
118
96
|
|
|
119
97
|
module.exports = { createGraphHandlerCommand }
|
|
@@ -18,7 +18,7 @@ const { ensureAppForSite, executeCreateApiTokenMutation } = OneGraphCliClient
|
|
|
18
18
|
* @returns
|
|
19
19
|
*/
|
|
20
20
|
const graphInit = async (options, command) => {
|
|
21
|
-
const { api, site, state } = command.netlify
|
|
21
|
+
const { api, config, site, state } = command.netlify
|
|
22
22
|
const siteId = site.id
|
|
23
23
|
|
|
24
24
|
if (!siteId) {
|
|
@@ -66,6 +66,7 @@ const graphInit = async (options, command) => {
|
|
|
66
66
|
|
|
67
67
|
const netlifyGraphConfig = await getNetlifyGraphConfig({ command, options })
|
|
68
68
|
await ensureCLISession({
|
|
69
|
+
config,
|
|
69
70
|
metadata: {},
|
|
70
71
|
netlifyToken,
|
|
71
72
|
site,
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
+
const { GraphQL } = require('netlify-onegraph-internal')
|
|
3
|
+
|
|
2
4
|
const { readLockfile } = require('../../lib/one-graph/cli-client')
|
|
3
5
|
const {
|
|
4
6
|
buildSchema,
|
|
@@ -19,10 +21,19 @@ const { NETLIFYDEVERR, chalk, error, log } = require('../../utils')
|
|
|
19
21
|
* @returns
|
|
20
22
|
*/
|
|
21
23
|
const graphLibrary = async (options, command) => {
|
|
24
|
+
const { config } = command.netlify
|
|
22
25
|
const netlifyGraphConfig = await getNetlifyGraphConfig({ command, options })
|
|
23
26
|
|
|
24
27
|
const schemaString = readGraphQLSchemaFile(netlifyGraphConfig)
|
|
25
28
|
|
|
29
|
+
let currentOperationsDoc = readGraphQLOperationsSourceFile(netlifyGraphConfig)
|
|
30
|
+
if (currentOperationsDoc.trim().length === 0) {
|
|
31
|
+
currentOperationsDoc = defaultExampleOperationsDoc
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const parsedDoc = parse(currentOperationsDoc)
|
|
35
|
+
const { fragments, functions } = extractFunctionsFromOperationDoc(GraphQL, parsedDoc)
|
|
36
|
+
|
|
26
37
|
let schema
|
|
27
38
|
|
|
28
39
|
try {
|
|
@@ -33,29 +44,24 @@ const graphLibrary = async (options, command) => {
|
|
|
33
44
|
|
|
34
45
|
if (!schema) {
|
|
35
46
|
error(`Failed to parse Netlify GraphQL schema`)
|
|
47
|
+
return
|
|
36
48
|
}
|
|
37
49
|
|
|
38
|
-
let currentOperationsDoc = readGraphQLOperationsSourceFile(netlifyGraphConfig)
|
|
39
|
-
if (currentOperationsDoc.trim().length === 0) {
|
|
40
|
-
currentOperationsDoc = defaultExampleOperationsDoc
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const parsedDoc = parse(currentOperationsDoc)
|
|
44
|
-
const { fragments, functions } = extractFunctionsFromOperationDoc(parsedDoc)
|
|
45
|
-
|
|
46
50
|
const lockfile = readLockfile({ siteRoot: command.netlify.site.root })
|
|
47
51
|
|
|
48
|
-
if (lockfile
|
|
52
|
+
if (lockfile === undefined) {
|
|
49
53
|
error(
|
|
50
54
|
`${NETLIFYDEVERR} Error: no lockfile found, unable to run \`netlify graph:library\`. To pull a remote schema (and create a lockfile), run ${chalk.yellow(
|
|
51
55
|
'netlify graph:pull',
|
|
52
56
|
)} `,
|
|
53
57
|
)
|
|
58
|
+
return
|
|
54
59
|
}
|
|
55
60
|
|
|
56
|
-
const schemaId = lockfile
|
|
61
|
+
const { schemaId } = lockfile.locked
|
|
57
62
|
|
|
58
|
-
|
|
63
|
+
const payload = {
|
|
64
|
+
config,
|
|
59
65
|
logger: log,
|
|
60
66
|
netlifyGraphConfig,
|
|
61
67
|
schema,
|
|
@@ -63,7 +69,9 @@ const graphLibrary = async (options, command) => {
|
|
|
63
69
|
operationsDoc: currentOperationsDoc,
|
|
64
70
|
functions,
|
|
65
71
|
fragments,
|
|
66
|
-
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
generateFunctionsFile(payload)
|
|
67
75
|
}
|
|
68
76
|
|
|
69
77
|
/**
|
|
@@ -26,13 +26,18 @@ const graphOperations = async (options, command) => {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
const parsedDoc = parse(currentOperationsDoc)
|
|
29
|
-
const { fragments, functions } = extractFunctionsFromOperationDoc(parsedDoc)
|
|
29
|
+
const { fragments, functions } = extractFunctionsFromOperationDoc(GraphQL, parsedDoc)
|
|
30
30
|
|
|
31
31
|
const sorted = {
|
|
32
|
+
/** @type {import('netlify-onegraph-internal/dist/netlifyGraph').ExtractedFunction[]} */
|
|
32
33
|
queries: [],
|
|
34
|
+
/** @type {import('netlify-onegraph-internal/dist/netlifyGraph').ExtractedFunction[]} */
|
|
33
35
|
mutations: [],
|
|
36
|
+
/** @type {import('netlify-onegraph-internal/dist/netlifyGraph').ExtractedFunction[]} */
|
|
34
37
|
subscriptions: [],
|
|
38
|
+
/** @type {import('netlify-onegraph-internal/dist/netlifyGraph').ExtractedFragment[]} */
|
|
35
39
|
fragments: [],
|
|
40
|
+
/** @type {any[]} */
|
|
36
41
|
other: [],
|
|
37
42
|
}
|
|
38
43
|
|