From 199b72303245baf02b99e08d9f3091a6d36e229d Mon Sep 17 00:00:00 2001 From: Dylan Spyer Date: Mon, 14 Oct 2024 17:06:24 -0500 Subject: [PATCH 1/8] chore: created new type for API errors, created and implemented type predicates Created APIError interface in `command-helpers.ts`. Created two type predicates `isAPIError` and `errorHasStatus`. Implemented predicates across the code base. Co-authored-by: Ben Hancock --- src/commands/addons/addons-config.ts | 5 ++--- src/commands/addons/addons-create.ts | 5 ++--- src/commands/addons/addons-delete.ts | 5 ++--- src/commands/api/api.ts | 1 - src/commands/base-command.ts | 1 - src/commands/deploy/deploy.ts | 8 ++++---- src/commands/functions/functions-create.ts | 17 +++++++++++------ src/commands/functions/functions-invoke.ts | 5 ++--- src/commands/link/link.ts | 21 ++++++--------------- src/commands/sites/sites-create-template.ts | 7 +++---- src/commands/sites/sites-create.ts | 8 +++----- src/commands/sites/sites-delete.ts | 13 ++++++------- src/commands/status/status.ts | 7 ++++--- src/utils/addons/prepare.ts | 14 +++++--------- src/utils/command-helpers.ts | 17 +++++++++++++++-- src/utils/dev.ts | 12 +++++++----- src/utils/hooks/requires-site-info.ts | 10 ++++------ 17 files changed, 76 insertions(+), 80 deletions(-) diff --git a/src/commands/addons/addons-config.ts b/src/commands/addons/addons-config.ts index 9d23fe36380..dd3ec8557e7 100644 --- a/src/commands/addons/addons-config.ts +++ b/src/commands/addons/addons-config.ts @@ -8,7 +8,7 @@ import { ADDON_VALIDATION, prepareAddonCommand } from '../../utils/addons/prepar import generatePrompts from '../../utils/addons/prompts.js' import { renderConfigValues } from '../../utils/addons/render.js' import { missingConfigValues, requiredConfigValues, updateConfigValues } from '../../utils/addons/validation.js' -import { chalk, error, log } from '../../utils/command-helpers.js' +import { chalk, error, log, isAPIError } from '../../utils/command-helpers.js' import { parseRawFlags } from '../../utils/parse-raw-flags.js' import BaseCommand from '../base-command.js' @@ -35,8 +35,7 @@ const update = async function ({ addonName, api, currentConfig, instanceId, newC }) log(`Add-on "${addonName}" successfully updated`) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(error_.message) + isAPIError(error_) ? error(error_.message) : error(error_) } } diff --git a/src/commands/addons/addons-create.ts b/src/commands/addons/addons-create.ts index 1fa244725d3..57d9925c1d9 100644 --- a/src/commands/addons/addons-create.ts +++ b/src/commands/addons/addons-create.ts @@ -6,7 +6,7 @@ import { ADDON_VALIDATION, prepareAddonCommand } from '../../utils/addons/prepar import generatePrompts from '../../utils/addons/prompts.js' import { renderConfigValues, renderMissingValues } from '../../utils/addons/render.js' import { missingConfigValues, requiredConfigValues, updateConfigValues } from '../../utils/addons/validation.js' -import { chalk, error, log } from '../../utils/command-helpers.js' +import { chalk, error, log, isAPIError } from '../../utils/command-helpers.js' import { parseRawFlags } from '../../utils/parse-raw-flags.js' import BaseCommand from '../base-command.js' // @ts-expect-error TS(7031) FIXME: Binding element 'addonName' implicitly has an 'any... Remove this comment to see the full error message @@ -23,8 +23,7 @@ const createAddon = async ({ addonName, api, config, siteData, siteId }) => { log(`${response.config.message}`) } } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(error_.message) + isAPIError(error_) ? error(error_.message) : error(error_) } } diff --git a/src/commands/addons/addons-delete.ts b/src/commands/addons/addons-delete.ts index 32f9e46eb61..17693ed4e33 100644 --- a/src/commands/addons/addons-delete.ts +++ b/src/commands/addons/addons-delete.ts @@ -2,7 +2,7 @@ import { OptionValues } from 'commander' import inquirer from 'inquirer' import { ADDON_VALIDATION, prepareAddonCommand } from '../../utils/addons/prepare.js' -import { error, exit, log } from '../../utils/command-helpers.js' +import { error, exit, log, isAPIError } from '../../utils/command-helpers.js' import BaseCommand from '../base-command.js' export const addonsDelete = async (addonName: string, options: OptionValues, command: BaseCommand) => { @@ -31,7 +31,6 @@ export const addonsDelete = async (addonName: string, options: OptionValues, com }) log(`Addon "${addonName}" deleted`) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(error_.message) + isAPIError(error_) ? error(error_.message) : error(error_) } } diff --git a/src/commands/api/api.ts b/src/commands/api/api.ts index 10caa2d127c..b594b1646dd 100644 --- a/src/commands/api/api.ts +++ b/src/commands/api/api.ts @@ -40,7 +40,6 @@ export const apiCommand = async (apiMethod: string, options: OptionValues, comma const apiResponse = await api[apiMethod](payload) logJson(apiResponse) } catch (error_) { - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message error(error_) } } diff --git a/src/commands/base-command.ts b/src/commands/base-command.ts index 592f464b755..f8a7c469124 100644 --- a/src/commands/base-command.ts +++ b/src/commands/base-command.ts @@ -725,7 +725,6 @@ export default class BaseCommand extends Command { // the option to say that we don't need API data.) if (isUserError && !config.offline && config.token) { if (flags.debug) { - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message error(error_, { exit: false }) warn('Failed to resolve config, falling back to offline resolution') } diff --git a/src/commands/deploy/deploy.ts b/src/commands/deploy/deploy.ts index a2e535c412c..bc008f54524 100644 --- a/src/commands/deploy/deploy.ts +++ b/src/commands/deploy/deploy.ts @@ -30,6 +30,8 @@ import { log, logJson, warn, + isAPIError, + errorHasStatus, } from '../../utils/command-helpers.js' import { DEFAULT_DEPLOY_TIMEOUT } from '../../utils/deploy/constants.js' import { deploySite } from '../../utils/deploy/deploy-site.js' @@ -58,12 +60,10 @@ const triggerDeploy = async ({ api, options, siteData, siteId }) => { ) } } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.status === 404) { + if (errorHasStatus(error_, 404)) { error('Site not found. Please rerun "netlify link" and make sure that your site has CI configured.') } else { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(error_.message) + isAPIError(error_) ? error(error_.message) : error(error_) } } } diff --git a/src/commands/functions/functions-create.ts b/src/commands/functions/functions-create.ts index 1fcf7fe4781..9948387462d 100644 --- a/src/commands/functions/functions-create.ts +++ b/src/commands/functions/functions-create.ts @@ -15,7 +15,15 @@ import ora from 'ora' import { fileExistsAsync } from '../../lib/fs.js' import { getAddons, getCurrentAddon, getSiteData } from '../../utils/addons/prepare.js' -import { NETLIFYDEVERR, NETLIFYDEVLOG, NETLIFYDEVWARN, chalk, error, log } from '../../utils/command-helpers.js' +import { + NETLIFYDEVERR, + NETLIFYDEVLOG, + NETLIFYDEVWARN, + chalk, + error, + log, + isAPIError, +} from '../../utils/command-helpers.js' import { copyTemplateDir } from '../../utils/copy-template-dir/copy-template-dir.js' import { getDotEnvVariables, injectEnvVariables } from '../../utils/dev.js' import execa from '../../utils/execa.js' @@ -492,7 +500,6 @@ const scaffoldFromTemplate = async function (command, options, argumentName, fun await downloadFromURL(command, options, argumentName, functionsDir) } catch (error_) { error(`$${NETLIFYDEVERR} Error downloading from URL: ${options.url}`) - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message error(error_) process.exit(1) } @@ -583,8 +590,7 @@ const createFunctionAddon = async function ({ addonName, addons, api, siteData, log(`Add-on "${addonName}" created for ${siteData.name}`) return true } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(error_.message) + isAPIError(error_) ? error(error_.message) : error(error_) } } @@ -682,8 +688,7 @@ const installAddons = async function (command, functionAddons, fnPath) { await handleAddonDidInstall({ addonCreated, addonDidInstall, command, fnPath }) } catch (error_) { - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message - error(`${NETLIFYDEVERR} Error installing addon: `, error_) + error(`${NETLIFYDEVERR} Error installing addon: ${error_}`) } }) return Promise.all(arr) diff --git a/src/commands/functions/functions-invoke.ts b/src/commands/functions/functions-invoke.ts index 0ca0da575f0..66d367da0bf 100644 --- a/src/commands/functions/functions-invoke.ts +++ b/src/commands/functions/functions-invoke.ts @@ -6,7 +6,7 @@ import { OptionValues } from 'commander' import inquirer from 'inquirer' import fetch from 'node-fetch' -import { NETLIFYDEVWARN, chalk, error, exit } from '../../utils/command-helpers.js' +import { NETLIFYDEVWARN, chalk, error, exit, isAPIError } from '../../utils/command-helpers.js' import { BACKGROUND, CLOCKWORK_USERAGENT, getFunctions } from '../../utils/functions/index.js' import BaseCommand from '../base-command.js' @@ -234,7 +234,6 @@ export const functionsInvoke = async (nameArgument: string, options: OptionValue const data = await response.text() console.log(data) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(`Ran into an error invoking your function: ${error_.message}`) + isAPIError(error_) ? error(`Ran into an error invoking your function: ${error_.message}`) : error(error_) } } diff --git a/src/commands/link/link.ts b/src/commands/link/link.ts index 30889cb2525..f923ba82c36 100644 --- a/src/commands/link/link.ts +++ b/src/commands/link/link.ts @@ -3,7 +3,7 @@ import inquirer from 'inquirer' import isEmpty from 'lodash/isEmpty.js' import { listSites } from '../../lib/api.js' -import { chalk, error, exit, log } from '../../utils/command-helpers.js' +import { chalk, error, exit, log, errorHasStatus } from '../../utils/command-helpers.js' import getRepoData from '../../utils/get-repo-data.js' import { ensureNetlifyIgnore } from '../../utils/gitignore.js' import { track } from '../../utils/telemetry/index.js' @@ -125,11 +125,9 @@ Run ${chalk.cyanBright('git remote -v')} to see a list of your git remotes.`) options: { name: searchTerm, filter: 'all' }, }) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.status === 404) { + if (errorHasStatus(error_, 404)) { error(`'${searchTerm}' not found`) } else { - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message error(error_) } } @@ -172,7 +170,6 @@ or run ${chalk.cyanBright('netlify sites:create')} to create a site.`) try { sites = await listSites({ api, options: { maxPages: 1, filter: 'all' } }) } catch (error_) { - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message error(error_) } @@ -209,11 +206,9 @@ or run ${chalk.cyanBright('netlify sites:create')} to create a site.`) try { site = await api.getSite({ siteId }) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.status === 404) { - error(new Error(`Site ID '${siteId}' not found`)) + if (errorHasStatus(error_, 404)) { + error(`Site ID '${siteId}' not found`) // this is where we removed the new Error() call } else { - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message error(error_) } } @@ -281,11 +276,9 @@ export const link = async (options: OptionValues, command: BaseCommand) => { try { siteData = await api.getSite({ site_id: options.id }) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.status === 404) { + if (errorHasStatus(error_, 404)) { error(new Error(`Site id ${options.id} not found`)) } else { - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message error(error_) } } @@ -310,11 +303,9 @@ export const link = async (options: OptionValues, command: BaseCommand) => { }, }) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.status === 404) { + if (errorHasStatus(error_, 404)) { error(new Error(`${options.name} not found`)) } else { - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message error(error_) } } diff --git a/src/commands/sites/sites-create-template.ts b/src/commands/sites/sites-create-template.ts index ff2747f1086..22dd714282d 100644 --- a/src/commands/sites/sites-create-template.ts +++ b/src/commands/sites/sites-create-template.ts @@ -5,7 +5,7 @@ import pick from 'lodash/pick.js' import parseGitHubUrl from 'parse-github-url' import { render } from 'prettyjson' -import { chalk, error, getTerminalLink, log, logJson, warn } from '../../utils/command-helpers.js' +import { chalk, error, getTerminalLink, log, logJson, warn, isAPIError } from '../../utils/command-helpers.js' import execa from '../../utils/execa.js' import getRepoData from '../../utils/get-repo-data.js' import { getGitHubToken } from '../../utils/init/config-github.js' @@ -164,15 +164,14 @@ export const sitesCreateTemplate = async (repository: string, options: OptionVal } } catch (error_) { // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.status === 422 || error_.message === 'Duplicate repo') { + if (hasErrorStatus(error_, 422) || (isAPIError(error_) && error_.message === 'Duplicate repo')) { warn( `${name}.netlify.app already exists or a repository named ${name} already exists on this account. Please try a different slug.`, ) // @ts-expect-error TS(2554) FIXME: Expected 1 arguments, but got 0. await inputSiteName() } else { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(`createSiteInTeam error: ${error_.status}: ${error_.message}`) + isAPIError(error_) ? error(`createSiteInTeam error: ${error_.status}: ${error_.message}`) : error(error_) } } } diff --git a/src/commands/sites/sites-create.ts b/src/commands/sites/sites-create.ts index c7a21f3d81a..f29416ad962 100644 --- a/src/commands/sites/sites-create.ts +++ b/src/commands/sites/sites-create.ts @@ -3,7 +3,7 @@ import inquirer from 'inquirer' import pick from 'lodash/pick.js' import prettyjson from 'prettyjson' -import { chalk, error, log, logJson, warn } from '../../utils/command-helpers.js' +import { chalk, error, isAPIError, errorHasStatus, log, logJson, warn } from '../../utils/command-helpers.js' import getRepoData from '../../utils/get-repo-data.js' import { configureRepo } from '../../utils/init/config.js' import { track } from '../../utils/telemetry/index.js' @@ -70,14 +70,12 @@ export const sitesCreate = async (options: OptionValues, command: BaseCommand) = body, }) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.status === 422) { + if (errorHasStatus(error_, 422)) { warn(`${siteName}.netlify.app already exists. Please try a different slug.`) // @ts-expect-error TS(2554) FIXME: Expected 1 arguments, but got 0. await inputSiteName() } else { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(`createSiteInTeam error: ${error_.status}: ${error_.message}`) + isAPIError(error_) ? error(`createSiteInTeam error: ${error_.status}: ${error_.message}`) : error(error_) } } } diff --git a/src/commands/sites/sites-delete.ts b/src/commands/sites/sites-delete.ts index 70956c893c3..cf56805ac5c 100644 --- a/src/commands/sites/sites-delete.ts +++ b/src/commands/sites/sites-delete.ts @@ -1,7 +1,7 @@ import { OptionValues } from 'commander' import inquirer from 'inquirer' -import { chalk, error, exit, log } from '../../utils/command-helpers.js' +import { chalk, error, exit, log, errorHasStatus, isAPIError } from '../../utils/command-helpers.js' import BaseCommand from '../base-command.js' export const sitesDelete = async (siteId: string, options: OptionValues, command: BaseCommand) => { @@ -17,9 +17,10 @@ export const sitesDelete = async (siteId: string, options: OptionValues, command try { siteData = await api.getSite({ siteId }) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.status === 404) { + if (errorHasStatus(error_, 404)) { error(`No site with id ${siteId} found. Please verify the siteId & try again.`) + } else { + error(error_) } } @@ -74,12 +75,10 @@ export const sitesDelete = async (siteId: string, options: OptionValues, command try { await api.deleteSite({ site_id: siteId }) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.status === 404) { + if (errorHasStatus(error_, 404)) { error(`No site with id ${siteId} found. Please verify the siteId & try again.`) } else { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(`Delete Site error: ${error_.status}: ${error_.message}`) + isAPIError(error_) ? error(`Delete Site error: ${error_.status}: ${error_.message}`) : error(error_) } } log(`Site "${siteId}" successfully deleted!`) diff --git a/src/commands/status/status.ts b/src/commands/status/status.ts index 15cf6b745f5..be5e2d37f97 100644 --- a/src/commands/status/status.ts +++ b/src/commands/status/status.ts @@ -2,7 +2,7 @@ import clean from 'clean-deep' import { OptionValues } from 'commander' import prettyjson from 'prettyjson' -import { chalk, error, exit, getToken, log, logJson, warn } from '../../utils/command-helpers.js' +import { chalk, error, exit, getToken, log, logJson, warn, errorHasStatus } from '../../utils/command-helpers.js' import BaseCommand from '../base-command.js' export const status = async (options: OptionValues, command: BaseCommand) => { @@ -31,9 +31,10 @@ export const status = async (options: OptionValues, command: BaseCommand) => { // eslint-disable-next-line @typescript-eslint/no-extra-semi ;[accounts, user] = await Promise.all([api.listAccountsForUser(), api.getCurrentUser()]) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.status === 401) { + if (errorHasStatus(error_)) { error('Your session has expired. Please try to re-authenticate by running `netlify logout` and `netlify login`.') + } else { + error(error_) } } diff --git a/src/utils/addons/prepare.ts b/src/utils/addons/prepare.ts index 3a31e07b077..ac575928b41 100644 --- a/src/utils/addons/prepare.ts +++ b/src/utils/addons/prepare.ts @@ -1,4 +1,4 @@ -import { chalk, error, exit, log, warn } from '../command-helpers.js' +import { chalk, error, exit, log, warn, isAPIError } from '../command-helpers.js' export const ADDON_VALIDATION = { EXISTS: 'EXISTS', @@ -55,12 +55,10 @@ export const getAddonManifest = async ({ addonName, api }) => { try { manifest = await api.showServiceManifest({ addonName }) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (typeof error_.message === 'string' && error_.message.includes('Not Found')) { + if (isAPIError(error_) && error_.message.includes('Not Found')) { error(`No add-on "${addonName}" found. Please double check your add-on name and try again`) } else { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(error_.message) + isAPIError(error_) ? error(error_.message) : error(error_) } } return manifest @@ -72,8 +70,7 @@ export const getSiteData = async ({ api, siteId }) => { try { siteData = await api.getSite({ siteId }) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(`Failed getting list of site data: ${error_.message}`) + isAPIError(error_) ? error(`Failed getting list of site data: ${error_.message}`) : error(error_) } return siteData } @@ -84,8 +81,7 @@ export const getAddons = async ({ api, siteId }) => { try { addons = await api.listServiceInstancesForSite({ siteId }) } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(`Failed getting list of addons: ${error_.message}`) + isAPIError(error_) ? error(`Failed getting list of addons: ${error_.message}`) : error(error_) } return addons } diff --git a/src/utils/command-helpers.ts b/src/utils/command-helpers.ts index c71311bb7a2..af93aa2dda1 100644 --- a/src/utils/command-helpers.ts +++ b/src/utils/command-helpers.ts @@ -112,7 +112,6 @@ export const pollForToken = async ({ api, ticket }) => { )}, then run ${chalk.cyanBright('netlify login')} again.`, ) } else { - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message error(error_) } } finally { @@ -186,7 +185,7 @@ export const warn = (message = '') => { } /** Throws an error or logs it */ -export const error = (message: Error | string = '', options: { exit?: boolean } = {}) => { +export const error = (message: unknown | Error | string = '', options: { exit?: boolean } = {}) => { const err = message instanceof Error ? message @@ -300,3 +299,17 @@ export const nonNullable = (value: T): value is NonNullable => value !== n export const noOp = () => { // no-op } + +interface HTTPError extends Error { + status: number + message: string +} + +export const isAPIError = (errorObject: unknown): errorObject is HTTPError => + errorObject instanceof Error && 'status' in errorObject && 'message' in errorObject + +export const errorHasStatus = (errorObject: unknown, statusCode: number) => + isAPIError(errorObject) && errorObject.status === statusCode + +// export const isMessageError = (errorObject: unknown): errorObject is HTTPError => +// errorObject instanceof Error && 'message' in errorObject diff --git a/src/utils/dev.ts b/src/utils/dev.ts index 3d2fd51d335..e9e077ec2ef 100644 --- a/src/utils/dev.ts +++ b/src/utils/dev.ts @@ -5,7 +5,7 @@ import isEmpty from 'lodash/isEmpty.js' import { supportsBackgroundFunctions } from '../lib/account.js' -import { NETLIFYDEVLOG, chalk, error, log, warn } from './command-helpers.js' +import { NETLIFYDEVLOG, chalk, error, log, warn, isAPIError } from './command-helpers.js' import { loadDotEnvFiles } from './dot-env.js' // Possible sources of environment variables. For the purpose of printing log messages only. Order does not matter. @@ -52,8 +52,9 @@ const getAccounts = async ({ api }) => { const accounts = await api.listAccountsForUser() return accounts } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(`Failed retrieving user account: ${error_.message}. ${ERROR_CALL_TO_ACTION}`) + isAPIError(error_) + ? error(`Failed retrieving user account: ${error_.message}. ${ERROR_CALL_TO_ACTION}`) + : error(error_) } } @@ -63,8 +64,9 @@ const getAddons = async ({ api, site }) => { const addons = await api.listServiceInstancesForSite({ siteId: site.id }) return addons } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - error(`Failed retrieving addons for site ${chalk.yellow(site.id)}: ${error_.message}. ${ERROR_CALL_TO_ACTION}`) + isAPIError(error_) + ? error(`Failed retrieving addons for site ${chalk.yellow(site.id)}: ${error_.message}. ${ERROR_CALL_TO_ACTION}`) + : error(error_) } } diff --git a/src/utils/hooks/requires-site-info.ts b/src/utils/hooks/requires-site-info.ts index 69b38e1a5f1..d3d0f07d62b 100644 --- a/src/utils/hooks/requires-site-info.ts +++ b/src/utils/hooks/requires-site-info.ts @@ -1,4 +1,5 @@ -import { error, warn } from '../command-helpers.js' +import { error, warn, errorHasStatus } from '../command-helpers.js' + /** * A preAction hook that errors out if siteInfo is an empty object * @param {*} command @@ -15,17 +16,14 @@ const requiresSiteInfo = async (command) => { await api.getSite({ siteId }) } catch (error_) { // unauthorized - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.status === 401) { + if (errorHasStatus(error_, 404)) { warn(`Log in with a different account or re-link to a site you have permission for`) return error(`Not authorized to view the currently linked site (${siteId})`) } // missing - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (error_.status === 404) { + if (errorHasStatus(error_, 404)) { return error(`The site this folder is linked to can't be found`) } - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message return error(error_) } } From 9c8dc4976b2cd8f0c3b97cd062c21132add0cb91 Mon Sep 17 00:00:00 2001 From: Dylan Spyer Date: Tue, 15 Oct 2024 09:18:19 -0500 Subject: [PATCH 2/8] chore: deleted remaining TSFIXME comments that were not needed anymore Deleted remaining TSFIXME comments that were not needed anymore due to adding `unknown` type to custom `error` function. Co-authored-by: Ben Hancock --- src/commands/link/link.ts | 2 +- src/commands/lm/lm-setup.ts | 1 - src/commands/watch/watch.ts | 1 - src/lib/exec-fetcher.ts | 2 -- src/utils/command-helpers.ts | 4 ++-- src/utils/framework-server.ts | 2 +- 6 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/commands/link/link.ts b/src/commands/link/link.ts index f923ba82c36..940375de2d3 100644 --- a/src/commands/link/link.ts +++ b/src/commands/link/link.ts @@ -207,7 +207,7 @@ or run ${chalk.cyanBright('netlify sites:create')} to create a site.`) site = await api.getSite({ siteId }) } catch (error_) { if (errorHasStatus(error_, 404)) { - error(`Site ID '${siteId}' not found`) // this is where we removed the new Error() call + error(`Site ID '${siteId}' not found`) } else { error(error_) } diff --git a/src/commands/lm/lm-setup.ts b/src/commands/lm/lm-setup.ts index 409b0fad854..f32940ab4c5 100644 --- a/src/commands/lm/lm-setup.ts +++ b/src/commands/lm/lm-setup.ts @@ -65,7 +65,6 @@ export const lmSetup = async (options: OptionValues, command: BaseCommand) => { try { helperInstalled = await installHelperIfMissing({ force: options.forceInstall }) } catch (error_) { - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message error(error_) } } diff --git a/src/commands/watch/watch.ts b/src/commands/watch/watch.ts index 071ccce8b02..2bf7a016878 100644 --- a/src/commands/watch/watch.ts +++ b/src/commands/watch/watch.ts @@ -114,7 +114,6 @@ export const watch = async (options: OptionValues, command: BaseCommand) => { ) console.timeEnd('Deploy time') } catch (error_) { - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message error(error_) } } diff --git a/src/lib/exec-fetcher.ts b/src/lib/exec-fetcher.ts index 3a1910f0877..ea703eaa157 100644 --- a/src/lib/exec-fetcher.ts +++ b/src/lib/exec-fetcher.ts @@ -148,8 +148,6 @@ export const fetchLatestVersion = async ({ destination, execName, extension, lat Please open up an issue on our CLI repository so that we can support it: ${issueLink}`) } - - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message error(error_) } } diff --git a/src/utils/command-helpers.ts b/src/utils/command-helpers.ts index af93aa2dda1..50a94f7d607 100644 --- a/src/utils/command-helpers.ts +++ b/src/utils/command-helpers.ts @@ -300,12 +300,12 @@ export const noOp = () => { // no-op } -interface HTTPError extends Error { +interface APIError extends Error { status: number message: string } -export const isAPIError = (errorObject: unknown): errorObject is HTTPError => +export const isAPIError = (errorObject: unknown): errorObject is APIError => errorObject instanceof Error && 'status' in errorObject && 'message' in errorObject export const errorHasStatus = (errorObject: unknown, statusCode: number) => diff --git a/src/utils/framework-server.ts b/src/utils/framework-server.ts index 1b7e97b77bd..43771bb8af2 100644 --- a/src/utils/framework-server.ts +++ b/src/utils/framework-server.ts @@ -70,7 +70,7 @@ export const startFrameworkServer = async function ({ stopSpinner({ error: true, spinner }) log(NETLIFYDEVERR, `Netlify Dev could not start or connect to localhost:${settings.frameworkPort}.`) log(NETLIFYDEVERR, `Please make sure your framework server is running on port ${settings.frameworkPort}`) - // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message + // error(error_) exit(1) } From 9853fb1bd2b47159166dd99f79d71a914c6978cb Mon Sep 17 00:00:00 2001 From: Dylan Spyer <115838205+dylanspyer@users.noreply.github.com> Date: Tue, 15 Oct 2024 09:45:08 -0500 Subject: [PATCH 3/8] fix: deleted unnecessary comment in command-helpers.ts --- src/utils/command-helpers.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/utils/command-helpers.ts b/src/utils/command-helpers.ts index 50a94f7d607..1fda66132c1 100644 --- a/src/utils/command-helpers.ts +++ b/src/utils/command-helpers.ts @@ -310,6 +310,3 @@ export const isAPIError = (errorObject: unknown): errorObject is APIError => export const errorHasStatus = (errorObject: unknown, statusCode: number) => isAPIError(errorObject) && errorObject.status === statusCode - -// export const isMessageError = (errorObject: unknown): errorObject is HTTPError => -// errorObject instanceof Error && 'message' in errorObject From 7220cd1192ce0bc3b544cf29b946c169697d9e2c Mon Sep 17 00:00:00 2001 From: Dylan Spyer <115838205+dylanspyer@users.noreply.github.com> Date: Tue, 15 Oct 2024 09:51:43 -0500 Subject: [PATCH 4/8] fix: added missing argument in fn call in status.ts --- src/commands/status/status.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/status/status.ts b/src/commands/status/status.ts index be5e2d37f97..569e17770bc 100644 --- a/src/commands/status/status.ts +++ b/src/commands/status/status.ts @@ -31,7 +31,7 @@ export const status = async (options: OptionValues, command: BaseCommand) => { // eslint-disable-next-line @typescript-eslint/no-extra-semi ;[accounts, user] = await Promise.all([api.listAccountsForUser(), api.getCurrentUser()]) } catch (error_) { - if (errorHasStatus(error_)) { + if (errorHasStatus(error_, 401)) { error('Your session has expired. Please try to re-authenticate by running `netlify logout` and `netlify login`.') } else { error(error_) From 09f1e6a350b67972d73031fe2c887671e4158286 Mon Sep 17 00:00:00 2001 From: Dylan Spyer <115838205+dylanspyer@users.noreply.github.com> Date: Thu, 17 Oct 2024 12:13:10 -0500 Subject: [PATCH 5/8] Update src/utils/hooks/requires-site-info.ts Co-authored-by: Daniel Lew <51924260+DanielSLew@users.noreply.github.com> --- src/utils/hooks/requires-site-info.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/hooks/requires-site-info.ts b/src/utils/hooks/requires-site-info.ts index d3d0f07d62b..6cc841f89a4 100644 --- a/src/utils/hooks/requires-site-info.ts +++ b/src/utils/hooks/requires-site-info.ts @@ -16,7 +16,7 @@ const requiresSiteInfo = async (command) => { await api.getSite({ siteId }) } catch (error_) { // unauthorized - if (errorHasStatus(error_, 404)) { + if ((error_ as APIError).status === 401) { warn(`Log in with a different account or re-link to a site you have permission for`) return error(`Not authorized to view the currently linked site (${siteId})`) } From c50073e650145eccb1840056cde8aae2ebbf4446 Mon Sep 17 00:00:00 2001 From: Dylan Spyer <115838205+dylanspyer@users.noreply.github.com> Date: Thu, 17 Oct 2024 12:13:29 -0500 Subject: [PATCH 6/8] Update src/utils/framework-server.ts Co-authored-by: Daniel Lew <51924260+DanielSLew@users.noreply.github.com> --- src/utils/framework-server.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/utils/framework-server.ts b/src/utils/framework-server.ts index 43771bb8af2..b7652a806e2 100644 --- a/src/utils/framework-server.ts +++ b/src/utils/framework-server.ts @@ -70,7 +70,6 @@ export const startFrameworkServer = async function ({ stopSpinner({ error: true, spinner }) log(NETLIFYDEVERR, `Netlify Dev could not start or connect to localhost:${settings.frameworkPort}.`) log(NETLIFYDEVERR, `Please make sure your framework server is running on port ${settings.frameworkPort}`) - // error(error_) exit(1) } From afbd5abce7e51e49560116225e75f90dcec03a55 Mon Sep 17 00:00:00 2001 From: Dylan Spyer Date: Thu, 17 Oct 2024 13:11:35 -0500 Subject: [PATCH 7/8] fix: removed type predicates in favor of type assertion for APIError Co-authored-by: Ben Hancock --- src/commands/addons/addons-config.ts | 4 ++-- src/commands/addons/addons-create.ts | 4 ++-- src/commands/addons/addons-delete.ts | 4 ++-- src/commands/deploy/deploy.ts | 8 +++----- src/commands/functions/functions-create.ts | 4 ++-- src/commands/functions/functions-invoke.ts | 4 ++-- src/commands/link/link.ts | 10 +++++----- src/commands/sites/sites-create-template.ts | 7 +++---- src/commands/sites/sites-create.ts | 6 +++--- src/commands/sites/sites-delete.ts | 8 ++++---- src/commands/status/status.ts | 4 ++-- src/utils/addons/prepare.ts | 10 +++++----- src/utils/command-helpers.ts | 8 +------- src/utils/dev.ts | 14 +++++++------- src/utils/hooks/requires-site-info.ts | 5 +++-- 15 files changed, 46 insertions(+), 54 deletions(-) diff --git a/src/commands/addons/addons-config.ts b/src/commands/addons/addons-config.ts index dd3ec8557e7..2d2698b0224 100644 --- a/src/commands/addons/addons-config.ts +++ b/src/commands/addons/addons-config.ts @@ -8,7 +8,7 @@ import { ADDON_VALIDATION, prepareAddonCommand } from '../../utils/addons/prepar import generatePrompts from '../../utils/addons/prompts.js' import { renderConfigValues } from '../../utils/addons/render.js' import { missingConfigValues, requiredConfigValues, updateConfigValues } from '../../utils/addons/validation.js' -import { chalk, error, log, isAPIError } from '../../utils/command-helpers.js' +import { chalk, error, log, APIError } from '../../utils/command-helpers.js' import { parseRawFlags } from '../../utils/parse-raw-flags.js' import BaseCommand from '../base-command.js' @@ -35,7 +35,7 @@ const update = async function ({ addonName, api, currentConfig, instanceId, newC }) log(`Add-on "${addonName}" successfully updated`) } catch (error_) { - isAPIError(error_) ? error(error_.message) : error(error_) + error((error_ as APIError).message) } } diff --git a/src/commands/addons/addons-create.ts b/src/commands/addons/addons-create.ts index 57d9925c1d9..4453f87edba 100644 --- a/src/commands/addons/addons-create.ts +++ b/src/commands/addons/addons-create.ts @@ -6,7 +6,7 @@ import { ADDON_VALIDATION, prepareAddonCommand } from '../../utils/addons/prepar import generatePrompts from '../../utils/addons/prompts.js' import { renderConfigValues, renderMissingValues } from '../../utils/addons/render.js' import { missingConfigValues, requiredConfigValues, updateConfigValues } from '../../utils/addons/validation.js' -import { chalk, error, log, isAPIError } from '../../utils/command-helpers.js' +import { chalk, error, log, APIError } from '../../utils/command-helpers.js' import { parseRawFlags } from '../../utils/parse-raw-flags.js' import BaseCommand from '../base-command.js' // @ts-expect-error TS(7031) FIXME: Binding element 'addonName' implicitly has an 'any... Remove this comment to see the full error message @@ -23,7 +23,7 @@ const createAddon = async ({ addonName, api, config, siteData, siteId }) => { log(`${response.config.message}`) } } catch (error_) { - isAPIError(error_) ? error(error_.message) : error(error_) + error((error_ as APIError).message) } } diff --git a/src/commands/addons/addons-delete.ts b/src/commands/addons/addons-delete.ts index 17693ed4e33..dee08cf0d7a 100644 --- a/src/commands/addons/addons-delete.ts +++ b/src/commands/addons/addons-delete.ts @@ -2,7 +2,7 @@ import { OptionValues } from 'commander' import inquirer from 'inquirer' import { ADDON_VALIDATION, prepareAddonCommand } from '../../utils/addons/prepare.js' -import { error, exit, log, isAPIError } from '../../utils/command-helpers.js' +import { error, exit, log, APIError } from '../../utils/command-helpers.js' import BaseCommand from '../base-command.js' export const addonsDelete = async (addonName: string, options: OptionValues, command: BaseCommand) => { @@ -31,6 +31,6 @@ export const addonsDelete = async (addonName: string, options: OptionValues, com }) log(`Addon "${addonName}" deleted`) } catch (error_) { - isAPIError(error_) ? error(error_.message) : error(error_) + error((error_ as APIError).message) } } diff --git a/src/commands/deploy/deploy.ts b/src/commands/deploy/deploy.ts index bc008f54524..bea5f29ee37 100644 --- a/src/commands/deploy/deploy.ts +++ b/src/commands/deploy/deploy.ts @@ -30,8 +30,7 @@ import { log, logJson, warn, - isAPIError, - errorHasStatus, + APIError, } from '../../utils/command-helpers.js' import { DEFAULT_DEPLOY_TIMEOUT } from '../../utils/deploy/constants.js' import { deploySite } from '../../utils/deploy/deploy-site.js' @@ -60,11 +59,10 @@ const triggerDeploy = async ({ api, options, siteData, siteId }) => { ) } } catch (error_) { - if (errorHasStatus(error_, 404)) { + if ((error_ as APIError).status === 404) { error('Site not found. Please rerun "netlify link" and make sure that your site has CI configured.') } else { - isAPIError(error_) ? error(error_.message) : error(error_) - } + error((error_ as APIError).message) } } diff --git a/src/commands/functions/functions-create.ts b/src/commands/functions/functions-create.ts index 9948387462d..40bacd10617 100644 --- a/src/commands/functions/functions-create.ts +++ b/src/commands/functions/functions-create.ts @@ -16,13 +16,13 @@ import ora from 'ora' import { fileExistsAsync } from '../../lib/fs.js' import { getAddons, getCurrentAddon, getSiteData } from '../../utils/addons/prepare.js' import { + APIError, NETLIFYDEVERR, NETLIFYDEVLOG, NETLIFYDEVWARN, chalk, error, log, - isAPIError, } from '../../utils/command-helpers.js' import { copyTemplateDir } from '../../utils/copy-template-dir/copy-template-dir.js' import { getDotEnvVariables, injectEnvVariables } from '../../utils/dev.js' @@ -590,7 +590,7 @@ const createFunctionAddon = async function ({ addonName, addons, api, siteData, log(`Add-on "${addonName}" created for ${siteData.name}`) return true } catch (error_) { - isAPIError(error_) ? error(error_.message) : error(error_) + error((error_ as APIError).message) } } diff --git a/src/commands/functions/functions-invoke.ts b/src/commands/functions/functions-invoke.ts index 66d367da0bf..f8af526ba4d 100644 --- a/src/commands/functions/functions-invoke.ts +++ b/src/commands/functions/functions-invoke.ts @@ -6,7 +6,7 @@ import { OptionValues } from 'commander' import inquirer from 'inquirer' import fetch from 'node-fetch' -import { NETLIFYDEVWARN, chalk, error, exit, isAPIError } from '../../utils/command-helpers.js' +import { APIError, NETLIFYDEVWARN, chalk, error, exit } from '../../utils/command-helpers.js' import { BACKGROUND, CLOCKWORK_USERAGENT, getFunctions } from '../../utils/functions/index.js' import BaseCommand from '../base-command.js' @@ -234,6 +234,6 @@ export const functionsInvoke = async (nameArgument: string, options: OptionValue const data = await response.text() console.log(data) } catch (error_) { - isAPIError(error_) ? error(`Ran into an error invoking your function: ${error_.message}`) : error(error_) + error(`Ran into an error invoking your function: ${(error_ as APIError).message}`) } } diff --git a/src/commands/link/link.ts b/src/commands/link/link.ts index 940375de2d3..31be9b20fc3 100644 --- a/src/commands/link/link.ts +++ b/src/commands/link/link.ts @@ -3,7 +3,7 @@ import inquirer from 'inquirer' import isEmpty from 'lodash/isEmpty.js' import { listSites } from '../../lib/api.js' -import { chalk, error, exit, log, errorHasStatus } from '../../utils/command-helpers.js' +import { chalk, error, exit, log, APIError } from '../../utils/command-helpers.js' import getRepoData from '../../utils/get-repo-data.js' import { ensureNetlifyIgnore } from '../../utils/gitignore.js' import { track } from '../../utils/telemetry/index.js' @@ -125,7 +125,7 @@ Run ${chalk.cyanBright('git remote -v')} to see a list of your git remotes.`) options: { name: searchTerm, filter: 'all' }, }) } catch (error_) { - if (errorHasStatus(error_, 404)) { + if ((error_ as APIError).status === 404) { error(`'${searchTerm}' not found`) } else { error(error_) @@ -206,7 +206,7 @@ or run ${chalk.cyanBright('netlify sites:create')} to create a site.`) try { site = await api.getSite({ siteId }) } catch (error_) { - if (errorHasStatus(error_, 404)) { + if ((error_ as APIError).status === 404) { error(`Site ID '${siteId}' not found`) } else { error(error_) @@ -276,7 +276,7 @@ export const link = async (options: OptionValues, command: BaseCommand) => { try { siteData = await api.getSite({ site_id: options.id }) } catch (error_) { - if (errorHasStatus(error_, 404)) { + if ((error_ as APIError).status === 404) { error(new Error(`Site id ${options.id} not found`)) } else { error(error_) @@ -303,7 +303,7 @@ export const link = async (options: OptionValues, command: BaseCommand) => { }, }) } catch (error_) { - if (errorHasStatus(error_, 404)) { + if ((error_ as APIError).status === 404) { error(new Error(`${options.name} not found`)) } else { error(error_) diff --git a/src/commands/sites/sites-create-template.ts b/src/commands/sites/sites-create-template.ts index 22dd714282d..5a88d6b8c2c 100644 --- a/src/commands/sites/sites-create-template.ts +++ b/src/commands/sites/sites-create-template.ts @@ -5,7 +5,7 @@ import pick from 'lodash/pick.js' import parseGitHubUrl from 'parse-github-url' import { render } from 'prettyjson' -import { chalk, error, getTerminalLink, log, logJson, warn, isAPIError } from '../../utils/command-helpers.js' +import { chalk, error, getTerminalLink, log, logJson, warn, APIError } from '../../utils/command-helpers.js' import execa from '../../utils/execa.js' import getRepoData from '../../utils/get-repo-data.js' import { getGitHubToken } from '../../utils/init/config-github.js' @@ -163,15 +163,14 @@ export const sitesCreateTemplate = async (repository: string, options: OptionVal }) } } catch (error_) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - if (hasErrorStatus(error_, 422) || (isAPIError(error_) && error_.message === 'Duplicate repo')) { + if ((error_ as APIError).status === 422 || (error_ as APIError).message === 'Duplicate repo') { warn( `${name}.netlify.app already exists or a repository named ${name} already exists on this account. Please try a different slug.`, ) // @ts-expect-error TS(2554) FIXME: Expected 1 arguments, but got 0. await inputSiteName() } else { - isAPIError(error_) ? error(`createSiteInTeam error: ${error_.status}: ${error_.message}`) : error(error_) + error(`createSiteInTeam error: ${(error_ as APIError).status}: ${(error_ as APIError).message}`) } } } diff --git a/src/commands/sites/sites-create.ts b/src/commands/sites/sites-create.ts index f29416ad962..5fcbf5f2d16 100644 --- a/src/commands/sites/sites-create.ts +++ b/src/commands/sites/sites-create.ts @@ -3,7 +3,7 @@ import inquirer from 'inquirer' import pick from 'lodash/pick.js' import prettyjson from 'prettyjson' -import { chalk, error, isAPIError, errorHasStatus, log, logJson, warn } from '../../utils/command-helpers.js' +import { chalk, error, log, logJson, warn, APIError } from '../../utils/command-helpers.js' import getRepoData from '../../utils/get-repo-data.js' import { configureRepo } from '../../utils/init/config.js' import { track } from '../../utils/telemetry/index.js' @@ -70,12 +70,12 @@ export const sitesCreate = async (options: OptionValues, command: BaseCommand) = body, }) } catch (error_) { - if (errorHasStatus(error_, 422)) { + if ((error_ as APIError).status === 422) { warn(`${siteName}.netlify.app already exists. Please try a different slug.`) // @ts-expect-error TS(2554) FIXME: Expected 1 arguments, but got 0. await inputSiteName() } else { - isAPIError(error_) ? error(`createSiteInTeam error: ${error_.status}: ${error_.message}`) : error(error_) + error(`createSiteInTeam error: ${(error_ as APIError).status}: ${(error_ as APIError).message}`) } } } diff --git a/src/commands/sites/sites-delete.ts b/src/commands/sites/sites-delete.ts index cf56805ac5c..512fab816ff 100644 --- a/src/commands/sites/sites-delete.ts +++ b/src/commands/sites/sites-delete.ts @@ -1,7 +1,7 @@ import { OptionValues } from 'commander' import inquirer from 'inquirer' -import { chalk, error, exit, log, errorHasStatus, isAPIError } from '../../utils/command-helpers.js' +import { chalk, error, exit, log, APIError } from '../../utils/command-helpers.js' import BaseCommand from '../base-command.js' export const sitesDelete = async (siteId: string, options: OptionValues, command: BaseCommand) => { @@ -17,7 +17,7 @@ export const sitesDelete = async (siteId: string, options: OptionValues, command try { siteData = await api.getSite({ siteId }) } catch (error_) { - if (errorHasStatus(error_, 404)) { + if ((error_ as APIError).status === 404) { error(`No site with id ${siteId} found. Please verify the siteId & try again.`) } else { error(error_) @@ -75,10 +75,10 @@ export const sitesDelete = async (siteId: string, options: OptionValues, command try { await api.deleteSite({ site_id: siteId }) } catch (error_) { - if (errorHasStatus(error_, 404)) { + if ((error_ as APIError).status === 404) { error(`No site with id ${siteId} found. Please verify the siteId & try again.`) } else { - isAPIError(error_) ? error(`Delete Site error: ${error_.status}: ${error_.message}`) : error(error_) + error(`Delete Site error: ${(error_ as APIError).status}: ${(error_ as APIError).message}`) } } log(`Site "${siteId}" successfully deleted!`) diff --git a/src/commands/status/status.ts b/src/commands/status/status.ts index 569e17770bc..1a14e3592bb 100644 --- a/src/commands/status/status.ts +++ b/src/commands/status/status.ts @@ -2,7 +2,7 @@ import clean from 'clean-deep' import { OptionValues } from 'commander' import prettyjson from 'prettyjson' -import { chalk, error, exit, getToken, log, logJson, warn, errorHasStatus } from '../../utils/command-helpers.js' +import { chalk, error, exit, getToken, log, logJson, warn, APIError } from '../../utils/command-helpers.js' import BaseCommand from '../base-command.js' export const status = async (options: OptionValues, command: BaseCommand) => { @@ -31,7 +31,7 @@ export const status = async (options: OptionValues, command: BaseCommand) => { // eslint-disable-next-line @typescript-eslint/no-extra-semi ;[accounts, user] = await Promise.all([api.listAccountsForUser(), api.getCurrentUser()]) } catch (error_) { - if (errorHasStatus(error_, 401)) { + if ((error_ as APIError).status === 401) { error('Your session has expired. Please try to re-authenticate by running `netlify logout` and `netlify login`.') } else { error(error_) diff --git a/src/utils/addons/prepare.ts b/src/utils/addons/prepare.ts index ac575928b41..2b0342429df 100644 --- a/src/utils/addons/prepare.ts +++ b/src/utils/addons/prepare.ts @@ -1,4 +1,4 @@ -import { chalk, error, exit, log, warn, isAPIError } from '../command-helpers.js' +import { APIError, chalk, error, exit, log, warn } from '../command-helpers.js' export const ADDON_VALIDATION = { EXISTS: 'EXISTS', @@ -55,10 +55,10 @@ export const getAddonManifest = async ({ addonName, api }) => { try { manifest = await api.showServiceManifest({ addonName }) } catch (error_) { - if (isAPIError(error_) && error_.message.includes('Not Found')) { + if ((error_ as APIError).message.includes('Not Found')) { error(`No add-on "${addonName}" found. Please double check your add-on name and try again`) } else { - isAPIError(error_) ? error(error_.message) : error(error_) + error((error_ as APIError).message) } } return manifest @@ -70,7 +70,7 @@ export const getSiteData = async ({ api, siteId }) => { try { siteData = await api.getSite({ siteId }) } catch (error_) { - isAPIError(error_) ? error(`Failed getting list of site data: ${error_.message}`) : error(error_) + error(`Failed getting list of site data: ${(error_ as APIError).message}`) } return siteData } @@ -81,7 +81,7 @@ export const getAddons = async ({ api, siteId }) => { try { addons = await api.listServiceInstancesForSite({ siteId }) } catch (error_) { - isAPIError(error_) ? error(`Failed getting list of addons: ${error_.message}`) : error(error_) + error(`Failed getting list of addons: ${(error_ as APIError).message}`) } return addons } diff --git a/src/utils/command-helpers.ts b/src/utils/command-helpers.ts index 1fda66132c1..1ea8c39a2cf 100644 --- a/src/utils/command-helpers.ts +++ b/src/utils/command-helpers.ts @@ -300,13 +300,7 @@ export const noOp = () => { // no-op } -interface APIError extends Error { +export interface APIError extends Error { status: number message: string } - -export const isAPIError = (errorObject: unknown): errorObject is APIError => - errorObject instanceof Error && 'status' in errorObject && 'message' in errorObject - -export const errorHasStatus = (errorObject: unknown, statusCode: number) => - isAPIError(errorObject) && errorObject.status === statusCode diff --git a/src/utils/dev.ts b/src/utils/dev.ts index e9e077ec2ef..5e08de4aafd 100644 --- a/src/utils/dev.ts +++ b/src/utils/dev.ts @@ -5,7 +5,7 @@ import isEmpty from 'lodash/isEmpty.js' import { supportsBackgroundFunctions } from '../lib/account.js' -import { NETLIFYDEVLOG, chalk, error, log, warn, isAPIError } from './command-helpers.js' +import { NETLIFYDEVLOG, chalk, error, log, warn, APIError } from './command-helpers.js' import { loadDotEnvFiles } from './dot-env.js' // Possible sources of environment variables. For the purpose of printing log messages only. Order does not matter. @@ -52,9 +52,7 @@ const getAccounts = async ({ api }) => { const accounts = await api.listAccountsForUser() return accounts } catch (error_) { - isAPIError(error_) - ? error(`Failed retrieving user account: ${error_.message}. ${ERROR_CALL_TO_ACTION}`) - : error(error_) + error(`Failed retrieving user account: ${(error_ as APIError).message}. ${ERROR_CALL_TO_ACTION}`) } } @@ -64,9 +62,11 @@ const getAddons = async ({ api, site }) => { const addons = await api.listServiceInstancesForSite({ siteId: site.id }) return addons } catch (error_) { - isAPIError(error_) - ? error(`Failed retrieving addons for site ${chalk.yellow(site.id)}: ${error_.message}. ${ERROR_CALL_TO_ACTION}`) - : error(error_) + error( + `Failed retrieving addons for site ${chalk.yellow(site.id)}: ${ + (error_ as APIError).message + }. ${ERROR_CALL_TO_ACTION}`, + ) } } diff --git a/src/utils/hooks/requires-site-info.ts b/src/utils/hooks/requires-site-info.ts index 6cc841f89a4..6065dd3c831 100644 --- a/src/utils/hooks/requires-site-info.ts +++ b/src/utils/hooks/requires-site-info.ts @@ -1,4 +1,4 @@ -import { error, warn, errorHasStatus } from '../command-helpers.js' +import { error, warn, APIError } from '../command-helpers.js' /** * A preAction hook that errors out if siteInfo is an empty object @@ -21,9 +21,10 @@ const requiresSiteInfo = async (command) => { return error(`Not authorized to view the currently linked site (${siteId})`) } // missing - if (errorHasStatus(error_, 404)) { + if ((error_ as APIError).status === 404) { return error(`The site this folder is linked to can't be found`) } + return error(error_) } } From bddb6431d0057af0eaab0a0e6de35f9f830bc798 Mon Sep 17 00:00:00 2001 From: Dylan Spyer Date: Thu, 17 Oct 2024 13:19:52 -0500 Subject: [PATCH 8/8] fix: fixed syntax error in deploy.ts Co-authored-by: Ben Hancock --- src/commands/deploy/deploy.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/commands/deploy/deploy.ts b/src/commands/deploy/deploy.ts index bea5f29ee37..a4fc76877fb 100644 --- a/src/commands/deploy/deploy.ts +++ b/src/commands/deploy/deploy.ts @@ -63,6 +63,7 @@ const triggerDeploy = async ({ api, options, siteData, siteId }) => { error('Site not found. Please rerun "netlify link" and make sure that your site has CI configured.') } else { error((error_ as APIError).message) + } } }