From 5c5c69be7765fb5dfc4ef16f5c16923116ce41e3 Mon Sep 17 00:00:00 2001 From: Kunal Kundu <51631122+tinfoil-knight@users.noreply.github.com> Date: Mon, 26 Jul 2021 15:29:39 +0530 Subject: [PATCH] fix: enable and fix class-methods-use-this (#2827) --- .eslintrc.js | 1 - src/commands/addons/auth.js | 15 +- src/commands/addons/config.js | 41 +++--- src/commands/addons/create.js | 45 +++--- src/commands/addons/delete.js | 3 +- src/commands/addons/list.js | 12 +- src/commands/api.js | 11 +- src/commands/build/index.js | 3 +- src/commands/deploy.js | 34 ++--- src/commands/dev/exec.js | 4 +- src/commands/dev/index.js | 35 ++--- src/commands/dev/trace.js | 2 +- src/commands/env/get.js | 9 +- src/commands/env/import.js | 13 +- src/commands/env/list.js | 11 +- src/commands/env/set.js | 7 +- src/commands/env/unset.js | 7 +- src/commands/functions/build.js | 11 +- src/commands/functions/create.js | 29 ++-- src/commands/functions/list.js | 15 +- src/commands/functions/serve.js | 5 +- src/commands/init.js | 25 ++-- src/commands/link.js | 13 +- src/commands/lm/info.js | 1 + src/commands/lm/install.js | 2 +- src/commands/lm/setup.js | 6 +- src/commands/lm/uninstall.js | 1 + src/commands/login.js | 15 +- src/commands/logout.js | 19 +-- src/commands/open/admin.js | 17 ++- src/commands/open/site.js | 7 +- src/commands/sites/create.js | 16 +- src/commands/sites/delete.js | 33 +++-- src/commands/sites/list.js | 15 +- src/commands/status/hooks.js | 6 +- src/commands/status/index.js | 21 +-- src/commands/switch.js | 5 +- src/commands/unlink.js | 7 +- src/commands/watch.js | 7 +- .../js/slack-rate-limit/slack-rate-limit.js | 20 +-- src/lib/exec-fetcher.js | 3 +- src/lib/exec-fetcher.test.js | 6 +- src/lib/functions/registry.js | 39 ++--- src/lib/functions/server.js | 7 +- src/lib/http-agent.js | 3 +- src/lib/http-agent.test.js | 22 +-- src/utils/addons/prepare.js | 16 +- src/utils/command-helpers.js | 112 ++++++++++++++ src/utils/command.js | 139 ++---------------- src/utils/detect-server-settings.js | 25 ++-- src/utils/dev.js | 3 +- src/utils/get-repo-data.js | 4 +- src/utils/gh-auth.js | 9 +- src/utils/init/config-github.js | 19 ++- src/utils/init/config-manual.js | 12 +- src/utils/init/config.js | 8 +- src/utils/link/link-by-prompt.js | 51 +++---- src/utils/live-tunnel.js | 14 +- src/utils/lm/install.js | 11 +- src/utils/open-browser.js | 12 +- src/utils/state-config.js | 22 +-- src/utils/traffic-mesh.js | 8 +- tests/command.deploy.test.js | 2 +- 63 files changed, 559 insertions(+), 537 deletions(-) create mode 100644 src/utils/command-helpers.js diff --git a/.eslintrc.js b/.eslintrc.js index 18775d4d238..e3d6869fec1 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,7 +5,6 @@ module.exports = { rules: { // Those rules from @netlify/eslint-config-node are currently disabled // TODO: remove, so those rules are enabled - 'class-methods-use-this': 0, complexity: 0, 'max-depth': 0, 'max-lines': 0, diff --git a/src/commands/addons/auth.js b/src/commands/addons/auth.js index 1c8d3f95b2a..3e4d2d958ba 100644 --- a/src/commands/addons/auth.js +++ b/src/commands/addons/auth.js @@ -1,5 +1,6 @@ const { prepareAddonCommand, ADDON_VALIDATION } = require('../../utils/addons/prepare') const Command = require('../../utils/command') +const { log } = require('../../utils/command-helpers') const openBrowser = require('../../utils/open-browser') class AddonsAuthCommand extends Command { @@ -14,16 +15,16 @@ class AddonsAuthCommand extends Command { }) if (!addon.auth_url) { - console.log(`No Admin URL found for the "${addonName} add-on"`) + log(`No Admin URL found for the "${addonName} add-on"`) return false } - this.log() - this.log(`Opening ${addonName} add-on admin URL:`) - this.log() - this.log(addon.auth_url) - this.log() - await openBrowser({ url: addon.auth_url, log: this.log }) + log() + log(`Opening ${addonName} add-on admin URL:`) + log() + log(addon.auth_url) + log() + await openBrowser({ url: addon.auth_url }) this.exit() } } diff --git a/src/commands/addons/config.js b/src/commands/addons/config.js index de7ad558523..628efeca3ea 100644 --- a/src/commands/addons/config.js +++ b/src/commands/addons/config.js @@ -9,6 +9,7 @@ const generatePrompts = require('../../utils/addons/prompts') const render = require('../../utils/addons/render') const { requiredConfigValues, missingConfigValues, updateConfigValues } = require('../../utils/addons/validation') const Command = require('../../utils/command') +const { log } = require('../../utils/command-helpers') const { parseRawFlags } = require('../../utils/parse-raw-flags') class AddonsConfigCommand extends Command { @@ -33,7 +34,7 @@ class AddonsConfigCommand extends Command { const currentConfig = addon.config || {} const words = `Current "${addonName} add-on" Settings:` - this.log(` ${chalk.yellowBright.bold(words)}`) + log(` ${chalk.yellowBright.bold(words)}`) if (hasConfig) { if (!rawFlags.silent) { render.configValues(addonName, manifest.config, currentConfig) @@ -41,7 +42,7 @@ class AddonsConfigCommand extends Command { } else { // For addons without manifest. TODO remove once we enfore manifests Object.keys(currentConfig).forEach((key) => { - this.log(`${key} - ${currentConfig[key]}`) + log(`${key} - ${currentConfig[key]}`) }) } @@ -61,7 +62,6 @@ class AddonsConfigCommand extends Command { instanceId: addon.id, api, error: this.error, - log: this.log, }) return false } @@ -75,16 +75,16 @@ class AddonsConfigCommand extends Command { }, ]) if (!updatePrompt.updateNow) { - this.log('Sounds good! Exiting configuration...') + log('Sounds good! Exiting configuration...') return false } - this.log() - this.log(` - Hit ${chalk.white.bold('enter')} to keep the existing value in (parentheses)`) - this.log(` - Hit ${chalk.white.bold('down arrow')} to remove the value`) - this.log(` - Hit ${chalk.white.bold('ctrl + C')} to cancel & exit configuration`) - this.log() - this.log(` You will need to verify the changed before we push them to your live site!`) - this.log() + log() + log(` - Hit ${chalk.white.bold('enter')} to keep the existing value in (parentheses)`) + log(` - Hit ${chalk.white.bold('down arrow')} to remove the value`) + log(` - Hit ${chalk.white.bold('ctrl + C')} to cancel & exit configuration`) + log() + log(` You will need to verify the changed before we push them to your live site!`) + log() const prompts = generatePrompts({ config: manifest.config, configValues: currentConfig, @@ -94,20 +94,20 @@ class AddonsConfigCommand extends Command { const newConfig = updateConfigValues(manifest.config, currentConfig, userInput) const diffs = compare(currentConfig, newConfig) - // this.log('compare', diffs) + // log('compare', diffs) if (diffs.isEqual) { - this.log(`No changes. exiting early`) + log(`No changes. exiting early`) return false } - this.log() - this.log(`${chalk.yellowBright.bold.underline('Confirm your updates:')}`) - this.log() + log() + log(`${chalk.yellowBright.bold.underline('Confirm your updates:')}`) + log() diffs.keys.forEach((key) => { const { newValue, oldValue } = diffs.diffs[key] const oldVal = oldValue || 'NO VALUE' - this.log(`${chalk.cyan(key)} changed from ${chalk.whiteBright(oldVal)} to ${chalk.green(newValue)}`) + log(`${chalk.cyan(key)} changed from ${chalk.whiteBright(oldVal)} to ${chalk.green(newValue)}`) }) - this.log() + log() const confirmPrompt = await inquirer.prompt([ { @@ -121,7 +121,7 @@ class AddonsConfigCommand extends Command { ]) if (!confirmPrompt.confirmChange) { - this.log('Canceling changes... You are good to go!') + log('Canceling changes... You are good to go!') return false } @@ -133,13 +133,12 @@ class AddonsConfigCommand extends Command { instanceId: addon.id, api, error: this.error, - log: this.log, }) } } } -const update = async function ({ addonName, currentConfig, newConfig, siteId, instanceId, api, error, log }) { +const update = async function ({ addonName, currentConfig, newConfig, siteId, instanceId, api, error }) { const codeDiff = diffValues(currentConfig, newConfig) if (!codeDiff) { log('No changes, exiting early') diff --git a/src/commands/addons/create.js b/src/commands/addons/create.js index 04c795685ad..ecf68023b91 100644 --- a/src/commands/addons/create.js +++ b/src/commands/addons/create.js @@ -7,9 +7,10 @@ const generatePrompts = require('../../utils/addons/prompts') const render = require('../../utils/addons/render') const { requiredConfigValues, missingConfigValues, updateConfigValues } = require('../../utils/addons/validation') const Command = require('../../utils/command') +const { log } = require('../../utils/command-helpers') const { parseRawFlags } = require('../../utils/parse-raw-flags') -const createAddon = async ({ api, siteId, addonName, config, siteData, log, error }) => { +const createAddon = async ({ api, siteId, addonName, config, siteData, error }) => { try { const response = await api.createServiceInstance({ siteId, @@ -37,7 +38,7 @@ class AddonsCreateCommand extends Command { validation: ADDON_VALIDATION.NOT_EXISTS, }) - const { log, error, netlify } = this + const { error, netlify } = this const { api, site } = netlify const siteId = site.id @@ -50,43 +51,41 @@ class AddonsCreateCommand extends Command { if (hasConfig) { const required = requiredConfigValues(manifest.config) const missingValues = missingConfigValues(required, rawFlags) - this.log(`Starting the setup for "${addonName} add-on"`) - this.log() + log(`Starting the setup for "${addonName} add-on"`) + log() if (Object.keys(rawFlags).length !== 0) { const newConfig = updateConfigValues(manifest.config, {}, rawFlags) if (missingValues.length !== 0) { /* Warn user of missing required values */ - this.log( - `${chalk.redBright.underline.bold(`Error: Missing required configuration for "${addonName} add-on"`)}`, - ) - this.log() + log(`${chalk.redBright.underline.bold(`Error: Missing required configuration for "${addonName} add-on"`)}`) + log() render.missingValues(missingValues, manifest) - this.log() + log() const msg = `netlify addons:create ${addonName}` - this.log(`Please supply the configuration values as CLI flags`) - this.log() - this.log(`Alternatively, you can run ${chalk.cyan(msg)} with no flags to walk through the setup steps`) - this.log() + log(`Please supply the configuration values as CLI flags`) + log() + log(`Alternatively, you can run ${chalk.cyan(msg)} with no flags to walk through the setup steps`) + log() return false } - await createAddon({ api, siteId, addonName, config: newConfig, siteData, log, error }) + await createAddon({ api, siteId, addonName, config: newConfig, siteData, error }) return false } const words = `The ${addonName} add-on has the following configurable options:` - this.log(` ${chalk.yellowBright.bold(words)}`) + log(` ${chalk.yellowBright.bold(words)}`) render.configValues(addonName, manifest.config) - this.log() - this.log(` ${chalk.greenBright.bold('Lets configure those!')}`) + log() + log(` ${chalk.greenBright.bold('Lets configure those!')}`) - this.log() - this.log(` - Hit ${chalk.white.bold('enter')} to confirm value or set empty value`) - this.log(` - Hit ${chalk.white.bold('ctrl + C')} to cancel & exit configuration`) - this.log() + log() + log(` - Hit ${chalk.white.bold('enter')} to confirm value or set empty value`) + log(` - Hit ${chalk.white.bold('ctrl + C')} to cancel & exit configuration`) + log() const prompts = generatePrompts({ config: manifest.config, @@ -99,13 +98,13 @@ class AddonsCreateCommand extends Command { const missingRequiredValues = missingConfigValues(required, configValues) if (missingRequiredValues && missingRequiredValues.length !== 0) { missingRequiredValues.forEach((val) => { - this.log(`Missing required value "${val}". Please run the command again`) + log(`Missing required value "${val}". Please run the command again`) }) return false } } - await createAddon({ api, siteId, addonName, config: configValues, siteData, log, error }) + await createAddon({ api, siteId, addonName, config: configValues, siteData, error }) } } diff --git a/src/commands/addons/delete.js b/src/commands/addons/delete.js index 1f643707e62..78c404bc9e4 100644 --- a/src/commands/addons/delete.js +++ b/src/commands/addons/delete.js @@ -3,6 +3,7 @@ const inquirer = require('inquirer') const { prepareAddonCommand, ADDON_VALIDATION } = require('../../utils/addons/prepare') const Command = require('../../utils/command') +const { log } = require('../../utils/command-helpers') const { parseRawFlags } = require('../../utils/parse-raw-flags') class AddonsDeleteCommand extends Command { @@ -35,7 +36,7 @@ class AddonsDeleteCommand extends Command { addon: addonName, instanceId: addon.id, }) - this.log(`Addon "${addonName}" deleted`) + log(`Addon "${addonName}" deleted`) } catch (error) { this.error(error.message) } diff --git a/src/commands/addons/list.js b/src/commands/addons/list.js index 485680ae593..7b5c2cd6c4d 100644 --- a/src/commands/addons/list.js +++ b/src/commands/addons/list.js @@ -3,22 +3,22 @@ const AsciiTable = require('ascii-table') const { prepareAddonCommand } = require('../../utils/addons/prepare') const Command = require('../../utils/command') +const { log, logJson } = require('../../utils/command-helpers') class AddonsListCommand extends Command { async run() { const { flags } = this.parse(AddonsListCommand) const { addons, siteData } = await prepareAddonCommand({ context: this }) - // Return json response for piping commands if (flags.json) { - this.logJson(addons) + logJson(addons) return false } if (!addons || addons.length === 0) { - this.log(`No addons currently installed for ${siteData.name}`) - this.log(`> Run \`netlify addons:create addon-namespace\` to install an addon`) + log(`No addons currently installed for ${siteData.name}`) + log(`> Run \`netlify addons:create addon-namespace\` to install an addon`) return false } @@ -29,7 +29,7 @@ class AddonsListCommand extends Command { })) // Build a table out of addons - this.log(`site: ${siteData.name}`) + log(`site: ${siteData.name}`) const table = new AsciiTable(`Currently Installed addons`) table.setHeading('NameSpace', 'Name', 'Instance Id') @@ -38,7 +38,7 @@ class AddonsListCommand extends Command { table.addRow(namespace, name, id) }) // Log da addons - this.log(table.toString()) + log(table.toString()) } } diff --git a/src/commands/api.js b/src/commands/api.js index fcc8d5a5f98..af13f3ad70a 100644 --- a/src/commands/api.js +++ b/src/commands/api.js @@ -5,6 +5,7 @@ const { methods } = require('netlify') const { isEmptyCommand } = require('../utils/check-command-inputs') const Command = require('../utils/command') +const { log, logJson } = require('../utils/command-helpers') class APICommand extends Command { async run() { @@ -20,10 +21,10 @@ class APICommand extends Command { const { operationId } = method table.addRow(operationId, `https://open-api.netlify.com/#operation/${operationId}`) }) - this.log(table.toString()) - this.log() - this.log('Above is a list of available API methods') - this.log(`To run a method use "${chalk.cyanBright('netlify api methodName')}"`) + log(table.toString()) + log() + log('Above is a list of available API methods') + log(`To run a method use "${chalk.cyanBright('netlify api methodName')}"`) this.exit() } @@ -43,7 +44,7 @@ class APICommand extends Command { } try { const apiResponse = await api[apiMethod](payload) - this.logJson(apiResponse) + logJson(apiResponse) } catch (error) { this.error(error) } diff --git a/src/commands/build/index.js b/src/commands/build/index.js index 739371451a3..5563c708529 100644 --- a/src/commands/build/index.js +++ b/src/commands/build/index.js @@ -2,6 +2,7 @@ const { flags: flagsLib } = require('@oclif/command') const { getBuildOptions, runBuild } = require('../../lib/build') const Command = require('../../utils/command') +const { getToken } = require('../../utils/command-helpers') class BuildCommand extends Command { // Run Netlify Build @@ -11,7 +12,7 @@ class BuildCommand extends Command { this.setAnalyticsPayload({ dry: flags.dry }) // Retrieve Netlify Build options - const [token] = await this.getConfigToken() + const [token] = await getToken() const options = await getBuildOptions({ context: this, diff --git a/src/commands/deploy.js b/src/commands/deploy.js index 2e0adeb4b8c..e95423a0fc7 100644 --- a/src/commands/deploy.js +++ b/src/commands/deploy.js @@ -15,6 +15,7 @@ const { normalizeFunctionsConfig } = require('../lib/functions/config') const { getLogMessage } = require('../lib/log') const { startSpinner, stopSpinner } = require('../lib/spinner') const Command = require('../utils/command') +const { log, logJson, getToken } = require('../utils/command-helpers') const { deploySite } = require('../utils/deploy/deploy-site') const { deployEdgeHandlers } = require('../utils/edge-handlers') const { getInternalFunctionsDir } = require('../utils/functions') @@ -26,7 +27,7 @@ const SitesCreateCommand = require('./sites/create') const DEFAULT_DEPLOY_TIMEOUT = 1.2e6 -const triggerDeploy = async ({ api, siteId, siteData, log, error }) => { +const triggerDeploy = async ({ api, siteId, siteData, error }) => { try { const siteBuild = await api.createSiteBuild({ siteId }) log( @@ -41,7 +42,7 @@ const triggerDeploy = async ({ api, siteId, siteData, log, error }) => { } } -const getDeployFolder = async ({ flags, config, site, siteData, log }) => { +const getDeployFolder = async ({ flags, config, site, siteData }) => { let deployFolder if (flags.dir) { deployFolder = path.resolve(process.cwd(), flags.dir) @@ -105,7 +106,7 @@ const getFunctionsFolder = ({ flags, config, site, siteData }) => { return functionsFolder } -const validateFunctionsFolder = async ({ functionsFolder, log, error }) => { +const validateFunctionsFolder = async ({ functionsFolder, error }) => { let stat if (functionsFolder) { // we used to hard error if functions folder is specified but doesn't exist @@ -132,9 +133,9 @@ const validateFunctionsFolder = async ({ functionsFolder, log, error }) => { return stat } -const validateFolders = async ({ deployFolder, functionsFolder, error, log }) => { +const validateFolders = async ({ deployFolder, functionsFolder, error }) => { const deployFolderStat = await validateDeployFolder({ deployFolder, error }) - const functionsFolderStat = await validateFunctionsFolder({ functionsFolder, error, log }) + const functionsFolderStat = await validateFunctionsFolder({ functionsFolder, error }) return { deployFolderStat, functionsFolderStat } } @@ -167,7 +168,7 @@ const SEC_TO_MILLISEC = 1e3 // 100 bytes const SYNC_FILE_LIMIT = 1e2 -const prepareProductionDeploy = async ({ siteData, api, log, exit }) => { +const prepareProductionDeploy = async ({ siteData, api, exit }) => { if (isObject(siteData.published_deploy) && siteData.published_deploy.locked) { log(`\n${NETLIFYDEVERR} Deployments are "locked" for production context of this site\n`) const { unlockChoice } = await inquirer.prompt([ @@ -236,7 +237,6 @@ const runDeploy = async ({ functionsConfig, functionsFolder, alias, - log, warn, error, exit, @@ -245,7 +245,7 @@ const runDeploy = async ({ let deployId try { if (deployToProduction) { - await prepareProductionDeploy({ siteData, api, log, exit }) + await prepareProductionDeploy({ siteData, api, exit }) } else { log('Deploying to draft URL...') } @@ -309,7 +309,7 @@ const handleBuild = async ({ context, flags }) => { if (!flags.build) { return } - const [token] = await context.getConfigToken() + const [token] = await getToken() const options = await getBuildOptions({ context, token, @@ -322,7 +322,7 @@ const handleBuild = async ({ context, flags }) => { return newConfig } -const printResults = ({ flags, results, deployToProduction, log, logJson, exit }) => { +const printResults = ({ flags, results, deployToProduction, exit }) => { const msgData = { Logs: `${results.logsUrl}`, 'Unique Deploy URL': results.deployUrl, @@ -369,7 +369,7 @@ const printResults = ({ flags, results, deployToProduction, log, logJson, exit } class DeployCommand extends Command { async run() { const { flags } = this.parse(DeployCommand) - const { log, logJson, warn, error, exit } = this + const { warn, error, exit } = this const { api, site } = this.netlify const alias = flags.alias || flags.branch @@ -395,7 +395,7 @@ class DeployCommand extends Command { } } } else { - this.log("This folder isn't linked to a site yet") + log("This folder isn't linked to a site yet") const NEW_SITE = '+ Create & configure a new site' const EXISTING_SITE = 'Link this directory to an existing site' @@ -426,13 +426,13 @@ class DeployCommand extends Command { const deployToProduction = flags.prod || (flags.prodIfUnlocked && !siteData.published_deploy.locked) if (flags.trigger) { - return triggerDeploy({ api, siteId, siteData, log, error }) + return triggerDeploy({ api, siteId, siteData, error }) } const newConfig = await handleBuild({ context: this, flags }) const config = newConfig || this.netlify.config - const deployFolder = await getDeployFolder({ flags, config, site, siteData, log }) + const deployFolder = await getDeployFolder({ flags, config, site, siteData }) const functionsFolder = getFunctionsFolder({ flags, config, site, siteData }) const { configPath } = site @@ -448,7 +448,6 @@ class DeployCommand extends Command { deployFolder, functionsFolder, error, - log, }) const functionsConfig = normalizeFunctionsConfig({ functionsConfig: config.functions, projectRoot: site.root }) const results = await runDeploy({ @@ -464,17 +463,16 @@ class DeployCommand extends Command { // pass undefined functionsFolder if doesn't exist functionsFolder: functionsFolderStat && functionsFolder, alias, - log, warn, error, exit, }) - printResults({ flags, results, deployToProduction, log, logJson, exit }) + printResults({ flags, results, deployToProduction, exit }) if (flags.open) { const urlToOpen = deployToProduction ? results.siteUrl : results.deployUrl - await openBrowser({ url: urlToOpen, log }) + await openBrowser({ url: urlToOpen }) exit() } } diff --git a/src/commands/dev/exec.js b/src/commands/dev/exec.js index b108f672f6c..6ed36565131 100644 --- a/src/commands/dev/exec.js +++ b/src/commands/dev/exec.js @@ -10,9 +10,9 @@ class ExecCommand extends Command { } async run() { - const { log, warn, netlify } = this + const { warn, netlify } = this const { cachedConfig, site } = netlify - await injectEnvVariables({ env: cachedConfig.env, log, site, warn }) + await injectEnvVariables({ env: cachedConfig.env, site, warn }) execa(this.argv[0], this.argv.slice(1), { stdio: 'inherit', diff --git a/src/commands/dev/index.js b/src/commands/dev/index.js index 5483fe91f5e..5c44bb566f0 100644 --- a/src/commands/dev/index.js +++ b/src/commands/dev/index.js @@ -12,6 +12,7 @@ const waitPort = require('wait-port') const { startFunctionsServer } = require('../../lib/functions/server') const Command = require('../../utils/command') +const { log } = require('../../utils/command-helpers') const { detectServerSettings } = require('../../utils/detect-server-settings') const { getSiteInformation, injectEnvVariables } = require('../../utils/dev') const { startLiveTunnel } = require('../../utils/live-tunnel') @@ -20,7 +21,7 @@ const openBrowser = require('../../utils/open-browser') const { startProxy } = require('../../utils/proxy') const { startForwardProxy } = require('../../utils/traffic-mesh') -const startStaticServer = async ({ settings, log }) => { +const startStaticServer = async ({ settings }) => { const server = new StaticServer({ rootPath: settings.dist, name: 'netlify-dev', @@ -52,9 +53,9 @@ const isNonExistingCommandError = ({ command, error }) => { ) } -const startFrameworkServer = async function ({ settings, log, exit }) { +const startFrameworkServer = async function ({ settings, exit }) { if (settings.useStaticServer) { - return await startStaticServer({ settings, log }) + return await startStaticServer({ settings }) } log(`${NETLIFYDEVLOG} Starting Netlify Dev with ${settings.framework || 'custom config'}`) @@ -114,7 +115,7 @@ const startFrameworkServer = async function ({ settings, log, exit }) { // 10 minutes const FRAMEWORK_PORT_TIMEOUT = 6e5 -const startProxyServer = async ({ flags, settings, site, log, exit, addonsUrls }) => { +const startProxyServer = async ({ flags, settings, site, exit, addonsUrls }) => { let url if (flags.edgeHandlers || flags.trafficMesh) { url = await startForwardProxy({ @@ -122,7 +123,6 @@ const startProxyServer = async ({ flags, settings, site, log, exit, addonsUrls } frameworkPort: settings.frameworkPort, functionsPort: settings.functionsPort, publishDir: settings.dist, - log, debug: flags.debug, locationDb: flags.locationDb, jwtRolesPath: settings.jwtRolePath, @@ -142,20 +142,19 @@ const startProxyServer = async ({ flags, settings, site, log, exit, addonsUrls } return url } -const handleLiveTunnel = async ({ flags, site, api, settings, log }) => { +const handleLiveTunnel = async ({ flags, site, api, settings }) => { if (flags.live) { const sessionUrl = await startLiveTunnel({ siteId: site.id, netlifyApiToken: api.accessToken, localPort: settings.port, - log, }) process.env.BASE_URL = sessionUrl return sessionUrl } } -const printBanner = ({ url, log }) => { +const printBanner = ({ url }) => { const banner = chalk.bold(`${NETLIFYDEVLOG} Server now ready on ${url}`) log( @@ -175,8 +174,8 @@ class DevCommand extends Command { } async run() { - this.log(`${NETLIFYDEV}`) - const { error: errorExit, log, warn, exit } = this + log(`${NETLIFYDEV}`) + const { error: errorExit, warn, exit } = this const { flags } = this.parse(DevCommand) const { api, site, config, siteInfo } = this.netlify config.dev = { ...config.dev } @@ -195,8 +194,7 @@ class DevCommand extends Command { ) } - await injectEnvVariables({ env: this.netlify.cachedConfig.env, log, site, warn }) - + await injectEnvVariables({ env: this.netlify.cachedConfig.env, site, warn }) const { addonsUrls, siteUrl, capabilities, timeouts } = await getSiteInformation({ flags, api, @@ -208,7 +206,7 @@ class DevCommand extends Command { let settings = {} try { - settings = await detectServerSettings(devConfig, flags, site.root, log) + settings = await detectServerSettings(devConfig, flags, site.root) } catch (error) { log(NETLIFYDEVERR, error.message) exit(1) @@ -220,28 +218,27 @@ class DevCommand extends Command { config, settings, site, - log, warn, errorExit, siteUrl, capabilities, timeouts, }) - await startFrameworkServer({ settings, log, exit }) + await startFrameworkServer({ settings, exit }) - let url = await startProxyServer({ flags, settings, site, log, exit, addonsUrls }) + let url = await startProxyServer({ flags, settings, site, exit, addonsUrls }) - const liveTunnelUrl = await handleLiveTunnel({ flags, site, api, settings, log }) + const liveTunnelUrl = await handleLiveTunnel({ flags, site, api, settings }) url = liveTunnelUrl || url if (devConfig.autoLaunch !== false) { - await openBrowser({ url, log, silentBrowserNoneError: true }) + await openBrowser({ url, silentBrowserNoneError: true }) } process.env.URL = url process.env.DEPLOY_URL = url - printBanner({ url, log }) + printBanner({ url }) } } diff --git a/src/commands/dev/trace.js b/src/commands/dev/trace.js index 592f5fc521c..255145ddf4c 100644 --- a/src/commands/dev/trace.js +++ b/src/commands/dev/trace.js @@ -13,7 +13,7 @@ class TraceCommand extends Command { this.parse(TraceCommand) const args = ['trace', ...this.argv] - const { subprocess } = runProcess({ log: this.log, args }) + const { subprocess } = runProcess({ args }) await subprocess } } diff --git a/src/commands/env/get.js b/src/commands/env/get.js index b90ce71f2bb..b8fa5ead8ef 100644 --- a/src/commands/env/get.js +++ b/src/commands/env/get.js @@ -1,4 +1,5 @@ const Command = require('../../utils/command') +const { log, logJson } = require('../../utils/command-helpers') class EnvGetCommand extends Command { async run() { @@ -7,7 +8,7 @@ class EnvGetCommand extends Command { const siteId = site.id if (!siteId) { - this.log('No site id found, please run inside a site folder or `netlify link`') + log('No site id found, please run inside a site folder or `netlify link`') return false } @@ -18,16 +19,16 @@ class EnvGetCommand extends Command { // Return json response for piping commands if (flags.json) { - this.logJson(value ? { [name]: value } : {}) + logJson(value ? { [name]: value } : {}) return false } if (!value) { - this.log(`Environment variable ${name} not set for site ${siteData.name}`) + log(`Environment variable ${name} not set for site ${siteData.name}`) return false } - this.log(value) + log(value) } } diff --git a/src/commands/env/import.js b/src/commands/env/import.js index ccac475f69f..fe483fa5dfb 100644 --- a/src/commands/env/import.js +++ b/src/commands/env/import.js @@ -6,6 +6,7 @@ const dotenv = require('dotenv') const isEmpty = require('lodash/isEmpty') const Command = require('../../utils/command') +const { log, logJson } = require('../../utils/command-helpers') class EnvImportCommand extends Command { async run() { @@ -14,7 +15,7 @@ class EnvImportCommand extends Command { const siteId = site.id if (!siteId) { - this.log('No site id found, please run inside a site folder or `netlify link`') + log('No site id found, please run inside a site folder or `netlify link`') return false } @@ -32,12 +33,12 @@ class EnvImportCommand extends Command { const envFileContents = fs.readFileSync(fileName) importedEnv = dotenv.parse(envFileContents) } catch (error) { - this.log(error.message) + log(error.message) this.exit(1) } if (isEmpty(importedEnv)) { - this.log(`No environment variables found in file ${fileName} to import`) + log(`No environment variables found in file ${fileName} to import`) return false } @@ -55,17 +56,17 @@ class EnvImportCommand extends Command { // Return new environment variables of site if using json flag if (flags.json) { - this.logJson(siteResult.build_settings.env) + logJson(siteResult.build_settings.env) return false } // List newly imported environment variables in a table - this.log(`site: ${siteData.name}`) + log(`site: ${siteData.name}`) const table = new AsciiTable(`Imported environment variables`) table.setHeading('Key', 'Value') table.addRowMatrix(Object.entries(importedEnv)) - this.log(table.toString()) + log(table.toString()) } } diff --git a/src/commands/env/list.js b/src/commands/env/list.js index a1e42af3a9b..7cb1bc7ecf8 100644 --- a/src/commands/env/list.js +++ b/src/commands/env/list.js @@ -3,6 +3,7 @@ const AsciiTable = require('ascii-table') const isEmpty = require('lodash/isEmpty') const Command = require('../../utils/command') +const { log, logJson } = require('../../utils/command-helpers') class EnvListCommand extends Command { async run() { @@ -11,7 +12,7 @@ class EnvListCommand extends Command { const siteId = site.id if (!siteId) { - this.log('No site id found, please run inside a site folder or `netlify link`') + log('No site id found, please run inside a site folder or `netlify link`') return false } @@ -25,22 +26,22 @@ class EnvListCommand extends Command { // Return json response for piping commands if (flags.json) { - this.logJson(environment) + logJson(environment) return false } if (isEmpty(environment)) { - this.log(`No environment variables set for site ${siteData.name}`) + log(`No environment variables set for site ${siteData.name}`) return false } // List environment variables using a table - this.log(`site: ${siteData.name}`) + log(`site: ${siteData.name}`) const table = new AsciiTable(`Environment variables`) table.setHeading('Key', 'Value') table.addRowMatrix(Object.entries(environment)) - this.log(table.toString()) + log(table.toString()) } } diff --git a/src/commands/env/set.js b/src/commands/env/set.js index 2eae3e26c1f..a9dbe42aea9 100644 --- a/src/commands/env/set.js +++ b/src/commands/env/set.js @@ -1,4 +1,5 @@ const Command = require('../../utils/command') +const { log, logJson } = require('../../utils/command-helpers') class EnvSetCommand extends Command { async run() { @@ -7,7 +8,7 @@ class EnvSetCommand extends Command { const siteId = site.id if (!siteId) { - this.log('No site id found, please run inside a site folder or `netlify link`') + log('No site id found, please run inside a site folder or `netlify link`') return false } @@ -37,11 +38,11 @@ class EnvSetCommand extends Command { // Return new environment variables of site if using json flag if (flags.json) { - this.logJson(siteResult.build_settings.env) + logJson(siteResult.build_settings.env) return false } - this.log(`Set environment variable ${name}=${value} for site ${siteData.name}`) + log(`Set environment variable ${name}=${value} for site ${siteData.name}`) } } diff --git a/src/commands/env/unset.js b/src/commands/env/unset.js index c5d31ccd88e..12a115399e2 100644 --- a/src/commands/env/unset.js +++ b/src/commands/env/unset.js @@ -1,4 +1,5 @@ const Command = require('../../utils/command') +const { log, logJson } = require('../../utils/command-helpers') class EnvUnsetCommand extends Command { async run() { @@ -8,7 +9,7 @@ class EnvUnsetCommand extends Command { const { name } = args if (!siteId) { - this.log('No site id found, please run inside a site folder or `netlify link`') + log('No site id found, please run inside a site folder or `netlify link`') return false } @@ -36,11 +37,11 @@ class EnvUnsetCommand extends Command { // Return new environment variables of site if using json flag if (flags.json) { - this.logJson(siteResult.build_settings.env) + logJson(siteResult.build_settings.env) return false } - this.log(`Unset environment variable ${name} for site ${siteData.name}`) + log(`Unset environment variable ${name} for site ${siteData.name}`) } } diff --git a/src/commands/functions/build.js b/src/commands/functions/build.js index 4f84d5cf330..0245e5e36e4 100644 --- a/src/commands/functions/build.js +++ b/src/commands/functions/build.js @@ -4,6 +4,7 @@ const { zipFunctions } = require('@netlify/zip-it-and-ship-it') const { flags: flagsLib } = require('@oclif/command') const Command = require('../../utils/command') +const { log } = require('../../utils/command-helpers') const { getFunctionsDir } = require('../../utils/functions') const { NETLIFYDEVLOG, NETLIFYDEVERR } = require('../../utils/logo') @@ -17,17 +18,17 @@ class FunctionsBuildCommand extends Command { const dst = getFunctionsDir({ flags, config }) if (src === dst) { - this.log(`${NETLIFYDEVERR} Source and destination for function build can't be the same`) + log(`${NETLIFYDEVERR} Source and destination for function build can't be the same`) this.exit(1) } if (!src || !dst) { if (!src) - this.log( + log( `${NETLIFYDEVERR} Error: You must specify a source folder with a --src flag or a functionsSource field in your config`, ) if (!dst) - this.log( + log( `${NETLIFYDEVERR} Error: You must specify a destination functions folder with a --functions flag or a functions field in your config`, ) this.exit(1) @@ -35,9 +36,9 @@ class FunctionsBuildCommand extends Command { fs.mkdirSync(dst, { recursive: true }) - this.log(`${NETLIFYDEVLOG} Building functions`) + log(`${NETLIFYDEVLOG} Building functions`) zipFunctions(src, dst, { skipGo: true }) - this.log(`${NETLIFYDEVLOG} Functions built to `, dst) + log(`${NETLIFYDEVLOG} Functions built to `, dst) } } diff --git a/src/commands/functions/create.js b/src/commands/functions/create.js index 0b883bf5c43..e566fd68fa4 100644 --- a/src/commands/functions/create.js +++ b/src/commands/functions/create.js @@ -16,6 +16,7 @@ const ora = require('ora') const { mkdirRecursiveSync } = require('../../lib/fs') const { getSiteData, getAddons, getCurrentAddon } = require('../../utils/addons/prepare') const Command = require('../../utils/command') +const { log } = require('../../utils/command-helpers') const { injectEnvVariables } = require('../../utils/dev') const { NETLIFYDEVLOG, NETLIFYDEVWARN, NETLIFYDEVERR } = require('../../utils/logo') const { readRepoURL, validateRepoURL } = require('../../utils/read-repo-url') @@ -196,7 +197,6 @@ const DEFAULT_PRIORITY = 999 const ensureFunctionDirExists = async function (context) { const { api, config, site } = context.netlify const siteId = site.id - const { log } = context let functionsDirHolder = config.functionsDirectory if (!functionsDirHolder) { @@ -259,7 +259,7 @@ const downloadFromURL = async function (context, flags, args, functionsDir) { const fnFolder = path.join(functionsDir, nameToUse) if (fs.existsSync(`${fnFolder}.js`) && fs.lstatSync(`${fnFolder}.js`).isFile()) { - context.log( + log( `${NETLIFYDEVWARN}: A single file version of the function ${nameToUse} already exists at ${fnFolder}.js. Terminating without further action.`, ) process.exit(1) @@ -283,9 +283,9 @@ const downloadFromURL = async function (context, flags, args, functionsDir) { }), ) - context.log(`${NETLIFYDEVLOG} Installing dependencies for ${nameToUse}...`) + log(`${NETLIFYDEVLOG} Installing dependencies for ${nameToUse}...`) cp.exec('npm i', { cwd: path.join(functionsDir, nameToUse) }, () => { - context.log(`${NETLIFYDEVLOG} Installing dependencies for ${nameToUse} complete `) + log(`${NETLIFYDEVLOG} Installing dependencies for ${nameToUse} complete `) }) // read, execute, and delete function template file if exists @@ -333,7 +333,7 @@ const scaffoldFromTemplate = async function (context, flags, args, functionsDir) process.exit(1) } } else if (chosentemplate === 'report') { - context.log(`${NETLIFYDEVLOG} Open in browser: https://github.com/netlify/cli/issues/new`) + log(`${NETLIFYDEVLOG} Open in browser: https://github.com/netlify/cli/issues/new`) } else { const { onComplete, name: templateName, lang, addons = [] } = chosentemplate @@ -346,11 +346,11 @@ const scaffoldFromTemplate = async function (context, flags, args, functionsDir) const name = await getNameFromArgs(args, flags, templateName) - context.log(`${NETLIFYDEVLOG} Creating function ${chalk.cyan.inverse(name)}`) - const functionPath = ensureFunctionPathIsOk(context, functionsDir, name) + log(`${NETLIFYDEVLOG} Creating function ${chalk.cyan.inverse(name)}`) + const functionPath = ensureFunctionPathIsOk(functionsDir, name) // SWYX: note to future devs - useful for debugging source to output issues - // this.log('from ', pathToTemplate, ' to ', functionPath) + // log('from ', pathToTemplate, ' to ', functionPath) // SWYX: TODO const vars = { NETLIFY_STUFF_TO_REPLACE: 'REPLACEMENT' } let hasPackageJSON = false @@ -385,7 +385,7 @@ const scaffoldFromTemplate = async function (context, flags, args, functionsDir) const TEMPLATE_PERMISSIONS = 0o777 -const createFunctionAddon = async function ({ api, addons, siteId, addonName, siteData, log, error }) { +const createFunctionAddon = async function ({ api, addons, siteId, addonName, siteData, error }) { try { const addon = getCurrentAddon({ addons, addonName }) if (addon && addon.id) { @@ -405,9 +405,9 @@ const createFunctionAddon = async function ({ api, addons, siteId, addonName, si } const injectEnvVariablesFromContext = async ({ context }) => { - const { log, warn, netlify } = context + const { warn, netlify } = context const { cachedConfig, site } = netlify - await injectEnvVariables({ env: cachedConfig.env, log, site, warn }) + await injectEnvVariables({ env: cachedConfig.env, site, warn }) } const handleOnComplete = async ({ context, onComplete }) => { @@ -444,7 +444,7 @@ const installAddons = async function (context, functionAddons, fnPath) { return } - const { log, error } = context + const { error } = context const { api, site } = context.netlify const siteId = site.id if (!siteId) { @@ -467,7 +467,6 @@ const installAddons = async function (context, functionAddons, fnPath) { siteId, addonName, siteData, - log, error, }) @@ -481,10 +480,10 @@ const installAddons = async function (context, functionAddons, fnPath) { // we used to allow for a --dir command, // but have retired that to force every scaffolded function to be a directory -const ensureFunctionPathIsOk = function (context, functionsDir, name) { +const ensureFunctionPathIsOk = function (functionsDir, name) { const functionPath = path.join(functionsDir, name) if (fs.existsSync(functionPath)) { - context.log(`${NETLIFYDEVLOG} Function ${functionPath} already exists, cancelling...`) + log(`${NETLIFYDEVLOG} Function ${functionPath} already exists, cancelling...`) process.exit(1) } return functionPath diff --git a/src/commands/functions/list.js b/src/commands/functions/list.js index 49ecf80bd60..aa4b5742476 100644 --- a/src/commands/functions/list.js +++ b/src/commands/functions/list.js @@ -4,6 +4,7 @@ const { flags: flagsLib } = require('@oclif/command') const AsciiTable = require('ascii-table') const Command = require('../../utils/command') +const { log, logJson } = require('../../utils/command-helpers') const { getFunctionsDir } = require('../../utils/functions') const { getFunctions } = require('../../utils/get-functions') @@ -41,9 +42,9 @@ class FunctionsListCommand extends Command { const functionsDir = getFunctionsDir({ flags, config }) if (typeof functionsDir === 'undefined') { - this.log('Functions directory is undefined') - this.log('Please verify functions.directory is set in your Netlify configuration file (netlify.toml/yml/json)') - this.log('See https://docs.netlify.com/configure-builds/file-based-configuration/ for more information') + log('Functions directory is undefined') + log('Please verify functions.directory is set in your Netlify configuration file (netlify.toml/yml/json)') + log('See https://docs.netlify.com/configure-builds/file-based-configuration/ for more information') process.exit(1) } @@ -51,23 +52,23 @@ class FunctionsListCommand extends Command { const normalizedFunctions = functions.map(normalizeFunction.bind(null, deployedFunctions)) if (normalizedFunctions.length === 0) { - this.log(`No functions found in ${functionsDir}`) + log(`No functions found in ${functionsDir}`) this.exit() } if (flags.json) { - this.logJson(normalizedFunctions) + logJson(normalizedFunctions) this.exit() } // Make table - this.log(`Based on local functions folder ${functionsDir}, these are the functions detected`) + log(`Based on local functions folder ${functionsDir}, these are the functions detected`) const table = new AsciiTable(`Netlify Functions (in local functions folder)`) table.setHeading('Name', 'URL', 'deployed') normalizedFunctions.forEach(({ name, url, isDeployed }) => { table.addRow(name, url, isDeployed ? 'yes' : 'no') }) - this.log(table.toString()) + log(table.toString()) } } diff --git a/src/commands/functions/serve.js b/src/commands/functions/serve.js index 5002610955b..e56d801ef36 100644 --- a/src/commands/functions/serve.js +++ b/src/commands/functions/serve.js @@ -13,12 +13,12 @@ class FunctionsServeCommand extends Command { async run() { const { flags } = this.parse(FunctionsServeCommand) - const { error: errorExit, log, warn, netlify } = this + const { error: errorExit, warn, netlify } = this const { api, site, config, siteInfo } = netlify const functionsDir = getFunctionsDir({ flags, config }, join('netlify', 'functions')) - await injectEnvVariables({ env: this.netlify.cachedConfig.env, log, site, warn }) + await injectEnvVariables({ env: this.netlify.cachedConfig.env, site, warn }) const { siteUrl, capabilities, timeouts } = await getSiteInformation({ flags, @@ -39,7 +39,6 @@ class FunctionsServeCommand extends Command { config, settings: { functions: functionsDir, functionsPort }, site, - log, warn, errorExit, siteUrl, diff --git a/src/commands/init.js b/src/commands/init.js index e73ffbcef11..74ec188209f 100644 --- a/src/commands/init.js +++ b/src/commands/init.js @@ -5,6 +5,7 @@ const inquirer = require('inquirer') const isEmpty = require('lodash/isEmpty') const Command = require('../utils/command') +const { log } = require('../utils/command-helpers') const { getRepoData } = require('../utils/get-repo-data') const ensureNetlifyIgnore = require('../utils/gitignore') const { configureRepo } = require('../utils/init/config') @@ -20,7 +21,7 @@ const persistState = ({ state, siteInfo }) => { const getRepoUrl = ({ siteInfo }) => dotProp.get(siteInfo, 'build_settings.repo_url') -const logExistingAndExit = ({ log, exit, siteInfo }) => { +const logExistingAndExit = ({ exit, siteInfo }) => { log() log(`This site has been initialized`) log() @@ -36,7 +37,7 @@ const logExistingAndExit = ({ log, exit, siteInfo }) => { exit() } -const createNewSiteAndExit = async ({ log, exit, state }) => { +const createNewSiteAndExit = async ({ exit, state }) => { const siteInfo = await SitesCreateCommand.run([]) log(`"${siteInfo.name}" site was created`) @@ -48,7 +49,7 @@ const createNewSiteAndExit = async ({ log, exit, state }) => { exit() } -const logGitSetupInstructionsAndExit = ({ log, exit }) => { +const logGitSetupInstructionsAndExit = ({ exit }) => { log() log(`${chalk.bold('To initialize a new git repo follow the steps below.')} @@ -81,7 +82,7 @@ const logGitSetupInstructionsAndExit = ({ log, exit }) => { exit() } -const handleNoGitRemoteAndExit = async ({ log, exit, error, state }) => { +const handleNoGitRemoteAndExit = async ({ exit, error, state }) => { log() log(`${chalk.yellow('No git remote was found, would you like to set one up?')}`) log(` @@ -111,9 +112,9 @@ git remote add origin https://github.com/YourUserName/RepoName.git ]) if (noGitRemoteChoice === NEW_SITE_NO_GIT) { - await createNewSiteAndExit({ log, exit, state }) + await createNewSiteAndExit({ exit, state }) } else if (noGitRemoteChoice === NO_ABORT) { - logGitSetupInstructionsAndExit({ log, exit }) + logGitSetupInstructionsAndExit({ exit }) } } @@ -146,7 +147,7 @@ const createOrLinkSiteToRepo = async () => { } } -const logExistingRepoSetupAndExit = ({ log, exit, siteName, repoUrl }) => { +const logExistingRepoSetupAndExit = ({ exit, siteName, repoUrl }) => { log() log(chalk.underline.bold(`Success`)) log(`This site "${siteName}" is configured to automatically deploy via ${repoUrl}`) @@ -160,7 +161,7 @@ class InitCommand extends Command { this.setAnalyticsPayload({ manual: flags.manual, force: flags.force }) - const { log, exit, netlify } = this + const { exit, netlify } = this const { repositoryRoot, state } = netlify let { siteInfo } = this.netlify @@ -172,13 +173,13 @@ class InitCommand extends Command { const repoUrl = getRepoUrl({ siteInfo }) if (repoUrl && !flags.force) { - logExistingAndExit({ log, exit, siteInfo }) + logExistingAndExit({ exit, siteInfo }) } // Look for local repo - const repoData = await getRepoData({ log, remoteName: flags.gitRemoteName }) + const repoData = await getRepoData({ remoteName: flags.gitRemoteName }) if (repoData.error) { - await handleNoGitRemoteAndExit({ log, exit, error: repoData.error, state }) + await handleNoGitRemoteAndExit({ exit, error: repoData.error, state }) } if (isEmpty(siteInfo)) { @@ -188,7 +189,7 @@ class InitCommand extends Command { // Check for existing CI setup const remoteBuildRepo = getRepoUrl({ siteInfo }) if (remoteBuildRepo && !flags.force) { - logExistingRepoSetupAndExit({ log, exit, siteName: siteInfo.name, repoUrl: remoteBuildRepo }) + logExistingRepoSetupAndExit({ exit, siteName: siteInfo.name, repoUrl: remoteBuildRepo }) } persistState({ state, siteInfo }) diff --git a/src/commands/link.js b/src/commands/link.js index 4c10a2fc74b..26628ff36f3 100644 --- a/src/commands/link.js +++ b/src/commands/link.js @@ -6,6 +6,7 @@ const chalk = require('chalk') const { listSites } = require('../lib/api') const Command = require('../utils/command') +const { log } = require('../utils/command-helpers') const ensureNetlifyIgnore = require('../utils/gitignore') const linkPrompt = require('../utils/link/link-by-prompt') const { track } = require('../utils/telemetry') @@ -41,10 +42,10 @@ class LinkCommand extends Command { // If already linked to site. exit and prompt for unlink if (siteData) { - this.log(`Site already linked to "${siteData.name}"`) - this.log(`Admin url: ${siteData.admin_url}`) - this.log() - this.log(`To unlink this site, run: ${chalk.cyanBright('netlify unlink')}`) + log(`Site already linked to "${siteData.name}"`) + log(`Admin url: ${siteData.admin_url}`) + log() + log(`To unlink this site, run: ${chalk.cyanBright('netlify unlink')}`) return this.exit() } @@ -61,7 +62,7 @@ class LinkCommand extends Command { // Save site ID state.set('siteId', siteData.id) - this.log(`Linked to ${siteData.name} in ${state.path}`) + log(`Linked to ${siteData.name} in ${state.path}`) await track('sites_linked', { siteId: siteData.id, @@ -96,7 +97,7 @@ class LinkCommand extends Command { const [firstSiteData] = results state.set('siteId', firstSiteData.id) - this.log(`Linked to ${firstSiteData.name} in ${path.relative(path.join(process.cwd(), '..'), state.path)}`) + log(`Linked to ${firstSiteData.name} in ${path.relative(path.join(process.cwd(), '..'), state.path)}`) await track('sites_linked', { siteId: (firstSiteData && firstSiteData.id) || siteId, diff --git a/src/commands/lm/info.js b/src/commands/lm/info.js index 5b3eebaed14..450fd80d5ad 100644 --- a/src/commands/lm/info.js +++ b/src/commands/lm/info.js @@ -10,6 +10,7 @@ const { class LmInfoCommand extends Command { async run() { + this.parse(LmInfoCommand) const steps = [ checkGitVersionStep, checkGitLFSVersionStep, diff --git a/src/commands/lm/install.js b/src/commands/lm/install.js index 7fad94aeffb..f658a9296e6 100644 --- a/src/commands/lm/install.js +++ b/src/commands/lm/install.js @@ -8,7 +8,7 @@ class LmInstallCommand extends Command { async run() { const { flags } = this.parse(LmInstallCommand) - const installed = await installPlatform({ force: flags.force, log: this.log }) + const installed = await installPlatform({ force: flags.force }) if (installed) { printBanner(this, flags.force) } diff --git a/src/commands/lm/setup.js b/src/commands/lm/setup.js index aa4d151bef6..f45cb29c212 100644 --- a/src/commands/lm/setup.js +++ b/src/commands/lm/setup.js @@ -7,7 +7,7 @@ const { installPlatform } = require('../../utils/lm/install') const { checkHelperVersion } = require('../../utils/lm/requirements') const { printBanner } = require('../../utils/lm/ui') -const installHelperIfMissing = async function ({ force, log }) { +const installHelperIfMissing = async function ({ force }) { let installHelper = false try { const version = await checkHelperVersion() @@ -19,7 +19,7 @@ const installHelperIfMissing = async function ({ force, log }) { } if (installHelper) { - return installPlatform({ force, log }) + return installPlatform({ force }) } return false @@ -60,7 +60,7 @@ class LmSetupCommand extends Command { let helperInstalled = false if (!flags['skip-install']) { try { - helperInstalled = await installHelperIfMissing({ force: flags['force-install'], log: this.log }) + helperInstalled = await installHelperIfMissing({ force: flags['force-install'] }) } catch (error_) { this.error(error_) } diff --git a/src/commands/lm/uninstall.js b/src/commands/lm/uninstall.js index 0a595930ab1..9bd95167962 100644 --- a/src/commands/lm/uninstall.js +++ b/src/commands/lm/uninstall.js @@ -3,6 +3,7 @@ const { uninstall } = require('../../utils/lm/install') class LmUninstallCommand extends Command { async run() { + this.parse(LmUninstallCommand) await uninstall() } } diff --git a/src/commands/login.js b/src/commands/login.js index d633f0160ca..6713c259f89 100644 --- a/src/commands/login.js +++ b/src/commands/login.js @@ -2,21 +2,22 @@ const { flags: flagsLib } = require('@oclif/command') const chalk = require('chalk') const Command = require('../utils/command') +const { log, getToken } = require('../utils/command-helpers') class LoginCommand extends Command { async run() { + const [accessToken, location] = await getToken() const { flags } = this.parse(LoginCommand) this.setAnalyticsPayload({ new: flags.new }) - const [accessToken, location] = await this.getConfigToken() if (accessToken && !flags.new) { - this.log(`Already logged in ${msg(location)}`) - this.log() - this.log(`Run ${chalk.cyanBright('netlify status')} for account details`) - this.log() - this.log(`To see all available commands run: ${chalk.cyanBright('netlify help')}`) - this.log() + log(`Already logged in ${msg(location)}`) + log() + log(`Run ${chalk.cyanBright('netlify status')} for account details`) + log() + log(`To see all available commands run: ${chalk.cyanBright('netlify help')}`) + log() return this.exit() } diff --git a/src/commands/logout.js b/src/commands/logout.js index 7d0b7b977a7..908a35a4fa4 100644 --- a/src/commands/logout.js +++ b/src/commands/logout.js @@ -1,14 +1,15 @@ const Command = require('../utils/command') +const { log, getToken } = require('../utils/command-helpers') const { track } = require('../utils/telemetry') class LogoutCommand extends Command { async run() { - const [accessToken, location] = await this.getConfigToken() + const [accessToken, location] = await getToken() if (!accessToken) { - this.log(`Already logged out`) - this.log() - this.log('To login run "netlify login"') + log(`Already logged out`) + log() + log('To login run "netlify login"') this.exit() } @@ -18,14 +19,14 @@ class LogoutCommand extends Command { this.netlify.globalConfig.set('userId', null) if (location === 'env') { - this.log('The "process.env.NETLIFY_AUTH_TOKEN" is still set in your terminal session') - this.log() - this.log('To logout completely, unset the environment variable') - this.log() + log('The "process.env.NETLIFY_AUTH_TOKEN" is still set in your terminal session') + log() + log('To logout completely, unset the environment variable') + log() this.exit() } - this.log(`Logging you out of Netlify. Come back soon!`) + log(`Logging you out of Netlify. Come back soon!`) } } diff --git a/src/commands/open/admin.js b/src/commands/open/admin.js index 6c6abe2f4b5..c1724b59731 100644 --- a/src/commands/open/admin.js +++ b/src/commands/open/admin.js @@ -1,4 +1,5 @@ const Command = require('../../utils/command') +const { log } = require('../../utils/command-helpers') const openBrowser = require('../../utils/open-browser') class OpenAdminCommand extends Command { @@ -18,8 +19,8 @@ Run \`netlify link\` to connect to this folder to a site`) let siteData try { siteData = await api.getSite({ siteId }) - this.log(`Opening "${siteData.name}" site admin UI:`) - this.log(`> ${siteData.admin_url}`) + log(`Opening "${siteData.name}" site admin UI:`) + log(`> ${siteData.admin_url}`) } catch (error) { // unauthorized if (error.status === 401) { @@ -28,17 +29,17 @@ Run \`netlify link\` to connect to this folder to a site`) } // site not found if (error.status === 404) { - this.log() - this.log('Please double check this ID and verify you are logged in with the correct account') - this.log() - this.log('To fix this, run `netlify unlink` then `netlify link` to reconnect to the correct site ID') - this.log() + log() + log('Please double check this ID and verify you are logged in with the correct account') + log() + log('To fix this, run `netlify unlink` then `netlify link` to reconnect to the correct site ID') + log() this.error(`Site "${siteId}" not found in account`) } this.error(error) } - await openBrowser({ url: siteData.admin_url, log: this.log }) + await openBrowser({ url: siteData.admin_url }) this.exit() } } diff --git a/src/commands/open/site.js b/src/commands/open/site.js index 84774233e13..d47d83174cc 100644 --- a/src/commands/open/site.js +++ b/src/commands/open/site.js @@ -1,4 +1,5 @@ const Command = require('../../utils/command') +const { log } = require('../../utils/command-helpers') const openBrowser = require('../../utils/open-browser') class OpenAdminCommand extends Command { @@ -19,8 +20,8 @@ Run \`netlify link\` to connect to this folder to a site`) try { siteData = await api.getSite({ siteId }) url = siteData.ssl_url || siteData.url - this.log(`Opening "${siteData.name}" site url:`) - this.log(`> ${url}`) + log(`Opening "${siteData.name}" site url:`) + log(`> ${url}`) } catch (error) { // unauthorized if (error.status === 401) { @@ -30,7 +31,7 @@ Run \`netlify link\` to connect to this folder to a site`) this.error(error) } - await openBrowser({ url, log: this.log }) + await openBrowser({ url }) this.exit() } } diff --git a/src/commands/sites/create.js b/src/commands/sites/create.js index cf8c3d5f2f4..8210a0cba73 100644 --- a/src/commands/sites/create.js +++ b/src/commands/sites/create.js @@ -8,6 +8,7 @@ const prettyjson = require('prettyjson') const { v4: uuidv4 } = require('uuid') const Command = require('../../utils/command') +const { log, logJson } = require('../../utils/command-helpers') const { getRepoData } = require('../../utils/get-repo-data') const { configureRepo } = require('../../utils/init/config') const { track } = require('../../utils/telemetry') @@ -17,7 +18,6 @@ const SITE_NAME_SUGGESTION_SUFFIX_LENGTH = 5 class SitesCreateCommand extends Command { async run() { const { flags } = this.parse(SitesCreateCommand) - const { api } = this.netlify await this.authenticate() @@ -104,12 +104,12 @@ class SitesCreateCommand extends Command { } await inputSiteName(nameFlag) - this.log() - this.log(chalk.greenBright.bold.underline(`Site Created`)) - this.log() + log() + log(chalk.greenBright.bold.underline(`Site Created`)) + log() const siteUrl = site.ssl_url || site.url - this.log( + log( prettyjson.render({ 'Admin URL': site.admin_url, URL: siteUrl, @@ -124,13 +124,13 @@ class SitesCreateCommand extends Command { }) if (flags['with-ci']) { - this.log('Configuring CI') - const repoData = await getRepoData({ log: this.log }) + log('Configuring CI') + const repoData = await getRepoData() await configureRepo({ context: this, siteId: site.id, repoData, manual: flags.manual }) } if (flags.json) { - this.logJson( + logJson( pick(site, [ 'id', 'state', diff --git a/src/commands/sites/delete.js b/src/commands/sites/delete.js index 66671e355e7..c15d292d1d0 100644 --- a/src/commands/sites/delete.js +++ b/src/commands/sites/delete.js @@ -3,6 +3,7 @@ const chalk = require('chalk') const inquirer = require('inquirer') const Command = require('../../utils/command') +const { log } = require('../../utils/command-helpers') const { parseRawFlags } = require('../../utils/parse-raw-flags') class SitesDeleteCommand extends Command { @@ -36,19 +37,19 @@ class SitesDeleteCommand extends Command { /* Verify the user wants to delete the site */ if (noForce) { - this.log(`${chalk.redBright('Warning')}: You are about to permanently delete "${chalk.bold(siteData.name)}"`) - this.log(` Verify this siteID "${siteId}" supplied is correct and proceed.`) - this.log(' To skip this prompt, pass a --force flag to the delete command') - this.log() - this.log(`${chalk.bold('Be careful here. There is no undo!')}`) - this.log() + log(`${chalk.redBright('Warning')}: You are about to permanently delete "${chalk.bold(siteData.name)}"`) + log(` Verify this siteID "${siteId}" supplied is correct and proceed.`) + log(' To skip this prompt, pass a --force flag to the delete command') + log() + log(`${chalk.bold('Be careful here. There is no undo!')}`) + log() const { wantsToDelete } = await inquirer.prompt({ type: 'confirm', name: 'wantsToDelete', message: `WARNING: Are you sure you want to delete the "${siteData.name}" site?`, default: false, }) - this.log() + log() if (!wantsToDelete) { this.exit() } @@ -56,13 +57,13 @@ class SitesDeleteCommand extends Command { /* Validation logic if siteId passed in does not match current site ID */ if (noForce && cwdSiteId && cwdSiteId !== siteId) { - this.log(`${chalk.redBright('Warning')}: The siteId supplied does not match the current working directory siteId`) - this.log() - this.log(`Supplied: "${siteId}"`) - this.log(`Current Site: "${cwdSiteId}"`) - this.log() - this.log(`Verify this siteID "${siteId}" supplied is correct and proceed.`) - this.log('To skip this prompt, pass a --force flag to the delete command') + log(`${chalk.redBright('Warning')}: The siteId supplied does not match the current working directory siteId`) + log() + log(`Supplied: "${siteId}"`) + log(`Current Site: "${cwdSiteId}"`) + log() + log(`Verify this siteID "${siteId}" supplied is correct and proceed.`) + log('To skip this prompt, pass a --force flag to the delete command') const { wantsToDelete } = await inquirer.prompt({ type: 'confirm', name: 'wantsToDelete', @@ -74,7 +75,7 @@ class SitesDeleteCommand extends Command { } } - this.log(`Deleting site "${siteId}"...`) + log(`Deleting site "${siteId}"...`) try { await api.deleteSite({ site_id: siteId }) @@ -85,7 +86,7 @@ class SitesDeleteCommand extends Command { this.error(`Delete Site error: ${error.status}: ${error.message}`) } } - this.log(`Site "${siteId}" successfully deleted!`) + log(`Site "${siteId}" successfully deleted!`) } } diff --git a/src/commands/sites/list.js b/src/commands/sites/list.js index bf96532f6bf..ff0081f72ed 100644 --- a/src/commands/sites/list.js +++ b/src/commands/sites/list.js @@ -4,6 +4,7 @@ const { cli } = require('cli-ux') const { listSites } = require('../../lib/api') const Command = require('../../utils/command') +const { log, logJson } = require('../../utils/command-helpers') class SitesListCommand extends Command { async run() { @@ -44,11 +45,11 @@ class SitesListCommand extends Command { } return site }) - this.logJson(redactedSites) + logJson(redactedSites) return false } - this.log(` + log(` ────────────────────────────┐ Current Netlify Sites │ ────────────────────────────┘ @@ -57,15 +58,15 @@ Count: ${logSites.length} `) logSites.forEach((logSite) => { - this.log(`${chalk.greenBright(logSite.name)} - ${logSite.id}`) - this.log(` ${chalk.whiteBright.bold('url:')} ${chalk.yellowBright(logSite.ssl_url)}`) + log(`${chalk.greenBright(logSite.name)} - ${logSite.id}`) + log(` ${chalk.whiteBright.bold('url:')} ${chalk.yellowBright(logSite.ssl_url)}`) if (logSite.repo_url) { - this.log(` ${chalk.whiteBright.bold('repo:')} ${chalk.white(logSite.repo_url)}`) + log(` ${chalk.whiteBright.bold('repo:')} ${chalk.white(logSite.repo_url)}`) } if (logSite.account_name) { - this.log(` ${chalk.whiteBright.bold('account:')} ${chalk.white(logSite.account_name)}`) + log(` ${chalk.whiteBright.bold('account:')} ${chalk.white(logSite.account_name)}`) } - this.log(`─────────────────────────────────────────────────`) + log(`─────────────────────────────────────────────────`) }) } } diff --git a/src/commands/status/hooks.js b/src/commands/status/hooks.js index 23fcf87d897..0f8449cc4e9 100644 --- a/src/commands/status/hooks.js +++ b/src/commands/status/hooks.js @@ -2,10 +2,12 @@ const { get } = require('dot-prop') const prettyjson = require('prettyjson') const Command = require('../../utils/command') +const { log } = require('../../utils/command-helpers') class StatusHooksCommand extends Command { async run() { const { site, api } = this.netlify + await this.authenticate() const siteId = site.id @@ -46,10 +48,10 @@ class StatusHooksCommand extends Command { data.hooks[hook.id].repo_url = get(siteData, 'build_settings.repo_url') } }) - this.log(`─────────────────┐ + log(`─────────────────┐ Site Hook Status │ ─────────────────┘`) - this.log(prettyjson.render(data)) + log(prettyjson.render(data)) } } diff --git a/src/commands/status/index.js b/src/commands/status/index.js index cd24ff7fe8e..36a033bad06 100644 --- a/src/commands/status/index.js +++ b/src/commands/status/index.js @@ -4,6 +4,7 @@ const clean = require('clean-deep') const prettyjson = require('prettyjson') const Command = require('../../utils/command') +const { log, logJson, getToken } = require('../../utils/command-helpers') class StatusCommand extends Command { async run() { @@ -11,18 +12,18 @@ class StatusCommand extends Command { const { flags } = this.parse(StatusCommand) const current = globalConfig.get('userId') - const [accessToken] = await this.getConfigToken() + const [accessToken] = await getToken if (!accessToken) { - this.log(`Not logged in. Please log in to see site status.`) - this.log() - this.log('Login with "netlify login" command') + log(`Not logged in. Please log in to see site status.`) + log() + log('Login with "netlify login" command') this.exit() } const siteId = site.id - this.log(`──────────────────────┐ + log(`──────────────────────┐ Current Netlify User │ ──────────────────────┘`) @@ -55,7 +56,7 @@ class StatusCommand extends Command { const cleanAccountData = clean(accountData) - this.log(prettyjson.render(cleanAccountData)) + log(prettyjson.render(cleanAccountData)) if (!siteId) { this.warn('Did you run `netlify link` yet?') @@ -79,7 +80,7 @@ class StatusCommand extends Command { // Json only logs out if --json flag is passed if (flags.json) { - this.logJson({ + logJson({ account: cleanAccountData, siteData: { 'site-name': `${siteData.name}`, @@ -91,10 +92,10 @@ class StatusCommand extends Command { }) } - this.log(`────────────────────┐ + log(`────────────────────┐ Netlify Site Info │ ────────────────────┘`) - this.log( + log( prettyjson.render({ 'Current site': `${siteData.name}`, 'Netlify TOML': site.configPath, @@ -103,7 +104,7 @@ class StatusCommand extends Command { 'Site Id': chalk.yellowBright(siteData.id), }), ) - this.log() + log() } } diff --git a/src/commands/switch.js b/src/commands/switch.js index c1b9bbd869b..025d11abd97 100644 --- a/src/commands/switch.js +++ b/src/commands/switch.js @@ -2,6 +2,7 @@ const chalk = require('chalk') const inquirer = require('inquirer') const Command = require('../utils/command') +const { log } = require('../utils/command-helpers') const LoginCommand = require('./login') @@ -30,8 +31,8 @@ class SwitchCommand extends Command { ([, availableUsersChoice]) => availableUsersChoice === accountSwitchChoice, ) this.netlify.globalConfig.set('userId', selectedAccount[0]) - this.log('') - this.log(`You're now using ${chalk.bold(selectedAccount[1])}.`) + log('') + log(`You're now using ${chalk.bold(selectedAccount[1])}.`) } return this.exit() diff --git a/src/commands/unlink.js b/src/commands/unlink.js index dd8dae47b38..bf3b7da6b67 100644 --- a/src/commands/unlink.js +++ b/src/commands/unlink.js @@ -1,4 +1,5 @@ const Command = require('../utils/command') +const { log } = require('../utils/command-helpers') const { track } = require('../utils/telemetry') class UnlinkCommand extends Command { @@ -7,7 +8,7 @@ class UnlinkCommand extends Command { const siteId = site.id if (!siteId) { - this.log(`Folder is not linked to a Netlify site. Run 'netlify link' to link it`) + log(`Folder is not linked to a Netlify site. Run 'netlify link' to link it`) return this.exit() } @@ -25,9 +26,9 @@ class UnlinkCommand extends Command { }) if (site) { - this.log(`Unlinked ${site.configPath} from ${siteData ? siteData.name : siteId}`) + log(`Unlinked ${site.configPath} from ${siteData ? siteData.name : siteId}`) } else { - this.log('Unlinked site') + log('Unlinked site') } } } diff --git a/src/commands/watch.js b/src/commands/watch.js index 15227b58b9f..4b9fa74db55 100644 --- a/src/commands/watch.js +++ b/src/commands/watch.js @@ -5,6 +5,7 @@ const pWaitFor = require('p-wait-for') const prettyjson = require('prettyjson') const Command = require('../utils/command') +const { log } = require('../utils/command-helpers') const InitCommand = require('./init') @@ -56,9 +57,9 @@ class SitesWatchCommand extends Command { const siteData = await client.getSite({ siteId }) const message = chalk.cyanBright.bold.underline(noActiveBuilds ? 'Last build' : 'Deploy complete') - this.log() - this.log(message) - this.log( + log() + log(message) + log( prettyjson.render({ URL: siteData.ssl_url || siteData.url, Admin: siteData.admin_url, diff --git a/src/functions-templates/js/slack-rate-limit/slack-rate-limit.js b/src/functions-templates/js/slack-rate-limit/slack-rate-limit.js index 2446231aa0e..c46157311a5 100644 --- a/src/functions-templates/js/slack-rate-limit/slack-rate-limit.js +++ b/src/functions-templates/js/slack-rate-limit/slack-rate-limit.js @@ -6,6 +6,15 @@ const process = require('process') const slackURL = process.env.SLACK_WEBHOOK_URL const fetch = require('node-fetch') +const parseJsonResponse = async (response) => { + const json = await response.json() + if (!response.ok) { + const error = `JSON: ${JSON.stringify(json)}. Status: ${response.status}` + throw new Error(error) + } + return json +} + class IdentityAPI { constructor(apiURL, token) { this.apiURL = apiURL @@ -20,21 +29,12 @@ class IdentityAPI { } } - async parseJsonResponse(response) { - const json = await response.json() - if (!response.ok) { - const error = `JSON: ${JSON.stringify(json)}. Status: ${response.status}` - throw new Error(error) - } - return json - } - async request(path, options = {}) { const headers = this.headers(options.headers || {}) const response = await fetch(this.apiURL + path, { ...options, headers }) const contentType = response.headers.get('Content-Type') if (contentType && /json/.test(contentType)) { - return this.parseJsonResponse(response) + return parseJsonResponse(response) } if (!response.ok) { diff --git a/src/lib/exec-fetcher.js b/src/lib/exec-fetcher.js index ec3a31dfacc..c86408b2da4 100644 --- a/src/lib/exec-fetcher.js +++ b/src/lib/exec-fetcher.js @@ -5,6 +5,7 @@ const execa = require('execa') const { fetchVersion, fetchLatest, updateAvailable, newerVersion } = require('gh-release-fetch') const isExe = require('isexe') +const { log } = require('../utils/command-helpers') const { NETLIFYDEVWARN } = require('../utils/logo') const isWindows = () => process.platform === 'win32' @@ -32,7 +33,7 @@ const isVersionOutdated = async ({ packageName, currentVersion, latestVersion }) return outdated } -const shouldFetchLatestVersion = async ({ binPath, packageName, execName, execArgs, pattern, latestVersion, log }) => { +const shouldFetchLatestVersion = async ({ binPath, packageName, execName, execArgs, pattern, latestVersion }) => { const execPath = path.join(binPath, getExecName({ execName })) const exists = await isExe(execPath, { ignoreErrors: true }) diff --git a/src/lib/exec-fetcher.test.js b/src/lib/exec-fetcher.test.js index 60028f4460b..3154b57042b 100644 --- a/src/lib/exec-fetcher.test.js +++ b/src/lib/exec-fetcher.test.js @@ -39,11 +39,9 @@ const packages = [ ] packages.forEach(({ packageName, execName, execArgs, pattern, extension }) => { - const { log } = console - test(`${packageName} - should return true on empty directory`, async (t) => { const { binPath } = t.context - const actual = await shouldFetchLatestVersion({ binPath, packageName, execName, execArgs, pattern, log }) + const actual = await shouldFetchLatestVersion({ binPath, packageName, execName, execArgs, pattern }) t.is(actual, true) }) @@ -52,7 +50,7 @@ packages.forEach(({ packageName, execName, execArgs, pattern, extension }) => { await fetchLatestVersion({ packageName, execName, destination: binPath, extension }) - const actual = await shouldFetchLatestVersion({ binPath, packageName, execName, execArgs, pattern, log }) + const actual = await shouldFetchLatestVersion({ binPath, packageName, execName, execArgs, pattern }) t.is(actual, false) }) diff --git a/src/lib/functions/registry.js b/src/lib/functions/registry.js index 4ea74223172..ea36688045b 100644 --- a/src/lib/functions/registry.js +++ b/src/lib/functions/registry.js @@ -2,6 +2,7 @@ const { env } = require('process') const chalk = require('chalk') +const { log } = require('../../utils/command-helpers') const { NETLIFYDEVLOG, NETLIFYDEVERR } = require('../../utils/logo') const { mkdirRecursiveAsync } = require('../fs') const { getLogMessage } = require('../log') @@ -10,8 +11,25 @@ const { NetlifyFunction } = require('./netlify-function') const runtimes = require('./runtimes') const { watchDebounced } = require('./watcher') +const prepareDirectoryScan = async (directory) => { + await mkdirRecursiveAsync(directory) + + // We give runtimes the opportunity to react to a directory scan and run + // additional logic before the directory is read. So if they implement a + // `onDirectoryScan` hook, we run it. + await Promise.all( + Object.values(runtimes).map((runtime) => { + if (typeof runtime.onDirectoryScan !== 'function') { + return null + } + + return runtime.onDirectoryScan({ directory }) + }), + ) +} + class FunctionsRegistry { - constructor({ capabilities, config, errorExit, functionsDirectory, log, projectRoot, timeouts, warn }) { + constructor({ capabilities, config, errorExit, functionsDirectory, projectRoot, timeouts, warn }) { this.capabilities = capabilities this.config = config this.errorExit = errorExit @@ -104,23 +122,6 @@ class FunctionsRegistry { return this.functions.get(name) } - async prepareDirectoryScan(directory) { - await mkdirRecursiveAsync(directory) - - // We give runtimes the opportunity to react to a directory scan and run - // additional logic before the directory is read. So if they implement a - // `onDirectoryScan` hook, we run it. - await Promise.all( - Object.values(runtimes).map((runtime) => { - if (typeof runtime.onDirectoryScan !== 'function') { - return null - } - - return runtime.onDirectoryScan({ directory }) - }), - ) - } - registerFunction(name, func) { if (func.isBackground && !this.capabilities.backgroundFunctions) { this.logger.warn(getLogMessage('functions.backgroundNotSupported')) @@ -137,7 +138,7 @@ class FunctionsRegistry { return } - await Promise.all(directories.map((path) => this.prepareDirectoryScan(path))) + await Promise.all(directories.map((path) => prepareDirectoryScan(path))) const functions = await this.listFunctions(directories, { featureFlags: { buildGoSource: env.NETLIFY_EXPERIMENTAL_BUILD_GO_SOURCE === 'true' }, diff --git a/src/lib/functions/server.js b/src/lib/functions/server.js index fd2ccc74e73..bca9a87492c 100644 --- a/src/lib/functions/server.js +++ b/src/lib/functions/server.js @@ -1,6 +1,7 @@ const bodyParser = require('body-parser') const jwtDecode = require('jwt-decode') +const { log } = require('../../utils/command-helpers') const { getInternalFunctionsDir } = require('../../utils/functions') const { NETLIFYDEVERR, NETLIFYDEVLOG } = require('../../utils/logo') @@ -139,7 +140,6 @@ const startFunctionsServer = async ({ config, settings, site, - log, warn, errorExit, siteUrl, @@ -155,7 +155,6 @@ const startFunctionsServer = async ({ config, errorExit, functionsDirectory: settings.functions, - log, projectRoot: site.root, timeouts, warn, @@ -171,11 +170,11 @@ const startFunctionsServer = async ({ warn, }) - await startWebServer({ server, settings, log, errorExit }) + await startWebServer({ server, settings, errorExit }) } } -const startWebServer = async ({ server, settings, log, errorExit }) => { +const startWebServer = async ({ server, settings, errorExit }) => { await new Promise((resolve) => { server.listen(settings.functionsPort, (err) => { if (err) { diff --git a/src/lib/http-agent.js b/src/lib/http-agent.js index a52935d70cd..c47073f4134 100644 --- a/src/lib/http-agent.js +++ b/src/lib/http-agent.js @@ -3,6 +3,7 @@ const { URL } = require('url') const { HttpsProxyAgent } = require('https-proxy-agent') const waitPort = require('wait-port') +const { log } = require('../utils/command-helpers') const { NETLIFYDEVERR, NETLIFYDEVWARN } = require('../utils/logo') const fs = require('./fs') @@ -27,7 +28,7 @@ const DEFAULT_HTTPS_PORT = 443 // 50 seconds const AGENT_PORT_TIMEOUT = 50 -const getAgent = async ({ httpProxy, certificateFile, log, exit }) => { +const getAgent = async ({ httpProxy, certificateFile, exit }) => { if (!httpProxy) { return } diff --git a/src/lib/http-agent.test.js b/src/lib/http-agent.test.js index 4a913208f64..f35511fcb62 100644 --- a/src/lib/http-agent.test.js +++ b/src/lib/http-agent.test.js @@ -13,37 +13,26 @@ test(`should return undefined when there is no httpProxy`, async (t) => { test(`should exit with error on invalid url`, async (t) => { const httpProxy = 'invalid_url' - const log = sinon.stub() const exit = sinon.stub() exit.withArgs(1).throws('error') - await t.throwsAsync(getAgent({ httpProxy, log, exit })) - - t.is(log.getCall(0).args[1], 'invalid_url is not a valid URL') + await t.throwsAsync(getAgent({ httpProxy, exit })) }) test(`should exit with error on when scheme is not http or https`, async (t) => { const httpProxy = 'file://localhost' - const log = sinon.stub() const exit = sinon.stub() exit.withArgs(1).throws('error') - await t.throwsAsync(getAgent({ httpProxy, log, exit })) - - t.is(log.getCall(0).args[1], 'file://localhost must have a scheme of http or https') + await t.throwsAsync(getAgent({ httpProxy, exit })) }) -test(`should exit with error when proxy is no available`, async (t) => { +test(`should exit with error when proxy is not available`, async (t) => { const httpProxy = 'https://unknown:7979' - const log = sinon.stub() const exit = sinon.stub() exit.withArgs(1).throws('error') - await t.throwsAsync(getAgent({ httpProxy, log, exit })) - - const [, actual] = log.getCall(0).args - - t.true(["Could not connect to 'https://unknown:7979'", 'https://unknown:7979 is not available.'].includes(actual)) + await t.throwsAsync(getAgent({ httpProxy, exit })) }) test(`should return agent for a valid proxy`, async (t) => { @@ -57,11 +46,10 @@ test(`should return agent for a valid proxy`, async (t) => { }) const httpProxyUrl = `http://localhost:${server.address().port}` - const log = sinon.stub() const exit = sinon.stub() exit.withArgs(1).throws('error') - const agent = await getAgent({ httpProxy: httpProxyUrl, log, exit }) + const agent = await getAgent({ httpProxy: httpProxyUrl, exit }) t.is(agent instanceof HttpsProxyAgent, true) diff --git a/src/utils/addons/prepare.js b/src/utils/addons/prepare.js index fad5666325b..c04f8cf85e5 100644 --- a/src/utils/addons/prepare.js +++ b/src/utils/addons/prepare.js @@ -1,11 +1,13 @@ const chalk = require('chalk') +const { log } = require('../command-helpers') + const ADDON_VALIDATION = { EXISTS: 'EXISTS', NOT_EXISTS: 'NOT_EXISTS', } -const validateExists = ({ addon, addonName, siteData, log, exit }) => { +const validateExists = ({ addon, addonName, siteData, exit }) => { if (!addon || !addon.id) { log(`Add-on ${addonName} doesn't exist for ${siteData.name}`) log(`> Run \`netlify addons:create ${addonName}\` to create an instance for this site`) @@ -13,7 +15,7 @@ const validateExists = ({ addon, addonName, siteData, log, exit }) => { } } -const validateNotExists = ({ addon, addonName, siteData, log, exit }) => { +const validateNotExists = ({ addon, addonName, siteData, exit }) => { if (addon && addon.id) { log(`The "${addonName} add-on" already exists for ${siteData.name}`) log() @@ -28,14 +30,14 @@ const validateNotExists = ({ addon, addonName, siteData, log, exit }) => { const getCurrentAddon = ({ addons, addonName }) => addons.find((addon) => addon.service_slug === addonName) -const validateCurrentAddon = ({ addon, validation, addonName, siteData, log, warn, exit }) => { +const validateCurrentAddon = ({ addon, validation, addonName, siteData, warn, exit }) => { switch (validation) { case ADDON_VALIDATION.EXISTS: { - validateExists({ addon, addonName, siteData, log, exit }) + validateExists({ addon, addonName, siteData, exit }) break } case ADDON_VALIDATION.NOT_EXISTS: { - validateNotExists({ addon, addonName, siteData, log, exit }) + validateNotExists({ addon, addonName, siteData, exit }) break } default: { @@ -80,7 +82,7 @@ const getAddons = async ({ api, siteId, error }) => { } const prepareAddonCommand = async ({ context, addonName, validation }) => { - const { netlify, log, warn, error, exit } = context + const { netlify, warn, error, exit } = context const { api, site } = netlify const siteId = site.id if (!siteId) { @@ -98,7 +100,7 @@ const prepareAddonCommand = async ({ context, addonName, validation }) => { let addon if (addonName) { addon = getCurrentAddon({ addons, addonName }) - validateCurrentAddon({ addon, validation, addonName, siteData, log, warn, exit }) + validateCurrentAddon({ addon, validation, addonName, siteData, warn, exit }) } return { manifest, addons, addon, siteData } diff --git a/src/utils/command-helpers.js b/src/utils/command-helpers.js new file mode 100644 index 00000000000..2efb7cde652 --- /dev/null +++ b/src/utils/command-helpers.js @@ -0,0 +1,112 @@ +const process = require('process') +const { format, inspect } = require('util') + +const argv = require('minimist')(process.argv.slice(2)) +const omit = require('omit.js').default + +const { startSpinner, clearSpinner } = require('../lib/spinner') + +const chalkInstance = require('./chalk') +const getGlobalConfig = require('./get-global-config') + +const { NETLIFY_AUTH_TOKEN } = process.env + +// 5 Minutes +const TOKEN_TIMEOUT = 3e5 + +const chalk = chalkInstance(argv.json) + +const pollForToken = async ({ api, ticket, exitWithError }) => { + const spinner = startSpinner({ text: 'Waiting for authorization...' }) + try { + const accessToken = await api.getAccessToken(ticket, { timeout: TOKEN_TIMEOUT }) + if (!accessToken) { + exitWithError('Could not retrieve access token') + } + return accessToken + } catch (error) { + if (error.name === 'TimeoutError') { + exitWithError( + `Timed out waiting for authorization. If you do not have a ${chalk.bold.greenBright( + 'Netlify', + )} account, please create one at ${chalk.magenta( + 'https://app.netlify.com/signup', + )}, then run ${chalk.cyanBright('netlify login')} again.`, + ) + } else { + exitWithError(error) + } + } finally { + clearSpinner({ spinner }) + } +} + +const getCwd = () => argv.cwd || process.cwd() + +const getAuthArg = () => { + // If deploy command. Support shorthand 'a' flag + if (argv && argv._ && argv._[0] === 'deploy') { + return argv.auth || argv.a + } + return argv.auth +} + +const getToken = async (authFromFlag) => { + const tokenFromFlag = authFromFlag || getAuthArg(argv) + + // 1. First honor command flag --auth + if (tokenFromFlag) { + return [tokenFromFlag, 'flag'] + } + // 2. then Check ENV var + if (NETLIFY_AUTH_TOKEN && NETLIFY_AUTH_TOKEN !== 'null') { + return [NETLIFY_AUTH_TOKEN, 'env'] + } + // 3. If no env var use global user setting + const globalConfig = await getGlobalConfig() + const userId = globalConfig.get('userId') + const tokenFromConfig = globalConfig.get(`users.${userId}.auth.token`) + if (tokenFromConfig) { + return [tokenFromConfig, 'config'] + } + return [null, 'not found'] +} + +// 'api' command uses JSON output by default +// 'functions:invoke' need to return the data from the function as is +const isDefaultJson = () => argv._[0] === 'functions:invoke' || (argv._[0] === 'api' && argv.list !== true) + +const logJson = (message = '') => { + if (argv.json || isDefaultJson()) { + process.stdout.write(JSON.stringify(message, null, 2)) + } +} + +const log = (message = '', ...args) => { + /* If --silent or --json flag passed disable logger */ + if (argv.silent || argv.json || isDefaultJson()) { + return + } + message = typeof message === 'string' ? message : inspect(message) + process.stdout.write(`${format(message, ...args)}\n`) +} + +// When `build.publish` is not set by the user, the CLI behavior differs in +// several ways. It detects it by checking if `build.publish` is `undefined`. +// However, `@netlify/config` adds a default value to `build.publish`. +// This removes it. +const normalizeConfig = (config) => + config.build.publishOrigin === 'default' + ? { ...config, build: omit(config.build, ['publish', 'publishOrigin']) } + : config + +module.exports = { + argv, + getCwd, + pollForToken, + log, + logJson, + getToken, + normalizeConfig, + chalk, +} diff --git a/src/utils/command.js b/src/utils/command.js index a737bb66474..70dbbd0b671 100644 --- a/src/utils/command.js +++ b/src/utils/command.js @@ -1,92 +1,35 @@ const process = require('process') const { URL } = require('url') -const { format, inspect } = require('util') const resolveConfig = require('@netlify/config') const { flags: flagsLib } = require('@oclif/command') const oclifParser = require('@oclif/parser') const merge = require('lodash/merge') -const argv = require('minimist')(process.argv.slice(2)) const API = require('netlify') -const omit = require('omit.js').default const { getAgent } = require('../lib/http-agent') -const { startSpinner, clearSpinner } = require('../lib/spinner') -const chalkInstance = require('./chalk') +const { pollForToken, log, getToken, getCwd, argv, normalizeConfig, chalk } = require('./command-helpers') const getGlobalConfig = require('./get-global-config') const openBrowser = require('./open-browser') const StateConfig = require('./state-config') const { track, identify } = require('./telemetry') const { TrackedCommand } = require('./telemetry/tracked-command') -const { NETLIFY_AUTH_TOKEN, NETLIFY_API_URL } = process.env +const { NETLIFY_API_URL } = process.env // Netlify CLI client id. Lives in bot@netlify.com // Todo setup client for multiple environments const CLIENT_ID = 'd6f37de6614df7ae58664cfca524744d73807a377f5ee71f1a254f78412e3750' -// 'api' command uses JSON output by default -// 'functions:invoke' need to return the data from the function as is -const isDefaultJson = () => argv._[0] === 'functions:invoke' || (argv._[0] === 'api' && argv.list !== true) - -const getToken = async (tokenFromFlag) => { - // 1. First honor command flag --auth - if (tokenFromFlag) { - return [tokenFromFlag, 'flag'] - } - // 2. then Check ENV var - if (NETLIFY_AUTH_TOKEN && NETLIFY_AUTH_TOKEN !== 'null') { - return [NETLIFY_AUTH_TOKEN, 'env'] - } - // 3. If no env var use global user setting - const globalConfig = await getGlobalConfig() - const userId = globalConfig.get('userId') - const tokenFromConfig = globalConfig.get(`users.${userId}.auth.token`) - if (tokenFromConfig) { - return [tokenFromConfig, 'config'] - } - return [null, 'not found'] -} - -// 5 Minutes -const TOKEN_TIMEOUT = 3e5 - -const pollForToken = async ({ api, ticket, exitWithError, chalk }) => { - const spinner = startSpinner({ text: 'Waiting for authorization...' }) - try { - const accessToken = await api.getAccessToken(ticket, { timeout: TOKEN_TIMEOUT }) - if (!accessToken) { - exitWithError('Could not retrieve access token') - } - return accessToken - } catch (error) { - if (error.name === 'TimeoutError') { - exitWithError( - `Timed out waiting for authorization. If you do not have a ${chalk.bold.greenBright( - 'Netlify', - )} account, please create one at ${chalk.magenta( - 'https://app.netlify.com/signup', - )}, then run ${chalk.cyanBright('netlify login')} again.`, - ) - } else { - exitWithError(error) - } - } finally { - clearSpinner({ spinner }) - } -} - class BaseCommand extends TrackedCommand { // Initialize context async init() { await super.init() - const cwd = argv.cwd || process.cwd() - // Grab netlify API token - const authViaFlag = getAuthArg(argv) + const cwd = getCwd() - const [token] = await this.getConfigToken(authViaFlag) + const [token] = await getToken() // Get site id & build state const state = new StateConfig(cwd) @@ -102,11 +45,10 @@ class BaseCommand extends TrackedCommand { const cachedConfig = await this.getConfig({ cwd, state, token, ...apiUrlOpts }) const { configPath, config, buildDir, repositoryRoot, siteInfo } = cachedConfig - const normalizedConfig = this.normalizeConfig(config) + const normalizedConfig = normalizeConfig(config) const { flags } = this.parse(BaseCommand) const agent = await getAgent({ - log: this.log, exit: this.exit, httpProxy: flags.httpProxy, certificateFile: flags.httpProxyCertificateFilename, @@ -177,16 +119,6 @@ class BaseCommand extends TrackedCommand { } } - // When `build.publish` is not set by the user, the CLI behavior differs in - // several ways. It detects it by checking if `build.publish` is `undefined`. - // However, `@netlify/config` adds a default value to `build.publish`. - // This removes it. - normalizeConfig(config) { - return config.build.publishOrigin === 'default' - ? { ...config, build: omit(config.build, ['publish', 'publishOrigin']) } - : config - } - async isLoggedIn() { try { await this.netlify.api.getCurrentUser() @@ -196,21 +128,6 @@ class BaseCommand extends TrackedCommand { } } - logJson(message = '') { - if (argv.json || isDefaultJson()) { - process.stdout.write(JSON.stringify(message, null, 2)) - } - } - - log(message = '', ...args) { - /* If --silent or --json flag passed disable logger */ - if (argv.silent || argv.json || isDefaultJson()) { - return - } - message = typeof message === 'string' ? message : inspect(message) - process.stdout.write(`${format(message, ...args)}\n`) - } - /* Modified flag parser to support global --auth, --json, & --silent flags */ parse(opts, args = this.argv) { /* Set flags object for commands without flags */ @@ -254,22 +171,8 @@ class BaseCommand extends TrackedCommand { }) } - get chalk() { - // If --json flag disable chalk colors - return chalkInstance(argv.json) - } - - /** - * Get user netlify API token - * @param {string} - [tokenFromFlag] - value passed in by CLI flag - * @return {Promise<[string, string]>} - Promise containing tokenValue & location of resolved Netlify API token - */ - getConfigToken(tokenFromFlag) { - return getToken(tokenFromFlag) - } - async authenticate(tokenFromFlag) { - const [token] = await this.getConfigToken(tokenFromFlag) + const [token] = await getToken(tokenFromFlag) if (token) { return token } @@ -278,7 +181,7 @@ class BaseCommand extends TrackedCommand { async expensivelyAuthenticate() { const webUI = process.env.NETLIFY_WEB_UI || 'https://app.netlify.com' - this.log(`Logging into your Netlify account...`) + log(`Logging into your Netlify account...`) // Create ticket for auth const ticket = await this.netlify.api.createTicket({ @@ -288,14 +191,13 @@ class BaseCommand extends TrackedCommand { // Open browser for authentication const authLink = `${webUI}/authorize?response_type=ticket&ticket=${ticket.id}` - this.log(`Opening ${authLink}`) - await openBrowser({ url: authLink, log: this.log }) + log(`Opening ${authLink}`) + await openBrowser({ url: authLink }) const accessToken = await pollForToken({ api: this.netlify.api, ticket, exitWithError: this.error, - chalk: this.chalk, }) const { id: userId, full_name: name, email } = await this.netlify.api.getCurrentUser() @@ -327,25 +229,17 @@ class BaseCommand extends TrackedCommand { }) // Log success - this.log() - this.log(`${this.chalk.greenBright('You are now logged into your Netlify account!')}`) - this.log() - this.log(`Run ${this.chalk.cyanBright('netlify status')} for account details`) - this.log() - this.log(`To see all available commands run: ${this.chalk.cyanBright('netlify help')}`) - this.log() + log() + log(`${chalk.greenBright('You are now logged into your Netlify account!')}`) + log() + log(`Run ${chalk.cyanBright('netlify status')} for account details`) + log() + log(`To see all available commands run: ${chalk.cyanBright('netlify help')}`) + log() return accessToken } } -const getAuthArg = function (cliArgs) { - // If deploy command. Support shorthand 'a' flag - if (cliArgs && cliArgs._ && cliArgs._[0] === 'deploy') { - return cliArgs.auth || cliArgs.a - } - return cliArgs.auth -} - BaseCommand.strict = false BaseCommand.flags = { debug: flagsLib.boolean({ @@ -361,5 +255,4 @@ BaseCommand.flags = { }), } -BaseCommand.getToken = getToken module.exports = BaseCommand diff --git a/src/utils/detect-server-settings.js b/src/utils/detect-server-settings.js index 36530e0d271..f538d7fe152 100644 --- a/src/utils/detect-server-settings.js +++ b/src/utils/detect-server-settings.js @@ -10,6 +10,7 @@ const isPlainObject = require('is-plain-obj') const { readFileAsyncCatchError } = require('../lib/fs') +const { log } = require('./command-helpers') const { acquirePort } = require('./dev') const { NETLIFYDEVWARN } = require('./logo') @@ -92,14 +93,14 @@ const validateConfiguredPort = ({ devConfig, detectedPort }) => { const DEFAULT_PORT = 8888 const DEFAULT_STATIC_PORT = 3999 -const getDefaultDist = ({ log }) => { +const getDefaultDist = () => { log(`${NETLIFYDEVWARN} Unable to determine public folder to serve files from. Using current working directory`) log(`${NETLIFYDEVWARN} Setup a netlify.toml file with a [dev] section to specify your dev server settings.`) log(`${NETLIFYDEVWARN} See docs at: https://cli.netlify.com/netlify-dev#project-detection`) return process.cwd() } -const handleStaticServer = async ({ flags, log, devConfig, projectDir }) => { +const handleStaticServer = async ({ flags, devConfig, projectDir }) => { validateNumberProperty({ devConfig, property: 'staticServerPort' }) if (flags.dir) { @@ -128,7 +129,7 @@ const handleStaticServer = async ({ flags, log, devConfig, projectDir }) => { ) } - const dist = flags.dir || devConfig.publish || getDefaultDist({ log }) + const dist = flags.dir || devConfig.publish || getDefaultDist() log(`${NETLIFYDEVWARN} Running static server from "${path.relative(path.dirname(projectDir), dist)}"`) const frameworkPort = await acquirePort({ @@ -168,7 +169,7 @@ const getSettingsFromFramework = (framework) => { const hasDevCommand = (framework) => Array.isArray(framework.dev.commands) && framework.dev.commands.length !== 0 -const detectFrameworkSettings = async ({ projectDir, log }) => { +const detectFrameworkSettings = async ({ projectDir }) => { const frameworks = (await listFrameworks({ projectDir })).filter((framework) => hasDevCommand(framework)) if (frameworks.length === 1) { @@ -208,7 +209,7 @@ const detectFrameworkSettings = async ({ projectDir, log }) => { const hasCommandAndTargetPort = ({ devConfig }) => devConfig.command && devConfig.targetPort -const handleCustomFramework = ({ devConfig, log }) => { +const handleCustomFramework = ({ devConfig }) => { if (!hasCommandAndTargetPort({ devConfig })) { throw new Error( `${formatProperty('command')} and ${formatProperty('targetPort')} properties are required when ${formatProperty( @@ -219,7 +220,7 @@ const handleCustomFramework = ({ devConfig, log }) => { return { command: devConfig.command, frameworkPort: devConfig.targetPort, - dist: devConfig.publish || getDefaultDist({ log }), + dist: devConfig.publish || getDefaultDist(), framework: '#custom', pollingStrategies: devConfig.pollingStrategies || [], } @@ -240,31 +241,31 @@ const handleForcedFramework = async ({ devConfig, projectDir }) => { } } -const detectServerSettings = async (devConfig, flags, projectDir, log) => { +const detectServerSettings = async (devConfig, flags, projectDir) => { validateStringProperty({ devConfig, property: 'framework' }) let settings = {} if (flags.dir || devConfig.framework === '#static') { // serving files statically without a framework server - settings = await handleStaticServer({ flags, log, devConfig, projectDir }) + settings = await handleStaticServer({ flags, devConfig, projectDir }) } else if (devConfig.framework === '#auto') { // this is the default CLI behavior // we don't need to run the detection if both command and targetPort are configured const runDetection = !hasCommandAndTargetPort({ devConfig }) - const frameworkSettings = runDetection ? await detectFrameworkSettings({ projectDir, log }) : undefined + const frameworkSettings = runDetection ? await detectFrameworkSettings({ projectDir }) : undefined if (frameworkSettings === undefined && runDetection) { log(`${NETLIFYDEVWARN} No app server detected. Using simple static server`) - settings = await handleStaticServer({ flags, log, devConfig, projectDir }) + settings = await handleStaticServer({ flags, devConfig, projectDir }) } else { validateFrameworkConfig({ devConfig }) const { command, frameworkPort, dist, framework, env, pollingStrategies = [] } = frameworkSettings || {} settings = { command: devConfig.command || command, frameworkPort: devConfig.targetPort || frameworkPort, - dist: devConfig.publish || dist || getDefaultDist({ log }), + dist: devConfig.publish || dist || getDefaultDist(), framework, env, pollingStrategies, @@ -273,7 +274,7 @@ const detectServerSettings = async (devConfig, flags, projectDir, log) => { } else if (devConfig.framework === '#custom') { validateFrameworkConfig({ devConfig }) // when the users wants to configure `command` and `targetPort` - settings = handleCustomFramework({ devConfig, log }) + settings = handleCustomFramework({ devConfig }) } else if (devConfig.framework) { validateFrameworkConfig({ devConfig }) // this is when the user explicitly configures a framework, e.g. `framework = "gatsby"` diff --git a/src/utils/dev.js b/src/utils/dev.js index d8e1c6183d7..c383e134511 100644 --- a/src/utils/dev.js +++ b/src/utils/dev.js @@ -8,6 +8,7 @@ const isEmpty = require('lodash/isEmpty') const { supportsBackgroundFunctions } = require('../lib/account') +const { log } = require('./command-helpers') const { loadDotEnvFiles } = require('./dot-env') const { NETLIFYDEVLOG } = require('./logo') @@ -131,7 +132,7 @@ const getEnvSourceName = (source) => { // Takes a set of environment variables in the format provided by @netlify/config, augments it with variables from both // dot-env files and the process itself, and injects into `process.env`. -const injectEnvVariables = async ({ env, log, site, warn }) => { +const injectEnvVariables = async ({ env, site, warn }) => { const environment = new Map(Object.entries(env)) const dotEnvFiles = await loadDotEnvFiles({ projectDir: site.root, warn }) diff --git a/src/utils/get-repo-data.js b/src/utils/get-repo-data.js index 0f7843d4e75..5a5e2435a18 100644 --- a/src/utils/get-repo-data.js +++ b/src/utils/get-repo-data.js @@ -8,7 +8,9 @@ const gitconfiglocal = require('gitconfiglocal') const isEmpty = require('lodash/isEmpty') const parseGitRemote = require('parse-github-url') -const getRepoData = async function ({ log, remoteName }) { +const { log } = require('./command-helpers') + +const getRepoData = async function ({ remoteName }) { try { const cwd = process.cwd() const [gitConfig, gitDirectory] = await Promise.all([ diff --git a/src/utils/gh-auth.js b/src/utils/gh-auth.js index cc79b3add47..51ff108ee2b 100644 --- a/src/utils/gh-auth.js +++ b/src/utils/gh-auth.js @@ -7,6 +7,7 @@ const { Octokit } = require('@octokit/rest') const getPort = require('get-port') const inquirer = require('inquirer') +const { log } = require('./command-helpers') const { createDeferred } = require('./deferred') const openBrowser = require('./open-browser') @@ -31,7 +32,7 @@ const promptForAuthMethod = async () => { return authMethod === authChoiceNetlify } -const authWithNetlify = async ({ log }) => { +const authWithNetlify = async () => { const port = await getPort({ port: SERVER_PORT }) const { promise: deferredPromise, reject: deferredReject, resolve: deferredResolve } = createDeferred() @@ -64,7 +65,7 @@ const authWithNetlify = async ({ log }) => { provider: 'github', })}` - await openBrowser({ url, log }) + await openBrowser({ url }) return deferredPromise } @@ -95,12 +96,12 @@ const authWithToken = async () => { throw error } -module.exports = async function getGitHubToken({ log }) { +module.exports = async function getGitHubToken() { log('') const withNetlify = await promptForAuthMethod() if (withNetlify) { - return await authWithNetlify({ log }) + return await authWithNetlify() } return await authWithToken() diff --git a/src/utils/init/config-github.js b/src/utils/init/config-github.js index c0f99bf8588..655afa84766 100644 --- a/src/utils/init/config-github.js +++ b/src/utils/init/config-github.js @@ -1,6 +1,7 @@ const { Octokit } = require('@octokit/rest') const chalk = require('chalk') +const { log } = require('../command-helpers') const ghauth = require('../gh-auth') const { getBuildSettings, saveNetlifyToml, formatErrorMessage, createDeployKey, setupSite } = require('./utils') @@ -14,7 +15,7 @@ const PAGE_SIZE = 100 const isValidToken = (token) => token && token.user && token.token -const getGitHubToken = async ({ log, globalConfig }) => { +const getGitHubToken = async ({ globalConfig }) => { const userId = globalConfig.get('userId') const githubToken = globalConfig.get(`users.${userId}.auth.github`) @@ -22,9 +23,7 @@ const getGitHubToken = async ({ log, globalConfig }) => { return githubToken.token } - const newToken = await ghauth({ - log, - }) + const newToken = await ghauth() globalConfig.set(`users.${userId}.auth.github`, newToken) return newToken.token } @@ -36,7 +35,7 @@ const getGitHubClient = ({ token }) => { return octokit } -const addDeployKey = async ({ log, api, octokit, repoOwner, repoName, failAndExit }) => { +const addDeployKey = async ({ api, octokit, repoOwner, repoName, failAndExit }) => { log('Adding deploy key to repository...') const key = await createDeployKey({ api, failAndExit }) try { @@ -149,7 +148,7 @@ const upsertHook = async ({ ntlHooks, event, api, siteId, token }) => { }) } -const addNotificationHooks = async ({ log, failAndExit, siteId, api, token }) => { +const addNotificationHooks = async ({ failAndExit, siteId, api, token }) => { log(`Creating Netlify GitHub Notification Hooks...`) let ntlHooks @@ -174,7 +173,7 @@ const addNotificationHooks = async ({ log, failAndExit, siteId, api, token }) => } module.exports = async function configGithub({ context, siteId, repoOwner, repoName }) { - const { log, warn, error: failAndExit, netlify } = context + const { warn, error: failAndExit, netlify } = context const { api, globalConfig, @@ -184,7 +183,7 @@ module.exports = async function configGithub({ context, siteId, repoOwner, repoN cachedConfig: { env, configPath }, } = netlify - const token = await getGitHubToken({ log, globalConfig }) + const token = await getGitHubToken({ globalConfig }) const { baseDir, buildCmd, buildDir, functionsDir, pluginsToInstall } = await getBuildSettings({ repositoryRoot, @@ -197,7 +196,7 @@ module.exports = async function configGithub({ context, siteId, repoOwner, repoN const octokit = getGitHubClient({ token }) const [deployKey, githubRepo] = await Promise.all([ - addDeployKey({ log, api, octokit, repoOwner, repoName, failAndExit }), + addDeployKey({ api, octokit, repoOwner, repoName, failAndExit }), getGitHubRepo({ octokit, repoOwner, repoName, failAndExit }), ]) @@ -224,5 +223,5 @@ module.exports = async function configGithub({ context, siteId, repoOwner, repoN }) await addDeployHook({ deployHook: updatedSite.deploy_hook, octokit, repoOwner, repoName, failAndExit }) log() - await addNotificationHooks({ log, failAndExit, siteId, api, token }) + await addNotificationHooks({ failAndExit, siteId, api, token }) } diff --git a/src/utils/init/config-manual.js b/src/utils/init/config-manual.js index fdbc9dff692..1127c2b8456 100644 --- a/src/utils/init/config-manual.js +++ b/src/utils/init/config-manual.js @@ -1,8 +1,10 @@ const inquirer = require('inquirer') +const { log } = require('../command-helpers') + const { getBuildSettings, saveNetlifyToml, createDeployKey, setupSite } = require('./utils') -const addDeployKey = async ({ log, exit, deployKey }) => { +const addDeployKey = async ({ exit, deployKey }) => { log('\nGive this Netlify SSH public key access to your repository:\n') log(`\n${deployKey.public_key}\n\n`) @@ -34,7 +36,7 @@ const getRepoPath = async ({ repoData }) => { return repoPath } -const addDeployHook = async ({ log, deployHook }) => { +const addDeployHook = async ({ deployHook }) => { log('\nConfigure the following webhook for your repository:\n') log(`\n${deployHook}\n\n`) const { deployHookAdded } = await inquirer.prompt([ @@ -50,7 +52,7 @@ const addDeployHook = async ({ log, deployHook }) => { } module.exports = async function configManual({ context, siteId, repoData }) { - const { log, warn, error: failAndExit, exit, netlify } = context + const { warn, error: failAndExit, exit, netlify } = context const { api, config, @@ -69,7 +71,7 @@ module.exports = async function configManual({ context, siteId, repoData }) { await saveNetlifyToml({ repositoryRoot, config, configPath, baseDir, buildCmd, buildDir, functionsDir, warn }) const deployKey = await createDeployKey({ api, failAndExit }) - await addDeployKey({ log, exit, deployKey }) + await addDeployKey({ exit, deployKey }) const repoPath = await getRepoPath({ repoData }) const repo = { @@ -92,7 +94,7 @@ module.exports = async function configManual({ context, siteId, repoData }) { configPlugins: config.plugins, pluginsToInstall, }) - const deployHookAdded = await addDeployHook({ log, deployHook: updatedSite.deploy_hook }) + const deployHookAdded = await addDeployHook({ deployHook: updatedSite.deploy_hook }) if (!deployHookAdded) { exit() } diff --git a/src/utils/init/config.js b/src/utils/init/config.js index f4ef5acaea7..57dbef685e2 100644 --- a/src/utils/init/config.js +++ b/src/utils/init/config.js @@ -1,9 +1,11 @@ const chalk = require('chalk') +const { log } = require('../command-helpers') + const configGithub = require('./config-github') const configManual = require('./config-manual') -const logSuccess = ({ log, repoData }) => { +const logSuccess = (repoData) => { log() log(chalk.greenBright.bold.underline(`Success! Netlify CI/CD Configured!`)) log() @@ -22,10 +24,10 @@ const configureRepo = async ({ context, siteId, repoData, manual }) => { } else if (repoData.provider === 'github') { await configGithub({ context, siteId, repoName: repoData.name, repoOwner: repoData.owner }) } else { - context.log(`No configurator found for the provided git remote. Configuring manually...`) + log(`No configurator found for the provided git remote. Configuring manually...`) await configManual({ context, siteId, repoData }) } - logSuccess({ log: context.log, repoData }) + logSuccess(repoData) } module.exports = { configureRepo } diff --git a/src/utils/link/link-by-prompt.js b/src/utils/link/link-by-prompt.js index 7f1eb63fa77..e25e45f9889 100644 --- a/src/utils/link/link-by-prompt.js +++ b/src/utils/link/link-by-prompt.js @@ -5,6 +5,7 @@ const inquirer = require('inquirer') const isEmpty = require('lodash/isEmpty') const { listSites } = require('../../lib/api') +const { log } = require('../command-helpers') const { getRepoData } = require('../get-repo-data') const { track } = require('../telemetry') @@ -18,7 +19,7 @@ module.exports = async function linkPrompts(context, flags = {}) { let GIT_REMOTE_PROMPT = 'Use the current git remote origin URL' let site // Get git remote data if exists - const repoData = await getRepoData({ log: context.log, remoteName: flags.gitRemoteName }) + const repoData = await getRepoData({ remoteName: flags.gitRemoteName }) let linkChoices = [SITE_NAME_PROMPT, SITE_LIST_PROMPT, SITE_ID_PROMPT] @@ -28,9 +29,9 @@ module.exports = async function linkPrompts(context, flags = {}) { linkChoices = [GIT_REMOTE_PROMPT, ...linkChoices] } - context.log() - context.log(`${chalk.cyanBright('netlify link')} will connect this folder to a site on Netlify`) - context.log() + log() + log(`${chalk.cyanBright('netlify link')} will connect this folder to a site on Netlify`) + log() const { linkType } = await inquirer.prompt([ { type: 'list', @@ -44,9 +45,9 @@ module.exports = async function linkPrompts(context, flags = {}) { switch (linkType) { case GIT_REMOTE_PROMPT: { kind = 'gitRemote' - context.log() - context.log(`Looking for sites connected to '${repoData.httpsUrl}'...`) - context.log() + log() + log(`Looking for sites connected to '${repoData.httpsUrl}'...`) + log() const sites = await listSites({ api, options: { filter: 'all' } }) if (isEmpty(sites)) { @@ -61,9 +62,9 @@ module.exports = async function linkPrompts(context, flags = {}) { // If no remote matches. Throw error if (isEmpty(matchingSites)) { - context.log(chalk.redBright.bold.underline(`No Matching Site Found`)) - context.log() - context.log(`No site found with the remote ${repoData.httpsUrl}. + log(chalk.redBright.bold.underline(`No Matching Site Found`)) + log() + log(`No site found with the remote ${repoData.httpsUrl}. Double check you are in the correct working directory and a remote origin repo is configured. @@ -108,8 +109,8 @@ Run ${chalk.cyanBright('git remote -v')} to see a list of your git remotes.`) message: 'Enter the site name (or just part of it):', }, ]) - context.log(`Looking for sites with names containing '${searchTerm}'...`) - context.log() + log(`Looking for sites with names containing '${searchTerm}'...`) + log() let matchingSites try { @@ -155,8 +156,8 @@ or run ${chalk.cyanBright('netlify sites:create')} to create a site.`) } case SITE_LIST_PROMPT: { kind = 'fromList' - context.log(`Fetching recently updated sites...`) - context.log() + log(`Fetching recently updated sites...`) + log() let sites try { @@ -223,17 +224,17 @@ or run ${chalk.cyanBright('netlify sites:create')} to create a site.`) }) // Log output - context.log() - context.log(chalk.greenBright.bold.underline(`Directory Linked`)) - context.log() - context.log(`Admin url: ${chalk.magentaBright(site.admin_url)}`) - context.log(`Site url: ${chalk.cyanBright(site.ssl_url || site.url)}`) - context.log() - - context.log(`Site id saved to ${path.join(context.netlify.site.root, '/.netlify/state.json')}`) - // context.log(`Local Config: .netlify/config.json`) - context.log() - context.log(`You can now run other \`netlify\` cli commands in this directory`) + log() + log(chalk.greenBright.bold.underline(`Directory Linked`)) + log() + log(`Admin url: ${chalk.magentaBright(site.admin_url)}`) + log(`Site url: ${chalk.cyanBright(site.ssl_url || site.url)}`) + log() + + log(`Site id saved to ${path.join(context.netlify.site.root, '/.netlify/state.json')}`) + // log(`Local Config: .netlify/config.json`) + log() + log(`You can now run other \`netlify\` cli commands in this directory`) return site } diff --git a/src/utils/live-tunnel.js b/src/utils/live-tunnel.js index 5fbb318f2e7..2cc435e01e4 100644 --- a/src/utils/live-tunnel.js +++ b/src/utils/live-tunnel.js @@ -8,6 +8,7 @@ const pWaitFor = require('p-wait-for') const { shouldFetchLatestVersion, fetchLatestVersion } = require('../lib/exec-fetcher') const { getPathInHome } = require('../lib/settings') +const { log } = require('./command-helpers') const { NETLIFYDEVLOG, NETLIFYDEVERR } = require('./logo') const PACKAGE_NAME = 'live-tunnel-client' @@ -18,8 +19,8 @@ const TUNNEL_POLL_INTERVAL = 1e3 // 5 minutes const TUNNEL_POLL_TIMEOUT = 3e5 -const createTunnel = async function ({ siteId, netlifyApiToken, log }) { - await installTunnelClient(log) +const createTunnel = async function ({ siteId, netlifyApiToken }) { + await installTunnelClient() if (!siteId) { console.error( @@ -50,7 +51,7 @@ const createTunnel = async function ({ siteId, netlifyApiToken, log }) { return data } -const connectTunnel = function ({ session, netlifyApiToken, localPort, log }) { +const connectTunnel = function ({ session, netlifyApiToken, localPort }) { const execPath = getPathInHome(['tunnel', 'bin', EXEC_NAME]) const args = ['connect', '-s', session.id, '-t', netlifyApiToken, '-l', localPort] if (process.env.DEBUG) { @@ -64,7 +65,7 @@ const connectTunnel = function ({ session, netlifyApiToken, localPort, log }) { ps.on('SIGTERM', process.exit) } -const installTunnelClient = async function (log) { +const installTunnelClient = async function () { const binPath = getPathInHome(['tunnel', 'bin']) const shouldFetch = await shouldFetchLatestVersion({ binPath, @@ -87,11 +88,10 @@ const installTunnelClient = async function (log) { }) } -const startLiveTunnel = async ({ siteId, netlifyApiToken, localPort, log }) => { +const startLiveTunnel = async ({ siteId, netlifyApiToken, localPort }) => { const session = await createTunnel({ siteId, netlifyApiToken, - log, }) const isLiveTunnelReady = async function () { @@ -112,7 +112,7 @@ const startLiveTunnel = async ({ siteId, netlifyApiToken, localPort, log }) => { return data.state === 'online' } - await connectTunnel({ session, netlifyApiToken, localPort, log }) + await connectTunnel({ session, netlifyApiToken, localPort }) // Waiting for the live session to have a state of `online`. await pWaitFor(isLiveTunnelReady, { diff --git a/src/utils/lm/install.js b/src/utils/lm/install.js index fd73c9f2718..ba19adf6bb3 100644 --- a/src/utils/lm/install.js +++ b/src/utils/lm/install.js @@ -33,7 +33,7 @@ const SUPPORTED_PLATFORMS = { win32: 'Windows', } -const getSetupStep = ({ skipInstall, log }) => { +const getSetupStep = ({ skipInstall }) => { const platform = os.platform() const platformName = SUPPORTED_PLATFORMS[platform] if (platformName === undefined) { @@ -49,7 +49,7 @@ See manual setup instructions in https://github.com/netlify/netlify-credential-h } }, task: async () => { - await installHelper({ log }) + await installHelper() await (platform === 'win32' ? setupWindowsPath() : setupUnixPath()) }, } @@ -60,7 +60,7 @@ const setupGitConfigStep = { task: () => configureGitConfig(), } -const installPlatform = async function ({ force, log }) { +const installPlatform = async function ({ force }) { const skipInstall = !force && (await installedWithPackageManager()) const steps = [ checkGitVersionStep, @@ -71,7 +71,7 @@ const installPlatform = async function ({ force, log }) { task.title += chalk.dim(' [installed]') } }), - getSetupStep({ skipInstall, log }), + getSetupStep({ skipInstall }), setupGitConfigStep, ] @@ -92,7 +92,7 @@ const installedWithPackageManager = async function () { return withPackageManager } -const installHelper = async function ({ log }) { +const installHelper = async function () { // remove any old versions that might still exist in `~/.netlify/helper/bin` await rmdirRecursiveAsync(getLegacyBinPath()) const binPath = getBinPath() @@ -102,7 +102,6 @@ const installHelper = async function ({ log }) { execArgs: ['version'], pattern: `${EXEC_NAME}\\/v?([^\\s]+)`, execName: EXEC_NAME, - log, }) if (!shouldFetch) { return diff --git a/src/utils/open-browser.js b/src/utils/open-browser.js index df6aee1bb0b..e743b0e9592 100644 --- a/src/utils/open-browser.js +++ b/src/utils/open-browser.js @@ -4,7 +4,9 @@ const open = require('better-opn') const chalk = require('chalk') const isDockerContainer = require('is-docker') -const unableToOpenBrowserMessage = function ({ url, log, message }) { +const { log } = require('./command-helpers') + +const unableToOpenBrowserMessage = function ({ url, message }) { log('---------------------------') log(chalk.redBright(`Error: Unable to open browser automatically: ${message}`)) log(chalk.cyan('Please open your browser and open the URL below:')) @@ -12,14 +14,14 @@ const unableToOpenBrowserMessage = function ({ url, log, message }) { log('---------------------------') } -const openBrowser = async function ({ url, log, silentBrowserNoneError }) { +const openBrowser = async function ({ url, silentBrowserNoneError }) { if (isDockerContainer()) { - unableToOpenBrowserMessage({ url, log, message: 'Running inside a docker container' }) + unableToOpenBrowserMessage({ url, message: 'Running inside a docker container' }) return } if (process.env.BROWSER === 'none') { if (!silentBrowserNoneError) { - unableToOpenBrowserMessage({ url, log, message: "BROWSER environment variable is set to 'none'" }) + unableToOpenBrowserMessage({ url, message: "BROWSER environment variable is set to 'none'" }) } return } @@ -27,7 +29,7 @@ const openBrowser = async function ({ url, log, silentBrowserNoneError }) { try { await open(url) } catch (error) { - unableToOpenBrowserMessage({ url, log, message: error.message }) + unableToOpenBrowserMessage({ url, message: error.message }) } } diff --git a/src/utils/state-config.js b/src/utils/state-config.js index d750581f0c1..4c798311be9 100644 --- a/src/utils/state-config.js +++ b/src/utils/state-config.js @@ -12,20 +12,20 @@ const { getPathInProject } = require('../lib/settings') const STATE_PATH = getPathInProject(['state.json']) const permissionError = "You don't have access to this file." -class StateConfig { - constructor(cwd) { - this.path = this.findStatePath(cwd) - } +// Finds location of `.netlify/state.json` +const findStatePath = (cwd) => { + const statePath = findUp.sync([STATE_PATH], { cwd }) - // Finds location of `.netlify/state.json` - findStatePath(cwd) { - const statePath = findUp.sync([STATE_PATH], { cwd }) + if (!statePath) { + return path.join(cwd, STATE_PATH) + } - if (!statePath) { - return path.join(cwd, STATE_PATH) - } + return statePath +} - return statePath +class StateConfig { + constructor(cwd) { + this.path = findStatePath(cwd) } get all() { diff --git a/src/utils/traffic-mesh.js b/src/utils/traffic-mesh.js index f01f0c4f135..d61f5c634c8 100644 --- a/src/utils/traffic-mesh.js +++ b/src/utils/traffic-mesh.js @@ -10,6 +10,7 @@ const waitPort = require('wait-port') const { getPathInProject } = require('../lib/settings') const { clearSpinner, startSpinner, stopSpinner } = require('../lib/spinner') +const { log } = require('./command-helpers') const { createDeferred } = require('./deferred') const { NETLIFYDEVLOG, NETLIFYDEVERR, NETLIFYDEVWARN } = require('./logo') @@ -20,7 +21,6 @@ const startForwardProxy = async ({ frameworkPort, functionsPort, publishDir, - log, debug, locationDb, jwtRolesPath, @@ -61,8 +61,8 @@ const startForwardProxy = async ({ args.push('--signature-secret', jwtSecret) } - const { subprocess } = runProcess({ log, args }) - const { forwarder, firstBundleReady } = forwardMessagesToLog({ log, subprocess }) + const { subprocess } = runProcess({ args }) + const { forwarder, firstBundleReady } = forwardMessagesToLog({ subprocess }) subprocess.on('close', process.exit) subprocess.on('SIGINT', process.exit) @@ -94,7 +94,7 @@ const startForwardProxy = async ({ } } -const forwardMessagesToLog = ({ log, subprocess }) => { +const forwardMessagesToLog = ({ subprocess }) => { const { promise: firstBundleReady, reject: firstBundleReject, resolve: firstBundleResolve } = createDeferred() let currentId = null diff --git a/tests/command.deploy.test.js b/tests/command.deploy.test.js index 2fe6292260b..7f441ec8aeb 100644 --- a/tests/command.deploy.test.js +++ b/tests/command.deploy.test.js @@ -4,7 +4,7 @@ const test = require('ava') const omit = require('omit.js').default const { supportsEdgeHandlers } = require('../src/lib/account') -const { getToken } = require('../src/utils/command') +const { getToken } = require('../src/utils/command-helpers') const callCli = require('./utils/call-cli') const { generateSiteName, createLiveTestSite } = require('./utils/create-live-test-site')