netlify 27.10.1 → 27.11.0

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
@@ -5,9 +5,9 @@ import { createRequire } from 'module';
5
5
  import path, { dirname, join, relative } from 'path';
6
6
  import process from 'process';
7
7
  import { fileURLToPath, pathToFileURL } from 'url';
8
+ import { confirm, input, search, select, Separator } from '@inquirer/prompts';
8
9
  import { findUp } from 'find-up';
9
10
  import fuzzy from 'fuzzy';
10
- import inquirer from 'inquirer';
11
11
  import fetch from 'node-fetch';
12
12
  import { createSpinner } from 'nanospinner';
13
13
  import { fileExistsAsync } from '../../lib/fs.js';
@@ -57,18 +57,12 @@ const getNameFromArgs = async function (argumentName, options, defaultName) {
57
57
  validateFunctionName(argumentName);
58
58
  return argumentName;
59
59
  }
60
- const { name } = await inquirer.prompt([
61
- {
62
- name: 'name',
63
- message: 'Name your function:',
64
- default: defaultName,
65
- type: 'input',
66
- validate: (val) => isValidFunctionName(val),
67
- // make sure it is not undefined and is a valid filename.
68
- // this has some nuance i have ignored, eg crossenv and i18n concerns
69
- },
70
- ]);
71
- return name;
60
+ return await input({
61
+ message: 'Name your function:',
62
+ default: defaultName,
63
+ // this has some nuance i have ignored, eg crossenv and i18n concerns
64
+ validate: (val) => isValidFunctionName(val),
65
+ });
72
66
  };
73
67
  // @ts-expect-error TS(7006) FIXME: Parameter 'registry' implicitly has an 'any' type.
74
68
  const filterRegistry = function (registry, input) {
@@ -140,7 +134,7 @@ const formatRegistryArrayForInquirer = async function (lang, funcType) {
140
134
  // @ts-expect-error TS(7031) FIXME: Binding element 'languageFromFlag' implicitly has ... Remove this comment to see the full error message
141
135
  const pickTemplate = async function ({ language: languageFromFlag, template: templateFromFlag }, funcType) {
142
136
  const specialCommands = [
143
- new inquirer.Separator(),
137
+ new Separator(),
144
138
  {
145
139
  name: `Clone template from GitHub URL`,
146
140
  value: 'url',
@@ -151,20 +145,17 @@ const pickTemplate = async function ({ language: languageFromFlag, template: tem
151
145
  value: 'report',
152
146
  short: 'gh-report',
153
147
  },
154
- new inquirer.Separator(),
148
+ new Separator(),
155
149
  ];
156
150
  let language = languageFromFlag;
157
151
  if (language === undefined) {
158
152
  const langs = funcType === 'edge'
159
153
  ? languages.filter((lang) => lang.value === 'javascript' || lang.value === 'typescript')
160
154
  : languages.filter(Boolean);
161
- const { language: languageFromPrompt } = await inquirer.prompt({
155
+ language = await select({
162
156
  choices: langs,
163
157
  message: 'Select the language of your function',
164
- name: 'language',
165
- type: 'list',
166
158
  });
167
- language = languageFromPrompt;
168
159
  }
169
160
  let templatesForLanguage;
170
161
  try {
@@ -180,12 +171,9 @@ const pickTemplate = async function ({ language: languageFromFlag, template: tem
180
171
  }
181
172
  return match.value;
182
173
  }
183
- const { chosenTemplate } = await inquirer.prompt({
184
- name: 'chosenTemplate',
174
+ const chosenTemplate = await search({
185
175
  message: 'Pick a template',
186
- // @ts-expect-error TS(2769) FIXME: No overload matches this call.
187
- type: 'autocomplete',
188
- source(_answersSoFar, input) {
176
+ source(input) {
189
177
  // if Edge Functions template, don't show url option
190
178
  // @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'Separator... Remove this comment to see the full error message
191
179
  const edgeCommands = specialCommands.filter((val) => val.value !== 'url');
@@ -207,15 +195,10 @@ const selectTypeOfFunc = async () => {
207
195
  { name: 'Edge function (Deno)', value: 'edge' },
208
196
  { name: 'Serverless function (Node)', value: 'serverless' },
209
197
  ];
210
- const { functionType } = await inquirer.prompt([
211
- {
212
- name: 'functionType',
213
- message: "Select the type of function you'd like to create",
214
- type: 'list',
215
- choices: functionTypes,
216
- },
217
- ]);
218
- return functionType;
198
+ return await select({
199
+ message: "Select the type of function you'd like to create",
200
+ choices: functionTypes,
201
+ });
219
202
  };
220
203
  /**
221
204
  * @param {import('../base-command.js').default} command
@@ -248,14 +231,10 @@ const promptFunctionsDirectory = async (command) => {
248
231
  if (!site.id) {
249
232
  return logAndThrowError(`${NETLIFYDEVERR} No project id found, please run inside a project directory or \`netlify link\``);
250
233
  }
251
- const { functionsDir } = await inquirer.prompt([
252
- {
253
- type: 'input',
254
- name: 'functionsDir',
255
- message: 'Enter the path, relative to your project, where your functions should live:',
256
- default: 'netlify/functions',
257
- },
258
- ]);
234
+ const functionsDir = await input({
235
+ message: 'Enter the path, relative to your project, where your functions should live:',
236
+ default: 'netlify/functions',
237
+ });
259
238
  try {
260
239
  log(`${NETLIFYDEVLOG} updating project settings with ${chalk.magenta.inverse(functionsDir)}`);
261
240
  await api.updateSite({
@@ -405,16 +384,10 @@ const scaffoldFromTemplate = async function (command, options, argumentName, fun
405
384
  // pull the rest of the metadata from the template
406
385
  const chosenTemplate = await pickTemplate(options, funcType);
407
386
  if (chosenTemplate === 'url') {
408
- const { chosenUrl } = await inquirer.prompt([
409
- {
410
- name: 'chosenUrl',
411
- message: 'URL to clone: ',
412
- type: 'input',
413
- validate: (/** @type {string} */ val) => Boolean(validateRepoURL(val)),
414
- // make sure it is not undefined and is a valid filename.
415
- // this has some nuance i have ignored, eg crossenv and i18n concerns
416
- },
417
- ]);
387
+ const chosenUrl = await input({
388
+ message: 'URL to clone: ',
389
+ validate: (val) => Boolean(validateRepoURL(val)),
390
+ });
418
391
  options.url = chosenUrl.trim();
419
392
  try {
420
393
  await downloadFromURL(command, options, argumentName, functionsDir);
@@ -522,14 +495,10 @@ const handleAddonDidInstall = async ({ addonCreated, addonDidInstall, command, f
522
495
  if (!addonCreated || !addonDidInstall) {
523
496
  return;
524
497
  }
525
- const { confirmPostInstall } = await inquirer.prompt([
526
- {
527
- type: 'confirm',
528
- name: 'confirmPostInstall',
529
- message: `This template has an optional setup script that runs after addon install. This can be helpful for first time users to try out templates. Run the script?`,
530
- default: false,
531
- },
532
- ]);
498
+ const confirmPostInstall = await confirm({
499
+ message: `This template has an optional setup script that runs after addon install. This can be helpful for first time users to try out templates. Run the script?`,
500
+ default: false,
501
+ });
533
502
  if (!confirmPostInstall) {
534
503
  return;
535
504
  }
@@ -590,19 +559,14 @@ const registerEFInToml = async (funcName, options) => {
590
559
  if (!fs.existsSync(configFilePath)) {
591
560
  log(`${NETLIFYDEVLOG} \`${relConfigFilePath}\` file does not exist yet. Creating it...`);
592
561
  }
593
- let { funcPath } = await inquirer.prompt([
594
- {
595
- type: 'input',
596
- name: 'funcPath',
597
- message: `What route do you want your edge function to be invoked on?`,
598
- default: '/test',
599
- validate: (val) => Boolean(val),
600
- // Make sure route isn't undefined and is valid
601
- // Todo: add more validation?
602
- },
603
- ]);
562
+ let funcPath = await input({
563
+ message: `What route do you want your edge function to be invoked on?`,
564
+ default: '/test',
565
+ // Todo: add more validation?
566
+ validate: (val) => Boolean(val),
567
+ });
604
568
  // Make sure path begins with a '/'
605
- if (funcPath[0] !== '/') {
569
+ if (!funcPath.startsWith('/')) {
606
570
  funcPath = `/${funcPath}`;
607
571
  }
608
572
  const functionRegister = `\n\n[[edge_functions]]\nfunction = "${funcName}"\npath = "${funcPath}"`;
@@ -1,7 +1,7 @@
1
1
  import fs from 'fs';
2
2
  import { createRequire } from 'module';
3
3
  import path from 'path';
4
- import inquirer from 'inquirer';
4
+ import { select } from '@inquirer/prompts';
5
5
  import fetch from 'node-fetch';
6
6
  import { NETLIFYDEVWARN, chalk, logAndThrowError, exit } from '../../utils/command-helpers.js';
7
7
  import { BACKGROUND, CLOCKWORK_USERAGENT, getFunctions } from '../../utils/functions/index.js';
@@ -94,15 +94,10 @@ const getNameFromArgs = async function (functions, options, argumentName) {
94
94
  }
95
95
  console.warn(`Function name ${chalk.yellow(functionToTrigger)} supplied but no matching function found in your functions folder, forcing you to pick a valid one...`);
96
96
  }
97
- const { trigger } = await inquirer.prompt([
98
- {
99
- type: 'list',
100
- message: 'Pick a function to trigger',
101
- name: 'trigger',
102
- choices: functionNames,
103
- },
104
- ]);
105
- return trigger;
97
+ return await select({
98
+ message: 'Pick a function to trigger',
99
+ choices: functionNames,
100
+ });
106
101
  };
107
102
  /**
108
103
  * get the function name out of the argument or options
@@ -1,4 +1,4 @@
1
- import inquirer from 'inquirer';
1
+ import { confirm, select } from '@inquirer/prompts';
2
2
  import { isEmpty } from '../../utils/object-utilities.js';
3
3
  import { chalk, exit, log, netlifyCommand } from '../../utils/command-helpers.js';
4
4
  import getRepoData from '../../utils/get-repo-data.js';
@@ -38,13 +38,9 @@ const createNewSiteAndExit = async ({ command, state, disableLinking, customizeE
38
38
  log();
39
39
  persistState({ state, siteInfo });
40
40
  if (!disableLinking) {
41
- const { shouldConfigureBuild } = await inquirer.prompt([
42
- {
43
- type: 'confirm',
44
- name: 'shouldConfigureBuild',
45
- message: `Do you want to configure build settings? We'll suggest settings for your project automatically`,
46
- },
47
- ]);
41
+ const shouldConfigureBuild = await confirm({
42
+ message: `Do you want to configure build settings? We'll suggest settings for your project automatically`,
43
+ });
48
44
  if (shouldConfigureBuild) {
49
45
  const { cachedConfig: { configPath }, config, repositoryRoot, } = command.netlify;
50
46
  const { baseDir, buildCmd, buildDir, functionsDir } = await getBuildSettings({
@@ -112,14 +108,10 @@ git remote add origin https://github.com/YourUserName/RepoName.git
112
108
  }
113
109
  const NEW_SITE_NO_GIT = 'Yes, create and deploy project manually';
114
110
  const NO_ABORT = 'No, I will connect this directory with GitHub first';
115
- const { noGitRemoteChoice } = await inquirer.prompt([
116
- {
117
- type: 'list',
118
- name: 'noGitRemoteChoice',
119
- message: 'Do you want to create a Netlify project without a git repository?',
120
- choices: [NEW_SITE_NO_GIT, NO_ABORT],
121
- },
122
- ]);
111
+ const noGitRemoteChoice = await select({
112
+ message: 'Do you want to create a Netlify project without a git repository?',
113
+ choices: [NEW_SITE_NO_GIT, NO_ABORT],
114
+ });
123
115
  if (noGitRemoteChoice === NEW_SITE_NO_GIT) {
124
116
  // TODO(ndhoule): Shove a custom error message in here
125
117
  return createNewSiteAndExit({ state, command, disableLinking, customizeExitMessage });
@@ -133,15 +125,10 @@ const createOrLinkSiteToRepo = async (command) => {
133
125
  const NEW_SITE = '+ Create & configure a new project';
134
126
  const EXISTING_SITE = '⇄ Connect this directory to an existing Netlify project';
135
127
  const initializeOpts = [EXISTING_SITE, NEW_SITE];
136
- // TODO(serhalp): inquirer should infer the choice type here, but doesn't. Fix.
137
- const { initChoice } = await inquirer.prompt([
138
- {
139
- type: 'list',
140
- name: 'initChoice',
141
- message: 'What would you like to do?',
142
- choices: initializeOpts,
143
- },
144
- ]);
128
+ const initChoice = await select({
129
+ message: 'What would you like to do?',
130
+ choices: initializeOpts,
131
+ });
145
132
  // create site or search for one
146
133
  if (initChoice === NEW_SITE) {
147
134
  await track('sites_initStarted', {
@@ -1,5 +1,5 @@
1
1
  import assert from 'node:assert';
2
- import inquirer from 'inquirer';
2
+ import { input, select } from '@inquirer/prompts';
3
3
  import { isEmpty } from '../../utils/object-utilities.js';
4
4
  import { listSites } from '../../lib/api.js';
5
5
  import { startSpinner } from '../../lib/spinner.js';
@@ -43,17 +43,13 @@ To search for projects:
43
43
  return firstSite;
44
44
  }
45
45
  spinner.warn({ text: `Found ${matchingSites.length} projects connected to ${repoUrl}` });
46
- const { selectedSite } = await inquirer.prompt([
47
- {
48
- type: 'list',
49
- name: 'selectedSite',
50
- message: 'Which project do you want to link?',
51
- choices: matchingSites.map((matchingSite) => ({
52
- name: `${matchingSite.name} - ${matchingSite.ssl_url}`,
53
- value: matchingSite,
54
- })),
55
- },
56
- ]);
46
+ const selectedSite = await select({
47
+ message: 'Which project do you want to link?',
48
+ choices: matchingSites.map((matchingSite) => ({
49
+ name: `${matchingSite.name} - ${matchingSite.ssl_url}`,
50
+ value: matchingSite,
51
+ })),
52
+ });
57
53
  if (!selectedSite) {
58
54
  return logAndThrowError('No project selected');
59
55
  }
@@ -77,14 +73,10 @@ const linkPrompt = async (command, options) => {
77
73
  log();
78
74
  log(`${chalk.cyanBright(`${netlifyCommand()} link`)} will connect this folder to a project on Netlify`);
79
75
  log();
80
- const { linkType } = await inquirer.prompt([
81
- {
82
- type: 'list',
83
- name: 'linkType',
84
- message: 'How do you want to link this folder to a project?',
85
- choices: linkChoices,
86
- },
87
- ]);
76
+ const linkType = await select({
77
+ message: 'How do you want to link this folder to a project?',
78
+ choices: linkChoices,
79
+ });
88
80
  let kind;
89
81
  switch (linkType) {
90
82
  case GIT_REMOTE_PROMPT: {
@@ -96,13 +88,9 @@ const linkPrompt = async (command, options) => {
96
88
  }
97
89
  case SITE_NAME_PROMPT: {
98
90
  kind = 'byName';
99
- const { searchTerm } = await inquirer.prompt([
100
- {
101
- type: 'input',
102
- name: 'searchTerm',
103
- message: 'Enter the project name (or just part of it):',
104
- },
105
- ]);
91
+ const searchTerm = await input({
92
+ message: 'Enter the project name (or just part of it):',
93
+ });
106
94
  log(`Looking for projects with names containing '${searchTerm}'...`);
107
95
  log();
108
96
  let matchingSites = [];
@@ -134,15 +122,10 @@ To create a new project:
134
122
  }
135
123
  if (matchingSites.length > 1) {
136
124
  log(`Found ${matchingSites.length} matching projects!`);
137
- const { selectedSite } = await inquirer.prompt([
138
- {
139
- type: 'list',
140
- name: 'selectedSite',
141
- message: 'Which project do you want to link?',
142
- paginated: true,
143
- choices: matchingSites.map((matchingSite) => ({ name: matchingSite.name, value: matchingSite })),
144
- },
145
- ]);
125
+ const selectedSite = await select({
126
+ message: 'Which project do you want to link?',
127
+ choices: matchingSites.map((matchingSite) => ({ name: matchingSite.name, value: matchingSite })),
128
+ });
146
129
  if (!selectedSite) {
147
130
  return logAndThrowError('No project selected');
148
131
  }
@@ -168,15 +151,10 @@ To create a new project:
168
151
  if (!sites || sites.length === 0) {
169
152
  return logAndThrowError(`You don't have any projects yet. Run ${chalk.cyanBright(`${netlifyCommand()} sites:create`)} to create a project.`);
170
153
  }
171
- const { selectedSite } = await inquirer.prompt([
172
- {
173
- type: 'list',
174
- name: 'selectedSite',
175
- message: 'Which project do you want to link?',
176
- paginated: true,
177
- choices: sites.map((matchingSite) => ({ name: matchingSite.name, value: matchingSite })),
178
- },
179
- ]);
154
+ const selectedSite = await select({
155
+ message: 'Which project do you want to link?',
156
+ choices: sites.map((matchingSite) => ({ name: matchingSite.name, value: matchingSite })),
157
+ });
180
158
  if (!selectedSite) {
181
159
  return logAndThrowError('No project selected');
182
160
  }
@@ -185,13 +163,9 @@ To create a new project:
185
163
  }
186
164
  case SITE_ID_PROMPT: {
187
165
  kind = 'bySiteId';
188
- const { siteId } = await inquirer.prompt([
189
- {
190
- type: 'input',
191
- name: 'siteId',
192
- message: 'What is the project ID?',
193
- },
194
- ]);
166
+ const siteId = await input({
167
+ message: 'What is the project ID?',
168
+ });
195
169
  try {
196
170
  site = await api.getSite({ siteId });
197
171
  }
@@ -1,4 +1,4 @@
1
- import inquirer from 'inquirer';
1
+ import { select } from '@inquirer/prompts';
2
2
  import { log, chalk } from '../../utils/command-helpers.js';
3
3
  import { getWebSocket } from '../../utils/websockets/index.js';
4
4
  export function getName({ deploy, userId }) {
@@ -41,9 +41,7 @@ export const logsBuild = async (_options, command) => {
41
41
  }
42
42
  let [deploy] = deploys;
43
43
  if (deploys.length > 1) {
44
- const { result } = await inquirer.prompt({
45
- name: 'result',
46
- type: 'list',
44
+ const result = await select({
47
45
  message: `Select a deploy\n\n${chalk.yellow('*')} indicates a deploy created by you`,
48
46
  choices: deploys.map((dep) => ({
49
47
  name: getName({ deploy: dep, userId }),
@@ -2,7 +2,7 @@ import process from 'process';
2
2
  import { Option } from 'commander';
3
3
  import envinfo from 'envinfo';
4
4
  import { closest } from 'fastest-levenshtein';
5
- import inquirer from 'inquirer';
5
+ import { confirm } from '@inquirer/prompts';
6
6
  import { getGlobalConfigStore } from '@netlify/dev-utils';
7
7
  import { BANG, chalk, logAndThrowError, exit, log, NETLIFY_CYAN, USER_AGENT, logError, } from '../utils/command-helpers.js';
8
8
  import execa from '../utils/execa.js';
@@ -170,22 +170,15 @@ const mainCommand = async function (options, command) {
170
170
  logError(`Run ${NETLIFY_CYAN(`${command.name()} help`)} for a list of available commands.`);
171
171
  exit(EXIT_CODES.USAGE_ERROR);
172
172
  }
173
- const applySuggestion = await new Promise((resolve) => {
174
- const prompt = inquirer.prompt({
175
- type: 'confirm',
176
- name: 'suggestion',
177
- message: `Did you mean ${chalk.blue(suggestion)}`,
178
- default: false,
179
- });
180
- setTimeout(() => {
181
- // @ts-expect-error TS(2445) FIXME: Property 'close' is protected and only accessible ... Remove this comment to see the full error message
182
- prompt.ui.close();
183
- resolve(false);
184
- }, SUGGESTION_TIMEOUT);
185
- prompt.then((value) => {
186
- resolve(value.suggestion);
187
- });
188
- });
173
+ let applySuggestion = false;
174
+ try {
175
+ applySuggestion = await confirm({ message: `Did you mean ${chalk.blue(suggestion)}`, default: false }, { signal: AbortSignal.timeout(SUGGESTION_TIMEOUT) });
176
+ }
177
+ catch (error) {
178
+ if (!(error instanceof Error && error.name === 'AbortPromptError')) {
179
+ throw error;
180
+ }
181
+ }
189
182
  // create new log line
190
183
  log();
191
184
  if (!applySuggestion) {
@@ -1,6 +1,6 @@
1
1
  import { basename } from 'path';
2
2
  import { closest } from 'fastest-levenshtein';
3
- import inquirer from 'inquirer';
3
+ import { confirm } from '@inquirer/prompts';
4
4
  import { NETLIFYDEVERR, chalk, log } from '../../utils/command-helpers.js';
5
5
  import { getRecipe, listRecipes } from './common.js';
6
6
  const SUGGESTION_TIMEOUT = 1e4;
@@ -29,21 +29,11 @@ export const recipesCommand = async (recipeName, options, command) => {
29
29
  const recipes = await listRecipes();
30
30
  const recipeNames = recipes.map(({ name }) => name);
31
31
  const suggestion = closest(recipeName, recipeNames);
32
- const applySuggestion = await new Promise((resolve) => {
33
- const prompt = inquirer.prompt({
34
- type: 'confirm',
35
- name: 'suggestion',
36
- message: `Did you mean ${chalk.blue(suggestion)}`,
37
- default: false,
38
- });
39
- setTimeout(() => {
40
- // @ts-expect-error TS(2445) FIXME: Property 'close' is protected and only accessible ... Remove this comment to see the full error message
41
- prompt.ui.close();
42
- resolve(false);
43
- }, SUGGESTION_TIMEOUT);
44
- prompt.then((value) => {
45
- resolve(value.suggestion);
46
- });
32
+ const applySuggestion = await confirm({ message: `Did you mean ${chalk.blue(suggestion)}`, default: false }, { signal: AbortSignal.timeout(SUGGESTION_TIMEOUT) }).catch((error) => {
33
+ if (error instanceof Error && error.name === 'AbortPromptError') {
34
+ return false;
35
+ }
36
+ throw error;
47
37
  });
48
38
  if (applySuggestion) {
49
39
  return recipesCommand(suggestion, options, command);
@@ -1,4 +1,4 @@
1
- import inquirer from 'inquirer';
1
+ import { input, select } from '@inquirer/prompts';
2
2
  import { pick } from '../../utils/object-utilities.js';
3
3
  import prettyjson from 'prettyjson';
4
4
  import { chalk, logAndThrowError, log, logJson, warn } from '../../utils/command-helpers.js';
@@ -11,15 +11,10 @@ import { MAX_SITE_NAME_LENGTH } from '../../utils/validation.js';
11
11
  import { link } from '../link/link.js';
12
12
  export const getSiteNameInput = async (name) => {
13
13
  if (!name) {
14
- const { name: nameInput } = await inquirer.prompt([
15
- {
16
- type: 'input',
17
- name: 'name',
18
- message: 'Project name (leave blank for a random name; you can change it later):',
19
- validate: (input) => /^[a-zA-Z\d-]+$/.test(input || undefined) || 'Only alphanumeric characters and hyphens are allowed',
20
- },
21
- ]);
22
- name = typeof nameInput === 'string' ? nameInput : '';
14
+ name = await input({
15
+ message: 'Project name (leave blank for a random name; you can change it later):',
16
+ validate: (value) => !value || /^[a-zA-Z\d-]+$/.test(value) || 'Only alphanumeric characters and hyphens are allowed',
17
+ });
23
18
  }
24
19
  return { name };
25
20
  };
@@ -34,18 +29,13 @@ export const sitesCreate = async (options, command) => {
34
29
  log(`Using team: ${team.name}`);
35
30
  }
36
31
  else {
37
- const { accountSlug: accountSlugInput } = await inquirer.prompt([
38
- {
39
- type: 'list',
40
- name: 'accountSlug',
41
- message: 'Team:',
42
- choices: accounts.map((account) => ({
43
- value: account.slug,
44
- name: account.name,
45
- })),
46
- },
47
- ]);
48
- accountSlug = accountSlugInput;
32
+ accountSlug = await select({
33
+ message: 'Team:',
34
+ choices: accounts.map((account) => ({
35
+ value: account.slug,
36
+ name: account.name,
37
+ })),
38
+ });
49
39
  }
50
40
  }
51
41
  let site;
@@ -1,4 +1,4 @@
1
- import inquirer from 'inquirer';
1
+ import { confirm } from '@inquirer/prompts';
2
2
  import { chalk, logAndThrowError, exit, log } from '../../utils/command-helpers.js';
3
3
  export const sitesDelete = async (siteId, options, command) => {
4
4
  command.setAnalyticsPayload({ force: options.force });
@@ -27,9 +27,7 @@ export const sitesDelete = async (siteId, options, command) => {
27
27
  log();
28
28
  log(chalk.bold('Be careful here. There is no undo!'));
29
29
  log();
30
- const { wantsToDelete } = await inquirer.prompt({
31
- type: 'confirm',
32
- name: 'wantsToDelete',
30
+ const wantsToDelete = await confirm({
33
31
  message: `WARNING: Are you sure you want to delete the "${siteData.name}" project?`,
34
32
  default: false,
35
33
  });
@@ -47,9 +45,7 @@ export const sitesDelete = async (siteId, options, command) => {
47
45
  log();
48
46
  log(`Verify this project ID "${siteId}" supplied is correct and proceed.`);
49
47
  log('To skip this prompt, pass a --force flag to the delete command');
50
- const { wantsToDelete } = await inquirer.prompt({
51
- type: 'confirm',
52
- name: 'wantsToDelete',
48
+ const wantsToDelete = await confirm({
53
49
  message: `Verify & Proceed with deletion of project "${siteId}"?`,
54
50
  default: false,
55
51
  });
@@ -1,4 +1,4 @@
1
- import inquirer from 'inquirer';
1
+ import { select } from '@inquirer/prompts';
2
2
  import { chalk, log } from '../../utils/command-helpers.js';
3
3
  import { login } from '../login/login.js';
4
4
  const LOGIN_NEW = 'I would like to login to a new account';
@@ -16,14 +16,10 @@ export const switchCommand = async (options, command) => {
16
16
  log(`No account found matching ${chalk.bold(options.email)}, showing all available accounts.`);
17
17
  log('');
18
18
  }
19
- const { accountSwitchChoice } = await inquirer.prompt([
20
- {
21
- type: 'list',
22
- name: 'accountSwitchChoice',
23
- message: 'Please select the account you want to use:',
24
- choices: [...Object.entries(availableUsersChoices).map(([, val]) => val), LOGIN_NEW],
25
- },
26
- ]);
19
+ const accountSwitchChoice = await select({
20
+ message: 'Please select the account you want to use:',
21
+ choices: [...Object.entries(availableUsersChoices).map(([, val]) => val), LOGIN_NEW],
22
+ });
27
23
  if (accountSwitchChoice === LOGIN_NEW) {
28
24
  await login({ new: true }, command);
29
25
  }
@@ -1,5 +1,5 @@
1
1
  import { env, stdin, stdout } from 'process';
2
- import inquirer from 'inquirer';
2
+ import { confirm } from '@inquirer/prompts';
3
3
  import { runRecipe } from '../../commands/recipes/recipes.js';
4
4
  const STATE_PROMPT_PROPERTY = 'promptVSCodeSettings';
5
5
  // @ts-expect-error TS(7031) FIXME: Binding element 'NETLIFYDEVLOG' implicitly has an ... Remove this comment to see the full error message
@@ -18,13 +18,11 @@ export const promptEditorHelper = async ({ NETLIFYDEVLOG, chalk, config, log, re
18
18
  }
19
19
  state.set(STATE_PROMPT_PROPERTY, true);
20
20
  const message = 'Would you like to configure VS Code to use Edge Functions?';
21
- const { confirm } = await inquirer.prompt({
22
- type: 'confirm',
23
- name: 'confirm',
21
+ const confirmed = await confirm({
24
22
  message,
25
23
  default: true,
26
24
  });
27
- if (!confirm) {
25
+ if (!confirmed) {
28
26
  log(`${NETLIFYDEVLOG} You can start this configuration manually by running ${chalk.magenta.bold('netlify recipes vscode')}.`);
29
27
  return;
30
28
  }