From 5c61169d43570ffb20cfd2976ca050a9558d9c3f Mon Sep 17 00:00:00 2001 From: Sarah Etter Date: Wed, 22 Nov 2023 16:51:32 -0500 Subject: [PATCH] feat: load command's actions async to improve performance (#6180) * feat: convert addon-auth * feat: convert addons:config * feat: convert addons:create * feat: convert addons:delete * feat: convert addons:list * feat: convert api * feat: types * feat: convert build, and types * feat: convert blobs:delelte * feat: convert blobs:get * feat: convert blobs:list * feat: convert blobs:set * feat: remove import * feat: convert completion * feat: convert deploy * feat: convert dev * feat: convert env:get * feat: convert env:import * feat: convert env:list * feat: convert env:set * feat: convert env:unset * feat: convert env:clone * feat: convert functions:build * feat: convert functions:create * feat: convert functions:invoke * feat: convert functions:list * feat: convert functions: serve * feat: convert init * chore: format * feat: convert integration * feat: convert link * feat: convert lm:info * feat: convert lm:install * feat: convert lm:setup * feat: convert lm:uninstall * feat: convert login * feat: convert logout * feat: convert logs:deploy * feat: convert logs:functions * feat: convert open * feat: convert recipes * feat: convert serve * feat: convert sites:create * feat: convert sites:create-template * feat: convert sites:list * feat: convert sites:delete * feat: convert status * feat: convert switch * feat: convert unlink * feat: convert watch * test: fix test for sites:create-template * test: fix tests for logs: * test: sites:create * test: loogs * test: integration * chore: prettier * chore: up timeout for start dev server in test utils * chore: return to original value * chore: try await execa * feat: nope, not that * chore: merge conflict stuff * chore: package lock from main * chore: merge conflict stuff * test: fix test import * test: fix wrong import * chore: revert dev because dev server tests are timing out, moving to another issue * chore: missing things --- src/commands/addons/addons-auth.ts | 31 +-- src/commands/addons/addons-config.ts | 31 +-- src/commands/addons/addons-create.ts | 35 +-- src/commands/addons/addons-delete.ts | 28 +-- src/commands/addons/addons-list.ts | 28 +-- src/commands/addons/addons.ts | 91 +++++--- src/commands/api/api.ts | 32 +-- src/commands/api/index.ts | 21 +- src/commands/blobs/blobs-delete.ts | 19 +- src/commands/blobs/blobs-get.ts | 23 +- src/commands/blobs/blobs-list.ts | 28 +-- src/commands/blobs/blobs-set.ts | 32 +-- src/commands/blobs/blobs.ts | 81 ++++++- src/commands/build/build.ts | 36 +--- src/commands/build/index.ts | 23 +- src/commands/completion/completion.ts | 59 ++--- src/commands/completion/index.ts | 33 ++- src/commands/deploy/deploy.ts | 143 +------------ src/commands/deploy/index.ts | 129 ++++++++++- src/commands/dev/dev-exec.ts | 18 +- src/commands/dev/dev.ts | 29 +-- src/commands/env/env-clone.ts | 202 ++++++++---------- src/commands/env/env-get.ts | 47 +--- src/commands/env/env-import.ts | 126 ++++------- src/commands/env/env-list.ts | 50 +---- src/commands/env/env-set.ts | 153 +++++-------- src/commands/env/env-unset.ts | 111 ++++------ src/commands/env/env.ts | 179 +++++++++++++--- src/commands/functions/functions-build.ts | 25 +-- src/commands/functions/functions-create.ts | 33 +-- src/commands/functions/functions-invoke.ts | 52 +---- src/commands/functions/functions-list.ts | 32 +-- src/commands/functions/functions-serve.ts | 29 +-- src/commands/functions/functions.ts | 126 ++++++++--- src/commands/init/index.ts | 24 ++- src/commands/init/init.ts | 38 +--- src/commands/integration/deploy.ts | 30 +-- src/commands/integration/index.ts | 39 ++-- src/commands/link/index.ts | 23 +- src/commands/link/link.ts | 33 +-- src/commands/lm/lm-info.ts | 11 +- src/commands/lm/lm-install.ts | 28 +-- src/commands/lm/lm-setup.ts | 25 +-- src/commands/lm/lm-uninstall.ts | 18 +- src/commands/lm/lm.ts | 71 ++++-- src/commands/login/index.ts | 17 +- src/commands/login/login.ts | 28 +-- src/commands/logout/index.ts | 13 +- src/commands/logout/logout.ts | 21 +- src/commands/logs/build.ts | 9 +- src/commands/logs/functions.ts | 33 +-- src/commands/logs/index.ts | 37 +++- src/commands/logs/log-levels.ts | 11 + src/commands/main.ts | 2 +- src/commands/open/index.ts | 38 +++- src/commands/open/open-admin.ts | 26 +-- src/commands/open/open-site.ts | 26 +-- src/commands/open/open.ts | 34 +-- src/commands/recipes/index.ts | 26 ++- src/commands/recipes/recipes-list.ts | 15 +- src/commands/recipes/recipes.ts | 44 +--- src/commands/serve/index.ts | 52 ++++- src/commands/serve/serve.ts | 67 +----- src/commands/sites/index.ts | 1 - src/commands/sites/sites-create-template.ts | 37 +--- src/commands/sites/sites-create.ts | 48 +---- src/commands/sites/sites-delete.ts | 26 +-- src/commands/sites/sites-list.ts | 28 +-- src/commands/sites/sites.ts | 106 +++++++-- src/commands/status/index.ts | 25 ++- src/commands/status/status-hooks.ts | 24 +-- src/commands/status/status.ts | 28 +-- src/commands/switch/index.ts | 13 +- src/commands/switch/switch.ts | 21 +- src/commands/unlink/index.ts | 13 +- src/commands/unlink/unlink.ts | 19 +- src/commands/watch/index.ts | 14 +- src/commands/watch/watch.ts | 25 +-- src/lib/edge-functions/editor-helper.ts | 2 +- .../commands/integration/deploy.test.ts | 5 +- tests/integration/commands/logs/build.test.ts | 2 +- .../commands/logs/functions.test.ts | 5 +- .../integration/commands/sites/sites.test.ts | 5 +- 83 files changed, 1465 insertions(+), 1936 deletions(-) create mode 100644 src/commands/logs/log-levels.ts diff --git a/src/commands/addons/addons-auth.ts b/src/commands/addons/addons-auth.ts index 0e1e1fe32ea..7bd89e2d261 100644 --- a/src/commands/addons/addons-auth.ts +++ b/src/commands/addons/addons-auth.ts @@ -1,17 +1,11 @@ - +import { OptionValues } from 'commander' + import { ADDON_VALIDATION, prepareAddonCommand } from '../../utils/addons/prepare.js' import { exit, log } from '../../utils/command-helpers.js' import openBrowser from '../../utils/open-browser.js' +import BaseCommand from '../base-command.js' -/** - * The addons:auth command - * @param {string} addonName - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - * @returns {Promise} - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'addonName' implicitly has an 'any' type... Remove this comment to see the full error message -const addonsAuth = async (addonName, options, command) => { +export const addonsAuth = async (addonName: string, options: OptionValues, command: BaseCommand) => { const { addon } = await prepareAddonCommand({ command, addonName, @@ -32,20 +26,3 @@ const addonsAuth = async (addonName, options, command) => { await openBrowser({ url: addon.auth_url }) exit() } - -/** - * Creates the `netlify addons:auth` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createAddonsAuthCommand = (program) => - program - .command('addons:auth', { hidden: true }) - .alias('addon:auth') - .argument('', 'Add-on slug') - .description('Login to add-on provider') - // @ts-expect-error TS(7006) FIXME: Parameter 'addonName' implicitly has an 'any' type... Remove this comment to see the full error message - .action(async (addonName, options, command) => { - await addonsAuth(addonName, options, command) - }) diff --git a/src/commands/addons/addons-config.ts b/src/commands/addons/addons-config.ts index 0b48ccc8997..9d23fe36380 100644 --- a/src/commands/addons/addons-config.ts +++ b/src/commands/addons/addons-config.ts @@ -1,3 +1,4 @@ +import { OptionValues } from 'commander' import inquirer from 'inquirer' import isEmpty from 'lodash/isEmpty.js' @@ -9,6 +10,7 @@ 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 { 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 const update = async function ({ addonName, api, currentConfig, instanceId, newConfig, siteId }) { @@ -38,15 +40,7 @@ const update = async function ({ addonName, api, currentConfig, instanceId, newC } } -/** - * The addons:config command - * @param {string} addonName - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - * @returns {Promise} - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'addonName' implicitly has an 'any' type... Remove this comment to see the full error message -const addonsConfig = async (addonName, options, command) => { +export const addonsConfig = async (addonName: string, options: OptionValues, command: BaseCommand) => { const { addon, manifest, siteData } = await prepareAddonCommand({ command, addonName, @@ -164,22 +158,3 @@ const addonsConfig = async (addonName, options, command) => { }) } } - -/** - * Creates the `netlify addons:config` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createAddonsConfigCommand = (program) => - program - .command('addons:config', { hidden: true }) - .alias('addon:config') - .argument('', 'Add-on namespace') - .description('Configure add-on settings') - // allow for any flags. Handy for variadic configuration options - .allowUnknownOption(true) - // @ts-expect-error TS(7006) FIXME: Parameter 'addonName' implicitly has an 'any' type... Remove this comment to see the full error message - .action(async (addonName, options, command) => { - await addonsConfig(addonName, options, command) - }) diff --git a/src/commands/addons/addons-create.ts b/src/commands/addons/addons-create.ts index bf55807fd16..1fa244725d3 100644 --- a/src/commands/addons/addons-create.ts +++ b/src/commands/addons/addons-create.ts @@ -1,3 +1,4 @@ +import { OptionValues } from 'commander' import inquirer from 'inquirer' import isEmpty from 'lodash/isEmpty.js' @@ -7,7 +8,7 @@ import { renderConfigValues, renderMissingValues } from '../../utils/addons/rend import { missingConfigValues, requiredConfigValues, updateConfigValues } from '../../utils/addons/validation.js' import { chalk, error, log } 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 const createAddon = async ({ addonName, api, config, siteData, siteId }) => { try { @@ -27,15 +28,7 @@ const createAddon = async ({ addonName, api, config, siteData, siteId }) => { } } -/** - * The addons:create command - * @param {string} addonName - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - * @returns {Promise} - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'addonName' implicitly has an 'any' type... Remove this comment to see the full error message -const addonsCreate = async (addonName, options, command) => { +export const addonsCreate = async (addonName: string, options: OptionValues, command: BaseCommand) => { const { manifest, siteData } = await prepareAddonCommand({ command, addonName, @@ -111,25 +104,3 @@ const addonsCreate = async (addonName, options, command) => { await createAddon({ api, siteId, addonName, config: configValues, siteData }) } - -/** - * Creates the `netlify addons:create` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createAddonsCreateCommand = (program) => - program - .command('addons:create', { hidden: true }) - .alias('addon:create') - .argument('', 'Add-on namespace') - .description( - `Add an add-on extension to your site -Add-ons are a way to extend the functionality of your Netlify site`, - ) - // allow for any flags. Handy for variadic configuration options - .allowUnknownOption(true) - // @ts-expect-error TS(7006) FIXME: Parameter 'addonName' implicitly has an 'any' type... Remove this comment to see the full error message - .action(async (addonName, options, command) => { - await addonsCreate(addonName, options, command) - }) diff --git a/src/commands/addons/addons-delete.ts b/src/commands/addons/addons-delete.ts index b9a39c3ae36..32f9e46eb61 100644 --- a/src/commands/addons/addons-delete.ts +++ b/src/commands/addons/addons-delete.ts @@ -1,16 +1,11 @@ +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 BaseCommand from '../base-command.js' -/** - * The addons:delete command - * @param {string} addonName - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'addonName' implicitly has an 'any' type... Remove this comment to see the full error message -const addonsDelete = async (addonName, options, command) => { +export const addonsDelete = async (addonName: string, options: OptionValues, command: BaseCommand) => { const { addon } = await prepareAddonCommand({ command, addonName, @@ -40,20 +35,3 @@ const addonsDelete = async (addonName, options, command) => { error(error_.message) } } - -/** - * Creates the `netlify addons:delete` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createAddonsDeleteCommand = (program) => - program - .command('addons:delete', { hidden: true }) - .alias('addon:delete') - .argument('', 'Add-on namespace') - .description( - `Remove an add-on extension to your site\nAdd-ons are a way to extend the functionality of your Netlify site`, - ) - .option('-f, --force', 'delete without prompting (useful for CI)') - .action(addonsDelete) diff --git a/src/commands/addons/addons-list.ts b/src/commands/addons/addons-list.ts index 470821b24f1..b021bb14244 100644 --- a/src/commands/addons/addons-list.ts +++ b/src/commands/addons/addons-list.ts @@ -1,16 +1,11 @@ import AsciiTable from 'ascii-table' +import { OptionValues } from 'commander' import { prepareAddonCommand } from '../../utils/addons/prepare.js' import { log, logJson } from '../../utils/command-helpers.js' +import BaseCommand from '../base-command.js' -/** - * The addons:list command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - * @returns {Promise} - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -const addonsList = async (options, command) => { +export const addonsList = async (options: OptionValues, command: BaseCommand) => { // @ts-expect-error TS(2345) FIXME: Argument of type '{ command: any; }' is not assign... Remove this comment to see the full error message const { addons, siteData } = await prepareAddonCommand({ command }) // Return json response for piping commands @@ -45,20 +40,3 @@ const addonsList = async (options, command) => { // Log da addons log(table.toString()) } - -/** - * Creates the `netlify addons:list` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createAddonsListCommand = (program) => - program - .command('addons:list', { hidden: true }) - .alias('addon:list') - .description(`List currently installed add-ons for site`) - .option('--json', 'Output add-on data as JSON') - // @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. - .action(async (options, command) => { - await addonsList(options, command) - }) diff --git a/src/commands/addons/addons.ts b/src/commands/addons/addons.ts index 7fc3f5a7cc7..8795f4484f3 100644 --- a/src/commands/addons/addons.ts +++ b/src/commands/addons/addons.ts @@ -1,32 +1,71 @@ - -import { createAddonsAuthCommand } from './addons-auth.js' -import { createAddonsConfigCommand } from './addons-config.js' -import { createAddonsCreateCommand } from './addons-create.js' -import { createAddonsDeleteCommand } from './addons-delete.js' -import { createAddonsListCommand } from './addons-list.js' - -/** - * The addons command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -const addons = (options, command) => { +import { OptionValues } from 'commander' + +import BaseCommand from '../base-command.js' + +const addons = (options: OptionValues, command: BaseCommand) => { command.help() } -/** - * Creates the `netlify addons` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createAddonsCommand = (program) => { - createAddonsAuthCommand(program) - createAddonsConfigCommand(program) - createAddonsCreateCommand(program) - createAddonsDeleteCommand(program) - createAddonsListCommand(program) +export const createAddonsCommand = (program: BaseCommand) => { + program + .command('addons:auth', { hidden: true }) + .alias('addon:auth') + .argument('', 'Add-on slug') + .description('Login to add-on provider') + .action(async (addonName: string, options: OptionValues, command: BaseCommand) => { + const { addonsAuth } = await import('./addons-auth.js') + await addonsAuth(addonName, options, command) + }) + + program + .command('addons:config', { hidden: true }) + .alias('addon:config') + .argument('', 'Add-on namespace') + .description('Configure add-on settings') + // allow for any flags. Handy for variadic configuration options + .allowUnknownOption(true) + .action(async (addonName: string, options: OptionValues, command: BaseCommand) => { + const { addonsConfig } = await import('./addons-config.js') + await addonsConfig(addonName, options, command) + }) + + program + .command('addons:create', { hidden: true }) + .alias('addon:create') + .argument('', 'Add-on namespace') + .description( + `Add an add-on extension to your site +Add-ons are a way to extend the functionality of your Netlify site`, + ) + // allow for any flags. Handy for variadic configuration options + .allowUnknownOption(true) + .action(async (addonName: string, options: OptionValues, command: BaseCommand) => { + const { addonsCreate } = await import('./addons-create.js') + await addonsCreate(addonName, options, command) + }) + + program + .command('addons:delete', { hidden: true }) + .alias('addon:delete') + .argument('', 'Add-on namespace') + .description( + `Remove an add-on extension to your site\nAdd-ons are a way to extend the functionality of your Netlify site`, + ) + .option('-f, --force', 'delete without prompting (useful for CI)') + .action(async (addonName: string, options: OptionValues, command: BaseCommand) => { + const { addonsDelete } = await import('./addons-delete.js') + await addonsDelete(addonName, options, command) + }) + + program + .command('addons:list', { hidden: true }) + .alias('addon:list') + .description(`List currently installed add-ons for site`) + .option('--json', 'Output add-on data as JSON') + .action(async (options: OptionValues, command: BaseCommand) => { + const { addonsList } = await import('./addons-list.js') + await addonsList(options, command) + }) return program .command('addons', { hidden: true }) diff --git a/src/commands/api/api.ts b/src/commands/api/api.ts index fa4f5f176a5..10caa2d127c 100644 --- a/src/commands/api/api.ts +++ b/src/commands/api/api.ts @@ -1,16 +1,11 @@ import AsciiTable from 'ascii-table' +import { OptionValues } from 'commander' import { methods } from 'netlify' import { chalk, error, exit, log, logJson } from '../../utils/command-helpers.js' +import BaseCommand from '../base-command.js' -/** - * The api command - * @param {string} apiMethod - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'apiMethod' implicitly has an 'any' type... Remove this comment to see the full error message -const apiCommand = async (apiMethod, options, command) => { +export const apiCommand = async (apiMethod: string, options: OptionValues, command: BaseCommand) => { const { api } = command.netlify if (options.list) { @@ -49,24 +44,3 @@ const apiCommand = async (apiMethod, options, command) => { error(error_) } } - -/** - * Creates the `netlify api` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createApiCommand = (program) => - program - .command('api') - .argument('[apiMethod]', 'Open API method to run') - .description( - `Run any Netlify API method -For more information on available methods checkout https://open-api.netlify.com/ or run '${chalk.grey( - 'netlify api --list', - )}'`, - ) - .option('-d, --data ', 'Data to use') - .option('--list', 'List out available API methods', false) - .addExamples(['netlify api --list', `netlify api getSite --data '{ "site_id": "123456" }'`]) - .action(apiCommand) diff --git a/src/commands/api/index.ts b/src/commands/api/index.ts index e962bc856e1..2de2729d807 100644 --- a/src/commands/api/index.ts +++ b/src/commands/api/index.ts @@ -1 +1,20 @@ -export { createApiCommand } from './api.js' +import { chalk } from '../../utils/command-helpers.js' +import BaseCommand from '../base-command.js' + +export const createApiCommand = (program: BaseCommand) => + program + .command('api') + .argument('[apiMethod]', 'Open API method to run') + .description( + `Run any Netlify API method +For more information on available methods checkout https://open-api.netlify.com/ or run '${chalk.grey( + 'netlify api --list', + )}'`, + ) + .option('-d, --data ', 'Data to use') + .option('--list', 'List out available API methods', false) + .addExamples(['netlify api --list', `netlify api getSite --data '{ "site_id": "123456" }'`]) + .action(async (apiMethod, options, command) => { + const { apiCommand } = await import('./api.js') + await apiCommand(apiMethod, options, command) + }) diff --git a/src/commands/blobs/blobs-delete.ts b/src/commands/blobs/blobs-delete.ts index 2e916ab3ae4..e26ffa813c3 100644 --- a/src/commands/blobs/blobs-delete.ts +++ b/src/commands/blobs/blobs-delete.ts @@ -1,12 +1,11 @@ import { getStore } from '@netlify/blobs' import { chalk, error as printError } from '../../utils/command-helpers.js' -import requiresSiteInfo from '../../utils/hooks/requires-site-info.js' /** * The blobs:delete command */ -const blobsDelete = async (storeName: string, key: string, _options: Record, command: any) => { +export const blobsDelete = async (storeName: string, key: string, _options: Record, command: any) => { const { api, siteInfo } = command.netlify const store = getStore({ apiURL: `${api.scheme}://${api.host}`, @@ -21,19 +20,3 @@ const blobsDelete = async (storeName: string, key: string, _options: Record - program - .command('blobs:delete') - .description(`(Beta) Deletes an object with a given key, if it exists, from a Netlify Blobs store`) - .argument('', 'Name of the store') - .argument('', 'Object key') - .alias('blob:delete') - .hook('preAction', requiresSiteInfo) - .action(blobsDelete) diff --git a/src/commands/blobs/blobs-get.ts b/src/commands/blobs/blobs-get.ts index 1472de5978e..f81a1d5585c 100644 --- a/src/commands/blobs/blobs-get.ts +++ b/src/commands/blobs/blobs-get.ts @@ -2,15 +2,16 @@ import { promises as fs } from 'fs' import { resolve } from 'path' import { getStore } from '@netlify/blobs' +import { OptionValues } from 'commander' import { chalk, error as printError } from '../../utils/command-helpers.js' -import requiresSiteInfo from '../../utils/hooks/requires-site-info.js' +import BaseCommand from '../base-command.js' -interface Options { +interface Options extends OptionValues { output?: string } -const blobsGet = async (storeName: string, key: string, options: Options, command: any) => { +export const blobsGet = async (storeName: string, key: string, options: Options, command: BaseCommand) => { const { api, siteInfo } = command.netlify const { output } = options const store = getStore({ @@ -40,19 +41,3 @@ const blobsGet = async (storeName: string, key: string, options: Options, comman console.log(blob) } } - -/** - * Creates the `netlify blobs:get` command - */ -export const createBlobsGetCommand = (program: any) => - program - .command('blobs:get') - .description( - `(Beta) Reads an object with a given key from a Netlify Blobs store and, if it exists, prints the content to the terminal or saves it to a file`, - ) - .argument('', 'Name of the store') - .argument('', 'Object key') - .option('-o, --output ', 'Defines the filesystem path where the blob data should be persisted') - .alias('blob:get') - .hook('preAction', requiresSiteInfo) - .action(blobsGet) diff --git a/src/commands/blobs/blobs-list.ts b/src/commands/blobs/blobs-list.ts index 09cc7bd9af1..f83a9e9ab87 100644 --- a/src/commands/blobs/blobs-list.ts +++ b/src/commands/blobs/blobs-list.ts @@ -1,16 +1,17 @@ import { getStore } from '@netlify/blobs' import AsciiTable from 'ascii-table' +import { OptionValues } from 'commander' import { chalk, error as printError, log, logJson } from '../../utils/command-helpers.js' -import requiresSiteInfo from '../../utils/hooks/requires-site-info.js' +import BaseCommand from '../base-command.js' -interface Options { +interface Options extends OptionValues { directories?: boolean json?: boolean prefix?: string } -const blobsList = async (storeName: string, options: Options, command: any) => { +export const blobsList = async (storeName: string, options: Options, command: BaseCommand) => { const { api, siteInfo } = command.netlify const store = getStore({ apiURL: `${api.scheme}://${api.host}`, @@ -48,24 +49,3 @@ const blobsList = async (storeName: string, options: Options, command: any) => { return printError(`Could not list blobs from store ${chalk.yellow(storeName)}`) } } - -/** - * Creates the `netlify blobs:list` command - */ -export const createBlobsListCommand = (program: any) => - program - .command('blobs:list') - .description(`(Beta) Lists objects in a Netlify Blobs store`) - .argument('', 'Name of the store') - .option( - '-d, --directories', - `Indicates that keys with the '/' character should be treated as directories, returning a list of sub-directories at a given level rather than all the keys inside them`, - ) - .option( - '-p, --prefix ', - `A string for filtering down the entries; when specified, only the entries whose key starts with that prefix are returned`, - ) - .option('--json', `Output list contents as JSON`) - .alias('blob:list') - .hook('preAction', requiresSiteInfo) - .action(blobsList) diff --git a/src/commands/blobs/blobs-set.ts b/src/commands/blobs/blobs-set.ts index 6e75cbed3ac..017b1040a29 100644 --- a/src/commands/blobs/blobs-set.ts +++ b/src/commands/blobs/blobs-set.ts @@ -2,16 +2,23 @@ import { promises as fs } from 'fs' import { resolve } from 'path' import { getStore } from '@netlify/blobs' +import { OptionValues } from 'commander' import { chalk, error as printError, isNodeError } from '../../utils/command-helpers.js' -import requiresSiteInfo from '../../utils/hooks/requires-site-info.js' +import BaseCommand from '../base-command.js' -interface Options { +interface Options extends OptionValues { input?: string } -// eslint-disable-next-line max-params -const blobsSet = async (storeName: string, key: string, valueParts: string[], options: Options, command: any) => { + +export const blobsSet = async ( + storeName: string, + key: string, + valueParts: string[], + options: Options, + command: BaseCommand, +) => { const { api, siteInfo } = command.netlify const { input } = options const store = getStore({ @@ -57,20 +64,3 @@ const blobsSet = async (storeName: string, key: string, valueParts: string[], op return printError(`Could not set blob ${chalk.yellow(key)} in store ${chalk.yellow(storeName)}`) } } - -/** - * Creates the `netlify blobs:set` command - */ -export const createBlobsSetCommand = (program: any) => - program - .command('blobs:set') - .description( - `(Beta) Writes to a Netlify Blobs store an object with the data provided in the command or the contents of a file defined by the 'input' parameter`, - ) - .argument('', 'Name of the store') - .argument('', 'Object key') - .argument('[value...]', 'Object value') - .option('-i, --input ', 'Defines the filesystem path where the blob data should be read from') - .alias('blob:set') - .hook('preAction', requiresSiteInfo) - .action(blobsSet) diff --git a/src/commands/blobs/blobs.ts b/src/commands/blobs/blobs.ts index 0aea41311a5..2c4bdcfd76a 100644 --- a/src/commands/blobs/blobs.ts +++ b/src/commands/blobs/blobs.ts @@ -1,23 +1,84 @@ -import { createBlobsDeleteCommand } from './blobs-delete.js' -import { createBlobsGetCommand } from './blobs-get.js' -import { createBlobsListCommand } from './blobs-list.js' -import { createBlobsSetCommand } from './blobs-set.js' +import { OptionValues } from 'commander' + +import requiresSiteInfo from '../../utils/hooks/requires-site-info.js' +import BaseCommand from '../base-command.js' /** * The blobs command */ -const blobs = (_options: Record, command: any) => { +const blobs = (_options: OptionValues, command: BaseCommand) => { command.help() } /** * Creates the `netlify blobs` command */ -export const createBlobsCommand = (program: any) => { - createBlobsDeleteCommand(program) - createBlobsGetCommand(program) - createBlobsListCommand(program) - createBlobsSetCommand(program) +export const createBlobsCommand = (program: BaseCommand) => { + program + .command('blobs:delete') + .description(`(Beta) Deletes an object with a given key, if it exists, from a Netlify Blobs store`) + .argument('', 'Name of the store') + .argument('', 'Object key') + .alias('blob:delete') + .hook('preAction', requiresSiteInfo) + .action(async (storeName: string, key: string, _options: OptionValues, command: BaseCommand) => { + const { blobsDelete } = await import('./blobs-delete.js') + await blobsDelete(storeName, key, _options, command) + }) + + program + .command('blobs:get') + .description( + `(Beta) Reads an object with a given key from a Netlify Blobs store and, if it exists, prints the content to the terminal or saves it to a file`, + ) + .argument('', 'Name of the store') + .argument('', 'Object key') + .option('-o, --output ', 'Defines the filesystem path where the blob data should be persisted') + .alias('blob:get') + .hook('preAction', requiresSiteInfo) + .action(async (storeName: string, key: string, options: OptionValues, command: BaseCommand) => { + const { blobsGet } = await import('./blobs-get.js') + await blobsGet(storeName, key, options, command) + }) + + program + .command('blobs:list') + .description(`(Beta) Lists objects in a Netlify Blobs store`) + .argument('', 'Name of the store') + .option( + '-d, --directories', + `Indicates that keys with the '/' character should be treated as directories, returning a list of sub-directories at a given level rather than all the keys inside them`, + ) + .option( + '-p, --prefix ', + `A string for filtering down the entries; when specified, only the entries whose key starts with that prefix are returned`, + ) + .option('--json', `Output list contents as JSON`) + .alias('blob:list') + .hook('preAction', requiresSiteInfo) + .action(async (storeName: string, options: OptionValues, command: BaseCommand) => { + const { blobsList } = await import('./blobs-list.js') + await blobsList(storeName, options, command) + }) + + program + .command('blobs:set') + .description( + `(Beta) Writes to a Netlify Blobs store an object with the data provided in the command or the contents of a file defined by the 'input' parameter`, + ) + .argument('', 'Name of the store') + .argument('', 'Object key') + .argument('[value...]', 'Object value') + .option('-i, --input ', 'Defines the filesystem path where the blob data should be read from') + .alias('blob:set') + .hook('preAction', requiresSiteInfo) + + .action( + async (storeName: string, key: string, valueParts: string[], options: OptionValues, command: BaseCommand) => { + const { blobsSet } = await import('./blobs-set.js') + await blobsSet(storeName, key, valueParts, options, command) + }, + ) return program .command('blobs') diff --git a/src/commands/build/build.ts b/src/commands/build/build.ts index 13217f867d5..a7e839806f9 100644 --- a/src/commands/build/build.ts +++ b/src/commands/build/build.ts @@ -1,9 +1,10 @@ -import process from 'process' +import { OptionValues } from 'commander' import { getBuildOptions, runBuild } from '../../lib/build.js' import { detectFrameworkSettings } from '../../utils/build-info.js' import { error, exit, getToken } from '../../utils/command-helpers.js' -import { getEnvelopeEnv, normalizeContext } from '../../utils/env/index.js' +import { getEnvelopeEnv } from '../../utils/env/index.js' +import BaseCommand from '../base-command.js' /** * @param {import('../../lib/build.js').BuildConfig} options @@ -32,13 +33,7 @@ const injectEnv = async function (command, { api, buildOptions, context, siteInf } } -/** - * The build command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -const build = async (options, command) => { +export const build = async (options: OptionValues, command: BaseCommand) => { const { cachedConfig, siteInfo } = command.netlify command.setAnalyticsPayload({ dry: options.dry }) // Retrieve Netlify Build options @@ -52,12 +47,12 @@ const build = async (options, command) => { cachedConfig.config.build.commandOrigin = 'heuristics' } - // @ts-expect-error TS(2345) FIXME: Argument of type '{ cachedConfig: any; packagePath... Remove this comment to see the full error message const buildOptions = await getBuildOptions({ cachedConfig, packagePath: command.workspacePackage, currentDir: command.workingDir, token, + // @ts-expect-error TS(2740) options, }) @@ -72,24 +67,3 @@ const build = async (options, command) => { const { exitCode } = await runBuild(buildOptions) exit(exitCode) } - -/** - * Creates the `netlify build` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createBuildCommand = (program) => - program - .command('build') - .description('Build on your local machine') - .option( - '--context ', - 'Specify a build context or branch (contexts: "production", "deploy-preview", "branch-deploy", "dev")', - normalizeContext, - process.env.CONTEXT || 'production', - ) - .option('--dry', 'Dry run: show instructions without running them', false) - .option('-o, --offline', 'disables any features that require network access', false) - .addExamples(['netlify build']) - .action(build) diff --git a/src/commands/build/index.ts b/src/commands/build/index.ts index fc6db1fd546..f17ace6a6ff 100644 --- a/src/commands/build/index.ts +++ b/src/commands/build/index.ts @@ -1 +1,22 @@ -export { createBuildCommand } from './build.js' +import process from 'process' + +import { normalizeContext } from '../../utils/env/index.js' +import BaseCommand from '../base-command.js' + +export const createBuildCommand = (program: BaseCommand) => + program + .command('build') + .description('Build on your local machine') + .option( + '--context ', + 'Specify a build context or branch (contexts: "production", "deploy-preview", "branch-deploy", "dev")', + normalizeContext, + process.env.CONTEXT || 'production', + ) + .option('--dry', 'Dry run: show instructions without running them', false) + .option('-o, --offline', 'disables any features that require network access', false) + .addExamples(['netlify build']) + .action(async (options, command) => { + const { build } = await import('./build.js') + await build(options, command) + }) diff --git a/src/commands/completion/completion.ts b/src/commands/completion/completion.ts index 607def0d3fc..049660234e8 100644 --- a/src/commands/completion/completion.ts +++ b/src/commands/completion/completion.ts @@ -1,22 +1,24 @@ import { dirname, join } from 'path' import { fileURLToPath } from 'url' +import { OptionValues } from 'commander' // @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module 'tabt... Remove this comment to see the full error message import { install, uninstall } from 'tabtab' import { generateAutocompletion } from '../../lib/completion/index.js' +import { error } from '../../utils/command-helpers.js' +import BaseCommand from '../base-command.js' const completer = join(dirname(fileURLToPath(import.meta.url)), '../../lib/completion/script.js') -/** - * The completion:generate command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -const completionGenerate = async (options, command) => { +export const completionGenerate = async (options: OptionValues, command: BaseCommand) => { const { parent } = command + if (!parent) { + error(`There has been an error generating the completion script.`) + return + } + generateAutocompletion(parent) await install({ @@ -27,39 +29,12 @@ const completionGenerate = async (options, command) => { console.log(`Completion for ${parent.name()} successful installed!`) } -/** - * Creates the `netlify completion` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createCompletionCommand = (program) => { - program - .command('completion:install') - .alias('completion:generate') - .description('Generates completion script for your preferred shell') - .action(completionGenerate) - - program - .command('completion:uninstall', { hidden: true }) - .alias('completion:remove') - .description('Uninstalls the installed completions') - .addExamples(['netlify completion:uninstall']) - // @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. - .action(async (options, command) => { - await uninstall({ - name: command.parent.name(), - }) - }) - - return ( - program - .command('completion') - .description('Generate shell completion script\nRun this command to see instructions for your shell.') - .addExamples(['netlify completion:install']) - // @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. - .action((options, command) => { - command.help() - }) - ) +export const completionUninstall = async (options: OptionValues, command: BaseCommand) => { + if (!command.parent) { + error(`There has been an error deleting the completion script.`) + return + } + await uninstall({ + name: command.parent.name(), + }) } diff --git a/src/commands/completion/index.ts b/src/commands/completion/index.ts index 622661846ed..6fd8e949651 100644 --- a/src/commands/completion/index.ts +++ b/src/commands/completion/index.ts @@ -1 +1,32 @@ -export { createCompletionCommand } from './completion.js' +import { OptionValues } from 'commander' + +import BaseCommand from '../base-command.js' + +export const createCompletionCommand = (program: BaseCommand) => { + program + .command('completion:install') + .alias('completion:generate') + .description('Generates completion script for your preferred shell') + .action(async (options: OptionValues, command: BaseCommand) => { + const { completionGenerate } = await import('./completion.js') + await completionGenerate(options, command) + }) + + program + .command('completion:uninstall', { hidden: true }) + .alias('completion:remove') + .description('Uninstalls the installed completions') + .addExamples(['netlify completion:uninstall']) + .action(async (options: OptionValues, command: BaseCommand) => { + const { completionUninstall } = await import('./completion.js') + await completionUninstall(options, command) + }) + + return program + .command('completion') + .description('Generate shell completion script\nRun this command to see instructions for your shell.') + .addExamples(['netlify completion:install']) + .action((options: OptionValues, command: BaseCommand) => { + command.help() + }) +} diff --git a/src/commands/deploy/deploy.ts b/src/commands/deploy/deploy.ts index b63ea075fc0..a4299b5ce07 100644 --- a/src/commands/deploy/deploy.ts +++ b/src/commands/deploy/deploy.ts @@ -1,9 +1,8 @@ import { stat } from 'fs/promises' import { basename, resolve } from 'path' -import { env } from 'process' import { runCoreSteps } from '@netlify/build' -import { Option } from 'commander' +import { OptionValues } from 'commander' import inquirer from 'inquirer' import isEmpty from 'lodash/isEmpty.js' import isObject from 'lodash/isObject.js' @@ -35,8 +34,9 @@ import { deploySite } from '../../utils/deploy/deploy-site.js' import { getEnvelopeEnv } from '../../utils/env/index.js' import { getFunctionsManifestPath, getInternalFunctionsDir } from '../../utils/functions/index.js' import openBrowser from '../../utils/open-browser.js' -import { link } from '../link/index.js' -import { sitesCreate } from '../sites/index.js' +import BaseCommand from '../base-command.js' +import { link } from '../link/link.js' +import { sitesCreate } from '../sites/sites-create.js' // @ts-expect-error TS(7031) FIXME: Binding element 'api' implicitly has an 'any' type... Remove this comment to see the full error message const triggerDeploy = async ({ api, options, siteData, siteId }) => { @@ -702,13 +702,7 @@ const prepAndRunDeploy = async ({ return results } -/** - * The deploy command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -export const deploy = async (options, command) => { +export const deploy = async (options: OptionValues, command: BaseCommand) => { const { workingDir } = command const { api, site, siteInfo } = command.netlify const alias = options.alias || options.branch @@ -825,130 +819,3 @@ export const deploy = async (options, command) => { exit() } } - -/** - * Creates the `netlify deploy` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createDeployCommand = (program) => - program - .command('deploy') - .description( - `Create a new deploy from the contents of a folder -Deploys from the build settings found in the netlify.toml file, or settings from the API. - -The following environment variables can be used to override configuration file lookups and prompts: - -- \`NETLIFY_AUTH_TOKEN\` - an access token to use when authenticating commands. Keep this value private. -- \`NETLIFY_SITE_ID\` - override any linked site in the current working directory. - -Lambda functions in the function folder can be in the following configurations for deployment: - - -Built Go binaries: ------------------- - -\`\`\` -functions/ -└── nameOfGoFunction -\`\`\` - -Build binaries of your Go language functions into the functions folder as part of your build process. - - -Single file Node.js functions: ------------------------------ - -Build dependency bundled Node.js lambda functions with tools like netlify-lambda, webpack or browserify into the function folder as part of your build process. - -\`\`\` -functions/ -└── nameOfBundledNodeJSFunction.js -\`\`\` - -Unbundled Node.js functions that have dependencies outside or inside of the functions folder: ---------------------------------------------------------------------------------------------- - -You can ship unbundled Node.js functions with the CLI, utilizing top level project dependencies, or a nested package.json. -If you use nested dependencies, be sure to populate the nested node_modules as part of your build process before deploying using npm or yarn. - -\`\`\` -project/ -├── functions -│ ├── functionName/ -│ │ ├── functionName.js (Note the folder and the function name need to match) -│ │ ├── package.json -│ │ └── node_modules/ -│ └── unbundledFunction.js -├── package.json -├── netlify.toml -└── node_modules/ -\`\`\` - -Any mix of these configurations works as well. - - -Node.js function entry points ------------------------------ - -Function entry points are determined by the file name and name of the folder they are in: - -\`\`\` -functions/ -├── aFolderlessFunctionEntrypoint.js -└── functionName/ - ├── notTheEntryPoint.js - └── functionName.js -\`\`\` - -Support for package.json's main field, and intrinsic index.js entrypoints are coming soon.`, - ) - .option('-d, --dir ', 'Specify a folder to deploy') - .option('-f, --functions ', 'Specify a functions folder to deploy') - .option('-p, --prod', 'Deploy to production', false) - .addOption( - new Option( - '--prodIfUnlocked', - 'Old, prefer --prod-if-unlocked. Deploy to production if unlocked, create a draft otherwise', - ) - .default(false) - .hideHelp(true), - ) - .option('--prod-if-unlocked', 'Deploy to production if unlocked, create a draft otherwise', false) - .option( - '--alias ', - 'Specifies the alias for deployment, the string at the beginning of the deploy subdomain. Useful for creating predictable deployment URLs. Avoid setting an alias string to the same value as a deployed branch. `alias` doesn’t create a branch deploy and can’t be used in conjunction with the branch subdomain feature. Maximum 37 characters.', - ) - .option( - '-b, --branch ', - 'Serves the same functionality as --alias. Deprecated and will be removed in future versions', - ) - .option('-o, --open', 'Open site after deploy', false) - .option('-m, --message ', 'A short message to include in the deploy log') - .option('-a, --auth ', 'Netlify auth token to deploy with', env.NETLIFY_AUTH_TOKEN) - .option('-s, --site ', 'A site name or ID to deploy to', env.NETLIFY_SITE_ID) - .option('--json', 'Output deployment data as JSON') - // @ts-expect-error TS(7006) FIXME: Parameter 'value' implicitly has an 'any' type. - .option('--timeout ', 'Timeout to wait for deployment to finish', (value) => Number.parseInt(value)) - .option('--trigger', 'Trigger a new build of your site on Netlify without uploading local files') - .option('--build', 'Run build command before deploying') - .option('--context ', 'Context to use when resolving build configuration') - .option( - '--skip-functions-cache', - 'Ignore any functions created as part of a previous `build` or `deploy` commands, forcing them to be bundled again as part of the deployment', - false, - ) - .addExamples([ - 'netlify deploy', - 'netlify deploy --site my-first-site', - 'netlify deploy --prod', - 'netlify deploy --prod --open', - 'netlify deploy --prod-if-unlocked', - 'netlify deploy --message "A message with an $ENV_VAR"', - 'netlify deploy --auth $NETLIFY_AUTH_TOKEN', - 'netlify deploy --trigger', - 'netlify deploy --build --context deploy-preview', - ]) - .action(deploy) diff --git a/src/commands/deploy/index.ts b/src/commands/deploy/index.ts index f82e1624536..e5c59a4bde9 100644 --- a/src/commands/deploy/index.ts +++ b/src/commands/deploy/index.ts @@ -1 +1,128 @@ -export { createDeployCommand } from './deploy.js' +import { env } from 'process' + +import { Option, OptionValues } from 'commander' + +import BaseCommand from '../base-command.js' + +export const createDeployCommand = (program: BaseCommand) => + program + .command('deploy') + .description( + `Create a new deploy from the contents of a folder +Deploys from the build settings found in the netlify.toml file, or settings from the API. + +The following environment variables can be used to override configuration file lookups and prompts: + +- \`NETLIFY_AUTH_TOKEN\` - an access token to use when authenticating commands. Keep this value private. +- \`NETLIFY_SITE_ID\` - override any linked site in the current working directory. + +Lambda functions in the function folder can be in the following configurations for deployment: + + +Built Go binaries: +------------------ + +\`\`\` +functions/ +└── nameOfGoFunction +\`\`\` + +Build binaries of your Go language functions into the functions folder as part of your build process. + + +Single file Node.js functions: +----------------------------- + +Build dependency bundled Node.js lambda functions with tools like netlify-lambda, webpack or browserify into the function folder as part of your build process. + +\`\`\` +functions/ +└── nameOfBundledNodeJSFunction.js +\`\`\` + +Unbundled Node.js functions that have dependencies outside or inside of the functions folder: +--------------------------------------------------------------------------------------------- + +You can ship unbundled Node.js functions with the CLI, utilizing top level project dependencies, or a nested package.json. +If you use nested dependencies, be sure to populate the nested node_modules as part of your build process before deploying using npm or yarn. + +\`\`\` +project/ +├── functions +│ ├── functionName/ +│ │ ├── functionName.js (Note the folder and the function name need to match) +│ │ ├── package.json +│ │ └── node_modules/ +│ └── unbundledFunction.js +├── package.json +├── netlify.toml +└── node_modules/ +\`\`\` + +Any mix of these configurations works as well. + + +Node.js function entry points +----------------------------- + +Function entry points are determined by the file name and name of the folder they are in: + +\`\`\` +functions/ +├── aFolderlessFunctionEntrypoint.js +└── functionName/ + ├── notTheEntryPoint.js + └── functionName.js +\`\`\` + +Support for package.json's main field, and intrinsic index.js entrypoints are coming soon.`, + ) + .option('-d, --dir ', 'Specify a folder to deploy') + .option('-f, --functions ', 'Specify a functions folder to deploy') + .option('-p, --prod', 'Deploy to production', false) + .addOption( + new Option( + '--prodIfUnlocked', + 'Old, prefer --prod-if-unlocked. Deploy to production if unlocked, create a draft otherwise', + ) + .default(false) + .hideHelp(true), + ) + .option('--prod-if-unlocked', 'Deploy to production if unlocked, create a draft otherwise', false) + .option( + '--alias ', + 'Specifies the alias for deployment, the string at the beginning of the deploy subdomain. Useful for creating predictable deployment URLs. Avoid setting an alias string to the same value as a deployed branch. `alias` doesn’t create a branch deploy and can’t be used in conjunction with the branch subdomain feature. Maximum 37 characters.', + ) + .option( + '-b, --branch ', + 'Serves the same functionality as --alias. Deprecated and will be removed in future versions', + ) + .option('-o, --open', 'Open site after deploy', false) + .option('-m, --message ', 'A short message to include in the deploy log') + .option('-a, --auth ', 'Netlify auth token to deploy with', env.NETLIFY_AUTH_TOKEN) + .option('-s, --site ', 'A site name or ID to deploy to', env.NETLIFY_SITE_ID) + .option('--json', 'Output deployment data as JSON') + .option('--timeout ', 'Timeout to wait for deployment to finish', (value) => Number.parseInt(value)) + .option('--trigger', 'Trigger a new build of your site on Netlify without uploading local files') + .option('--build', 'Run build command before deploying') + .option('--context ', 'Context to use when resolving build configuration') + .option( + '--skip-functions-cache', + 'Ignore any functions created as part of a previous `build` or `deploy` commands, forcing them to be bundled again as part of the deployment', + false, + ) + .addExamples([ + 'netlify deploy', + 'netlify deploy --site my-first-site', + 'netlify deploy --prod', + 'netlify deploy --prod --open', + 'netlify deploy --prod-if-unlocked', + 'netlify deploy --message "A message with an $ENV_VAR"', + 'netlify deploy --auth $NETLIFY_AUTH_TOKEN', + 'netlify deploy --trigger', + 'netlify deploy --build --context deploy-preview', + ]) + .action(async (options: OptionValues, command: BaseCommand) => { + const { deploy } = await import('./deploy.js') + await deploy(options, command) + }) diff --git a/src/commands/dev/dev-exec.ts b/src/commands/dev/dev-exec.ts index 140cc657f4f..e526892f9b0 100644 --- a/src/commands/dev/dev-exec.ts +++ b/src/commands/dev/dev-exec.ts @@ -1,15 +1,11 @@ +import { OptionValues } from 'commander' import execa from 'execa' import { getDotEnvVariables, injectEnvVariables } from '../../utils/dev.js' import { getEnvelopeEnv, normalizeContext } from '../../utils/env/index.js' +import BaseCommand from '../base-command.js' -/** - * The dev:exec command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'cmd' implicitly has an 'any' type. -const devExec = async (cmd, options, command) => { +export const devExec = async (cmd: string, options: OptionValues, command: BaseCommand) => { const { api, cachedConfig, config, site, siteInfo } = command.netlify let { env } = cachedConfig @@ -25,13 +21,7 @@ const devExec = async (cmd, options, command) => { }) } -/** - * Creates the `netlify dev:exec` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createDevExecCommand = (program) => +export const createDevExecCommand = (program: BaseCommand) => program .command('dev:exec') .argument('<...cmd>', `the command that should be executed`) diff --git a/src/commands/dev/dev.ts b/src/commands/dev/dev.ts index 7d19e617e3a..016e4a7f8a9 100644 --- a/src/commands/dev/dev.ts +++ b/src/commands/dev/dev.ts @@ -1,6 +1,6 @@ import process from 'process' -import { Option } from 'commander' +import { OptionValues, Option } from 'commander' import { getBlobsContext } from '../../lib/blobs/blobs.js' import { promptEditorHelper } from '../../lib/edge-functions/editor-helper.js' @@ -26,6 +26,7 @@ import { generateInspectSettings, startProxyServer } from '../../utils/proxy-ser import { getProxyUrl } from '../../utils/proxy.js' import { runDevTimeline } from '../../utils/run-build.js' import { getGeoCountryArgParser } from '../../utils/validation.js' +import BaseCommand from '../base-command.js' import { createDevExecCommand } from './dev-exec.js' @@ -68,11 +69,7 @@ const handleLiveTunnel = async ({ api, options, settings, site, state }) => { } } -/** - * @param {string} args - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'args' implicitly has an 'any' type. -const validateShortFlagArgs = (args) => { +const validateShortFlagArgs = (args: string) => { if (args.startsWith('=')) { throw new Error( `Short flag options like -e or -E don't support the '=' sign @@ -88,13 +85,7 @@ const validateShortFlagArgs = (args) => { return args } -/** - * The dev command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -const dev = async (options, command) => { +export const dev = async (options: OptionValues, command: BaseCommand) => { log(`${NETLIFYDEV}`) const { api, cachedConfig, config, repositoryRoot, site, siteInfo, state } = command.netlify config.dev = { ...config.dev } @@ -239,13 +230,8 @@ const dev = async (options, command) => { printBanner({ url }) } -/** - * Creates the `netlify dev` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createDevCommand = (program) => { + +export const createDevCommand = (program: BaseCommand) => { createDevExecCommand(program) return ( @@ -261,7 +247,6 @@ export const createDevCommand = (program) => { 'Specify a deploy context or branch for environment variables (contexts: "production", "deploy-preview", "branch-deploy", "dev")', normalizeContext, ) - // @ts-expect-error TS(7006) FIXME: Parameter 'value' implicitly has an 'any' type. .option('-p ,--port ', 'port of netlify dev', (value) => Number.parseInt(value)) .addOption( new Option('--targetPort ', 'Old, prefer --target-port. Port of target app server') @@ -269,7 +254,6 @@ export const createDevCommand = (program) => { .hideHelp(true), ) .addOption(new Option('--no-open', 'disables the automatic opening of a browser window')) - // @ts-expect-error TS(7006) FIXME: Parameter 'value' implicitly has an 'any' type. .option('--target-port ', 'port of target app server', (value) => Number.parseInt(value)) .option('--framework ', 'framework to use. Defaults to #auto which automatically detects a framework') .option('-d ,--dir ', 'dir with static files') @@ -285,7 +269,6 @@ export const createDevCommand = (program) => { .argParser((value) => Number.parseInt(value)) .hideHelp(true), ) - // @ts-expect-error TS(7006) FIXME: Parameter 'value' implicitly has an 'any' type. .option('--functions-port ', 'port of functions server', (value) => Number.parseInt(value)) .addOption( new Option( diff --git a/src/commands/env/env-clone.ts b/src/commands/env/env-clone.ts index aacc66659fb..7c5feb6edb9 100644 --- a/src/commands/env/env-clone.ts +++ b/src/commands/env/env-clone.ts @@ -1,6 +1,8 @@ - +import { OptionValues } from 'commander' + import { chalk, error as logError, log } from '../../utils/command-helpers.js' import { translateFromEnvelopeToMongo, translateFromMongoToEnvelope } from '../../utils/env/index.js' +import BaseCommand from '../base-command.js' // @ts-expect-error TS(7006) FIXME: Parameter 'api' implicitly has an 'any' type. const safeGetSite = async (api, siteId) => { @@ -11,65 +13,76 @@ const safeGetSite = async (api, siteId) => { return { error } } } - /** - * The env:clone command - * @param {string} siteIdA Site (From) - * @param {string} siteIdB Site (To) - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command + * Copies the env from a site configured with Envelope to a site not configured with Envelope * @returns {Promise} */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -const envClone = async (options, command) => { - const { api, site } = command.netlify +// @ts-expect-error TS(7031) FIXME: Binding element 'api' implicitly has an 'any' type... Remove this comment to see the full error message +const envelopeToMongo = async ({ api, siteFrom, siteTo }) => { + const envelopeVariables = await api.getEnvVars({ accountId: siteFrom.account_slug, siteId: siteFrom.id }) + const envFrom = translateFromEnvelopeToMongo(envelopeVariables) - if (!site.id && !options.from) { - log( - 'Please include the source site Id as the `--from` option, or run `netlify link` to link this folder to a Netlify site', - ) + if (Object.keys(envFrom).length === 0) { + log(`${chalk.green(siteFrom.name)} has no environment variables, nothing to clone`) return false } - const siteId = { - from: options.from || site.id, - to: options.to, + const envTo = siteTo.build_settings.env || {} + + // Merge from site A to site B + const mergedEnv = { + ...envTo, + ...envFrom, } - const [{ data: siteFrom, error: errorFrom }, { data: siteTo, error: errorTo }] = await Promise.all([ - safeGetSite(api, siteId.from), - safeGetSite(api, siteId.to), + // Apply environment variable updates + await api.updateSite({ + siteId: siteTo.id, + body: { + build_settings: { + env: mergedEnv, + }, + }, + }) + + return true +} + +/** + * Copies the env from a site configured with Envelope to a different site configured with Envelope + * @returns {Promise} + */ +// @ts-expect-error TS(7031) FIXME: Binding element 'api' implicitly has an 'any' type... Remove this comment to see the full error message +const envelopeToEnvelope = async ({ api, siteFrom, siteTo }) => { + const [envelopeFrom, envelopeTo] = await Promise.all([ + api.getEnvVars({ accountId: siteFrom.account_slug, siteId: siteFrom.id }), + api.getEnvVars({ accountId: siteTo.account_slug, siteId: siteTo.id }), ]) - if (errorFrom) { - logError(`Can't find site with id ${chalk.bold(siteId.from)}. Please make sure the site exists.`) - return false - } + // @ts-expect-error TS(7031) FIXME: Binding element 'key' implicitly has an 'any' type... Remove this comment to see the full error message + const keysFrom = envelopeFrom.map(({ key }) => key) - if (errorTo) { - logError(`Can't find site with id ${chalk.bold(siteId.to)}. Please make sure the site exists.`) + if (keysFrom.length === 0) { + log(`${chalk.green(siteFrom.name)} has no environment variables, nothing to clone`) return false } - // determine if siteFrom and/or siteTo is on Envelope - let method - if (!siteFrom.use_envelope && !siteTo.use_envelope) { - method = mongoToMongo - } else if (!siteFrom.use_envelope && siteTo.use_envelope) { - method = mongoToEnvelope - } else if (siteFrom.use_envelope && !siteTo.use_envelope) { - method = envelopeToMongo - } else { - method = envelopeToEnvelope - } - const success = await method({ api, siteFrom, siteTo }) + const accountId = siteTo.account_slug + const siteId = siteTo.id + // @ts-expect-error TS(7031) FIXME: Binding element 'key' implicitly has an 'any' type... Remove this comment to see the full error message + const envVarsToDelete = envelopeTo.filter(({ key }) => keysFrom.includes(key)) + // delete marked env vars in parallel + // @ts-expect-error TS(7031) FIXME: Binding element 'key' implicitly has an 'any' type... Remove this comment to see the full error message + await Promise.all(envVarsToDelete.map(({ key }) => api.deleteEnvVar({ accountId, siteId, key }))) - if (!success) { - return false + // hit create endpoint + try { + await api.createEnvVars({ accountId, siteId, body: envelopeFrom }) + } catch (error) { + // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. + throw error.json ? error.json.msg : error } - log(`Successfully cloned environment variables from ${chalk.green(siteFrom.name)} to ${chalk.green(siteTo.name)}`) - return true } @@ -149,91 +162,54 @@ const mongoToEnvelope = async ({ api, siteFrom, siteTo }) => { return true } -/** - * Copies the env from a site configured with Envelope to a site not configured with Envelope - * @returns {Promise} - */ -// @ts-expect-error TS(7031) FIXME: Binding element 'api' implicitly has an 'any' type... Remove this comment to see the full error message -const envelopeToMongo = async ({ api, siteFrom, siteTo }) => { - const envelopeVariables = await api.getEnvVars({ accountId: siteFrom.account_slug, siteId: siteFrom.id }) - const envFrom = translateFromEnvelopeToMongo(envelopeVariables) +export const envClone = async (options: OptionValues, command: BaseCommand) => { + const { api, site } = command.netlify - if (Object.keys(envFrom).length === 0) { - log(`${chalk.green(siteFrom.name)} has no environment variables, nothing to clone`) + if (!site.id && !options.from) { + log( + 'Please include the source site Id as the `--from` option, or run `netlify link` to link this folder to a Netlify site', + ) return false } - const envTo = siteTo.build_settings.env || {} - - // Merge from site A to site B - const mergedEnv = { - ...envTo, - ...envFrom, + const siteId = { + from: options.from || site.id, + to: options.to, } - // Apply environment variable updates - await api.updateSite({ - siteId: siteTo.id, - body: { - build_settings: { - env: mergedEnv, - }, - }, - }) - - return true -} - -/** - * Copies the env from a site configured with Envelope to a different site configured with Envelope - * @returns {Promise} - */ -// @ts-expect-error TS(7031) FIXME: Binding element 'api' implicitly has an 'any' type... Remove this comment to see the full error message -const envelopeToEnvelope = async ({ api, siteFrom, siteTo }) => { - const [envelopeFrom, envelopeTo] = await Promise.all([ - api.getEnvVars({ accountId: siteFrom.account_slug, siteId: siteFrom.id }), - api.getEnvVars({ accountId: siteTo.account_slug, siteId: siteTo.id }), + const [{ data: siteFrom, error: errorFrom }, { data: siteTo, error: errorTo }] = await Promise.all([ + safeGetSite(api, siteId.from), + safeGetSite(api, siteId.to), ]) - // @ts-expect-error TS(7031) FIXME: Binding element 'key' implicitly has an 'any' type... Remove this comment to see the full error message - const keysFrom = envelopeFrom.map(({ key }) => key) + if (errorFrom) { + logError(`Can't find site with id ${chalk.bold(siteId.from)}. Please make sure the site exists.`) + return false + } - if (keysFrom.length === 0) { - log(`${chalk.green(siteFrom.name)} has no environment variables, nothing to clone`) + if (errorTo) { + logError(`Can't find site with id ${chalk.bold(siteId.to)}. Please make sure the site exists.`) return false } - const accountId = siteTo.account_slug - const siteId = siteTo.id - // @ts-expect-error TS(7031) FIXME: Binding element 'key' implicitly has an 'any' type... Remove this comment to see the full error message - const envVarsToDelete = envelopeTo.filter(({ key }) => keysFrom.includes(key)) - // delete marked env vars in parallel - // @ts-expect-error TS(7031) FIXME: Binding element 'key' implicitly has an 'any' type... Remove this comment to see the full error message - await Promise.all(envVarsToDelete.map(({ key }) => api.deleteEnvVar({ accountId, siteId, key }))) + // determine if siteFrom and/or siteTo is on Envelope + let method + if (!siteFrom.use_envelope && !siteTo.use_envelope) { + method = mongoToMongo + } else if (!siteFrom.use_envelope && siteTo.use_envelope) { + method = mongoToEnvelope + } else if (siteFrom.use_envelope && !siteTo.use_envelope) { + method = envelopeToMongo + } else { + method = envelopeToEnvelope + } + const success = await method({ api, siteFrom, siteTo }) - // hit create endpoint - try { - await api.createEnvVars({ accountId, siteId, body: envelopeFrom }) - } catch (error) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - throw error.json ? error.json.msg : error + if (!success) { + return false } + log(`Successfully cloned environment variables from ${chalk.green(siteFrom.name)} to ${chalk.green(siteTo.name)}`) + return true } - -/** - * Creates the `netlify env:clone` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createEnvCloneCommand = (program) => - program - .command('env:clone') - .alias('env:migrate') - .option('-f, --from ', 'Site ID (From)') - .requiredOption('-t, --to ', 'Site ID (To)') - .description(`Clone environment variables from one site to another`) - .addExamples(['netlify env:clone --to ', 'netlify env:clone --to --from ']) - .action(envClone) diff --git a/src/commands/env/env-get.ts b/src/commands/env/env-get.ts index b1a38622ba4..a144fe460f4 100644 --- a/src/commands/env/env-get.ts +++ b/src/commands/env/env-get.ts @@ -1,16 +1,10 @@ -import { Option } from 'commander' +import { OptionValues } from 'commander' import { chalk, error, log, logJson } from '../../utils/command-helpers.js' -import { AVAILABLE_CONTEXTS, getEnvelopeEnv, normalizeContext } from '../../utils/env/index.js' +import { AVAILABLE_CONTEXTS, getEnvelopeEnv } from '../../utils/env/index.js' +import BaseCommand from '../base-command.js' -/** - * The env:get command - * @param {string} name Environment variable name - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'name' implicitly has an 'any' type. -const envGet = async (name, options, command) => { +export const envGet = async (name: string, options: OptionValues, command: BaseCommand) => { const { context, scope } = options const { api, cachedConfig, site } = command.netlify const siteId = site.id @@ -52,36 +46,3 @@ const envGet = async (name, options, command) => { log(value) } - -/** - * Creates the `netlify env:get` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createEnvGetCommand = (program) => - program - .command('env:get') - .argument('', 'Environment variable name') - .option( - '-c, --context ', - 'Specify a deploy context or branch (contexts: "production", "deploy-preview", "branch-deploy", "dev")', - normalizeContext, - 'dev', - ) - .addOption( - new Option('-s, --scope ', 'Specify a scope') - .choices(['builds', 'functions', 'post-processing', 'runtime', 'any']) - .default('any'), - ) - .addExamples([ - 'netlify env:get MY_VAR # get value for MY_VAR in dev context', - 'netlify env:get MY_VAR --context production', - 'netlify env:get MY_VAR --context branch:staging', - 'netlify env:get MY_VAR --scope functions', - ]) - .description('Get resolved value of specified environment variable (includes netlify.toml)') - // @ts-expect-error TS(7006) FIXME: Parameter 'name' implicitly has an 'any' type. - .action(async (name, options, command) => { - await envGet(name, options, command) - }) diff --git a/src/commands/env/env-import.ts b/src/commands/env/env-import.ts index ea802a9118d..ea1b4895479 100644 --- a/src/commands/env/env-import.ts +++ b/src/commands/env/env-import.ts @@ -1,63 +1,12 @@ import { readFile } from 'fs/promises' import AsciiTable from 'ascii-table' -import { Option } from 'commander' +import { OptionValues } from 'commander' import dotenv from 'dotenv' import { exit, log, logJson } from '../../utils/command-helpers.js' import { translateFromEnvelopeToMongo, translateFromMongoToEnvelope } from '../../utils/env/index.js' - -/** - * The env:import command - * @param {string} fileName .env file to import - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - * @returns {Promise} - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'fileName' implicitly has an 'any' type. -const envImport = async (fileName, options, command) => { - const { api, cachedConfig, site } = command.netlify - const siteId = site.id - - if (!siteId) { - log('No site id found, please run inside a site folder or `netlify link`') - return false - } - - let importedEnv = {} - try { - const envFileContents = await readFile(fileName, 'utf-8') - importedEnv = dotenv.parse(envFileContents) - } catch (error) { - // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. - log(error.message) - exit(1) - } - - if (Object.keys(importedEnv).length === 0) { - log(`No environment variables found in file ${fileName} to import`) - return false - } - - const { siteInfo } = cachedConfig - - const importIntoService = siteInfo.use_envelope ? importIntoEnvelope : importIntoMongo - const finalEnv = await importIntoService({ api, importedEnv, options, siteInfo }) - - // Return new environment variables of site if using json flag - if (options.json) { - logJson(finalEnv) - return false - } - - // List newly imported environment variables in a table - log(`site: ${siteInfo.name}`) - const table = new AsciiTable(`Imported environment variables`) - - table.setHeading('Key', 'Value') - table.addRowMatrix(Object.entries(importedEnv)) - log(table.toString()) -} +import BaseCommand from '../base-command.js' /** * Updates the imported env in the site record @@ -126,31 +75,46 @@ const importIntoEnvelope = async ({ api, importedEnv, options, siteInfo }) => { } } -/** - * Creates the `netlify env:import` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createEnvImportCommand = (program) => - program - .command('env:import') - .argument('', '.env file to import') - .addOption( - new Option( - '-r --replaceExisting', - 'Old, prefer --replace-existing. Replace all existing variables instead of merging them with the current ones', - ) - .default(false) - .hideHelp(true), - ) - .option( - '-r, --replace-existing', - 'Replace all existing variables instead of merging them with the current ones', - false, - ) - .description('Import and set environment variables from .env file') - // @ts-expect-error TS(7006) FIXME: Parameter 'fileName' implicitly has an 'any' type. - .action(async (fileName, options, command) => { - await envImport(fileName, options, command) - }) +export const envImport = async (fileName: string, options: OptionValues, command: BaseCommand) => { + const { api, cachedConfig, site } = command.netlify + const siteId = site.id + + if (!siteId) { + log('No site id found, please run inside a site folder or `netlify link`') + return false + } + + let importedEnv = {} + try { + const envFileContents = await readFile(fileName, 'utf-8') + importedEnv = dotenv.parse(envFileContents) + } catch (error) { + // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'. + log(error.message) + exit(1) + } + + if (Object.keys(importedEnv).length === 0) { + log(`No environment variables found in file ${fileName} to import`) + return false + } + + const { siteInfo } = cachedConfig + + const importIntoService = siteInfo.use_envelope ? importIntoEnvelope : importIntoMongo + const finalEnv = await importIntoService({ api, importedEnv, options, siteInfo }) + + // Return new environment variables of site if using json flag + if (options.json) { + logJson(finalEnv) + return false + } + + // List newly imported environment variables in a table + log(`site: ${siteInfo.name}`) + const table = new AsciiTable(`Imported environment variables`) + + table.setHeading('Key', 'Value') + table.addRowMatrix(Object.entries(importedEnv)) + log(table.toString()) +} diff --git a/src/commands/env/env-list.ts b/src/commands/env/env-list.ts index d5d2af14cdd..08b08a23a0a 100644 --- a/src/commands/env/env-list.ts +++ b/src/commands/env/env-list.ts @@ -1,13 +1,13 @@ import ansiEscapes from 'ansi-escapes' - import AsciiTable from 'ascii-table' import { isCI } from 'ci-info' -import { Option } from 'commander' +import { OptionValues } from 'commander' import inquirer from 'inquirer' import logUpdate from 'log-update' import { chalk, error, log, logJson } from '../../utils/command-helpers.js' -import { AVAILABLE_CONTEXTS, getEnvelopeEnv, getHumanReadableScopes, normalizeContext } from '../../utils/env/index.js' +import { AVAILABLE_CONTEXTS, getEnvelopeEnv, getHumanReadableScopes } from '../../utils/env/index.js' +import BaseCommand from '../base-command.js' const MASK_LENGTH = 50 const MASK = '*'.repeat(MASK_LENGTH) @@ -34,14 +34,7 @@ const getTable = ({ environment, hideValues, scopesColumn }) => { return table.toString() } -/** - * The env:list command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - * @returns {Promise} - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -const envList = async (options, command) => { +export const envList = async (options: OptionValues, command: BaseCommand) => { const { context, scope } = options const { api, cachedConfig, site } = command.netlify const siteId = site.id @@ -126,38 +119,3 @@ const envList = async (options, command) => { log(`${chalk.cyan('?')} Show values? ${chalk.cyan('Yes')}`) } } - -/** - * Creates the `netlify env:list` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createEnvListCommand = (program) => - program - .command('env:list') - .option( - '-c, --context ', - 'Specify a deploy context or branch (contexts: "production", "deploy-preview", "branch-deploy", "dev")', - normalizeContext, - 'dev', - ) - .option('--json', 'Output environment variables as JSON') - .addOption(new Option('--plain', 'Output environment variables as plaintext').conflicts('json')) - .addOption( - new Option('-s, --scope ', 'Specify a scope') - .choices(['builds', 'functions', 'post-processing', 'runtime', 'any']) - .default('any'), - ) - .addExamples([ - 'netlify env:list # list variables with values in the dev context and with any scope', - 'netlify env:list --context production', - 'netlify env:list --context branch:staging', - 'netlify env:list --scope functions', - 'netlify env:list --plain', - ]) - .description('Lists resolved environment variables for site (includes netlify.toml)') - // @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. - .action(async (options, command) => { - await envList(options, command) - }) diff --git a/src/commands/env/env-set.ts b/src/commands/env/env-set.ts index e6a3c30debb..68e40902813 100644 --- a/src/commands/env/env-set.ts +++ b/src/commands/env/env-set.ts @@ -1,69 +1,8 @@ -import { Option } from 'commander' +import { OptionValues } from 'commander' import { chalk, error, log, logJson } from '../../utils/command-helpers.js' -import { - AVAILABLE_CONTEXTS, - AVAILABLE_SCOPES, - normalizeContext, - translateFromEnvelopeToMongo, -} from '../../utils/env/index.js' - -/** - * The env:set command - * @param {string} key Environment variable key - * @param {string} value Value to set to - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - * @returns {Promise} - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'key' implicitly has an 'any' type. -const envSet = async (key, value, options, command) => { - const { context, scope, secret } = options - - const { api, cachedConfig, site } = command.netlify - const siteId = site.id - - if (!siteId) { - log('No site id found, please run inside a site folder or `netlify link`') - return false - } - - const { siteInfo } = cachedConfig - let finalEnv - - // Get current environment variables set in the UI - if (siteInfo.use_envelope) { - finalEnv = await setInEnvelope({ api, siteInfo, key, value, context, scope, secret }) - } else if (context || scope) { - error( - `To specify a context or scope, please run ${chalk.yellow( - 'netlify open:admin', - )} to open the Netlify UI and opt in to the new environment variables experience from Site settings`, - ) - return false - } else { - finalEnv = await setInMongo({ api, siteInfo, key, value }) - } - - if (!finalEnv) { - return false - } - - // Return new environment variables of site if using json flag - if (options.json) { - logJson(finalEnv) - return false - } - - const withScope = scope ? ` scoped to ${chalk.white(scope)}` : '' - const withSecret = secret ? ` as a ${chalk.blue('secret')}` : '' - const contextType = AVAILABLE_CONTEXTS.includes(context || 'all') ? 'context' : 'branch' - log( - `Set environment variable ${chalk.yellow( - `${key}${value && !secret ? `=${value}` : ''}`, - )}${withScope}${withSecret} in the ${chalk.magenta(context || 'all')} ${contextType}`, - ) -} +import { AVAILABLE_CONTEXTS, AVAILABLE_SCOPES, translateFromEnvelopeToMongo } from '../../utils/env/index.js' +import BaseCommand from '../base-command.js' /** * Updates the env for a site record with a new key/value pair @@ -191,44 +130,50 @@ const setInEnvelope = async ({ api, context, key, scope, secret, siteInfo, value } } -/** - * Creates the `netlify env:set` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createEnvSetCommand = (program) => - program - .command('env:set') - .argument('', 'Environment variable key') - .argument('[value]', 'Value to set to', '') - .option( - '-c, --context ', - 'Specify a deploy context or branch (contexts: "production", "deploy-preview", "branch-deploy", "dev") (default: all contexts)', - // spread over an array for variadic options - // @ts-expect-error TS(7006) FIXME: Parameter 'context' implicitly has an 'any' type. - (context, previous = []) => [...previous, normalizeContext(context)], - ) - .addOption( - new Option('-s, --scope ', 'Specify a scope (default: all scopes)').choices([ - 'builds', - 'functions', - 'post-processing', - 'runtime', - ]), +export const envSet = async (key: string, value: string, options: OptionValues, command: BaseCommand) => { + const { context, scope, secret } = options + + const { api, cachedConfig, site } = command.netlify + const siteId = site.id + + if (!siteId) { + log('No site id found, please run inside a site folder or `netlify link`') + return false + } + + const { siteInfo } = cachedConfig + let finalEnv + + // Get current environment variables set in the UI + if (siteInfo.use_envelope) { + finalEnv = await setInEnvelope({ api, siteInfo, key, value, context, scope, secret }) + } else if (context || scope) { + error( + `To specify a context or scope, please run ${chalk.yellow( + 'netlify open:admin', + )} to open the Netlify UI and opt in to the new environment variables experience from Site settings`, ) - .option('--secret', 'Indicate whether the environment variable value can be read again.') - .description('Set value of environment variable') - .addExamples([ - 'netlify env:set VAR_NAME value # set in all contexts and scopes', - 'netlify env:set VAR_NAME value --context production', - 'netlify env:set VAR_NAME value --context production deploy-preview', - 'netlify env:set VAR_NAME value --context production --secret', - 'netlify env:set VAR_NAME value --scope builds', - 'netlify env:set VAR_NAME value --scope builds functions', - 'netlify env:set VAR_NAME --secret # convert existing variable to secret', - ]) - // @ts-expect-error TS(7006) FIXME: Parameter 'key' implicitly has an 'any' type. - .action(async (key, value, options, command) => { - await envSet(key, value, options, command) - }) + return false + } else { + finalEnv = await setInMongo({ api, siteInfo, key, value }) + } + + if (!finalEnv) { + return false + } + + // Return new environment variables of site if using json flag + if (options.json) { + logJson(finalEnv) + return false + } + + const withScope = scope ? ` scoped to ${chalk.white(scope)}` : '' + const withSecret = secret ? ` as a ${chalk.blue('secret')}` : '' + const contextType = AVAILABLE_CONTEXTS.includes(context || 'all') ? 'context' : 'branch' + log( + `Set environment variable ${chalk.yellow( + `${key}${value && !secret ? `=${value}` : ''}`, + )}${withScope}${withSecret} in the ${chalk.magenta(context || 'all')} ${contextType}`, + ) +} diff --git a/src/commands/env/env-unset.ts b/src/commands/env/env-unset.ts index 274bb3a72db..8f80043ef0e 100644 --- a/src/commands/env/env-unset.ts +++ b/src/commands/env/env-unset.ts @@ -1,50 +1,8 @@ - -import { chalk, error, log, logJson } from '../../utils/command-helpers.js' -import { AVAILABLE_CONTEXTS, normalizeContext, translateFromEnvelopeToMongo } from '../../utils/env/index.js' - -/** - * The env:unset command - * @param {string} key Environment variable key - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - * @returns {Promise} - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'key' implicitly has an 'any' type. -const envUnset = async (key, options, command) => { - const { context } = options - const { api, cachedConfig, site } = command.netlify - const siteId = site.id +import { OptionValues } from 'commander' - if (!siteId) { - log('No site id found, please run inside a site folder or `netlify link`') - return false - } - - const { siteInfo } = cachedConfig - - let finalEnv - if (siteInfo.use_envelope) { - finalEnv = await unsetInEnvelope({ api, context, siteInfo, key }) - } else if (context) { - error( - `To specify a context, please run ${chalk.yellow( - 'netlify open:admin', - )} to open the Netlify UI and opt in to the new environment variables experience from Site settings`, - ) - return false - } else { - finalEnv = await unsetInMongo({ api, siteInfo, key }) - } - - // Return new environment variables of site if using json flag - if (options.json) { - logJson(finalEnv) - return false - } - - const contextType = AVAILABLE_CONTEXTS.includes(context || 'all') ? 'context' : 'branch' - log(`Unset environment variable ${chalk.yellow(key)} in the ${chalk.magenta(context || 'all')} ${contextType}`) -} +import { chalk, error, log, logJson } from '../../utils/command-helpers.js' +import { AVAILABLE_CONTEXTS, translateFromEnvelopeToMongo } from '../../utils/env/index.js' +import BaseCommand from '../base-command.js' /** * Deletes a given key from the env of a site record @@ -134,31 +92,38 @@ const unsetInEnvelope = async ({ api, context, key, siteInfo }) => { return env } -/** - * Creates the `netlify env:unset` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createEnvUnsetCommand = (program) => - program - .command('env:unset') - .aliases(['env:delete', 'env:remove']) - .argument('', 'Environment variable key') - .option( - '-c, --context ', - 'Specify a deploy context or branch (contexts: "production", "deploy-preview", "branch-deploy", "dev") (default: all contexts)', - // spread over an array for variadic options - // @ts-expect-error TS(7006) FIXME: Parameter 'context' implicitly has an 'any' type. - (context, previous = []) => [...previous, normalizeContext(context)], +export const envUnset = async (key: string, options: OptionValues, command: BaseCommand) => { + const { context } = options + const { api, cachedConfig, site } = command.netlify + const siteId = site.id + + if (!siteId) { + log('No site id found, please run inside a site folder or `netlify link`') + return false + } + + const { siteInfo } = cachedConfig + + let finalEnv + if (siteInfo.use_envelope) { + finalEnv = await unsetInEnvelope({ api, context, siteInfo, key }) + } else if (context) { + error( + `To specify a context, please run ${chalk.yellow( + 'netlify open:admin', + )} to open the Netlify UI and opt in to the new environment variables experience from Site settings`, ) - .addExamples([ - 'netlify env:unset VAR_NAME # unset in all contexts', - 'netlify env:unset VAR_NAME --context production', - 'netlify env:unset VAR_NAME --context production deploy-preview', - ]) - .description('Unset an environment variable which removes it from the UI') - // @ts-expect-error TS(7006) FIXME: Parameter 'key' implicitly has an 'any' type. - .action(async (key, options, command) => { - await envUnset(key, options, command) - }) + return false + } else { + finalEnv = await unsetInMongo({ api, siteInfo, key }) + } + + // Return new environment variables of site if using json flag + if (options.json) { + logJson(finalEnv) + return false + } + + const contextType = AVAILABLE_CONTEXTS.includes(context || 'all') ? 'context' : 'branch' + log(`Unset environment variable ${chalk.yellow(key)} in the ${chalk.magenta(context || 'all')} ${contextType}`) +} diff --git a/src/commands/env/env.ts b/src/commands/env/env.ts index 23ae1908a77..9016b4b3df2 100644 --- a/src/commands/env/env.ts +++ b/src/commands/env/env.ts @@ -1,34 +1,157 @@ - -import { createEnvCloneCommand } from './env-clone.js' -import { createEnvGetCommand } from './env-get.js' -import { createEnvImportCommand } from './env-import.js' -import { createEnvListCommand } from './env-list.js' -import { createEnvSetCommand } from './env-set.js' -import { createEnvUnsetCommand } from './env-unset.js' - -/** - * The env command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -const env = (options, command) => { +import { OptionValues, Option } from 'commander' + +import { normalizeContext } from '../../utils/env/index.js' +import BaseCommand from '../base-command.js' + +const env = (options: OptionValues, command: BaseCommand) => { command.help() } -/** - * Creates the `netlify env` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createEnvCommand = (program) => { - createEnvGetCommand(program) - createEnvImportCommand(program) - createEnvListCommand(program) - createEnvSetCommand(program) - createEnvUnsetCommand(program) - createEnvCloneCommand(program) +export const createEnvCommand = (program: BaseCommand) => { + program + .command('env:get') + .argument('', 'Environment variable name') + .option( + '-c, --context ', + 'Specify a deploy context or branch (contexts: "production", "deploy-preview", "branch-deploy", "dev")', + normalizeContext, + 'dev', + ) + .addOption( + new Option('-s, --scope ', 'Specify a scope') + .choices(['builds', 'functions', 'post-processing', 'runtime', 'any']) + .default('any'), + ) + .addExamples([ + 'netlify env:get MY_VAR # get value for MY_VAR in dev context', + 'netlify env:get MY_VAR --context production', + 'netlify env:get MY_VAR --context branch:staging', + 'netlify env:get MY_VAR --scope functions', + ]) + .description('Get resolved value of specified environment variable (includes netlify.toml)') + .action(async (name: string, options: OptionValues, command: BaseCommand) => { + const { envGet } = await import('./env-get.js') + await envGet(name, options, command) + }) + + program + .command('env:import') + .argument('', '.env file to import') + .addOption( + new Option( + '-r --replaceExisting', + 'Old, prefer --replace-existing. Replace all existing variables instead of merging them with the current ones', + ) + .default(false) + .hideHelp(true), + ) + .option( + '-r, --replace-existing', + 'Replace all existing variables instead of merging them with the current ones', + false, + ) + .description('Import and set environment variables from .env file') + .action(async (fileName: string, options: OptionValues, command: BaseCommand) => { + const { envImport } = await import('./env-import.js') + await envImport(fileName, options, command) + }) + + program + .command('env:list') + .option( + '-c, --context ', + 'Specify a deploy context or branch (contexts: "production", "deploy-preview", "branch-deploy", "dev")', + normalizeContext, + 'dev', + ) + .option('--json', 'Output environment variables as JSON') + .addOption(new Option('--plain', 'Output environment variables as plaintext').conflicts('json')) + .addOption( + new Option('-s, --scope ', 'Specify a scope') + .choices(['builds', 'functions', 'post-processing', 'runtime', 'any']) + .default('any'), + ) + .addExamples([ + 'netlify env:list # list variables with values in the dev context and with any scope', + 'netlify env:list --context production', + 'netlify env:list --context branch:staging', + 'netlify env:list --scope functions', + 'netlify env:list --plain', + ]) + .description('Lists resolved environment variables for site (includes netlify.toml)') + .action(async (options: OptionValues, command: BaseCommand) => { + const { envList } = await import('./env-list.js') + await envList(options, command) + }) + + program + .command('env:set') + .argument('', 'Environment variable key') + .argument('[value]', 'Value to set to', '') + .option( + '-c, --context ', + 'Specify a deploy context or branch (contexts: "production", "deploy-preview", "branch-deploy", "dev") (default: all contexts)', + // spread over an array for variadic options + // @ts-expect-error TS(7006) FIXME: Parameter 'context' implicitly has an 'any' type. + (context, previous = []) => [...previous, normalizeContext(context)], + ) + .addOption( + new Option('-s, --scope ', 'Specify a scope (default: all scopes)').choices([ + 'builds', + 'functions', + 'post-processing', + 'runtime', + ]), + ) + .option('--secret', 'Indicate whether the environment variable value can be read again.') + .description('Set value of environment variable') + .addExamples([ + 'netlify env:set VAR_NAME value # set in all contexts and scopes', + 'netlify env:set VAR_NAME value --context production', + 'netlify env:set VAR_NAME value --context production deploy-preview', + 'netlify env:set VAR_NAME value --context production --secret', + 'netlify env:set VAR_NAME value --scope builds', + 'netlify env:set VAR_NAME value --scope builds functions', + 'netlify env:set VAR_NAME --secret # convert existing variable to secret', + ]) + .action(async (key: string, value: string, options: OptionValues, command: BaseCommand) => { + const { envSet } = await import('./env-set.js') + await envSet(key, value, options, command) + }) + + program + .command('env:unset') + .aliases(['env:delete', 'env:remove']) + .argument('', 'Environment variable key') + .option( + '-c, --context ', + 'Specify a deploy context or branch (contexts: "production", "deploy-preview", "branch-deploy", "dev") (default: all contexts)', + // spread over an array for variadic options + // @ts-expect-error TS(7006) FIXME: Parameter 'context' implicitly has an 'any' type. + (context, previous = []) => [...previous, normalizeContext(context)], + ) + .addExamples([ + 'netlify env:unset VAR_NAME # unset in all contexts', + 'netlify env:unset VAR_NAME --context production', + 'netlify env:unset VAR_NAME --context production deploy-preview', + ]) + .description('Unset an environment variable which removes it from the UI') + .action(async (key: string, options: OptionValues, command: BaseCommand) => { + const { envUnset } = await import('./env-unset.js') + await envUnset(key, options, command) + }) + + program + .command('env:clone') + .alias('env:migrate') + .option('-f, --from ', 'Site ID (From)') + .requiredOption('-t, --to ', 'Site ID (To)') + .description(`Clone environment variables from one site to another`) + .addExamples(['netlify env:clone --to ', 'netlify env:clone --to --from ']) + .action(async (options: OptionValues, command: BaseCommand) => { + const { envClone } = await import('./env-clone.js') + await envClone(options, command) + }) return program .command('env') diff --git a/src/commands/functions/functions-build.ts b/src/commands/functions/functions-build.ts index 4306f51c17d..40c05d56f04 100644 --- a/src/commands/functions/functions-build.ts +++ b/src/commands/functions/functions-build.ts @@ -1,17 +1,13 @@ import { mkdir } from 'fs/promises' import { zipFunctions } from '@netlify/zip-it-and-ship-it' +import { OptionValues } from 'commander' import { NETLIFYDEVERR, NETLIFYDEVLOG, exit, log } from '../../utils/command-helpers.js' import { getFunctionsDir } from '../../utils/functions/index.js' +import BaseCommand from '../base-command.js' -/** - * The functions:build command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -const functionsBuild = async (options, command) => { +export const functionsBuild = async (options: OptionValues, command: BaseCommand) => { const { config } = command.netlify const src = options.src || config.build.functionsSource @@ -43,18 +39,3 @@ const functionsBuild = async (options, command) => { zipFunctions(src, dst, { skipGo: true }) log(`${NETLIFYDEVLOG} Functions built to `, dst) } - -/** - * Creates the `netlify functions:build` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createFunctionsBuildCommand = (program) => - program - .command('functions:build') - .alias('function:build') - .description('Build functions locally') - .option('-f, --functions ', 'Specify a functions directory to build to') - .option('-s, --src ', 'Specify the source directory for the functions') - .action(functionsBuild) diff --git a/src/commands/functions/functions-create.ts b/src/commands/functions/functions-create.ts index 36a3ed23b6f..709e34bfda7 100644 --- a/src/commands/functions/functions-create.ts +++ b/src/commands/functions/functions-create.ts @@ -7,6 +7,7 @@ import process from 'process' import { fileURLToPath, pathToFileURL } from 'url' import { promisify } from 'util' +import { OptionValues } from 'commander' // @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module 'copy... Remove this comment to see the full error message import copyTemplateDirOriginal from 'copy-template-dir' import { findUp } from 'find-up' @@ -23,6 +24,7 @@ import { getDotEnvVariables, injectEnvVariables } from '../../utils/dev.js' // @ts-expect-error TS(7034) FIXME: Variable 'execa' implicitly has type 'any' in some... Remove this comment to see the full error message import execa from '../../utils/execa.js' import { readRepoURL, validateRepoURL } from '../../utils/read-repo-url.js' +import BaseCommand from '../base-command.js' const copyTemplateDir = promisify(copyTemplateDirOriginal) @@ -748,14 +750,7 @@ const ensureFunctionPathIsOk = function (functionsDir, name) { return functionPath } -/** - * The functions:create command - * @param {string} name - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'name' implicitly has an 'any' type. -const functionsCreate = async (name, options, command) => { +export const functionsCreate = async (name: string, options: OptionValues, command: BaseCommand) => { const functionType = await selectTypeOfFunc() const functionsDir = functionType === 'edge' ? await ensureEdgeFuncDirExists(command) : await ensureFunctionDirExists(command) @@ -764,25 +759,3 @@ const functionsCreate = async (name, options, command) => { const mainFunc = options.url ? downloadFromURL : scaffoldFromTemplate await mainFunc(command, options, name, functionsDir, functionType) } - -/** - * Creates the `netlify functions:create` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createFunctionsCreateCommand = (program) => - program - .command('functions:create') - .alias('function:create') - .argument('[name]', 'name of your new function file inside your functions directory') - .description('Create a new function locally') - .option('-n, --name ', 'function name') - .option('-u, --url ', 'pull template from URL') - .option('-l, --language ', 'function language') - .addExamples([ - 'netlify functions:create', - 'netlify functions:create hello-world', - 'netlify functions:create --name hello-world', - ]) - .action(functionsCreate) diff --git a/src/commands/functions/functions-invoke.ts b/src/commands/functions/functions-invoke.ts index 1aef9633c6f..d224fb44746 100644 --- a/src/commands/functions/functions-invoke.ts +++ b/src/commands/functions/functions-invoke.ts @@ -2,12 +2,14 @@ import fs from 'fs' import { createRequire } from 'module' import path from 'path' +import { OptionValues } from 'commander' import inquirer from 'inquirer' // @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module 'node... Remove this comment to see the full error message import fetch from 'node-fetch' import { 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' const require = createRequire(import.meta.url) @@ -143,14 +145,7 @@ const getFunctionToTrigger = function (options, argumentName) { return argumentName } -/** - * The functions:invoke command - * @param {string} nameArgument - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'nameArgument' implicitly has an 'any' t... Remove this comment to see the full error message -const functionsInvoke = async (nameArgument, options, command) => { +export const functionsInvoke = async (nameArgument: string, options: OptionValues, command: BaseCommand) => { const { config, relConfigFilePath } = command.netlify const functionsDir = options.functions || (config.dev && config.dev.functions) || config.functionsDirectory @@ -244,44 +239,3 @@ const functionsInvoke = async (nameArgument, options, command) => { error(`Ran into an error invoking your function: ${error_.message}`) } } - -/** - * Creates the `netlify functions:invoke` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createFunctionsInvokeCommand = (program) => - program - .command('functions:invoke') - .alias('function:trigger') - .argument('[name]', 'function name to invoke') - .description( - `Trigger a function while in netlify dev with simulated data, good for testing function calls including Netlify's Event Triggered Functions`, - ) - .option('-n, --name ', 'function name to invoke') - .option('-f, --functions ', 'Specify a functions folder to parse, overriding netlify.toml') - .option('-q, --querystring ', 'Querystring to add to your function invocation') - .option('-p, --payload ', 'Supply POST payload in stringified json, or a path to a json file') - // TODO: refactor to not need the `undefined` state by removing the --identity flag (value `identity` will be then always defined to true or false) - .option( - '--identity', - 'simulate Netlify Identity authentication JWT. pass --identity to affirm unauthenticated request', - ) - .option( - '--no-identity', - 'simulate Netlify Identity authentication JWT. pass --no-identity to affirm unauthenticated request', - ) - // @ts-expect-error TS(7006) FIXME: Parameter 'value' implicitly has an 'any' type. - .option('--port ', 'Port where netlify dev is accessible. e.g. 8888', (value) => Number.parseInt(value)) - .addExamples([ - 'netlify functions:invoke', - 'netlify functions:invoke myfunction', - 'netlify functions:invoke --name myfunction', - 'netlify functions:invoke --name myfunction --identity', - 'netlify functions:invoke --name myfunction --no-identity', - `netlify functions:invoke myfunction --payload '{"foo": 1}'`, - 'netlify functions:invoke myfunction --querystring "foo=1', - 'netlify functions:invoke myfunction --payload "./pathTo.json"', - ]) - .action(functionsInvoke) diff --git a/src/commands/functions/functions-list.ts b/src/commands/functions/functions-list.ts index 528a6139e08..30265e0e45d 100644 --- a/src/commands/functions/functions-list.ts +++ b/src/commands/functions/functions-list.ts @@ -1,8 +1,9 @@ import AsciiTable from 'ascii-table' +import { OptionValues } from 'commander' import { exit, log, logJson } from '../../utils/command-helpers.js' import { getFunctions, getFunctionsDir } from '../../utils/functions/index.js' -import requiresSiteInfo from '../../utils/hooks/requires-site-info.js' +import BaseCommand from '../base-command.js' // @ts-expect-error TS(7006) FIXME: Parameter 'deployedFunctions' implicitly has an 'a... Remove this comment to see the full error message const normalizeFunction = function (deployedFunctions, { name, urlPath: url }) { @@ -11,13 +12,7 @@ const normalizeFunction = function (deployedFunctions, { name, urlPath: url }) { return { name, url, isDeployed } } -/** - * The functions:list command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -const functionsList = async (options, command) => { +export const functionsList = async (options: OptionValues, command: BaseCommand) => { const { config, relConfigFilePath, siteInfo } = command.netlify const deploy = siteInfo.published_deploy || {} @@ -55,24 +50,3 @@ const functionsList = async (options, command) => { }) log(table.toString()) } - -/** - * Creates the `netlify functions:list` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createFunctionsListCommand = (program) => - program - .command('functions:list') - .alias('function:list') - .description( - `List functions that exist locally -Helpful for making sure that you have formatted your functions correctly - -NOT the same as listing the functions that have been deployed. For that info you need to go to your Netlify deploy log.`, - ) - .option('-f, --functions ', 'Specify a functions directory to list') - .option('--json', 'Output function data as JSON') - .hook('preAction', requiresSiteInfo) - .action(functionsList) diff --git a/src/commands/functions/functions-serve.ts b/src/commands/functions/functions-serve.ts index 4877e979e26..25d6a6f630e 100644 --- a/src/commands/functions/functions-serve.ts +++ b/src/commands/functions/functions-serve.ts @@ -1,20 +1,17 @@ import { join } from 'path' +import { OptionValues } from 'commander' + import { startFunctionsServer } from '../../lib/functions/server.js' import { printBanner } from '../../utils/banner.js' import { acquirePort, getDotEnvVariables, getSiteInformation, injectEnvVariables } from '../../utils/dev.js' import { getFunctionsDir } from '../../utils/functions/index.js' import { getProxyUrl } from '../../utils/proxy.js' +import BaseCommand from '../base-command.js' const DEFAULT_PORT = 9999 -/** - * The functions:serve command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -const functionsServe = async (options, command) => { +export const functionsServe = async (options: OptionValues, command: BaseCommand) => { const { api, config, site, siteInfo, state } = command.netlify const functionsDir = getFunctionsDir({ options, config }, join('netlify', 'functions')) @@ -61,21 +58,3 @@ const functionsServe = async (options, command) => { const url = getProxyUrl({ port: functionsPort }) printBanner({ url }) } - -/** - * Creates the `netlify functions:serve` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createFunctionsServeCommand = (program) => - program - .command('functions:serve') - .alias('function:serve') - .description('Serve functions locally') - .option('-f, --functions ', 'Specify a functions directory to serve') - // @ts-expect-error TS(7006) FIXME: Parameter 'value' implicitly has an 'any' type. - .option('-p, --port ', 'Specify a port for the functions server', (value) => Number.parseInt(value)) - .option('-o, --offline', 'disables any features that require network access') - .addHelpText('after', 'Helpful for debugging functions.') - .action(functionsServe) diff --git a/src/commands/functions/functions.ts b/src/commands/functions/functions.ts index f79ceef8c09..e4a1698a27f 100644 --- a/src/commands/functions/functions.ts +++ b/src/commands/functions/functions.ts @@ -1,34 +1,108 @@ - +import { OptionValues } from 'commander' + import { chalk } from '../../utils/command-helpers.js' +import requiresSiteInfo from '../../utils/hooks/requires-site-info.js' +import BaseCommand from '../base-command.js' -import { createFunctionsBuildCommand } from './functions-build.js' -import { createFunctionsCreateCommand } from './functions-create.js' -import { createFunctionsInvokeCommand } from './functions-invoke.js' -import { createFunctionsListCommand } from './functions-list.js' -import { createFunctionsServeCommand } from './functions-serve.js' - -/** - * The functions command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -const functions = (options, command) => { +const functions = (options: OptionValues, command: BaseCommand) => { command.help() } -/** - * Creates the `netlify functions` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createFunctionsCommand = (program) => { - createFunctionsBuildCommand(program) - createFunctionsCreateCommand(program) - createFunctionsInvokeCommand(program) - createFunctionsListCommand(program) - createFunctionsServeCommand(program) +export const createFunctionsCommand = (program: BaseCommand) => { + program + .command('functions:build') + .alias('function:build') + .description('Build functions locally') + .option('-f, --functions ', 'Specify a functions directory to build to') + .option('-s, --src ', 'Specify the source directory for the functions') + .action(async (options: OptionValues, command: BaseCommand) => { + const { functionsBuild } = await import('./functions-build.js') + await functionsBuild(options, command) + }) + + program + .command('functions:create') + .alias('function:create') + .argument('[name]', 'name of your new function file inside your functions directory') + .description('Create a new function locally') + .option('-n, --name ', 'function name') + .option('-u, --url ', 'pull template from URL') + .option('-l, --language ', 'function language') + .addExamples([ + 'netlify functions:create', + 'netlify functions:create hello-world', + 'netlify functions:create --name hello-world', + ]) + .action(async (name: string, options: OptionValues, command: BaseCommand) => { + const { functionsCreate } = await import('./functions-create.js') + await functionsCreate(name, options, command) + }) + + program + .command('functions:invoke') + .alias('function:trigger') + .argument('[name]', 'function name to invoke') + .description( + `Trigger a function while in netlify dev with simulated data, good for testing function calls including Netlify's Event Triggered Functions`, + ) + .option('-n, --name ', 'function name to invoke') + .option('-f, --functions ', 'Specify a functions folder to parse, overriding netlify.toml') + .option('-q, --querystring ', 'Querystring to add to your function invocation') + .option('-p, --payload ', 'Supply POST payload in stringified json, or a path to a json file') + // TODO: refactor to not need the `undefined` state by removing the --identity flag (value `identity` will be then always defined to true or false) + .option( + '--identity', + 'simulate Netlify Identity authentication JWT. pass --identity to affirm unauthenticated request', + ) + .option( + '--no-identity', + 'simulate Netlify Identity authentication JWT. pass --no-identity to affirm unauthenticated request', + ) + .option('--port ', 'Port where netlify dev is accessible. e.g. 8888', (value) => Number.parseInt(value)) + .addExamples([ + 'netlify functions:invoke', + 'netlify functions:invoke myfunction', + 'netlify functions:invoke --name myfunction', + 'netlify functions:invoke --name myfunction --identity', + 'netlify functions:invoke --name myfunction --no-identity', + `netlify functions:invoke myfunction --payload '{"foo": 1}'`, + 'netlify functions:invoke myfunction --querystring "foo=1', + 'netlify functions:invoke myfunction --payload "./pathTo.json"', + ]) + .action(async (name: string, options: OptionValues, command: BaseCommand) => { + const { functionsInvoke } = await import('./functions-invoke.js') + await functionsInvoke(name, options, command) + }) + + program + .command('functions:list') + .alias('function:list') + .description( + `List functions that exist locally +Helpful for making sure that you have formatted your functions correctly + +NOT the same as listing the functions that have been deployed. For that info you need to go to your Netlify deploy log.`, + ) + .option('-f, --functions ', 'Specify a functions directory to list') + .option('--json', 'Output function data as JSON') + .hook('preAction', requiresSiteInfo) + .action(async (options: OptionValues, command: BaseCommand) => { + const { functionsList } = await import('./functions-list.js') + await functionsList(options, command) + }) + + program + .command('functions:serve') + .alias('function:serve') + .description('Serve functions locally') + .option('-f, --functions ', 'Specify a functions directory to serve') + .option('-p, --port ', 'Specify a port for the functions server', (value) => Number.parseInt(value)) + .option('-o, --offline', 'disables any features that require network access') + .addHelpText('after', 'Helpful for debugging functions.') + .action(async (options: OptionValues, command: BaseCommand) => { + const { functionsServe } = await import('./functions-serve.js') + await functionsServe(options, command) + }) const name = chalk.greenBright('`functions`') diff --git a/src/commands/init/index.ts b/src/commands/init/index.ts index 9adba007767..3c22c422548 100644 --- a/src/commands/init/index.ts +++ b/src/commands/init/index.ts @@ -1 +1,23 @@ -export { createInitCommand, init } from './init.js' +import { Option, OptionValues } from 'commander' + +import BaseCommand from '../base-command.js' + +export const createInitCommand = (program: BaseCommand) => + program + .command('init') + .description( + 'Configure continuous deployment for a new or existing site. To create a new site without continuous deployment, use `netlify sites:create`', + ) + .option('-m, --manual', 'Manually configure a git remote for CI') + .option('--force', 'Reinitialize CI hooks if the linked site is already configured to use CI') + .addOption( + new Option( + '--gitRemoteName ', + 'Old, prefer --git-remote-name. Name of Git remote to use. e.g. "origin"', + ).hideHelp(true), + ) + .option('--git-remote-name ', 'Name of Git remote to use. e.g. "origin"') + .action(async (options: OptionValues, command: BaseCommand) => { + const { init } = await import('./init.js') + await init(options, command) + }) diff --git a/src/commands/init/init.ts b/src/commands/init/init.ts index 7df894a9f15..c104649ab5a 100644 --- a/src/commands/init/init.ts +++ b/src/commands/init/init.ts @@ -1,4 +1,4 @@ -import { Option } from 'commander' +import { OptionValues } from 'commander' import inquirer from 'inquirer' import isEmpty from 'lodash/isEmpty.js' @@ -7,8 +7,9 @@ import getRepoData from '../../utils/get-repo-data.js' import { ensureNetlifyIgnore } from '../../utils/gitignore.js' import { configureRepo } from '../../utils/init/config.js' import { track } from '../../utils/telemetry/index.js' -import { link } from '../link/index.js' -import { sitesCreate } from '../sites/index.js' +import BaseCommand from '../base-command.js' +import { link } from '../link/link.js' +import { sitesCreate } from '../sites/sites-create.js' // @ts-expect-error TS(7031) FIXME: Binding element 'siteInfo' implicitly has an 'any'... Remove this comment to see the full error message const persistState = ({ siteInfo, state }) => { @@ -180,13 +181,7 @@ const logExistingRepoSetupAndExit = ({ repoUrl, siteName }) => { exit() } -/** - * The init command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -export const init = async (options, command) => { +export const init = async (options: OptionValues, command: BaseCommand) => { command.setAnalyticsPayload({ manual: options.manual, force: options.force }) const { repositoryRoot, state } = command.netlify @@ -227,26 +222,3 @@ export const init = async (options, command) => { return siteInfo } - -/** - * Creates the `netlify init` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createInitCommand = (program) => - program - .command('init') - .description( - 'Configure continuous deployment for a new or existing site. To create a new site without continuous deployment, use `netlify sites:create`', - ) - .option('-m, --manual', 'Manually configure a git remote for CI') - .option('--force', 'Reinitialize CI hooks if the linked site is already configured to use CI') - .addOption( - new Option( - '--gitRemoteName ', - 'Old, prefer --git-remote-name. Name of Git remote to use. e.g. "origin"', - ).hideHelp(true), - ) - .option('--git-remote-name ', 'Name of Git remote to use. e.g. "origin"') - .action(init) diff --git a/src/commands/integration/deploy.ts b/src/commands/integration/deploy.ts index 9c88d6244b4..d9e4da3806d 100644 --- a/src/commands/integration/deploy.ts +++ b/src/commands/integration/deploy.ts @@ -2,6 +2,7 @@ import fs from 'fs' import { resolve } from 'path' import { exit, env } from 'process' +import { OptionValues } from 'commander' import inquirer from 'inquirer' // @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module 'js-y... Remove this comment to see the full error message import yaml from 'js-yaml' @@ -12,6 +13,7 @@ import { z } from 'zod' import { getBuildOptions } from '../../lib/build.js' import { getToken, chalk, log } from '../../utils/command-helpers.js' import { getSiteInformation } from '../../utils/dev.js' +import BaseCommand from '../base-command.js' import { checkOptions } from '../build/build.js' import { deploy as siteDeploy } from '../deploy/deploy.js' @@ -390,24 +392,19 @@ export const getConfiguration = (workingDir) => { exit(1) } } -/** - * The deploy command for Netlify Integrations - * @param {import('commander').OptionValues} options - * * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -const deploy = async (options, command) => { + +export const deploy = async (options: OptionValues, command: BaseCommand) => { const { api, cachedConfig, site, siteInfo } = command.netlify const { id: siteId } = site // @ts-expect-error TS(2554) FIXME: Expected 1 arguments, but got 0. const [token] = await getToken() const workingDir = resolve(command.workingDir) - // @ts-expect-error TS(2345) FIXME: Argument of type '{ cachedConfig: any; packagePath... Remove this comment to see the full error message const buildOptions = await getBuildOptions({ cachedConfig, packagePath: command.workspacePackage, currentDir: command.workingDir, token, + // @ts-expect-error TS(2740) options, }) @@ -464,20 +461,3 @@ const deploy = async (options, command) => { )} https://ntl.fyi/create-private-integration`, ) } - -/** - * Creates the `netlify int deploy` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createDeployCommand = (program) => - program - .command('integration:deploy') - .alias('int:deploy') - .description('Register, build, and deploy a private integration on Netlify') - .option('-p, --prod', 'Deploy to production', false) - .option('-b, --build', 'Build the integration', false) - .option('-a, --auth ', 'Netlify auth token to deploy with', env.NETLIFY_AUTH_TOKEN) - .option('-s, --site ', 'A site name or ID to deploy to', env.NETLIFY_SITE_ID) - .action(deploy) diff --git a/src/commands/integration/index.ts b/src/commands/integration/index.ts index 33e07912e14..992146706eb 100644 --- a/src/commands/integration/index.ts +++ b/src/commands/integration/index.ts @@ -1,23 +1,30 @@ -import { createDeployCommand } from './deploy.js' +import { env } from 'process' -/** - * The int command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -const integrations = (options, command) => { +import { OptionValues } from 'commander' + +import BaseCommand from '../base-command.js' + +const integrations = (options: OptionValues, command: BaseCommand) => { command.help() } -/** - * Creates the `netlify integration` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createIntegrationCommand = (program) => { - createDeployCommand(program) +export const createIntegrationDeployCommand = (program: BaseCommand) => { + program + .command('integration:deploy') + .alias('int:deploy') + .description('Register, build, and deploy a private integration on Netlify') + .option('-p, --prod', 'Deploy to production', false) + .option('-b, --build', 'Build the integration', false) + .option('-a, --auth ', 'Netlify auth token to deploy with', env.NETLIFY_AUTH_TOKEN) + .option('-s, --site ', 'A site name or ID to deploy to', env.NETLIFY_SITE_ID) + .action(async (options: OptionValues, command: BaseCommand) => { + const { deploy } = await import('./deploy.js') + await deploy(options, command) + }) +} + +export const createIntegrationCommand = (program: BaseCommand) => { + createIntegrationDeployCommand(program) return program .command('integration') diff --git a/src/commands/link/index.ts b/src/commands/link/index.ts index 8a95d27a137..0f10f1e60d3 100644 --- a/src/commands/link/index.ts +++ b/src/commands/link/index.ts @@ -1 +1,22 @@ -export { createLinkCommand, link } from './link.js' +import { Option, OptionValues } from 'commander' + +import BaseCommand from '../base-command.js' + +export const createLinkCommand = (program: BaseCommand) => + program + .command('link') + .description('Link a local repo or project folder to an existing site on Netlify') + .option('--id ', 'ID of site to link to') + .option('--name ', 'Name of site to link to') + .addOption( + new Option( + '--gitRemoteName ', + 'Old, prefer --git-remote-name. Name of Git remote to use. e.g. "origin"', + ).hideHelp(true), + ) + .option('--git-remote-name ', 'Name of Git remote to use. e.g. "origin"') + .addExamples(['netlify link', 'netlify link --id 123-123-123-123', 'netlify link --name my-site-name']) + .action(async (options: OptionValues, command: BaseCommand) => { + const { link } = await import('./link.js') + await link(options, command) + }) diff --git a/src/commands/link/link.ts b/src/commands/link/link.ts index e256157de59..cce6a46a7bb 100644 --- a/src/commands/link/link.ts +++ b/src/commands/link/link.ts @@ -1,4 +1,4 @@ -import { Option } from 'commander' +import { OptionValues } from 'commander' import inquirer from 'inquirer' import isEmpty from 'lodash/isEmpty.js' @@ -7,6 +7,7 @@ import { chalk, error, exit, log } 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' +import BaseCommand from '../base-command.js' /** * @@ -249,13 +250,7 @@ or run ${chalk.cyanBright('netlify sites:create')} to create a site.`) return site } -/** - * The link command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -export const link = async (options, command) => { +export const link = async (options: OptionValues, command: BaseCommand) => { await command.authenticate() const { @@ -347,25 +342,3 @@ export const link = async (options, command) => { } return siteData } - -/** - * Creates the `netlify link` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createLinkCommand = (program) => - program - .command('link') - .description('Link a local repo or project folder to an existing site on Netlify') - .option('--id ', 'ID of site to link to') - .option('--name ', 'Name of site to link to') - .addOption( - new Option( - '--gitRemoteName ', - 'Old, prefer --git-remote-name. Name of Git remote to use. e.g. "origin"', - ).hideHelp(true), - ) - .option('--git-remote-name ', 'Name of Git remote to use. e.g. "origin"') - .addExamples(['netlify link', 'netlify link --id 123-123-123-123', 'netlify link --name my-site-name']) - .action(link) diff --git a/src/commands/lm/lm-info.ts b/src/commands/lm/lm-info.ts index 8f750e5f6b5..b8c37c31531 100644 --- a/src/commands/lm/lm-info.ts +++ b/src/commands/lm/lm-info.ts @@ -10,7 +10,7 @@ import { /** * The lm:info command */ -const lmInfo = async () => { +export const lmInfo = async () => { const steps = [ checkGitVersionStep, checkGitLFSVersionStep, @@ -30,12 +30,3 @@ const lmInfo = async () => { // an error is already reported when a task fails } } - -/** - * Creates the `netlify lm:info` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createLmInfoCommand = (program) => - program.command('lm:info', { hidden: true }).description('Show large media requirements information.').action(lmInfo) diff --git a/src/commands/lm/lm-install.ts b/src/commands/lm/lm-install.ts index 13ec6f48771..4efa3724028 100644 --- a/src/commands/lm/lm-install.ts +++ b/src/commands/lm/lm-install.ts @@ -1,33 +1,11 @@ - +import { OptionValues } from 'commander' + import { installPlatform } from '../../utils/lm/install.js' import { printBanner } from '../../utils/lm/ui.js' -/** - * The lm:install command - * @param {import('commander').OptionValues} options - */ -// @ts-expect-error TS(7031) FIXME: Binding element 'force' implicitly has an 'any' ty... Remove this comment to see the full error message -const lmInstall = async ({ force }) => { +export const lmInstall = async ({ force }: OptionValues) => { const installed = await installPlatform({ force }) if (installed) { printBanner(force) } } - -/** - * Creates the `netlify lm:install` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createLmInstallCommand = (program) => - program - .command('lm:install', { hidden: true }) - .alias('lm:init') - .description( - `Configures your computer to use Netlify Large Media -It installs the required credentials helper for Git, -and configures your Git environment with the right credentials.`, - ) - .option('-f, --force', 'Force the credentials helper installation') - .action(lmInstall) diff --git a/src/commands/lm/lm-setup.ts b/src/commands/lm/lm-setup.ts index 302f012b94d..4d4b054bf7b 100644 --- a/src/commands/lm/lm-setup.ts +++ b/src/commands/lm/lm-setup.ts @@ -1,3 +1,4 @@ +import { OptionValues } from 'commander' import { Listr } from 'listr2' import { error } from '../../utils/command-helpers.js' @@ -6,6 +7,7 @@ import execa from '../../utils/execa.js' import { installPlatform } from '../../utils/lm/install.js' import { checkHelperVersion } from '../../utils/lm/requirements.js' import { printBanner } from '../../utils/lm/ui.js' +import BaseCommand from '../base-command.js' // @ts-expect-error TS(7031) FIXME: Binding element 'force' implicitly has an 'any' ty... Remove this comment to see the full error message const installHelperIfMissing = async function ({ force }) { @@ -55,13 +57,7 @@ const configureLFSURL = async function (siteId, api) { return execa('git', ['config', '-f', '.lfsconfig', 'lfs.url', url]) } -/** - * The lm:setup command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -const lmSetup = async (options, command) => { +export const lmSetup = async (options: OptionValues, command: BaseCommand) => { await command.authenticate() const { api, site } = command.netlify @@ -96,18 +92,3 @@ const lmSetup = async (options, command) => { printBanner(options.forceInstall) } } - -/** - * Creates the `netlify lm:setup` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createLmSetupCommand = (program) => - program - .command('lm:setup', { hidden: true }) - .description('Configures your site to use Netlify Large Media') - .option('-s, --skip-install', 'Skip the credentials helper installation check') - .option('-f, --force-install', 'Force the credentials helper installation') - .addHelpText('after', 'It runs the install command if you have not installed the dependencies yet.') - .action(lmSetup) diff --git a/src/commands/lm/lm-uninstall.ts b/src/commands/lm/lm-uninstall.ts index 26552bea211..5235461e946 100644 --- a/src/commands/lm/lm-uninstall.ts +++ b/src/commands/lm/lm-uninstall.ts @@ -1,24 +1,8 @@ - import { uninstall } from '../../utils/lm/install.js' /** * The lm:uninstall command */ -const lmUninstall = async () => { +export const lmUninstall = async () => { await uninstall() } - -/** - * Creates the `netlify lm:uninstall` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createLmUninstallCommand = (program) => - program - .command('lm:uninstall', { hidden: true }) - .alias('lm:remove') - .description( - 'Uninstalls Netlify git credentials helper and cleans up any related configuration changes made by the install command.', - ) - .action(lmUninstall) diff --git a/src/commands/lm/lm.ts b/src/commands/lm/lm.ts index da1dc71cade..a11c08db22e 100644 --- a/src/commands/lm/lm.ts +++ b/src/commands/lm/lm.ts @@ -1,30 +1,55 @@ - -import { createLmInfoCommand } from './lm-info.js' -import { createLmInstallCommand } from './lm-install.js' -import { createLmSetupCommand } from './lm-setup.js' -import { createLmUninstallCommand } from './lm-uninstall.js' +import { OptionValues } from 'commander' -/** - * The lm command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -const lm = (options, command) => { +import BaseCommand from '../base-command.js' + +const lm = (options: OptionValues, command: BaseCommand) => { command.help() } -/** - * Creates the `netlify lm` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createLmCommand = (program) => { - createLmInfoCommand(program) - createLmInstallCommand(program) - createLmSetupCommand(program) - createLmUninstallCommand(program) +export const createLmCommand = (program: BaseCommand) => { + program + .command('lm:info', { hidden: true }) + .description('Show large media requirements information.') + .action(async () => { + const { lmInfo } = await import('./lm-info.js') + await lmInfo() + }) + + program + .command('lm:install', { hidden: true }) + .alias('lm:init') + .description( + `Configures your computer to use Netlify Large Media +It installs the required credentials helper for Git, +and configures your Git environment with the right credentials.`, + ) + .option('-f, --force', 'Force the credentials helper installation') + .action(async (options: OptionValues) => { + const { lmInstall } = await import('./lm-install.js') + await lmInstall(options) + }) + + program + .command('lm:setup', { hidden: true }) + .description('Configures your site to use Netlify Large Media') + .option('-s, --skip-install', 'Skip the credentials helper installation check') + .option('-f, --force-install', 'Force the credentials helper installation') + .addHelpText('after', 'It runs the install command if you have not installed the dependencies yet.') + .action(async (options: OptionValues, command: BaseCommand) => { + const { lmSetup } = await import('./lm-setup.js') + await lmSetup(options, command) + }) + + program + .command('lm:uninstall', { hidden: true }) + .alias('lm:remove') + .description( + 'Uninstalls Netlify git credentials helper and cleans up any related configuration changes made by the install command.', + ) + .action(async () => { + const { lmUninstall } = await import('./lm-uninstall.js') + await lmUninstall() + }) program .command('lm', { hidden: true }) diff --git a/src/commands/login/index.ts b/src/commands/login/index.ts index 40c4d4ad4e4..da4a12dcc7a 100644 --- a/src/commands/login/index.ts +++ b/src/commands/login/index.ts @@ -1 +1,16 @@ -export { createLoginCommand, login } from './login.js' +import { OptionValues } from 'commander' + +import BaseCommand from '../base-command.js' + +export const createLoginCommand = (program: BaseCommand) => + program + .command('login') + .description( + `Login to your Netlify account +Opens a web browser to acquire an OAuth token.`, + ) + .option('--new', 'Login to new Netlify account') + .action(async (options: OptionValues, command: BaseCommand) => { + const { login } = await import('./login.js') + await login(options, command) + }) diff --git a/src/commands/login/login.ts b/src/commands/login/login.ts index f2477e23e1a..4917460434e 100644 --- a/src/commands/login/login.ts +++ b/src/commands/login/login.ts @@ -1,5 +1,7 @@ - +import { OptionValues } from 'commander' + import { chalk, exit, getToken, log } from '../../utils/command-helpers.js' +import BaseCommand from '../base-command.js' // @ts-expect-error TS(7006) FIXME: Parameter 'location' implicitly has an 'any' type. const msg = function (location) { @@ -15,13 +17,7 @@ const msg = function (location) { } } -/** - * The login command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -export const login = async (options, command) => { +export const login = async (options: OptionValues, command: BaseCommand) => { // @ts-expect-error TS(2554) FIXME: Expected 1 arguments, but got 0. const [accessToken, location] = await getToken() @@ -41,19 +37,3 @@ export const login = async (options, command) => { await command.expensivelyAuthenticate() } - -/** - * Creates the `netlify login` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createLoginCommand = (program) => - program - .command('login') - .description( - `Login to your Netlify account -Opens a web browser to acquire an OAuth token.`, - ) - .option('--new', 'Login to new Netlify account') - .action(login) diff --git a/src/commands/logout/index.ts b/src/commands/logout/index.ts index d61ddf65abe..a15839bd8d4 100644 --- a/src/commands/logout/index.ts +++ b/src/commands/logout/index.ts @@ -1 +1,12 @@ -export { createLogoutCommand } from './logout.js' +import { OptionValues } from 'commander' + +import BaseCommand from '../base-command.js' + +export const createLogoutCommand = (program: BaseCommand) => + program + .command('logout', { hidden: true }) + .description('Logout of your Netlify account') + .action(async (options: OptionValues, command: BaseCommand) => { + const { logout } = await import('./logout.js') + await logout(options, command) + }) diff --git a/src/commands/logout/logout.ts b/src/commands/logout/logout.ts index bb09efd879b..f6abe875b6d 100644 --- a/src/commands/logout/logout.ts +++ b/src/commands/logout/logout.ts @@ -1,14 +1,10 @@ - +import { OptionValues } from 'commander' + import { exit, getToken, log } from '../../utils/command-helpers.js' import { track } from '../../utils/telemetry/index.js' +import BaseCommand from '../base-command.js' -/** - * The logout command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -const logout = async (options, command) => { +export const logout = async (options: OptionValues, command: BaseCommand) => { // @ts-expect-error TS(2554) FIXME: Expected 1 arguments, but got 0. const [accessToken, location] = await getToken() @@ -34,12 +30,3 @@ const logout = async (options, command) => { log(`Logging you out of Netlify. Come back soon!`) } - -/** - * Creates the `netlify logout` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createLogoutCommand = (program) => - program.command('logout', { hidden: true }).description('Logout of your Netlify account').action(logout) diff --git a/src/commands/logs/build.ts b/src/commands/logs/build.ts index 4775d0fd9e1..502d1b97d92 100644 --- a/src/commands/logs/build.ts +++ b/src/commands/logs/build.ts @@ -32,7 +32,7 @@ export function getName({ deploy, userId }: { deploy: any; userId: string }) { return `(${deploy.id.slice(0, 7)}) ${normalisedName}` } -const logsBuild = async (options: OptionValues, command: BaseCommand) => { +export const logsBuild = async (options: OptionValues, command: BaseCommand) => { await command.authenticate() const client = command.netlify.api const { site } = command.netlify @@ -83,10 +83,3 @@ const logsBuild = async (options: OptionValues, command: BaseCommand) => { log('---') }) } - -export const createLogsBuildCommand = (program: BaseCommand) => - program - .command('logs:deploy') - .alias('logs:build') - .description('(Beta) Stream the logs of deploys currently being built to the console') - .action(logsBuild) diff --git a/src/commands/logs/functions.ts b/src/commands/logs/functions.ts index f8fdd026737..e966114db60 100644 --- a/src/commands/logs/functions.ts +++ b/src/commands/logs/functions.ts @@ -1,21 +1,12 @@ -import { Argument, Option, OptionValues } from 'commander' +import { OptionValues } from 'commander' import inquirer from 'inquirer' import { chalk, log } from '../../utils/command-helpers.js' import { getWebSocket } from '../../utils/websockets/index.js' import type BaseCommand from '../base-command.js' -// Source: Source: https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs.html#monitoring-cloudwatchlogs-advanced -export const LOG_LEVELS = { - TRACE: 'TRACE', - DEBUG: 'DEBUG', - INFO: 'INFO', - WARN: 'WARN', - ERROR: 'ERROR', - FATAL: 'FATAL', -} -const LOG_LEVELS_LIST = Object.values(LOG_LEVELS).map((level) => level.toLowerCase()) -const CLI_LOG_LEVEL_CHOICES_STRING = LOG_LEVELS_LIST.map((level) => ` ${level}`) +import { CLI_LOG_LEVEL_CHOICES_STRING, LOG_LEVELS, LOG_LEVELS_LIST } from './log-levels.js' + function getLog(logData: { level: string; message: string }) { let logString = '' @@ -37,7 +28,7 @@ function getLog(logData: { level: string; message: string }) { return `${logString} ${logData.message}` } -const logsFunction = async (functionName: string | undefined, options: OptionValues, command: BaseCommand) => { +export const logsFunction = async (functionName: string | undefined, options: OptionValues, command: BaseCommand) => { const client = command.netlify.api const { site } = command.netlify const { id: siteId } = site @@ -106,19 +97,3 @@ const logsFunction = async (functionName: string | undefined, options: OptionVal log(err) }) } - -export const createLogsFunctionCommand = (program: BaseCommand) => - program - .command('logs:function') - .alias('logs:functions') - .addOption( - new Option('-l, --level ', `Log levels to stream. Choices are:${CLI_LOG_LEVEL_CHOICES_STRING}`), - ) - .addArgument(new Argument('[functionName]', 'Name of the function to stream logs for')) - .addExamples([ - 'netlify logs:function', - 'netlify logs:function my-function', - 'netlify logs:function my-function -l info warn', - ]) - .description('(Beta) Stream netlify function logs to the console') - .action(logsFunction) diff --git a/src/commands/logs/index.ts b/src/commands/logs/index.ts index 482dc75724b..54d682cf341 100644 --- a/src/commands/logs/index.ts +++ b/src/commands/logs/index.ts @@ -1,10 +1,43 @@ +import { Option, OptionValues, Argument } from 'commander' + import BaseCommand from '../base-command.js' -import { createLogsBuildCommand } from './build.js' -import { createLogsFunctionCommand } from './functions.js' +import { CLI_LOG_LEVEL_CHOICES_STRING } from './log-levels.js' + +export const createLogsBuildCommand = (program: BaseCommand) => { + program + .command('logs:deploy') + .alias('logs:build') + .description('(Beta) Stream the logs of deploys currently being built to the console') + .action(async (options: OptionValues, command: BaseCommand) => { + const { logsBuild } = await import('./build.js') + await logsBuild(options, command) + }) +} + +export const createLogsFunctionCommand = (program: BaseCommand) => { + program + .command('logs:function') + .alias('logs:functions') + .addOption( + new Option('-l, --level ', `Log levels to stream. Choices are:${CLI_LOG_LEVEL_CHOICES_STRING}`), + ) + .addArgument(new Argument('[functionName]', 'Name of the function to stream logs for')) + .addExamples([ + 'netlify logs:function', + 'netlify logs:function my-function', + 'netlify logs:function my-function -l info warn', + ]) + .description('(Beta) Stream netlify function logs to the console') + .action(async (functionName: string | undefined, options: OptionValues, command: BaseCommand) => { + const { logsFunction } = await import('./functions.js') + await logsFunction(functionName, options, command) + }) +} export const createLogsCommand = (program: BaseCommand) => { createLogsBuildCommand(program) + createLogsFunctionCommand(program) return program diff --git a/src/commands/logs/log-levels.ts b/src/commands/logs/log-levels.ts new file mode 100644 index 00000000000..22110a3cede --- /dev/null +++ b/src/commands/logs/log-levels.ts @@ -0,0 +1,11 @@ +// Source: Source: https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs.html#monitoring-cloudwatchlogs-advanced +export const LOG_LEVELS = { + TRACE: 'TRACE', + DEBUG: 'DEBUG', + INFO: 'INFO', + WARN: 'WARN', + ERROR: 'ERROR', + FATAL: 'FATAL', +} +export const LOG_LEVELS_LIST = Object.values(LOG_LEVELS).map((level) => level.toLowerCase()) +export const CLI_LOG_LEVEL_CHOICES_STRING = LOG_LEVELS_LIST.map((level) => ` ${level}`) diff --git a/src/commands/main.ts b/src/commands/main.ts index 78c4a1e1da1..bbc864ab23b 100644 --- a/src/commands/main.ts +++ b/src/commands/main.ts @@ -32,7 +32,7 @@ import { createLogoutCommand } from './logout/index.js' import { createLogsCommand } from './logs/index.js' import { createOpenCommand } from './open/index.js' import { createRecipesCommand } from './recipes/index.js' -import { createServeCommand } from './serve/serve.js' +import { createServeCommand } from './serve/index.js' import { createSitesCommand } from './sites/index.js' import { createStatusCommand } from './status/index.js' import { createSwitchCommand } from './switch/index.js' diff --git a/src/commands/open/index.ts b/src/commands/open/index.ts index f3b57908c26..7952ac14a73 100644 --- a/src/commands/open/index.ts +++ b/src/commands/open/index.ts @@ -1 +1,37 @@ -export { createOpenCommand } from './open.js' +import { OptionValues } from 'commander' + +import requiresSiteInfo from '../../utils/hooks/requires-site-info.js' +import BaseCommand from '../base-command.js' + +export const createOpenCommand = (program: BaseCommand) => { + program + .command('open:admin') + .description('Opens current site admin UI in Netlify') + .addExamples(['netlify open:admin']) + .hook('preAction', requiresSiteInfo) + .action(async (options: OptionValues, command: BaseCommand) => { + const { openAdmin } = await import('./open-admin.js') + await openAdmin(options, command) + }) + + program + .command('open:site') + .description('Opens current site url in browser') + .addExamples(['netlify open:site']) + .hook('preAction', requiresSiteInfo) + .action(async (options: OptionValues, command: BaseCommand) => { + const { openSite } = await import('./open-site.js') + await openSite(options, command) + }) + + return program + .command('open') + .description(`Open settings for the site linked to the current folder`) + .option('--site', 'Open site') + .option('--admin', 'Open Netlify site') + .addExamples(['netlify open --site', 'netlify open --admin', 'netlify open:admin', 'netlify open:site']) + .action(async (options: OptionValues, command: BaseCommand) => { + const { open } = await import('./open.js') + await open(options, command) + }) +} diff --git a/src/commands/open/open-admin.ts b/src/commands/open/open-admin.ts index 94331d98c85..bd34c37b205 100644 --- a/src/commands/open/open-admin.ts +++ b/src/commands/open/open-admin.ts @@ -1,14 +1,10 @@ +import { OptionValues } from 'commander' + import { exit, log } from '../../utils/command-helpers.js' -import requiresSiteInfo from '../../utils/hooks/requires-site-info.js' import openBrowser from '../../utils/open-browser.js' +import BaseCommand from '../base-command.js' -/** - * The open:admin command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -export const openAdmin = async (options, command) => { +export const openAdmin = async (options: OptionValues, command: BaseCommand) => { const { siteInfo } = command.netlify await command.authenticate() @@ -20,17 +16,3 @@ export const openAdmin = async (options, command) => { await openBrowser({ url: siteInfo.admin_url }) exit() } - -/** - * Creates the `netlify open:admin` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createOpenAdminCommand = (program) => - program - .command('open:admin') - .description('Opens current site admin UI in Netlify') - .addExamples(['netlify open:admin']) - .hook('preAction', requiresSiteInfo) - .action(openAdmin) diff --git a/src/commands/open/open-site.ts b/src/commands/open/open-site.ts index 30a06668142..a4730a096fc 100644 --- a/src/commands/open/open-site.ts +++ b/src/commands/open/open-site.ts @@ -1,14 +1,10 @@ +import { OptionValues } from 'commander' + import { exit, log } from '../../utils/command-helpers.js' -import requiresSiteInfo from '../../utils/hooks/requires-site-info.js' import openBrowser from '../../utils/open-browser.js' +import BaseCommand from '../base-command.js' -/** - * The open:site command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -export const openSite = async (options, command) => { +export const openSite = async (options: OptionValues, command: BaseCommand) => { const { siteInfo } = command.netlify await command.authenticate() @@ -21,17 +17,3 @@ export const openSite = async (options, command) => { await openBrowser({ url }) exit() } - -/** - * Creates the `netlify open:site` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createOpenSiteCommand = (program) => - program - .command('open:site') - .description('Opens current site url in browser') - .addExamples(['netlify open:site']) - .hook('preAction', requiresSiteInfo) - .action(openSite) diff --git a/src/commands/open/open.ts b/src/commands/open/open.ts index 3c28999450c..3d9d40a7391 100644 --- a/src/commands/open/open.ts +++ b/src/commands/open/open.ts @@ -1,15 +1,12 @@ +import { OptionValues } from 'commander' + import { log } from '../../utils/command-helpers.js' +import BaseCommand from '../base-command.js' -import { createOpenAdminCommand, openAdmin } from './open-admin.js' -import { createOpenSiteCommand, openSite } from './open-site.js' +import { openAdmin } from './open-admin.js' +import { openSite } from './open-site.js' -/** - * The open command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -const open = async (options, command) => { +export const open = async (options: OptionValues, command: BaseCommand) => { if (!options.site || !options.admin) { log(command.helpInformation()) } @@ -20,22 +17,3 @@ const open = async (options, command) => { // Default open netlify admin await openAdmin(options, command) } - -/** - * Creates the `netlify open` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createOpenCommand = (program) => { - createOpenAdminCommand(program) - createOpenSiteCommand(program) - - return program - .command('open') - .description(`Open settings for the site linked to the current folder`) - .option('--site', 'Open site') - .option('--admin', 'Open Netlify site') - .addExamples(['netlify open --site', 'netlify open --admin', 'netlify open:admin', 'netlify open:site']) - .action(open) -} diff --git a/src/commands/recipes/index.ts b/src/commands/recipes/index.ts index 3a0cdabb36e..826f9f721eb 100644 --- a/src/commands/recipes/index.ts +++ b/src/commands/recipes/index.ts @@ -1 +1,25 @@ -export { createRecipesCommand, runRecipe } from './recipes.js' +import { OptionValues } from 'commander' + +import BaseCommand from '../base-command.js' + +export const createRecipesCommand = (program: BaseCommand) => { + program + .command('recipes:list') + .description(`List the recipes available to create and modify files in a project`) + .addExamples(['netlify recipes:list']) + .action(async () => { + const { recipesListCommand } = await import('./recipes-list.js') + await recipesListCommand() + }) + + return program + .command('recipes') + .argument('[name]', 'name of the recipe') + .description(`Create and modify files in a project using pre-defined recipes`) + .option('-n, --name ', 'recipe name to use') + .addExamples(['netlify recipes my-recipe', 'netlify recipes --name my-recipe']) + .action(async (recipeName: string, options: OptionValues, command: BaseCommand) => { + const { recipesCommand } = await import('./recipes.js') + await recipesCommand(recipeName, options, command) + }) +} diff --git a/src/commands/recipes/recipes-list.ts b/src/commands/recipes/recipes-list.ts index a102d758dee..227adb1ab22 100644 --- a/src/commands/recipes/recipes-list.ts +++ b/src/commands/recipes/recipes-list.ts @@ -5,7 +5,7 @@ import { listRecipes } from './common.js' /** * The recipes:list command */ -const recipesListCommand = async () => { +export const recipesListCommand = async () => { const recipes = await listRecipes() const table = new AsciiTable(`Usage: netlify recipes `) @@ -17,16 +17,3 @@ const recipesListCommand = async () => { console.log(table.toString()) } - -/** - * Creates the `netlify recipes:list` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createRecipesListCommand = (program) => - program - .command('recipes:list') - .description(`List the recipes available to create and modify files in a project`) - .addExamples(['netlify recipes:list']) - .action(recipesListCommand) diff --git a/src/commands/recipes/recipes.ts b/src/commands/recipes/recipes.ts index 76378a383f1..b3e064826de 100644 --- a/src/commands/recipes/recipes.ts +++ b/src/commands/recipes/recipes.ts @@ -1,23 +1,24 @@ import { basename } from 'path' +import { OptionValues } from 'commander' import { closest } from 'fastest-levenshtein' import inquirer from 'inquirer' import { NETLIFYDEVERR, chalk, log } from '../../utils/command-helpers.js' +import BaseCommand from '../base-command.js' import { getRecipe, listRecipes } from './common.js' -import { createRecipesListCommand } from './recipes-list.js' const SUGGESTION_TIMEOUT = 1e4 -/** - * The recipes command - * @param {string} recipeName - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7023) FIXME: 'recipesCommand' implicitly has return type 'any' ... Remove this comment to see the full error message -const recipesCommand = async (recipeName, options, command) => { +// @ts-expect-error TS(7031) FIXME: Binding element 'config' implicitly has an 'any' t... Remove this comment to see the full error message +export const runRecipe = async ({ config, recipeName, repositoryRoot }) => { + const recipe = await getRecipe(recipeName) + + return recipe.run({ config, repositoryRoot }) +} + +export const recipesCommand = async (recipeName: string, options: OptionValues, command: BaseCommand): Promise => { const { config, repositoryRoot } = command.netlify const sanitizedRecipeName = basename(recipeName || '').toLowerCase() @@ -64,28 +65,3 @@ const recipesCommand = async (recipeName, options, command) => { } } } - -// @ts-expect-error TS(7031) FIXME: Binding element 'config' implicitly has an 'any' t... Remove this comment to see the full error message -export const runRecipe = async ({ config, recipeName, repositoryRoot }) => { - const recipe = await getRecipe(recipeName) - - return recipe.run({ config, repositoryRoot }) -} - -/** - * Creates the `netlify recipes` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createRecipesCommand = (program) => { - createRecipesListCommand(program) - - program - .command('recipes') - .argument('[name]', 'name of the recipe') - .description(`Create and modify files in a project using pre-defined recipes`) - .option('-n, --name ', 'recipe name to use') - .addExamples(['netlify recipes my-recipe', 'netlify recipes --name my-recipe']) - .action(recipesCommand) -} diff --git a/src/commands/serve/index.ts b/src/commands/serve/index.ts index 130db533127..8d6f88c1948 100644 --- a/src/commands/serve/index.ts +++ b/src/commands/serve/index.ts @@ -1 +1,51 @@ -export { createServeCommand } from './serve.js' +import { Option } from 'commander' + +import { normalizeContext } from '../../utils/env/index.js' +import { getGeoCountryArgParser } from '../../utils/validation.js' +import BaseCommand from '../base-command.js' + +export const createServeCommand = (program: BaseCommand) => + program + .command('serve') + .description( + 'Build the site for production and serve locally. This does not watch the code for changes, so if you need to rebuild your site then you must exit and run `serve` again.', + ) + .option( + '--context ', + 'Specify a deploy context or branch for environment variables (contexts: "production", "deploy-preview", "branch-deploy", "dev")', + normalizeContext, + ) + .option('-p ,--port ', 'port of netlify dev', (value) => Number.parseInt(value)) + .option('-d ,--dir ', 'dir with static files') + .option('-f ,--functions ', 'specify a functions folder to serve') + .option('-o ,--offline', 'disables any features that require network access') + .addOption( + new Option('--functionsPort ', 'Old, prefer --functions-port. Port of functions server') + .argParser((value) => Number.parseInt(value)) + .hideHelp(true), + ) + .option('--functions-port ', 'port of functions server', (value) => Number.parseInt(value)) + .addOption( + new Option( + '--geo ', + 'force geolocation data to be updated, use cached data from the last 24h if found, or use a mock location', + ) + .choices(['cache', 'mock', 'update']) + .default('cache'), + ) + .addOption( + new Option( + '--country ', + 'Two-letter country code (https://ntl.fyi/country-codes) to use as mock geolocation (enables --geo=mock automatically)', + ).argParser(getGeoCountryArgParser('netlify dev --geo=mock --country=FR')), + ) + .addOption( + new Option('--staticServerPort ', 'port of the static app server used when no framework is detected') + .argParser((value) => Number.parseInt(value)) + .hideHelp(), + ) + .addExamples(['netlify serve', 'BROWSER=none netlify serve # disable browser auto opening']) + .action(async (options: Option, command: BaseCommand) => { + const { serve } = await import('./serve.js') + await serve(options, command) + }) diff --git a/src/commands/serve/serve.ts b/src/commands/serve/serve.ts index e9d1d0859ba..762ab246662 100644 --- a/src/commands/serve/serve.ts +++ b/src/commands/serve/serve.ts @@ -1,6 +1,6 @@ import process from 'process' -import { Option } from 'commander' +import { OptionValues } from 'commander' import { getBlobsContext } from '../../lib/blobs/blobs.js' import { promptEditorHelper } from '../../lib/edge-functions/editor-helper.js' @@ -17,22 +17,16 @@ import { } from '../../utils/command-helpers.js' import detectServerSettings, { getConfigWithPlugins } from '../../utils/detect-server-settings.js' import { getDotEnvVariables, getSiteInformation, injectEnvVariables } from '../../utils/dev.js' -import { getEnvelopeEnv, normalizeContext } from '../../utils/env/index.js' +import { getEnvelopeEnv } from '../../utils/env/index.js' import { getInternalFunctionsDir } from '../../utils/functions/functions.js' import { ensureNetlifyIgnore } from '../../utils/gitignore.js' import openBrowser from '../../utils/open-browser.js' import { generateInspectSettings, startProxyServer } from '../../utils/proxy-server.js' import { runBuildTimeline } from '../../utils/run-build.js' import type { ServerSettings } from '../../utils/types.js' -import { getGeoCountryArgParser } from '../../utils/validation.js' - -/** - * The serve command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -const serve = async (options, command) => { +import BaseCommand from '../base-command.js' + +export const serve = async (options: OptionValues, command: BaseCommand) => { const { api, cachedConfig, config, repositoryRoot, site, siteInfo, state } = command.netlify config.dev = { ...config.dev } config.build = { ...config.build } @@ -168,54 +162,3 @@ const serve = async (options, command) => { printBanner({ url }) } - -/** - * Creates the `netlify serve` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createServeCommand = (program) => - program - .command('serve') - .description( - 'Build the site for production and serve locally. This does not watch the code for changes, so if you need to rebuild your site then you must exit and run `serve` again.', - ) - .option( - '--context ', - 'Specify a deploy context or branch for environment variables (contexts: "production", "deploy-preview", "branch-deploy", "dev")', - normalizeContext, - ) - // @ts-expect-error TS(7006) FIXME: Parameter 'value' implicitly has an 'any' type. - .option('-p ,--port ', 'port of netlify dev', (value) => Number.parseInt(value)) - .option('-d ,--dir ', 'dir with static files') - .option('-f ,--functions ', 'specify a functions folder to serve') - .option('-o ,--offline', 'disables any features that require network access') - .addOption( - new Option('--functionsPort ', 'Old, prefer --functions-port. Port of functions server') - .argParser((value) => Number.parseInt(value)) - .hideHelp(true), - ) - // @ts-expect-error TS(7006) FIXME: Parameter 'value' implicitly has an 'any' type. - .option('--functions-port ', 'port of functions server', (value) => Number.parseInt(value)) - .addOption( - new Option( - '--geo ', - 'force geolocation data to be updated, use cached data from the last 24h if found, or use a mock location', - ) - .choices(['cache', 'mock', 'update']) - .default('cache'), - ) - .addOption( - new Option( - '--country ', - 'Two-letter country code (https://ntl.fyi/country-codes) to use as mock geolocation (enables --geo=mock automatically)', - ).argParser(getGeoCountryArgParser('netlify dev --geo=mock --country=FR')), - ) - .addOption( - new Option('--staticServerPort ', 'port of the static app server used when no framework is detected') - .argParser((value) => Number.parseInt(value)) - .hideHelp(), - ) - .addExamples(['netlify serve', 'BROWSER=none netlify serve # disable browser auto opening']) - .action(serve) diff --git a/src/commands/sites/index.ts b/src/commands/sites/index.ts index c0ee131fc2f..d461c11effb 100644 --- a/src/commands/sites/index.ts +++ b/src/commands/sites/index.ts @@ -1,2 +1 @@ -export { sitesCreate } from './sites-create.js' export { createSitesCommand } from './sites.js' diff --git a/src/commands/sites/sites-create-template.ts b/src/commands/sites/sites-create-template.ts index fc50756d84d..69b481a20e7 100644 --- a/src/commands/sites/sites-create-template.ts +++ b/src/commands/sites/sites-create-template.ts @@ -1,3 +1,4 @@ +import { OptionValues } from 'commander' import inquirer from 'inquirer' import pick from 'lodash/pick.js' // @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module 'pars... Remove this comment to see the full error message @@ -12,6 +13,7 @@ import { getGitHubToken } from '../../utils/init/config-github.js' import { configureRepo } from '../../utils/init/config.js' import { createRepo, getTemplatesFromGitHub, validateTemplate } from '../../utils/sites/utils.js' import { track } from '../../utils/telemetry/index.js' +import BaseCommand from '../base-command.js' import { getSiteNameInput } from './sites-create.js' @@ -67,14 +69,7 @@ const getTemplateName = async ({ ghToken, options, repository }) => { // @ts-expect-error TS(7031) FIXME: Binding element 'options' implicitly has an 'any' ... Remove this comment to see the full error message const getGitHubLink = ({ options, templateName }) => options.url || `https://github.com/${templateName}` -/** - * The sites:create-template command - * @param repository {string} - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'repository' implicitly has an 'any' typ... Remove this comment to see the full error message -const sitesCreateTemplate = async (repository, options, command) => { +export const sitesCreateTemplate = async (repository: string, options: OptionValues, command: BaseCommand) => { const { api } = command.netlify await command.authenticate() @@ -258,29 +253,3 @@ const sitesCreateTemplate = async (repository, options, command) => { return site } - -/** - * Creates the `netlify sites:create-template` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createSitesFromTemplateCommand = (program) => - program - .command('sites:create-template') - .description( - `(Beta) Create a site from a starter template -Create a site from a starter template.`, - ) - .option('-n, --name [name]', 'name of site') - .option('-u, --url [url]', 'template url') - .option('-a, --account-slug [slug]', 'account slug to create the site under') - .option('-c, --with-ci', 'initialize CI hooks during site creation') - .argument('[repository]', 'repository to use as starter template') - .addHelpText('after', `(Beta) Create a site from starter template.`) - .addExamples([ - 'netlify sites:create-template', - 'netlify sites:create-template nextjs-blog-theme', - 'netlify sites:create-template my-github-profile/my-template', - ]) - .action(sitesCreateTemplate) diff --git a/src/commands/sites/sites-create.ts b/src/commands/sites/sites-create.ts index dcbabdca1b7..c7a21f3d81a 100644 --- a/src/commands/sites/sites-create.ts +++ b/src/commands/sites/sites-create.ts @@ -1,4 +1,4 @@ -import { InvalidArgumentError } from 'commander' +import { OptionValues } from 'commander' import inquirer from 'inquirer' import pick from 'lodash/pick.js' import prettyjson from 'prettyjson' @@ -7,7 +7,8 @@ import { chalk, error, 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' -import { link } from '../link/index.js' +import BaseCommand from '../base-command.js' +import { link } from '../link/link.js' // @ts-expect-error TS(7006) FIXME: Parameter 'name' implicitly has an 'any' type. export const getSiteNameInput = async (name) => { @@ -27,13 +28,7 @@ export const getSiteNameInput = async (name) => { return { name } } -/** - * The sites:create command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -export const sitesCreate = async (options, command) => { +export const sitesCreate = async (options: OptionValues, command: BaseCommand) => { const { api } = command.netlify await command.authenticate() @@ -158,38 +153,3 @@ export const sitesCreate = async (options, command) => { return site } - -const MAX_SITE_NAME_LENGTH = 63 -// @ts-expect-error TS(7006) FIXME: Parameter 'value' implicitly has an 'any' type. -const validateName = function (value) { - // netlify sites:create --name - if (typeof value === 'string' && value.length > MAX_SITE_NAME_LENGTH) { - throw new InvalidArgumentError(`--name should be less than 64 characters, input length: ${value.length}`) - } - - return value -} - -/** - * Creates the `netlify sites:create` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createSitesCreateCommand = (program) => - program - .command('sites:create') - .description( - `Create an empty site (advanced) -Create a blank site that isn't associated with any git remote. Will link the site to the current working directory.`, - ) - .option('-n, --name ', 'name of site', validateName) - .option('-a, --account-slug ', 'account slug to create the site under') - .option('-c, --with-ci', 'initialize CI hooks during site creation') - .option('-m, --manual', 'force manual CI setup. Used --with-ci flag') - .option('--disable-linking', 'create the site without linking it to current directory') - .addHelpText( - 'after', - `Create a blank site that isn't associated with any git remote. Will link the site to the current working directory.`, - ) - .action(sitesCreate) diff --git a/src/commands/sites/sites-delete.ts b/src/commands/sites/sites-delete.ts index ac7ac345d61..70956c893c3 100644 --- a/src/commands/sites/sites-delete.ts +++ b/src/commands/sites/sites-delete.ts @@ -1,15 +1,10 @@ +import { OptionValues } from 'commander' import inquirer from 'inquirer' import { chalk, error, exit, log } from '../../utils/command-helpers.js' +import BaseCommand from '../base-command.js' -/** - * The sites:delete command - * @param {string} siteId - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'siteId' implicitly has an 'any' type. -const sitesDelete = async (siteId, options, command) => { +export const sitesDelete = async (siteId: string, options: OptionValues, command: BaseCommand) => { command.setAnalyticsPayload({ force: options.force }) const { api, site } = command.netlify @@ -89,18 +84,3 @@ const sitesDelete = async (siteId, options, command) => { } log(`Site "${siteId}" successfully deleted!`) } - -/** - * Creates the `netlify sites:delete` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createSitesDeleteCommand = (program) => - program - .command('sites:delete') - .description('Delete a site\nThis command will permanently delete the site on Netlify. Use with caution.') - .argument('', 'Site ID to delete.') - .option('-f, --force', 'delete without prompting (useful for CI)') - .addExamples(['netlify sites:delete 1234-3262-1211']) - .action(sitesDelete) diff --git a/src/commands/sites/sites-list.ts b/src/commands/sites/sites-list.ts index a56d52b4e8e..9fb382d62f8 100644 --- a/src/commands/sites/sites-list.ts +++ b/src/commands/sites/sites-list.ts @@ -1,16 +1,11 @@ - +import { OptionValues } from 'commander' + import { listSites } from '../../lib/api.js' import { startSpinner, stopSpinner } from '../../lib/spinner.js' import { chalk, log, logJson } from '../../utils/command-helpers.js' +import BaseCommand from '../base-command.js' -/** - * The sites:list command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - * @returns {Promise<{ id: any; name: any; ssl_url: any; account_name: any; }|boolean>} - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -const sitesList = async (options, command) => { +export const sitesList = async (options: OptionValues, command: BaseCommand) => { const { api } = command.netlify /** @type {import('ora').Ora} */ let spinner @@ -78,18 +73,3 @@ Count: ${logSites.length} }) } } - -/** - * Creates the `netlify sites:list` command - * @param {import('../base-command.js').default} program - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createSitesListCommand = (program) => - program - .command('sites:list') - .description('List all sites you have access to') - .option('--json', 'Output site data as JSON') - // @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. - .action(async (options, command) => { - await sitesList(options, command) - }) diff --git a/src/commands/sites/sites.ts b/src/commands/sites/sites.ts index ee5b31b86ad..908c8940fc3 100644 --- a/src/commands/sites/sites.ts +++ b/src/commands/sites/sites.ts @@ -1,30 +1,92 @@ - -import { createSitesFromTemplateCommand } from './sites-create-template.js' -import { createSitesCreateCommand } from './sites-create.js' -import { createSitesDeleteCommand } from './sites-delete.js' -import { createSitesListCommand } from './sites-list.js' - -/** - * The sites command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -const sites = (options, command) => { +import { OptionValues, InvalidArgumentError } from 'commander' + +import BaseCommand from '../base-command.js' + +const MAX_SITE_NAME_LENGTH = 63 + +// @ts-expect-error TS(7006) FIXME: Parameter 'value' implicitly has an 'any' type. +const validateName = function (value) { + // netlify sites:create --name + if (typeof value === 'string' && value.length > MAX_SITE_NAME_LENGTH) { + throw new InvalidArgumentError(`--name should be less than 64 characters, input length: ${value.length}`) + } + + return value +} + +const sites = (options: OptionValues, command: BaseCommand) => { command.help() } -/** - * Creates the `netlify sites` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createSitesCommand = (program) => { +export const createSitesFromTemplateCommand = (program: BaseCommand) => { + program + .command('sites:create-template') + .description( + `(Beta) Create a site from a starter template +Create a site from a starter template.`, + ) + .option('-n, --name [name]', 'name of site') + .option('-u, --url [url]', 'template url') + .option('-a, --account-slug [slug]', 'account slug to create the site under') + .option('-c, --with-ci', 'initialize CI hooks during site creation') + .argument('[repository]', 'repository to use as starter template') + .addHelpText('after', `(Beta) Create a site from starter template.`) + .addExamples([ + 'netlify sites:create-template', + 'netlify sites:create-template nextjs-blog-theme', + 'netlify sites:create-template my-github-profile/my-template', + ]) + .action(async (repository: string, options: OptionValues, command: BaseCommand) => { + const { sitesCreateTemplate } = await import('./sites-create-template.js') + await sitesCreateTemplate(repository, options, command) + }) +} + +export const createSitesCreateCommand = (program: BaseCommand) => { + program + .command('sites:create') + .description( + `Create an empty site (advanced) +Create a blank site that isn't associated with any git remote. Will link the site to the current working directory.`, + ) + .option('-n, --name ', 'name of site', validateName) + .option('-a, --account-slug ', 'account slug to create the site under') + .option('-c, --with-ci', 'initialize CI hooks during site creation') + .option('-m, --manual', 'force manual CI setup. Used --with-ci flag') + .option('--disable-linking', 'create the site without linking it to current directory') + .addHelpText( + 'after', + `Create a blank site that isn't associated with any git remote. Will link the site to the current working directory.`, + ) + .action(async (options: OptionValues, command: BaseCommand) => { + const { sitesCreate } = await import('./sites-create.js') + await sitesCreate(options, command) + }) +} + +export const createSitesCommand = (program: BaseCommand) => { createSitesCreateCommand(program) createSitesFromTemplateCommand(program) - createSitesListCommand(program) - createSitesDeleteCommand(program) + + program + .command('sites:list') + .description('List all sites you have access to') + .option('--json', 'Output site data as JSON') + .action(async (options: OptionValues, command: BaseCommand) => { + const { sitesList } = await import('./sites-list.js') + await sitesList(options, command) + }) + + program + .command('sites:delete') + .description('Delete a site\nThis command will permanently delete the site on Netlify. Use with caution.') + .argument('', 'Site ID to delete.') + .option('-f, --force', 'delete without prompting (useful for CI)') + .addExamples(['netlify sites:delete 1234-3262-1211']) + .action(async (siteId: string, options: OptionValues, command: BaseCommand) => { + const { sitesDelete } = await import('./sites-delete.js') + await sitesDelete(siteId, options, command) + }) return program .command('sites') diff --git a/src/commands/status/index.ts b/src/commands/status/index.ts index 8816ed928ee..0804f9d4823 100644 --- a/src/commands/status/index.ts +++ b/src/commands/status/index.ts @@ -1 +1,24 @@ -export { createStatusCommand } from './status.js' +import { OptionValues } from 'commander' + +import requiresSiteInfo from '../../utils/hooks/requires-site-info.js' +import BaseCommand from '../base-command.js' + +export const createStatusCommand = (program: BaseCommand) => { + program + .command('status:hooks') + .description('Print hook information of the linked site') + .hook('preAction', requiresSiteInfo) + .action(async (options: OptionValues, command: BaseCommand) => { + const { statusHooks } = await import('./status-hooks.js') + await statusHooks(options, command) + }) + + return program + .command('status') + .description('Print status information') + .option('--verbose', 'Output system info') + .action(async (options: OptionValues, command: BaseCommand) => { + const { status } = await import('./status.js') + await status(options, command) + }) +} diff --git a/src/commands/status/status-hooks.ts b/src/commands/status/status-hooks.ts index 04fecb54ef9..d4d0a9f2475 100644 --- a/src/commands/status/status-hooks.ts +++ b/src/commands/status/status-hooks.ts @@ -1,15 +1,10 @@ +import { OptionValues } from 'commander' import prettyjson from 'prettyjson' import { log } from '../../utils/command-helpers.js' -import requiresSiteInfo from '../../utils/hooks/requires-site-info.js' +import BaseCommand from '../base-command.js' -/** - * The status:hooks command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -const statusHooks = async (options, command) => { +export const statusHooks = async (options: OptionValues, command: BaseCommand) => { const { api, siteInfo } = command.netlify await command.authenticate() @@ -38,16 +33,3 @@ Site Hook Status │ ─────────────────┘`) log(prettyjson.render(data)) } - -/** - * Creates the `netlify status:hooks` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createStatusHooksCommand = (program) => - program - .command('status:hooks') - .description('Print hook information of the linked site') - .hook('preAction', requiresSiteInfo) - .action(statusHooks) diff --git a/src/commands/status/status.ts b/src/commands/status/status.ts index d01f5e3b076..15cf6b745f5 100644 --- a/src/commands/status/status.ts +++ b/src/commands/status/status.ts @@ -1,17 +1,11 @@ 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 BaseCommand from '../base-command.js' -import { createStatusHooksCommand } from './status-hooks.js' - -/** - * The status command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -const status = async (options, command) => { +export const status = async (options: OptionValues, command: BaseCommand) => { const { api, globalConfig, site, siteInfo } = command.netlify const current = globalConfig.get('userId') // @ts-expect-error TS(2554) FIXME: Expected 1 arguments, but got 0. @@ -102,19 +96,3 @@ const status = async (options, command) => { ) log() } - -/** - * Creates the `netlify status` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createStatusCommand = (program) => { - createStatusHooksCommand(program) - - return program - .command('status') - .description('Print status information') - .option('--verbose', 'Output system info') - .action(status) -} diff --git a/src/commands/switch/index.ts b/src/commands/switch/index.ts index 4712166dc5c..49c4a1b59e6 100644 --- a/src/commands/switch/index.ts +++ b/src/commands/switch/index.ts @@ -1 +1,12 @@ -export { createSwitchCommand } from './switch.js' +import { OptionValues } from 'commander' + +import BaseCommand from '../base-command.js' + +export const createSwitchCommand = (program: BaseCommand) => + program + .command('switch') + .description('Switch your active Netlify account') + .action(async (options: OptionValues, command: BaseCommand) => { + const { switchCommand } = await import('./switch.js') + await switchCommand(options, command) + }) diff --git a/src/commands/switch/switch.ts b/src/commands/switch/switch.ts index b56afc315b7..b1052278ab5 100644 --- a/src/commands/switch/switch.ts +++ b/src/commands/switch/switch.ts @@ -1,17 +1,13 @@ +import { OptionValues } from 'commander' import inquirer from 'inquirer' import { chalk, log } from '../../utils/command-helpers.js' -import { login } from '../login/index.js' +import BaseCommand from '../base-command.js' +import { login } from '../login/login.js' const LOGIN_NEW = 'I would like to login to a new account' -/** - * The switch command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -const switchCommand = async (options, command) => { +export const switchCommand = async (options: OptionValues, command: BaseCommand) => { const availableUsersChoices = Object.values(command.netlify.globalConfig.get('users') || {}).reduce( (prev, current) => // @ts-expect-error TS(2769) FIXME: No overload matches this call. @@ -43,12 +39,3 @@ const switchCommand = async (options, command) => { log(`You're now using ${chalk.bold(selectedAccount[1])}.`) } } - -/** - * Creates the `netlify switch` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createSwitchCommand = (program) => - program.command('switch').description('Switch your active Netlify account').action(switchCommand) diff --git a/src/commands/unlink/index.ts b/src/commands/unlink/index.ts index 668bc3cb919..d074c42d14d 100644 --- a/src/commands/unlink/index.ts +++ b/src/commands/unlink/index.ts @@ -1 +1,12 @@ -export { createUnlinkCommand } from './unlink.js' +import { OptionValues } from 'commander' + +import BaseCommand from '../base-command.js' + +export const createUnlinkCommand = (program: BaseCommand) => + program + .command('unlink') + .description('Unlink a local folder from a Netlify site') + .action(async (options: OptionValues, command: BaseCommand) => { + const { unlink } = await import('./unlink.js') + await unlink(options, command) + }) diff --git a/src/commands/unlink/unlink.ts b/src/commands/unlink/unlink.ts index d3b3c48c56f..6db9ff499ad 100644 --- a/src/commands/unlink/unlink.ts +++ b/src/commands/unlink/unlink.ts @@ -1,14 +1,10 @@ +import { OptionValues } from 'commander' import { exit, log } from '../../utils/command-helpers.js' import { track } from '../../utils/telemetry/index.js' +import BaseCommand from '../base-command.js' -/** - * The unlink command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -const unlink = async (options, command) => { +export const unlink = async (options: OptionValues, command: BaseCommand) => { const { site, siteInfo, state } = command.netlify const siteId = site.id @@ -31,12 +27,3 @@ const unlink = async (options, command) => { log('Unlinked site') } } - -/** - * Creates the `netlify unlink` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createUnlinkCommand = (program) => - program.command('unlink').description('Unlink a local folder from a Netlify site').action(unlink) diff --git a/src/commands/watch/index.ts b/src/commands/watch/index.ts index 4abeaaa48ec..72972cbc179 100644 --- a/src/commands/watch/index.ts +++ b/src/commands/watch/index.ts @@ -1 +1,13 @@ -export { createWatchCommand } from './watch.js' +import { OptionValues } from 'commander' + +import BaseCommand from '../base-command.js' + +export const createWatchCommand = (program: BaseCommand) => + program + .command('watch') + .description('Watch for site deploy to finish') + .addExamples([`netlify watch`, `git push && netlify watch`]) + .action(async (options: OptionValues, command: BaseCommand) => { + const { watch } = await import('./watch.js') + await watch(options, command) + }) diff --git a/src/commands/watch/watch.ts b/src/commands/watch/watch.ts index 8da3c50e153..071ccce8b02 100644 --- a/src/commands/watch/watch.ts +++ b/src/commands/watch/watch.ts @@ -1,9 +1,11 @@ +import { OptionValues } from 'commander' import pWaitFor from 'p-wait-for' import prettyjson from 'prettyjson' import { startSpinner, stopSpinner } from '../../lib/spinner.js' import { chalk, error, log } from '../../utils/command-helpers.js' -import { init } from '../init/index.js' +import BaseCommand from '../base-command.js' +import { init } from '../init/init.js' // 1 second const INIT_WAIT = 1e3 @@ -54,13 +56,7 @@ const waitForBuildFinish = async function (api, siteId, spinner) { return firstPass } -/** - * The watch command - * @param {import('commander').OptionValues} options - * @param {import('../base-command.js').default} command - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type. -const watch = async (options, command) => { +export const watch = async (options: OptionValues, command: BaseCommand) => { await command.authenticate() const client = command.netlify.api let siteId = command.netlify.site.id @@ -122,16 +118,3 @@ const watch = async (options, command) => { error(error_) } } - -/** - * Creates the `netlify watch` command - * @param {import('../base-command.js').default} program - * @returns - */ -// @ts-expect-error TS(7006) FIXME: Parameter 'program' implicitly has an 'any' type. -export const createWatchCommand = (program) => - program - .command('watch') - .description('Watch for site deploy to finish') - .addExamples([`netlify watch`, `git push && netlify watch`]) - .action(watch) diff --git a/src/lib/edge-functions/editor-helper.ts b/src/lib/edge-functions/editor-helper.ts index 1943d0cf05b..1ee5d998cd6 100644 --- a/src/lib/edge-functions/editor-helper.ts +++ b/src/lib/edge-functions/editor-helper.ts @@ -2,7 +2,7 @@ import { env } from 'process' import inquirer from 'inquirer' -import { runRecipe } from '../../commands/recipes/index.js' +import { runRecipe } from '../../commands/recipes/recipes.js' const STATE_PROMPT_PROPERTY = 'promptVSCodeSettings' diff --git a/tests/integration/commands/integration/deploy.test.ts b/tests/integration/commands/integration/deploy.test.ts index a8718e4e6d7..556b197b5e9 100644 --- a/tests/integration/commands/integration/deploy.test.ts +++ b/tests/integration/commands/integration/deploy.test.ts @@ -4,7 +4,8 @@ import { beforeEach, describe, expect, test, vi } from 'vitest' import BaseCommand from '../../../../src/commands/base-command.js' import { deploy as siteDeploy } from '../../../../src/commands/deploy/deploy.js' -import { areScopesEqual, createDeployCommand } from '../../../../src/commands/integration/deploy.js' +import { areScopesEqual } from '../../../../src/commands/integration/deploy.js' +import { createIntegrationDeployCommand } from '../../../../src/commands/integration/index.js' import { getEnvironmentVariables, withMockApi } from '../../utils/mock-api.js' import { withSiteBuilder } from '../../utils/site-builder.js' @@ -86,7 +87,7 @@ describe(`integration:deploy`, () => { Object.assign(process.env, envVars) const program = new BaseCommand('netlify') - createDeployCommand(program) + createIntegrationDeployCommand(program) const simulatedArgv = ['', '', 'integration:deploy'] try { diff --git a/tests/integration/commands/logs/build.test.ts b/tests/integration/commands/logs/build.test.ts index 73a50ac94b4..dc1e654f039 100644 --- a/tests/integration/commands/logs/build.test.ts +++ b/tests/integration/commands/logs/build.test.ts @@ -1,7 +1,7 @@ import { Mock, afterEach, beforeEach, describe, expect, test, vi } from 'vitest' import BaseCommand from '../../../../src/commands/base-command.js' -import { createLogsBuildCommand } from '../../../../src/commands/logs/build.js' +import { createLogsBuildCommand } from '../../../../src/commands/logs/index.js' import { getWebSocket } from '../../../../src/utils/websockets/index.js' import { startMockApi } from '../../utils/mock-api-vitest.js' import { getEnvironmentVariables } from '../../utils/mock-api.js' diff --git a/tests/integration/commands/logs/functions.test.ts b/tests/integration/commands/logs/functions.test.ts index 89066017570..e5834bd03fd 100644 --- a/tests/integration/commands/logs/functions.test.ts +++ b/tests/integration/commands/logs/functions.test.ts @@ -1,9 +1,10 @@ import { Mock, afterEach, beforeEach, describe, expect, test, vi } from 'vitest' import BaseCommand from '../../../../src/commands/base-command.js' -import { LOG_LEVELS, createLogsFunctionCommand } from '../../../../src/commands/logs/functions.js' -import { getWebSocket } from '../../../../src/utils/websockets/index.js' +import { createLogsFunctionCommand } from '../../../../src/commands/logs/index.js' +import { LOG_LEVELS } from '../../../../src/commands/logs/log-levels.js' import { log } from '../../../../src/utils/command-helpers.js' +import { getWebSocket } from '../../../../src/utils/websockets/index.js' import { startMockApi } from '../../utils/mock-api-vitest.ts' import { getEnvironmentVariables } from '../../utils/mock-api.js' diff --git a/tests/integration/commands/sites/sites.test.ts b/tests/integration/commands/sites/sites.test.ts index f1cf7bc25d0..55566d59290 100644 --- a/tests/integration/commands/sites/sites.test.ts +++ b/tests/integration/commands/sites/sites.test.ts @@ -6,10 +6,11 @@ import { afterAll, beforeEach, describe, expect, test, vi } from 'vitest' import BaseCommand from '../../../../src/commands/base-command.js' import { - createSitesFromTemplateCommand, fetchTemplates, } from '../../../../src/commands/sites/sites-create-template.js' -import { createSitesCreateCommand } from '../../../../src/commands/sites/sites-create.js' +import { createSitesCreateCommand , + createSitesFromTemplateCommand +} from '../../../../src/commands/sites/sites.js' import { getGitHubToken } from '../../../../src/utils/init/config-github.js' import { createRepo, getTemplatesFromGitHub } from '../../../../src/utils/sites/utils.js' import { getEnvironmentVariables, withMockApi } from '../../utils/mock-api.js'