1import chalk from 'chalk';
2import fs from 'fs';
3import path from 'path';
4import prompts from 'prompts';
5
6import * as Template from './Template';
7import { Log } from './log';
8import { formatSelfCommand } from './resolvePackageManager';
9import { getConflictsForDirectory } from './utils/dir';
10
11export function assertValidName(folderName: string) {
12  const validation = Template.validateName(folderName);
13  if (typeof validation === 'string') {
14    Log.exit(chalk`{red Cannot create an app named {bold "${folderName}"}. ${validation}}`, 1);
15  }
16  const isFolderNameForbidden = Template.isFolderNameForbidden(folderName);
17  if (isFolderNameForbidden) {
18    Log.exit(
19      chalk`{red Cannot create an app named {bold "${folderName}"} because it would conflict with a dependency of the same name.}`,
20      1
21    );
22  }
23}
24
25export function assertFolderEmpty(projectRoot: string, folderName: string) {
26  const conflicts = getConflictsForDirectory(projectRoot);
27  if (conflicts.length) {
28    Log.log(chalk`The directory {cyan ${folderName}} has files that might be overwritten:`);
29    Log.log();
30    for (const file of conflicts) {
31      Log.log(`  ${file}`);
32    }
33    Log.log();
34    Log.exit('Try using a new directory name, or moving these files.\n');
35  }
36}
37
38export async function resolveProjectRootAsync(input: string): Promise<string> {
39  let name = input?.trim();
40
41  if (!name) {
42    const { answer } = await prompts({
43      type: 'text',
44      name: 'answer',
45      message: 'What is your app named?',
46      initial: 'my-app',
47      validate: (name) => {
48        const validation = Template.validateName(path.basename(path.resolve(name)));
49        if (typeof validation === 'string') {
50          return 'Invalid project name: ' + validation;
51        }
52        return true;
53      },
54    });
55
56    if (typeof answer === 'string') {
57      name = answer.trim();
58    }
59  }
60
61  if (!name) {
62    const selfCmd = formatSelfCommand();
63    Log.log();
64    Log.log('Please choose your app name:');
65    Log.log(chalk`  {dim $} {cyan ${selfCmd} <name>}`);
66    Log.log();
67    Log.log(`For more info, run:`);
68    Log.log(chalk`  {dim $} {cyan ${selfCmd} --help}`);
69    Log.log();
70    Log.exit('');
71  }
72
73  const projectRoot = path.resolve(name);
74  const folderName = path.basename(projectRoot);
75
76  assertValidName(folderName);
77
78  await fs.promises.mkdir(projectRoot, { recursive: true });
79
80  assertFolderEmpty(projectRoot, folderName);
81
82  return projectRoot;
83}
84