netlify-cli 8.0.7 → 8.1.0-rc

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.
Files changed (183) hide show
  1. package/bin/run +81 -3
  2. package/npm-shrinkwrap.json +305 -2105
  3. package/package.json +10 -33
  4. package/src/commands/addons/addons-auth.js +50 -0
  5. package/src/commands/addons/addons-config.js +180 -0
  6. package/src/commands/addons/addons-create.js +129 -0
  7. package/src/commands/addons/addons-delete.js +59 -0
  8. package/src/commands/addons/addons-list.js +62 -0
  9. package/src/commands/addons/addons.js +49 -0
  10. package/src/commands/addons/index.js +3 -24
  11. package/src/commands/api/api.js +83 -0
  12. package/src/commands/api/index.js +5 -0
  13. package/src/commands/base-command.js +322 -0
  14. package/src/commands/build/build.js +58 -0
  15. package/src/commands/build/index.js +3 -61
  16. package/src/commands/completion/completion.js +18 -0
  17. package/src/commands/completion/index.js +5 -0
  18. package/src/commands/{deploy.js → deploy/deploy.js} +306 -278
  19. package/src/commands/deploy/index.js +5 -0
  20. package/src/commands/dev/dev-exec.js +39 -0
  21. package/src/commands/dev/dev-trace.js +50 -0
  22. package/src/commands/dev/dev.js +349 -0
  23. package/src/commands/dev/index.js +3 -335
  24. package/src/commands/env/env-get.js +51 -0
  25. package/src/commands/env/env-import.js +93 -0
  26. package/src/commands/env/env-list.js +63 -0
  27. package/src/commands/env/env-set.js +67 -0
  28. package/src/commands/env/env-unset.js +66 -0
  29. package/src/commands/env/env.js +47 -0
  30. package/src/commands/env/index.js +3 -23
  31. package/src/commands/functions/functions-build.js +59 -0
  32. package/src/commands/functions/{create.js → functions-create.js} +133 -94
  33. package/src/commands/functions/functions-invoke.js +276 -0
  34. package/src/commands/functions/functions-list.js +107 -0
  35. package/src/commands/functions/functions-serve.js +63 -0
  36. package/src/commands/functions/functions.js +53 -0
  37. package/src/commands/functions/index.js +3 -45
  38. package/src/commands/index.js +5 -0
  39. package/src/commands/init/index.js +6 -0
  40. package/src/commands/{init.js → init/init.js} +79 -68
  41. package/src/commands/link/index.js +6 -0
  42. package/src/{utils/link/link-by-prompt.js → commands/link/link.js} +153 -14
  43. package/src/commands/lm/index.js +3 -19
  44. package/src/commands/lm/lm-info.js +42 -0
  45. package/src/commands/lm/lm-install.js +36 -0
  46. package/src/commands/lm/lm-setup.js +106 -0
  47. package/src/commands/lm/lm-uninstall.js +25 -0
  48. package/src/commands/lm/lm.js +39 -0
  49. package/src/commands/login/index.js +6 -0
  50. package/src/commands/login/login.js +52 -0
  51. package/src/commands/logout/index.js +5 -0
  52. package/src/commands/logout/logout.js +43 -0
  53. package/src/commands/main.js +117 -0
  54. package/src/commands/open/index.js +3 -39
  55. package/src/commands/open/open-admin.js +56 -0
  56. package/src/commands/open/open-site.js +49 -0
  57. package/src/commands/open/open.js +42 -0
  58. package/src/commands/sites/index.js +5 -20
  59. package/src/commands/sites/sites-create.js +184 -0
  60. package/src/commands/sites/sites-delete.js +108 -0
  61. package/src/commands/sites/sites-list.js +89 -0
  62. package/src/commands/sites/sites.js +36 -0
  63. package/src/commands/status/index.js +3 -118
  64. package/src/commands/status/status-hooks.js +73 -0
  65. package/src/commands/status/status.js +125 -0
  66. package/src/commands/switch/index.js +5 -0
  67. package/src/commands/switch/switch.js +50 -0
  68. package/src/commands/unlink/index.js +5 -0
  69. package/src/commands/unlink/unlink.js +48 -0
  70. package/src/commands/watch/index.js +5 -0
  71. package/src/commands/watch/watch.js +121 -0
  72. package/src/lib/build.js +21 -7
  73. package/src/lib/exec-fetcher.js +5 -3
  74. package/src/lib/fs.js +54 -36
  75. package/src/lib/functions/background.js +1 -1
  76. package/src/lib/functions/form-submissions-handler.js +2 -1
  77. package/src/lib/functions/local-proxy.js +2 -1
  78. package/src/lib/functions/netlify-function.js +4 -1
  79. package/src/lib/functions/registry.js +4 -6
  80. package/src/lib/functions/runtimes/go/index.js +2 -1
  81. package/src/lib/functions/runtimes/js/builders/netlify-lambda.js +6 -4
  82. package/src/lib/functions/runtimes/js/builders/zisi.js +3 -3
  83. package/src/lib/functions/runtimes/rust/index.js +4 -3
  84. package/src/lib/functions/server.js +2 -3
  85. package/src/lib/functions/synchronous.js +2 -1
  86. package/src/lib/functions/utils.js +2 -3
  87. package/src/lib/functions/watcher.js +1 -0
  88. package/src/lib/http-agent.js +3 -5
  89. package/src/lib/log.js +2 -1
  90. package/src/lib/spinner.js +22 -0
  91. package/src/utils/addons/diffs/index.js +1 -0
  92. package/src/utils/addons/diffs/options.js +3 -1
  93. package/src/utils/addons/prepare.js +13 -6
  94. package/src/utils/addons/prompts.js +2 -1
  95. package/src/utils/addons/render.js +3 -1
  96. package/src/utils/command-helpers.js +156 -43
  97. package/src/utils/create-stream-promise.js +5 -5
  98. package/src/utils/deferred.js +1 -0
  99. package/src/utils/deploy/deploy-site.js +1 -1
  100. package/src/utils/deploy/index.js +4 -0
  101. package/src/utils/detect-server-settings.js +10 -12
  102. package/src/utils/dev.js +18 -10
  103. package/src/utils/dot-env.js +4 -2
  104. package/src/utils/{edge-handlers.js → functions/edge-handlers.js} +8 -7
  105. package/src/utils/functions/functions.js +36 -0
  106. package/src/utils/{get-functions.js → functions/get-functions.js} +2 -1
  107. package/src/utils/functions/index.js +8 -26
  108. package/src/utils/get-global-config.js +3 -2
  109. package/src/utils/get-repo-data.js +1 -0
  110. package/src/utils/gh-auth.js +1 -0
  111. package/src/utils/gitignore.js +7 -5
  112. package/src/utils/header.js +2 -2
  113. package/src/utils/headers.js +1 -2
  114. package/src/utils/index.js +42 -0
  115. package/src/utils/init/config-github.js +12 -5
  116. package/src/utils/init/config-manual.js +9 -2
  117. package/src/utils/init/config.js +13 -7
  118. package/src/utils/init/frameworks.js +1 -0
  119. package/src/utils/init/node-version.js +4 -2
  120. package/src/utils/init/plugins.js +1 -0
  121. package/src/utils/init/utils.js +10 -6
  122. package/src/utils/live-tunnel.js +3 -4
  123. package/src/utils/lm/install.js +10 -15
  124. package/src/utils/lm/requirements.js +3 -1
  125. package/src/utils/lm/steps.js +1 -1
  126. package/src/utils/lm/ui.js +7 -3
  127. package/src/utils/open-browser.js +8 -2
  128. package/src/utils/parse-raw-flags.js +4 -4
  129. package/src/utils/proxy.js +6 -5
  130. package/src/utils/read-repo-url.js +1 -0
  131. package/src/utils/redirects.js +2 -2
  132. package/src/utils/rules-proxy.js +2 -1
  133. package/src/utils/state-config.js +1 -1
  134. package/src/utils/telemetry/index.js +2 -113
  135. package/src/utils/telemetry/request.js +3 -1
  136. package/src/utils/telemetry/telemetry.js +117 -0
  137. package/src/utils/telemetry/validation.js +13 -12
  138. package/src/utils/traffic-mesh.js +3 -3
  139. package/oclif.manifest.json +0 -1
  140. package/src/commands/addons/auth.js +0 -42
  141. package/src/commands/addons/config.js +0 -177
  142. package/src/commands/addons/create.js +0 -127
  143. package/src/commands/addons/delete.js +0 -69
  144. package/src/commands/addons/list.js +0 -54
  145. package/src/commands/api.js +0 -84
  146. package/src/commands/dev/exec.js +0 -32
  147. package/src/commands/dev/trace.js +0 -61
  148. package/src/commands/env/get.js +0 -44
  149. package/src/commands/env/import.js +0 -90
  150. package/src/commands/env/list.js +0 -49
  151. package/src/commands/env/set.js +0 -64
  152. package/src/commands/env/unset.js +0 -58
  153. package/src/commands/functions/build.js +0 -60
  154. package/src/commands/functions/invoke.js +0 -277
  155. package/src/commands/functions/list.js +0 -102
  156. package/src/commands/functions/serve.js +0 -70
  157. package/src/commands/link.js +0 -133
  158. package/src/commands/lm/info.js +0 -36
  159. package/src/commands/lm/install.js +0 -30
  160. package/src/commands/lm/setup.js +0 -107
  161. package/src/commands/lm/uninstall.js +0 -17
  162. package/src/commands/login.js +0 -54
  163. package/src/commands/logout.js +0 -37
  164. package/src/commands/open/admin.js +0 -51
  165. package/src/commands/open/site.js +0 -43
  166. package/src/commands/sites/create.js +0 -191
  167. package/src/commands/sites/delete.js +0 -116
  168. package/src/commands/sites/list.js +0 -84
  169. package/src/commands/status/hooks.js +0 -60
  170. package/src/commands/switch.js +0 -44
  171. package/src/commands/unlink.js +0 -38
  172. package/src/commands/watch.js +0 -115
  173. package/src/hooks/init.js +0 -46
  174. package/src/index.js +0 -25
  175. package/src/lib/help.js +0 -26
  176. package/src/utils/chalk.js +0 -16
  177. package/src/utils/check-command-inputs.js +0 -21
  178. package/src/utils/command.js +0 -262
  179. package/src/utils/detect-functions-builder.js +0 -25
  180. package/src/utils/difference.js +0 -4
  181. package/src/utils/logo.js +0 -11
  182. package/src/utils/show-help.js +0 -5
  183. package/src/utils/telemetry/tracked-command.js +0 -51
@@ -0,0 +1,39 @@
1
+ // @ts-check
2
+
3
+ const { generateCommandsHelp, generateDescriptionHelp, generateExamplesHelp } = require('../../utils')
4
+
5
+ const { createLmInfoCommand } = require('./lm-info')
6
+ const { createLmInstallCommand } = require('./lm-install')
7
+ const { createLmSetupCommand } = require('./lm-setup')
8
+ const { createLmUninstallCommand } = require('./lm-uninstall')
9
+
10
+ /**
11
+ * The lm command
12
+ * @param {import('commander').OptionValues} options
13
+ * @param {import('../base-command').BaseCommand} command
14
+ */
15
+ const lm = (options, command) => {
16
+ command.help()
17
+ }
18
+
19
+ /**
20
+ * Creates the `netlify lm` command
21
+ * @param {import('../base-command').BaseCommand} program
22
+ * @returns
23
+ */
24
+ const createLmCommand = (program) => {
25
+ createLmInfoCommand(program)
26
+ createLmInstallCommand(program)
27
+ createLmSetupCommand(program)
28
+ createLmUninstallCommand(program)
29
+
30
+ program
31
+ .command('lm')
32
+ .description('Handle Netlify Large Media operations')
33
+ .addHelpCommand('after', generateDescriptionHelp('The lm command will help you manage large media for a site'))
34
+ .addHelpCommand('after', generateExamplesHelp(['netlify lm:info', 'netlify lm:install', 'netlify lm:setup']))
35
+ .addHelpCommand('after', generateCommandsHelp('lm', program))
36
+ .action(lm)
37
+ }
38
+
39
+ module.exports = { createLmCommand }
@@ -0,0 +1,6 @@
1
+ const { createLoginCommand, login } = require('./login')
2
+
3
+ module.exports = {
4
+ createLoginCommand,
5
+ login,
6
+ }
@@ -0,0 +1,52 @@
1
+ // @ts-check
2
+ const { chalk, exit, getToken, log } = require('../../utils')
3
+
4
+ const msg = function (location) {
5
+ switch (location) {
6
+ case 'env':
7
+ return 'via process.env.NETLIFY_AUTH_TOKEN set in your terminal session'
8
+ case 'flag':
9
+ return 'via CLI --auth flag'
10
+ case 'config':
11
+ return 'via netlify config on your machine'
12
+ default:
13
+ return ''
14
+ }
15
+ }
16
+
17
+ /**
18
+ * The login command
19
+ * @param {import('commander').OptionValues} options
20
+ * @param {import('../base-command').BaseCommand} command
21
+ */
22
+ const login = async (options, command) => {
23
+ const [accessToken, location] = await getToken()
24
+
25
+ command.setAnalyticsPayload({ new: options.new })
26
+
27
+ if (accessToken && !options.new) {
28
+ log(`Already logged in ${msg(location)}`)
29
+ log()
30
+ log(`Run ${chalk.cyanBright('netlify status')} for account details`)
31
+ log()
32
+ log(`To see all available commands run: ${chalk.cyanBright('netlify help')}`)
33
+ log()
34
+ return exit()
35
+ }
36
+
37
+ await command.expensivelyAuthenticate()
38
+ }
39
+
40
+ /**
41
+ * Creates the `netlify login` command
42
+ * @param {import('../base-command').BaseCommand} program
43
+ * @returns
44
+ */
45
+ const createLoginCommand = (program) =>
46
+ program
47
+ .command('login')
48
+ .description('Login to your Netlify account')
49
+ .option('--new', 'Login to new Netlify account')
50
+ .action(login)
51
+
52
+ module.exports = { createLoginCommand, login }
@@ -0,0 +1,5 @@
1
+ const { createLogoutCommand } = require('./logout')
2
+
3
+ module.exports = {
4
+ createLogoutCommand,
5
+ }
@@ -0,0 +1,43 @@
1
+ // @ts-check
2
+ const { exit, getToken, log, track } = require('../../utils')
3
+
4
+ /**
5
+ * The logout command
6
+ * @param {import('commander').OptionValues} options
7
+ * @param {import('../base-command').BaseCommand} command
8
+ */
9
+ const logout = async (options, command) => {
10
+ const [accessToken, location] = await getToken()
11
+
12
+ if (!accessToken) {
13
+ log(`Already logged out`)
14
+ log()
15
+ log('To login run "netlify login"')
16
+ exit()
17
+ }
18
+
19
+ await track('user_logout')
20
+
21
+ // unset userID without deleting key
22
+ command.netlify.globalConfig.set('userId', null)
23
+
24
+ if (location === 'env') {
25
+ log('The "process.env.NETLIFY_AUTH_TOKEN" is still set in your terminal session')
26
+ log()
27
+ log('To logout completely, unset the environment variable')
28
+ log()
29
+ exit()
30
+ }
31
+
32
+ log(`Logging you out of Netlify. Come back soon!`)
33
+ }
34
+
35
+ /**
36
+ * Creates the `netlify logout` command
37
+ * @param {import('../base-command').BaseCommand} program
38
+ * @returns
39
+ */
40
+ const createLogoutCommand = (program) =>
41
+ program.command('logout', { hidden: true }).description('Logout of your Netlify account').action(logout)
42
+
43
+ module.exports = { createLogoutCommand, logout }
@@ -0,0 +1,117 @@
1
+ // @ts-check
2
+ const process = require('process')
3
+
4
+ const inquirer = require('inquirer')
5
+ const { findBestMatch } = require('string-similarity')
6
+
7
+ const { NETLIFY_CYAN, USER_AGENT, chalk, error, execa, exit, getGlobalConfig, log, track, warn } = require('../utils')
8
+
9
+ const SUGGESTION_TIMEOUT = 1e4
10
+
11
+ const getVersionPage = async () => {
12
+ // performance optimization - load envinfo on demand
13
+ // eslint-disable-next-line node/global-require
14
+ const envinfo = require('envinfo')
15
+ const data = await envinfo.run({
16
+ System: ['OS', 'CPU'],
17
+ Binaries: ['Node', 'Yarn', 'npm'],
18
+ Browsers: ['Chrome', 'Edge', 'Firefox', 'Safari'],
19
+ npmGlobalPackages: ['netlify-cli'],
20
+ })
21
+
22
+ return `
23
+ ────────────────────┐
24
+ Environment Info │
25
+ ────────────────────┘
26
+ ${data}
27
+ ${USER_AGENT}
28
+ `
29
+ }
30
+
31
+ /**
32
+ * The main CLI command without any command (root action)
33
+ * @param {import('commander').OptionValues} options
34
+ * @param {import('./base-command').BaseCommand} command
35
+ */
36
+ const mainCommand = async function (options, command) {
37
+ const globalConfig = await getGlobalConfig()
38
+
39
+ if (options.telemetryDisable) {
40
+ globalConfig.set('telemetryDisabled', true)
41
+ console.log('Netlify telemetry has been disabled')
42
+ console.log('You can renable it anytime with the --telemetry-enable flag')
43
+ exit()
44
+ }
45
+ if (options.telemetryEnable) {
46
+ globalConfig.set('telemetryDisabled', false)
47
+ console.log('Netlify telemetry has been enabled')
48
+ console.log('You can disable it anytime with the --telemetry-disable flag')
49
+ await track('user_telemetryEnabled')
50
+ exit()
51
+ }
52
+
53
+ if (command.args[0] === 'version') {
54
+ const versionPage = await getVersionPage()
55
+ log(versionPage)
56
+ exit()
57
+ }
58
+
59
+ warn(`${chalk.yellow(command.args[0])} is not a ${command.name()} command.`)
60
+
61
+ const allCommands = command.commands.map((cmd) => cmd.name())
62
+ const {
63
+ bestMatch: { target: suggestion },
64
+ } = findBestMatch(command.args[0], allCommands)
65
+
66
+ const applySuggestion = await new Promise((resolve) => {
67
+ const prompt = inquirer.prompt({
68
+ type: 'confirm',
69
+ name: 'suggestion',
70
+ message: `Did you mean ${chalk.blue(suggestion)}`,
71
+ default: false,
72
+ })
73
+
74
+ setTimeout(() => {
75
+ // @ts-ignore
76
+ prompt.ui.close()
77
+ resolve(false)
78
+ }, SUGGESTION_TIMEOUT)
79
+
80
+ // eslint-disable-next-line promise/catch-or-return
81
+ prompt.then((value) => resolve(value))
82
+ })
83
+ // create new log line
84
+ log()
85
+
86
+ if (!applySuggestion) {
87
+ error(`Run ${NETLIFY_CYAN(`${command.name()} help`)} for a list of available commands.`)
88
+ }
89
+
90
+ await execa(process.argv[0], [process.argv[1], suggestion], { stdio: 'inherit' })
91
+ }
92
+
93
+ /**
94
+ * Creates the `netlify functions:create` command
95
+ * @param {import('./base-command').BaseCommand} program
96
+ * @returns
97
+ */
98
+ const createMainCommand = async (program) =>
99
+ program
100
+ .version(await getVersionPage(), '-v, --version')
101
+ .showSuggestionAfterError(true)
102
+ .option('--telemetry-disable', 'Disable telemetry')
103
+ .option('--telemetry-enable', 'Enables telemetry')
104
+ .configureHelp({
105
+ // TODO: Add custom formater to have same visual styling
106
+ // formatHelp: (cmd, helper) => {
107
+ // console.log(cmd, helper);
108
+ // const longestFlag = Math.max(...cmd.options.map((option) => option.flags.length)) + 1;
109
+ // const table = cmd.options.map(({flags, description}) => ` ${flags}${new Array(longestFlag-flags.length).fill().join(' ')} {grey ${description}} `).join('\n')
110
+ // return chalk`{bold OPTIONS}
111
+ // ${table}
112
+ // `
113
+ // }
114
+ })
115
+ .action(mainCommand)
116
+
117
+ module.exports = { createMainCommand }
@@ -1,41 +1,5 @@
1
- const { flags: flagsLib } = require('@oclif/command')
1
+ const { createOpenCommand } = require('./open')
2
2
 
3
- const { isEmptyCommand } = require('../../utils/check-command-inputs')
4
- const Command = require('../../utils/command')
5
- const showHelp = require('../../utils/show-help')
6
-
7
- const OpenAdminCommand = require('./admin')
8
- const OpenSiteCommand = require('./site')
9
-
10
- class OpenCommand extends Command {
11
- async run() {
12
- const { args, flags } = this.parse(OpenCommand)
13
-
14
- // Show help on empty sub command
15
- if (isEmptyCommand(flags, args)) {
16
- showHelp(this.id)
17
- }
18
-
19
- if (flags.site) {
20
- await OpenSiteCommand.run()
21
- }
22
- // Default open netlify admin
23
- await OpenAdminCommand.run()
24
- }
3
+ module.exports = {
4
+ createOpenCommand,
25
5
  }
26
-
27
- OpenCommand.flags = {
28
- ...OpenCommand.flags,
29
- site: flagsLib.boolean({
30
- description: 'Open site',
31
- }),
32
- admin: flagsLib.boolean({
33
- description: 'Open Netlify site',
34
- }),
35
- }
36
-
37
- OpenCommand.description = `Open settings for the site linked to the current folder`
38
-
39
- OpenCommand.examples = ['netlify open --site', 'netlify open --admin', 'netlify open:admin', 'netlify open:site']
40
-
41
- module.exports = OpenCommand
@@ -0,0 +1,56 @@
1
+ const { error, exit, log, openBrowser, warn } = require('../../utils')
2
+
3
+ /**
4
+ * The open:admin command
5
+ * @param {import('commander').OptionValues} options
6
+ * @param {import('../base-command').BaseCommand} command
7
+ */
8
+ const openAdmin = async (options, command) => {
9
+ const { api, site } = command.netlify
10
+
11
+ await command.authenticate()
12
+
13
+ const siteId = site.id
14
+
15
+ if (!siteId) {
16
+ warn(`No Site ID found in current directory.
17
+ Run \`netlify link\` to connect to this folder to a site`)
18
+ return false
19
+ }
20
+
21
+ let siteData
22
+ try {
23
+ siteData = await api.getSite({ siteId })
24
+ log(`Opening "${siteData.name}" site admin UI:`)
25
+ log(`> ${siteData.admin_url}`)
26
+ } catch (error_) {
27
+ // unauthorized
28
+ if (error_.status === 401) {
29
+ warn(`Log in with a different account or re-link to a site you have permission for`)
30
+ error(`Not authorized to view the currently linked site (${siteId})`)
31
+ }
32
+ // site not found
33
+ if (error_.status === 404) {
34
+ log()
35
+ log('Please double check this ID and verify you are logged in with the correct account')
36
+ log()
37
+ log('To fix this, run `netlify unlink` then `netlify link` to reconnect to the correct site ID')
38
+ log()
39
+ error(`Site "${siteId}" not found in account`)
40
+ }
41
+ error(error_)
42
+ }
43
+
44
+ await openBrowser({ url: siteData.admin_url })
45
+ exit()
46
+ }
47
+
48
+ /**
49
+ * Creates the `netlify open:admin` command
50
+ * @param {import('../base-command').BaseCommand} program
51
+ * @returns
52
+ */
53
+ const createOpenAdminCommand = (program) =>
54
+ program.command('open:admin').description('Opens current site admin UI in Netlify').action(openAdmin)
55
+
56
+ module.exports = { createOpenAdminCommand, openAdmin }
@@ -0,0 +1,49 @@
1
+ const { error, exit, log, openBrowser, warn } = require('../../utils')
2
+
3
+ /**
4
+ * The open:site command
5
+ * @param {import('commander').OptionValues} options
6
+ * @param {import('../base-command').BaseCommand} command
7
+ */
8
+ const openSite = async (options, command) => {
9
+ const { api, site } = command.netlify
10
+
11
+ await command.authenticate()
12
+
13
+ const siteId = site.id
14
+
15
+ if (!siteId) {
16
+ warn(`No Site ID found in current directory.
17
+ Run \`netlify link\` to connect to this folder to a site`)
18
+ return false
19
+ }
20
+
21
+ let siteData
22
+ let url
23
+ try {
24
+ siteData = await api.getSite({ siteId })
25
+ url = siteData.ssl_url || siteData.url
26
+ log(`Opening "${siteData.name}" site url:`)
27
+ log(`> ${url}`)
28
+ } catch (error_) {
29
+ // unauthorized
30
+ if (error_.status === 401) {
31
+ warn(`Log in with a different account or re-link to a site you have permission for`)
32
+ error(`Not authorized to view the currently linked site (${siteId})`)
33
+ }
34
+ error(error_)
35
+ }
36
+
37
+ await openBrowser({ url })
38
+ exit()
39
+ }
40
+
41
+ /**
42
+ * Creates the `netlify open:site` command
43
+ * @param {import('../base-command').BaseCommand} program
44
+ * @returns
45
+ */
46
+ const createOpenSiteCommand = (program) =>
47
+ program.command('open:site').description('Opens current site url in browser').action(openSite)
48
+
49
+ module.exports = { createOpenSiteCommand, openSite }
@@ -0,0 +1,42 @@
1
+ const { generateCommandsHelp, generateExamplesHelp } = require('../../utils')
2
+
3
+ const { createOpenAdminCommand, openAdmin } = require('./open-admin')
4
+ const { createOpenSiteCommand, openSite } = require('./open-site')
5
+
6
+ /**
7
+ * The open command
8
+ * @param {import('commander').OptionValues} options
9
+ * @param {import('../base-command').BaseCommand} command
10
+ */
11
+ const open = async (options, command) => {
12
+ if (!options.site || !options.admin) {
13
+ command.help()
14
+ }
15
+
16
+ if (options.site) {
17
+ await openSite(options, command)
18
+ }
19
+ // Default open netlify admin
20
+ await openAdmin(options, command)
21
+ }
22
+
23
+ /**
24
+ * Creates the `netlify open` command
25
+ * @param {import('../base-command').BaseCommand} program
26
+ * @returns
27
+ */
28
+ const createOpenCommand = (program) => {
29
+ createOpenAdminCommand(program)
30
+ createOpenSiteCommand(program)
31
+
32
+ return program
33
+ .command('open')
34
+ .description('Open settings for the site linked to the current folder')
35
+ .addHelpText(
36
+ 'after',
37
+ generateExamplesHelp(['netlify open --site', 'netlify open --admin', 'netlify open:admin', 'netlify open:site']),
38
+ )
39
+ .addHelpText('after', generateCommandsHelp('open', program))
40
+ .action(open)
41
+ }
42
+ module.exports = { createOpenCommand }
@@ -1,22 +1,7 @@
1
- const { isEmptyCommand } = require('../../utils/check-command-inputs')
2
- const showHelp = require('../../utils/show-help')
3
- const { TrackedCommand } = require('../../utils/telemetry/tracked-command')
1
+ const { createSitesCommand } = require('./sites')
2
+ const { sitesCreate } = require('./sites-create')
4
3
 
5
- class SitesCommand extends TrackedCommand {
6
- run() {
7
- const { args, flags } = this.parse(SitesCommand)
8
-
9
- // Show help on empty sub command
10
- if (isEmptyCommand(flags, args)) {
11
- showHelp(this.id)
12
- }
13
- }
4
+ module.exports = {
5
+ createSitesCommand,
6
+ sitesCreate,
14
7
  }
15
-
16
- SitesCommand.description = `Handle various site operations
17
- The sites command will help you manage all your sites
18
- `
19
-
20
- SitesCommand.examples = ['netlify sites:create --name my-new-site', 'netlify sites:list']
21
-
22
- module.exports = SitesCommand
@@ -0,0 +1,184 @@
1
+ // @ts-check
2
+
3
+ const slugify = require('@sindresorhus/slugify')
4
+ const inquirer = require('inquirer')
5
+ const pick = require('lodash/pick')
6
+ const sample = require('lodash/sample')
7
+ const prettyjson = require('prettyjson')
8
+ const { v4: uuidv4 } = require('uuid')
9
+
10
+ const { chalk, error, getRepoData, log, logJson, track, warn } = require('../../utils')
11
+ const { configureRepo } = require('../../utils/init/config')
12
+
13
+ const SITE_NAME_SUGGESTION_SUFFIX_LENGTH = 5
14
+
15
+ /**
16
+ * The sites:create command
17
+ * @param {import('commander').OptionValues} options
18
+ * @param {import('../base-command').BaseCommand} command
19
+ */
20
+ const sitesCreate = async (options, command) => {
21
+ const { api } = command.netlify
22
+
23
+ await command.authenticate()
24
+
25
+ const accounts = await api.listAccountsForUser()
26
+
27
+ let { accountSlug } = options
28
+ if (!accountSlug) {
29
+ const { accountSlug: accountSlugInput } = await inquirer.prompt([
30
+ {
31
+ type: 'list',
32
+ name: 'accountSlug',
33
+ message: 'Team:',
34
+ choices: accounts.map((account) => ({
35
+ value: account.slug,
36
+ name: account.name,
37
+ })),
38
+ },
39
+ ])
40
+ accountSlug = accountSlugInput
41
+ }
42
+
43
+ const { name: nameFlag } = options
44
+ let user
45
+ let site
46
+
47
+ // Allow the user to reenter site name if selected one isn't available
48
+ const inputSiteName = async (name) => {
49
+ if (!user) user = await api.getCurrentUser()
50
+
51
+ if (!name) {
52
+ let { slug } = user
53
+ let suffix = ''
54
+
55
+ // If the user doesn't have a slug, we'll compute one. Because `full_name` is not guaranteed to be unique, we
56
+ // append a short randomly-generated ID to reduce the likelihood of a conflict.
57
+ if (!slug) {
58
+ slug = slugify(user.full_name || user.email)
59
+ suffix = `-${uuidv4().slice(0, SITE_NAME_SUGGESTION_SUFFIX_LENGTH)}`
60
+ }
61
+
62
+ const suggestions = [
63
+ `super-cool-site-by-${slug}${suffix}`,
64
+ `the-awesome-${slug}-site${suffix}`,
65
+ `${slug}-makes-great-sites${suffix}`,
66
+ `netlify-thinks-${slug}-is-great${suffix}`,
67
+ `the-great-${slug}-site${suffix}`,
68
+ `isnt-${slug}-awesome${suffix}`,
69
+ ]
70
+ const siteSuggestion = sample(suggestions)
71
+
72
+ console.log(
73
+ `Choose a unique site name (e.g. ${siteSuggestion}.netlify.app) or leave it blank for a random name. You can update the site name later.`,
74
+ )
75
+ const { name: nameInput } = await inquirer.prompt([
76
+ {
77
+ type: 'input',
78
+ name: 'name',
79
+ message: 'Site name (optional):',
80
+ filter: (val) => (val === '' ? undefined : val),
81
+ validate: (input) => /^[a-zA-Z\d-]+$/.test(input) || 'Only alphanumeric characters and hyphens are allowed',
82
+ },
83
+ ])
84
+ name = nameInput
85
+ }
86
+
87
+ const body = {}
88
+ if (typeof name === 'string') {
89
+ body.name = name.trim()
90
+ }
91
+ try {
92
+ site = await api.createSiteInTeam({
93
+ accountSlug,
94
+ body,
95
+ })
96
+ } catch (error_) {
97
+ if (error_.status === 422) {
98
+ warn(`${name}.netlify.app already exists. Please try a different slug.`)
99
+ await inputSiteName()
100
+ } else {
101
+ error(`createSiteInTeam error: ${error_.status}: ${error_.message}`)
102
+ }
103
+ }
104
+ }
105
+ await inputSiteName(nameFlag)
106
+
107
+ log()
108
+ log(chalk.greenBright.bold.underline(`Site Created`))
109
+ log()
110
+
111
+ const siteUrl = site.ssl_url || site.url
112
+ log(
113
+ prettyjson.render({
114
+ 'Admin URL': site.admin_url,
115
+ URL: siteUrl,
116
+ 'Site ID': site.id,
117
+ }),
118
+ )
119
+
120
+ track('sites_created', {
121
+ siteId: site.id,
122
+ adminUrl: site.admin_url,
123
+ siteUrl,
124
+ })
125
+
126
+ if (options.withCi) {
127
+ log('Configuring CI')
128
+ const repoData = await getRepoData()
129
+ await configureRepo({ context: this, siteId: site.id, repoData, manual: options.manual })
130
+ }
131
+
132
+ if (options.json) {
133
+ logJson(
134
+ pick(site, [
135
+ 'id',
136
+ 'state',
137
+ 'plan',
138
+ 'name',
139
+ 'custom_domain',
140
+ 'domain_aliases',
141
+ 'url',
142
+ 'ssl_url',
143
+ 'admin_url',
144
+ 'screenshot_url',
145
+ 'created_at',
146
+ 'updated_at',
147
+ 'user_id',
148
+ 'ssl',
149
+ 'force_ssl',
150
+ 'managed_dns',
151
+ 'deploy_url',
152
+ 'account_name',
153
+ 'account_slug',
154
+ 'git_provider',
155
+ 'deploy_hook',
156
+ 'capabilities',
157
+ 'id_domain',
158
+ ]),
159
+ )
160
+ }
161
+
162
+ return site
163
+ }
164
+
165
+ /**
166
+ * Creates the `netlify sites:create` command
167
+ * @param {import('../base-command').BaseCommand} program
168
+ * @returns
169
+ */
170
+ const createSitesCreateCommand = (program) =>
171
+ program
172
+ .command('sites:create')
173
+ .description('Create an empty site (advanced)')
174
+ .option('-n, --name [name]', 'name of site')
175
+ .option('-a, --account-slug [slug]', 'account slug to create the site under')
176
+ .option('-c, --with-ci', 'initialize CI hooks during site creation')
177
+ .option('-m, --manual', 'force manual CI setup. Used --with-ci flag')
178
+ .addHelpText(
179
+ 'after',
180
+ `Create a blank site that isn't associated with any git remote. Does not link to the current working directory.`,
181
+ )
182
+ .action(sitesCreate)
183
+
184
+ module.exports = { createSitesCreateCommand, sitesCreate }