netlify-cli 27.10.2 → 27.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/commands/agents/agents-create.js +19 -33
  2. package/dist/commands/base-command.d.ts +1 -1
  3. package/dist/commands/base-command.js +8 -11
  4. package/dist/commands/clone/clone.js +5 -10
  5. package/dist/commands/completion/completion.js +5 -9
  6. package/dist/commands/create/create-action.js +16 -28
  7. package/dist/commands/database/db-init.js +17 -31
  8. package/dist/commands/database/db-migration-new.js +13 -23
  9. package/dist/commands/database/db-migration-pull.js +5 -9
  10. package/dist/commands/database/db-reset.js +5 -9
  11. package/dist/commands/deploy/deploy.js +23 -36
  12. package/dist/commands/env/env-list.js +5 -9
  13. package/dist/commands/functions/functions-create.js +35 -71
  14. package/dist/commands/functions/functions-invoke.js +5 -10
  15. package/dist/commands/init/init.js +12 -25
  16. package/dist/commands/link/link.js +26 -52
  17. package/dist/commands/logs/build.js +2 -4
  18. package/dist/commands/main.js +10 -17
  19. package/dist/commands/recipes/recipes.js +6 -16
  20. package/dist/commands/sites/sites-create.js +12 -22
  21. package/dist/commands/sites/sites-delete.js +3 -7
  22. package/dist/commands/switch/switch.js +5 -9
  23. package/dist/lib/edge-functions/editor-helper.js +3 -5
  24. package/dist/recipes/ai-context/index.js +9 -17
  25. package/dist/recipes/blobs-migrate/index.js +3 -7
  26. package/dist/recipes/vscode/index.js +6 -10
  27. package/dist/utils/build-info.js +3 -6
  28. package/dist/utils/deploy/deploy-site.js +21 -12
  29. package/dist/utils/deploy/upload-files.js +6 -2
  30. package/dist/utils/deploy/util.d.ts +1 -0
  31. package/dist/utils/deploy/util.js +1 -0
  32. package/dist/utils/exit-codes.d.ts +2 -0
  33. package/dist/utils/exit-codes.js +2 -0
  34. package/dist/utils/gh-auth.js +10 -22
  35. package/dist/utils/github-api.d.ts +22 -0
  36. package/dist/utils/github-api.js +47 -0
  37. package/dist/utils/init/config-github.js +29 -45
  38. package/dist/utils/init/config-manual.js +8 -28
  39. package/dist/utils/init/utils.js +14 -38
  40. package/dist/utils/prompts/confirm-prompt.js +4 -15
  41. package/dist/utils/proxy.js +7 -48
  42. package/dist/utils/redirects.js +3 -2
  43. package/dist/utils/rules-proxy.js +31 -29
  44. package/dist/utils/types.d.ts +2 -2
  45. package/package.json +10 -14
@@ -1,4 +1,4 @@
1
- import inquirer from 'inquirer';
1
+ import { input, select } from '@inquirer/prompts';
2
2
  import { chalk, logAndThrowError, log, logJson } from '../../utils/command-helpers.js';
3
3
  import { startSpinner, stopSpinner } from '../../lib/spinner.js';
4
4
  import { validatePrompt, validateAgent, formatStatus, getAgentName } from './utils.js';
@@ -13,15 +13,10 @@ export const agentsCreate = async (promptArg, options, command) => {
13
13
  const isGitBased = Boolean(siteInfo.build_settings?.repo_branch);
14
14
  // Interactive prompt if not provided
15
15
  if (!prompt && !promptArg) {
16
- const { promptInput } = await inquirer.prompt([
17
- {
18
- type: 'input',
19
- name: 'promptInput',
20
- message: 'What would you like the agent to do?',
21
- validate: validatePrompt,
22
- },
23
- ]);
24
- finalPrompt = promptInput;
16
+ finalPrompt = await input({
17
+ message: 'What would you like the agent to do?',
18
+ validate: validatePrompt,
19
+ });
25
20
  }
26
21
  else {
27
22
  finalPrompt = (promptArg || prompt) ?? '';
@@ -32,16 +27,11 @@ export const agentsCreate = async (promptArg, options, command) => {
32
27
  }
33
28
  // Agent selection if not provided
34
29
  if (!agent) {
35
- const { agentInput } = await inquirer.prompt([
36
- {
37
- type: 'list',
38
- name: 'agentInput',
39
- message: 'Which agent would you like to use?',
40
- choices: AVAILABLE_AGENTS,
41
- default: 'claude',
42
- },
43
- ]);
44
- agent = agentInput;
30
+ agent = await select({
31
+ message: 'Which agent would you like to use?',
32
+ choices: AVAILABLE_AGENTS,
33
+ default: 'claude',
34
+ });
45
35
  }
46
36
  else {
47
37
  const agentIsValid = validateAgent(agent);
@@ -52,20 +42,16 @@ export const agentsCreate = async (promptArg, options, command) => {
52
42
  if (isGitBased) {
53
43
  if (!branch) {
54
44
  const defaultBranch = siteInfo.build_settings?.repo_branch;
55
- const { branchInput } = await inquirer.prompt([
56
- {
57
- type: 'input',
58
- name: 'branchInput',
59
- message: 'Which branch would you like to work on?',
60
- default: defaultBranch,
61
- validate: (input) => {
62
- if (!input || input.trim().length === 0) {
63
- return 'Branch name is required';
64
- }
65
- return true;
66
- },
45
+ const branchInput = await input({
46
+ message: 'Which branch would you like to work on?',
47
+ default: defaultBranch,
48
+ validate: (value) => {
49
+ if (!value || value.trim().length === 0) {
50
+ return 'Branch name is required';
51
+ }
52
+ return true;
67
53
  },
68
- ]);
54
+ });
69
55
  branch = branchInput.trim();
70
56
  }
71
57
  }
@@ -64,7 +64,7 @@ export default class BaseCommand extends Command {
64
64
  /** Overrides the help output of commander with custom styling */
65
65
  createHelp(): Help;
66
66
  /** Will be called on the end of an action to track the metrics */
67
- onEnd(error_?: unknown): Promise<void>;
67
+ onEnd(error_?: unknown): Promise<undefined>;
68
68
  authenticate(tokenFromFlag?: string): Promise<string>;
69
69
  private refreshAccounts;
70
70
  expensivelyAuthenticate(): Promise<string>;
@@ -8,23 +8,21 @@ import { NodeFS, NoopLogger } from '@netlify/build-info/node';
8
8
  import { resolveConfig } from '@netlify/config';
9
9
  import { getGlobalConfigStore, LocalState } from '@netlify/dev-utils';
10
10
  import { isCI } from 'ci-info';
11
+ import { search } from '@inquirer/prompts';
11
12
  import { Command, Option } from 'commander';
12
13
  import debug from 'debug';
13
14
  import { findUp } from 'find-up';
14
- import inquirer from 'inquirer';
15
- import inquirerAutocompletePrompt from 'inquirer-autocomplete-prompt';
16
15
  import { deepMerge, pick } from '../utils/object-utilities.js';
17
16
  import { getAgent } from '../lib/http-agent.js';
18
17
  import { NETLIFY_CYAN, USER_AGENT, chalk, logAndThrowError, logJson, exit, getRequestUserAgent, getToken, log, version, normalizeConfig, padLeft, pollForToken, sortOptions, warn, logError, } from '../utils/command-helpers.js';
19
18
  import { handleOptionError, isOptionError } from '../utils/command-error-handler.js';
19
+ import { EXIT_CODES } from '../utils/exit-codes.js';
20
20
  import { getFrameworksAPIPaths } from '../utils/frameworks-api.js';
21
21
  import { getSiteByName } from '../utils/get-site.js';
22
22
  import { buildAuthorizeUrl } from '../utils/login-url.js';
23
23
  import openBrowser from '../utils/open-browser.js';
24
24
  import { isInteractive } from '../utils/scripted-commands.js';
25
25
  import { identify, reportError, setCommandForErrorReporting, track } from '../utils/telemetry/index.js';
26
- // load the autocomplete plugin
27
- inquirer.registerPrompt('autocomplete', inquirerAutocompletePrompt);
28
26
  /** Netlify CLI client id. Lives in bot@netlify.com */
29
27
  // TODO: setup client for multiple environments
30
28
  export const CLIENT_ID = 'd6f37de6614df7ae58664cfca524744d73807a377f5ee71f1a254f78412e3750';
@@ -97,20 +95,15 @@ async function selectWorkspace(project, filter) {
97
95
  .map((pkg) => pkg.name || pkg.path)
98
96
  .join(', ')}. Configure the project you want to work with and try again. Refer to https://ntl.fyi/configure-site for more information.`);
99
97
  }
100
- const { result } = await inquirer.prompt({
101
- name: 'result',
102
- // @ts-expect-error(serhalp) -- I think this is because `inquirer-autocomplete-prompt` extends known
103
- // `type`s but TS doesn't know about it
104
- type: 'autocomplete',
98
+ return await search({
105
99
  message: 'Select the project you want to work with',
106
- source: (_unused, input = '') => (project.workspace?.packages || [])
100
+ source: (input = '') => (project.workspace?.packages || [])
107
101
  .filter((pkg) => pkg.path.includes(input))
108
102
  .map((pkg) => ({
109
103
  name: `${pkg.name ? `${chalk.bold(pkg.name)} ` : ''}${pkg.path} ${chalk.dim(`--filter ${pkg.name || pkg.path}`)}`,
110
104
  value: pkg.path,
111
105
  })),
112
106
  });
113
- return result;
114
107
  }
115
108
  return selected.path;
116
109
  }
@@ -346,6 +339,10 @@ export default class BaseCommand extends Command {
346
339
  catch (err) {
347
340
  debug(`${this.name()}:onEnd`)(`Command: ${command}. Telemetry tracking failed: ${err instanceof Error ? err.message : err?.toString()}`);
348
341
  }
342
+ // Inquirer prompts reject with this on Ctrl+C, but the error class isn't exported.
343
+ if (error_ instanceof Error && error_.name === 'ExitPromptError') {
344
+ return exit(EXIT_CODES.INTERRUPTED);
345
+ }
349
346
  if (error_ !== undefined) {
350
347
  logError(error_ instanceof Error ? error_ : format(error_));
351
348
  exit(1);
@@ -1,6 +1,6 @@
1
1
  import { resolve } from 'path';
2
2
  import { LocalState } from '@netlify/dev-utils';
3
- import inquirer from 'inquirer';
3
+ import { input } from '@inquirer/prompts';
4
4
  import { normalizeRepoUrl } from '../../utils/normalize-repo-url.js';
5
5
  import { chalk, logAndThrowError, log, getToken, netlifyCommand } from '../../utils/command-helpers.js';
6
6
  import { runGit } from '../../utils/run-git.js';
@@ -19,15 +19,10 @@ const isNetlifyGitServiceUrl = (repoUrl) => {
19
19
  }
20
20
  };
21
21
  const getTargetDir = async (defaultDir) => {
22
- const { selectedDir } = await inquirer.prompt([
23
- {
24
- type: 'input',
25
- name: 'selectedDir',
26
- message: 'Where should we clone the repository?',
27
- default: defaultDir,
28
- },
29
- ]);
30
- return selectedDir;
22
+ return await input({
23
+ message: 'Where should we clone the repository?',
24
+ default: defaultDir,
25
+ });
31
26
  };
32
27
  const cloneRepo = async (repoUrl, targetDir, debug) => {
33
28
  try {
@@ -2,7 +2,7 @@ import fs from 'fs';
2
2
  import { homedir } from 'os';
3
3
  import { dirname, join } from 'path';
4
4
  import { fileURLToPath } from 'url';
5
- import inquirer from 'inquirer';
5
+ import { confirm } from '@inquirer/prompts';
6
6
  import { install, uninstall } from '@pnpm/tabtab';
7
7
  import { generateAutocompletion } from '../../lib/completion/index.js';
8
8
  import { logAndThrowError, log, chalk, checkFileForLine, TABTAB_CONFIG_LINE, AUTOLOAD_COMPINIT, } from '../../utils/command-helpers.js';
@@ -30,14 +30,10 @@ export const completionGenerate = async (_options, command) => {
30
30
  !checkFileForLine(zshConfigFilepath, AUTOLOAD_COMPINIT)) {
31
31
  log(`To enable Tabtab autocompletion with zsh, the following line may need to be added to your ~/.zshrc:`);
32
32
  log(chalk.bold.cyan(`\n${AUTOLOAD_COMPINIT}\n`));
33
- const { compinitAdded } = await inquirer.prompt([
34
- {
35
- type: 'confirm',
36
- name: 'compinitAdded',
37
- message: `Would you like to add it?`,
38
- default: true,
39
- },
40
- ]);
33
+ const compinitAdded = await confirm({
34
+ message: `Would you like to add it?`,
35
+ default: true,
36
+ });
41
37
  if (compinitAdded) {
42
38
  fs.readFile(zshConfigFilepath, 'utf8', (_err, data) => {
43
39
  const updatedZshFile = AUTOLOAD_COMPINIT + '\n' + data;
@@ -6,13 +6,13 @@ import process from 'process';
6
6
  import readline from 'readline';
7
7
  import { pipeline } from 'stream/promises';
8
8
  import { promisify } from 'util';
9
- import inquirer from 'inquirer';
10
9
  import fetch from 'node-fetch';
10
+ import { select } from '@inquirer/prompts';
11
11
  import { LocalState } from '@netlify/dev-utils';
12
- import { Octokit } from '@octokit/rest';
13
12
  import { chalk, logAndThrowError, log, logJson, warn } from '../../utils/command-helpers.js';
14
13
  import { ensureNetlifyIgnore } from '../../utils/gitignore.js';
15
14
  import { getGitHubToken as promptForGitHubToken } from '../../utils/gh-auth.js';
15
+ import { requestGitHub } from '../../utils/github-api.js';
16
16
  import { startSpinner, stopSpinner } from '../../lib/spinner.js';
17
17
  import { isInteractive } from '../../utils/scripted-commands.js';
18
18
  import { track } from '../../utils/telemetry/index.js';
@@ -25,8 +25,7 @@ const resolveGitHubToken = async (globalConfig) => {
25
25
  const cached = globalConfig.get(`users.${userId}.auth.github`);
26
26
  if (cached?.token) {
27
27
  try {
28
- const octokit = new Octokit({ auth: `token ${cached.token}` });
29
- await octokit.rest.users.getAuthenticated();
28
+ await requestGitHub(cached.token, 'GET', '/user');
30
29
  return cached.token;
31
30
  }
32
31
  catch {
@@ -91,9 +90,8 @@ const selectRepoOwner = async (ghToken, repoOwnerFlag) => {
91
90
  if (repoOwnerFlag) {
92
91
  return repoOwnerFlag;
93
92
  }
94
- const octokit = new Octokit({ auth: `token ${ghToken}` });
95
- const { data: user } = await octokit.rest.users.getAuthenticated();
96
- const { data: orgs } = await octokit.rest.orgs.listForAuthenticatedUser();
93
+ const user = await requestGitHub(ghToken, 'GET', '/user');
94
+ const orgs = await requestGitHub(ghToken, 'GET', '/user/orgs');
97
95
  if (orgs.length === 0) {
98
96
  return user.login;
99
97
  }
@@ -101,15 +99,10 @@ const selectRepoOwner = async (ghToken, repoOwnerFlag) => {
101
99
  { name: `${user.login} (personal)`, value: user.login },
102
100
  ...orgs.map((org) => ({ name: org.login, value: org.login })),
103
101
  ];
104
- const { owner } = await inquirer.prompt([
105
- {
106
- type: 'list',
107
- name: 'owner',
108
- message: 'Where should the GitHub repo be created?',
109
- choices,
110
- },
111
- ]);
112
- return owner;
102
+ return await select({
103
+ message: 'Where should the GitHub repo be created?',
104
+ choices,
105
+ });
113
106
  };
114
107
  // TODO: Replace with api client call once the site repo endpoint is added to @netlify/open-api
115
108
  const createGitHubRepo = async (siteId, repoName, providerToken, repoOwner, api, apiOpts) => {
@@ -197,18 +190,13 @@ export const createAction = async (promptArg, options, command) => {
197
190
  accountSlug = accountSlugFlag;
198
191
  }
199
192
  else if (accounts.length > 1) {
200
- const { accountSlug: selected } = await inquirer.prompt([
201
- {
202
- type: 'list',
203
- name: 'accountSlug',
204
- message: 'Team:',
205
- choices: accounts.map((account) => ({
206
- value: account.slug,
207
- name: account.name,
208
- })),
209
- },
210
- ]);
211
- accountSlug = selected;
193
+ accountSlug = await select({
194
+ message: 'Team:',
195
+ choices: accounts.map((account) => ({
196
+ value: account.slug,
197
+ name: account.name,
198
+ })),
199
+ });
212
200
  }
213
201
  else {
214
202
  accountSlug = accounts[0]?.slug;
@@ -2,7 +2,7 @@ import { existsSync } from 'fs';
2
2
  import { mkdir, readdir, writeFile } from 'fs/promises';
3
3
  import { join } from 'path';
4
4
  import { applyMigrations } from '@netlify/dev';
5
- import inquirer from 'inquirer';
5
+ import { confirm, select } from '@inquirer/prompts';
6
6
  import { chalk, log, netlifyCommand } from '../../utils/command-helpers.js';
7
7
  import { startSpinner, stopSpinner } from '../../lib/spinner.js';
8
8
  import { isInteractive } from '../../utils/scripted-commands.js';
@@ -32,14 +32,10 @@ const success = (text) => {
32
32
  };
33
33
  const carefullyWriteFile = async (filePath, data, projectRoot) => {
34
34
  if (existsSync(filePath)) {
35
- const answers = await inquirer.prompt([
36
- {
37
- type: 'confirm',
38
- name: 'overwrite',
39
- message: `Overwrite existing file .${filePath.replace(projectRoot, '')}?`,
40
- },
41
- ]);
42
- if (answers.overwrite) {
35
+ const overwrite = await confirm({
36
+ message: `Overwrite existing file .${filePath.replace(projectRoot, '')}?`,
37
+ });
38
+ if (overwrite) {
43
39
  await writeFile(filePath, data);
44
40
  }
45
41
  }
@@ -62,19 +58,14 @@ const promptForQueryStyle = async (interactive) => {
62
58
  return 'drizzle';
63
59
  }
64
60
  log('');
65
- const { queryStyle } = await inquirer.prompt([
66
- {
67
- type: 'list',
68
- name: 'queryStyle',
69
- message: 'What is your preferred style?',
70
- default: 'drizzle',
71
- choices: [
72
- { name: 'Drizzle ORM (recommended)', value: 'drizzle' },
73
- { name: 'Direct SQL', value: 'raw' },
74
- ],
75
- },
76
- ]);
77
- return queryStyle;
61
+ return await select({
62
+ message: 'What is your preferred style?',
63
+ default: 'drizzle',
64
+ choices: [
65
+ { name: 'Drizzle ORM (recommended)', value: 'drizzle' },
66
+ { name: 'Direct SQL', value: 'raw' },
67
+ ],
68
+ });
78
69
  };
79
70
  const promptForStarter = async (interactive) => {
80
71
  if (!interactive) {
@@ -84,15 +75,10 @@ const promptForStarter = async (interactive) => {
84
75
  log('To see the database in action, we can create some migrations with sample data');
85
76
  log('and then query it. Alternatively, you can do this yourself at any time.');
86
77
  log('');
87
- const { answer } = await inquirer.prompt([
88
- {
89
- type: 'confirm',
90
- name: 'answer',
91
- message: 'Do you want to create sample data?',
92
- default: true,
93
- },
94
- ]);
95
- return answer;
78
+ return await confirm({
79
+ message: 'Do you want to create sample data?',
80
+ default: true,
81
+ });
96
82
  };
97
83
  const installDependencies = async (pm, projectRoot, migrationsDirectory, queryStyle) => {
98
84
  sectionHeading('Install dependencies');
@@ -1,6 +1,6 @@
1
1
  import { readdir, mkdir, writeFile } from 'fs/promises';
2
2
  import { join } from 'path';
3
- import inquirer from 'inquirer';
3
+ import { input, select } from '@inquirer/prompts';
4
4
  import { log, logJson } from '../../utils/command-helpers.js';
5
5
  import { resolveMigrationsDirectory } from './util/migrations-path.js';
6
6
  import { utcTimestampPrefix } from './util/timestamp.js';
@@ -69,15 +69,10 @@ export const migrationNew = async (options, command) => {
69
69
  let scheme = options.scheme;
70
70
  if (!description) {
71
71
  if (isInteractive()) {
72
- const answers = await inquirer.prompt([
73
- {
74
- type: 'input',
75
- name: 'description',
76
- message: 'What is the purpose of this migration?',
77
- validate: (input) => (input.trim().length > 0 ? true : 'Description cannot be empty'),
78
- },
79
- ]);
80
- description = answers.description;
72
+ description = await input({
73
+ message: 'What is the purpose of this migration?',
74
+ validate: (value) => (value.trim().length > 0 ? true : 'Description cannot be empty'),
75
+ });
81
76
  }
82
77
  else {
83
78
  throw new Error(`--description <description> argument is required when not running interactively. Provide a description of the migration (e.g. --description "add users table").`);
@@ -86,19 +81,14 @@ export const migrationNew = async (options, command) => {
86
81
  if (!scheme) {
87
82
  const defaultScheme = detectedScheme ?? 'timestamp';
88
83
  if (isInteractive()) {
89
- const answers = await inquirer.prompt([
90
- {
91
- type: 'list',
92
- name: 'scheme',
93
- message: 'Numbering scheme:',
94
- choices: [
95
- { name: 'Timestamp (e.g. 20260312143000) [Recommended]', value: 'timestamp' },
96
- { name: 'Sequential (e.g. 0001, 0002, ...)', value: 'sequential' },
97
- ],
98
- default: defaultScheme,
99
- },
100
- ]);
101
- scheme = answers.scheme;
84
+ scheme = await select({
85
+ message: 'Numbering scheme:',
86
+ choices: [
87
+ { name: 'Timestamp (e.g. 20260312143000) [Recommended]', value: 'timestamp' },
88
+ { name: 'Sequential (e.g. 0001, 0002, ...)', value: 'sequential' },
89
+ ],
90
+ default: defaultScheme,
91
+ });
102
92
  }
103
93
  else {
104
94
  scheme = defaultScheme;
@@ -1,6 +1,6 @@
1
1
  import { rm, mkdir, writeFile } from 'fs/promises';
2
2
  import { dirname, resolve, isAbsolute } from 'path';
3
- import inquirer from 'inquirer';
3
+ import { confirm } from '@inquirer/prompts';
4
4
  import { log, logJson } from '../../utils/command-helpers.js';
5
5
  import execa from '../../utils/execa.js';
6
6
  import { readApiErrorMessage } from './util/api-errors.js';
@@ -95,14 +95,10 @@ export const migrationPull = async (options, command) => {
95
95
  return filePath;
96
96
  });
97
97
  if (!force) {
98
- const { confirmed } = await inquirer.prompt([
99
- {
100
- type: 'confirm',
101
- name: 'confirmed',
102
- message: `This will overwrite all local migrations in ${migrationsDirectory} with ${String(migrations.length)} migration${migrations.length === 1 ? '' : 's'} from ${branch}. Continue?`,
103
- default: false,
104
- },
105
- ]);
98
+ const confirmed = await confirm({
99
+ message: `This will overwrite all local migrations in ${migrationsDirectory} with ${String(migrations.length)} migration${migrations.length === 1 ? '' : 's'} from ${branch}. Continue?`,
100
+ default: false,
101
+ });
106
102
  if (!confirmed) {
107
103
  log('Pull cancelled.');
108
104
  return;
@@ -1,5 +1,5 @@
1
1
  import { rm } from 'fs/promises';
2
- import inquirer from 'inquirer';
2
+ import { confirm } from '@inquirer/prompts';
3
3
  import { resetDatabase } from '@netlify/dev';
4
4
  import { log, logJson } from '../../utils/command-helpers.js';
5
5
  import { isInteractive } from '../../utils/scripted-commands.js';
@@ -12,14 +12,10 @@ const discardLocalDatabase = async (error, options) => {
12
12
  throw new Error(`${error.summary}\nRe-run with --force to delete ${directory} and start from an empty database.`);
13
13
  }
14
14
  log(error.summary);
15
- const { confirmed } = await inquirer.prompt([
16
- {
17
- type: 'confirm',
18
- name: 'confirmed',
19
- message: `Delete ${directory} and start from an empty database?`,
20
- default: false,
21
- },
22
- ]);
15
+ const confirmed = await confirm({
16
+ message: `Delete ${directory} and start from an empty database?`,
17
+ default: false,
18
+ });
23
19
  if (!confirmed) {
24
20
  log('Reset cancelled.');
25
21
  return;
@@ -2,8 +2,8 @@ import { randomBytes } from 'crypto';
2
2
  import { stat } from 'fs/promises';
3
3
  import { basename, resolve } from 'path';
4
4
  import { stdin, stdout } from 'process';
5
+ import { confirm, input, select } from '@inquirer/prompts';
5
6
  import { runCoreSteps } from '@netlify/build';
6
- import inquirer from 'inquirer';
7
7
  import { parseAllHeaders } from '@netlify/headers-parser';
8
8
  import { parseAllRedirects } from '@netlify/redirect-parser';
9
9
  import prettyjson from 'prettyjson';
@@ -89,16 +89,11 @@ const getDeployFolder = async ({ command, config, options, site, siteData, }) =>
89
89
  // Generate copy-pasteable command with current options
90
90
  const copyableCommand = generateDeployCommand({ ...options, dir: '<PATH>' }, [], command);
91
91
  log(`\nTo specify directory non-interactively, use: ${copyableCommand}\n`);
92
- const { promptPath } = await inquirer.prompt([
93
- {
94
- type: 'input',
95
- name: 'promptPath',
96
- message: 'Publish directory',
97
- default: '.',
98
- filter: (input) => resolve(command.workingDir, input),
99
- },
100
- ]);
101
- deployFolder = promptPath;
92
+ const promptPath = await input({
93
+ message: 'Publish directory',
94
+ default: '.',
95
+ });
96
+ deployFolder = resolve(command.workingDir, promptPath);
102
97
  }
103
98
  return deployFolder;
104
99
  };
@@ -276,14 +271,10 @@ const prepareProductionDeploy = async ({ api, siteData, options, command }) => {
276
271
  log('\nTo override deployment lock (USE WITH CAUTION), use:');
277
272
  log(` ${overrideCommand}`);
278
273
  log('\nWarning: Only use --prod-if-unlocked if you are absolutely sure you want to override the deployment lock.\n');
279
- const { unlockChoice } = await inquirer.prompt([
280
- {
281
- type: 'confirm',
282
- name: 'unlockChoice',
283
- message: 'Would you like to "unlock" deployments for production context to proceed?',
284
- default: false,
285
- },
286
- ]);
274
+ const unlockChoice = await confirm({
275
+ message: 'Would you like to "unlock" deployments for production context to proceed?',
276
+ default: false,
277
+ });
287
278
  if (!unlockChoice)
288
279
  exit(0);
289
280
  await api.unlockDeploy({ deploy_id: siteData.published_deploy.id });
@@ -812,23 +803,19 @@ const promptForSiteAction = async (options, command, site) => {
812
803
  if (availableTeams.length > 1) {
813
804
  log(`\nYou must pick a --team: ${availableTeams.map((team) => team.slug).join(', ')}`);
814
805
  }
815
- const { initChoice } = await inquirer.prompt([
816
- {
817
- type: 'list',
818
- name: 'initChoice',
819
- message: 'What would you like to do?',
820
- choices: [
821
- {
822
- name: '⇄ Link this directory to an existing project',
823
- value: 'link',
824
- },
825
- {
826
- name: '+ Create & configure a new project',
827
- value: 'create',
828
- },
829
- ],
830
- },
831
- ]);
806
+ const initChoice = await select({
807
+ message: 'What would you like to do?',
808
+ choices: [
809
+ {
810
+ name: '⇄ Link this directory to an existing project',
811
+ value: 'link',
812
+ },
813
+ {
814
+ name: '+ Create & configure a new project',
815
+ value: 'create',
816
+ },
817
+ ],
818
+ });
832
819
  const siteData = initChoice === 'create' ? await sitesCreate({}, command) : await link({}, command);
833
820
  site.id = siteData.id;
834
821
  return siteData;
@@ -1,7 +1,7 @@
1
1
  import ansiEscapes from 'ansi-escapes';
2
2
  import AsciiTable from 'ascii-table';
3
3
  import { isCI } from 'ci-info';
4
- import inquirer from 'inquirer';
4
+ import { confirm } from '@inquirer/prompts';
5
5
  import logUpdate from 'log-update';
6
6
  import { chalk, log, logJson } from '../../utils/command-helpers.js';
7
7
  import { SUPPORTED_CONTEXTS, getEnvelopeEnv, getHumanReadableScopes } from '../../utils/env/index.js';
@@ -69,14 +69,10 @@ export const envList = async (options, command) => {
69
69
  return false;
70
70
  }
71
71
  logUpdate(getTable({ environment, hideValues: true, scopesColumn: true }));
72
- const { showValues } = await inquirer.prompt([
73
- {
74
- type: 'confirm',
75
- name: 'showValues',
76
- message: 'Show values?',
77
- default: false,
78
- },
79
- ]);
72
+ const showValues = await confirm({
73
+ message: 'Show values?',
74
+ default: false,
75
+ });
80
76
  if (showValues) {
81
77
  // since inquirer adds a prompt, we need to account for it when printing the table again
82
78
  log(ansiEscapes.eraseLines(3));