Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: log files in selected directory and add option to replace them #2100

Merged
merged 4 commits into from
Jan 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions __e2e__/init.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,33 @@ if (process.platform === 'win32') {
templatePath = `file://${templatePath}`;
}

test('init fails if the directory already exists', () => {
test('init fails if the directory already exists and --replace-directory false', () => {
fs.mkdirSync(path.join(DIR, PROJECT_NAME));

const {stderr} = runCLI(DIR, ['init', PROJECT_NAME], {expectedFailure: true});
const {stderr} = runCLI(
DIR,
['init', PROJECT_NAME, '--replace-directory', 'false'],
{expectedFailure: true},
);

expect(stderr).toContain(
`error Cannot initialize new project because directory "${PROJECT_NAME}" already exists.`,
);
});

test('init should ask and print files in directory if exist', () => {
fs.mkdirSync(path.join(DIR, PROJECT_NAME));
fs.writeFileSync(path.join(DIR, PROJECT_NAME, 'package.json'), '{}');

const {stdout, stderr} = runCLI(DIR, ['init', PROJECT_NAME]);

expect(stdout).toContain(`Do you want to replace existing files?`);
expect(stderr).toContain(
`warn The directory ${PROJECT_NAME} contains files that will be overwritten:`,
);
expect(stderr).toContain(`package.json`);
});

test('init should prompt for the project name', () => {
const {stdout} = runCLI(DIR, ['init']);

Expand Down
5 changes: 5 additions & 0 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ Skip dependencies installation
Determine if CocoaPods should be installed when initializing a project. If set to `true` it will install pods, if set to `false`, it will skip the step entirely. If not used, prompt will be displayed

#### `--npm`

> [!WARNING]
> `--npm` is deprecated and will be removed in the future. Please use `--pm npm` instead.

Expand All @@ -121,6 +122,10 @@ Create project with custom package name for Android and bundle identifier for iO

Skip git repository initialization.

#### `--replace-directory <string>`

Replaces the directory if it already exists

### `upgrade`

Usage:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {CLIError} from '@react-native-community/cli-tools';
export default class DirectoryAlreadyExistsError extends CLIError {
constructor(directory: string) {
super(
`Cannot initialize new project because directory "${directory}" already exists.`,
`Cannot initialize new project because directory "${directory}" already exists. Please remove or rename the directory and try again.`,
);
}
}
4 changes: 4 additions & 0 deletions packages/cli/src/commands/init/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,9 @@ export default {
name: '--skip-git-init',
description: 'Skip git repository initialization',
},
{
name: '--replace-directory [boolean]',
description: 'Replaces the directory if it already exists.',
},
],
};
72 changes: 62 additions & 10 deletions packages/cli/src/commands/init/init.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import os from 'os';
import path from 'path';
import fs from 'fs-extra';
import fs, {readdirSync} from 'fs-extra';
import {validateProjectName} from './validate';
import chalk from 'chalk';
import DirectoryAlreadyExistsError from './errors/DirectoryAlreadyExistsError';
import printRunInstructions from './printRunInstructions';
import {
CLIError,
Expand Down Expand Up @@ -35,6 +34,7 @@ import {
} from './git';
import semver from 'semver';
import {executeCommand} from '../../tools/executeCommand';
import DirectoryAlreadyExistsError from './errors/DirectoryAlreadyExistsError';

const DEFAULT_VERSION = 'latest';

Expand All @@ -51,6 +51,7 @@ type Options = {
installPods?: string | boolean;
platformName?: string;
skipGitInit?: boolean;
replaceDirectory?: string | boolean;
};

interface TemplateOptions {
Expand All @@ -65,10 +66,12 @@ interface TemplateOptions {
packageName?: string;
installCocoaPods?: string | boolean;
version?: string;
replaceDirectory?: string | boolean;
}

interface TemplateReturnType {
didInstallPods?: boolean;
replaceDirectory?: string | boolean;
}

// Here we are defining explicit version of Yarn to be used in the new project because in some cases providing `3.x` don't work.
Expand Down Expand Up @@ -101,8 +104,58 @@ function doesDirectoryExist(dir: string) {
return fs.existsSync(dir);
}

async function setProjectDirectory(directory: string) {
function getConflictsForDirectory(directory: string) {
return readdirSync(directory);
}

async function setProjectDirectory(
directory: string,
replaceDirectory: string,
) {
const directoryExists = doesDirectoryExist(directory);

if (replaceDirectory === 'false' && directoryExists) {
throw new DirectoryAlreadyExistsError(directory);
}

let deleteDirectory = false;

if (replaceDirectory === 'true' && directoryExists) {
deleteDirectory = true;
} else if (directoryExists) {
const conflicts = getConflictsForDirectory(directory);

if (conflicts.length > 0) {
let warnMessage = `The directory ${chalk.bold(
directory,
)} contains files that will be overwritten:\n`;

for (const conflict of conflicts) {
warnMessage += ` ${conflict}\n`;
}

logger.warn(warnMessage);

const {replace} = await prompt({
type: 'confirm',
name: 'replace',
message: 'Do you want to replace existing files?',
});

deleteDirectory = replace;

if (!replace) {
throw new DirectoryAlreadyExistsError(directory);
}
}
}

try {
if (deleteDirectory) {
fs.removeSync(directory);
}

fs.mkdirSync(directory, {recursive: true});
process.chdir(directory);
} catch (error) {
throw new CLIError(
Expand Down Expand Up @@ -145,6 +198,7 @@ async function createFromTemplate({
skipInstall,
packageName,
installCocoaPods,
replaceDirectory,
}: TemplateOptions): Promise<TemplateReturnType> {
logger.debug('Initializing new project');
// Only print out the banner if we're not in a CI
Expand Down Expand Up @@ -173,7 +227,10 @@ async function createFromTemplate({
// if the project with the name already has cache, remove the cache to avoid problems with pods installation
cacheManager.removeProjectCache(projectName);

const projectDirectory = await setProjectDirectory(directory);
const projectDirectory = await setProjectDirectory(
directory,
String(replaceDirectory),
);

const loader = getLoader({text: 'Downloading template'});
const templateSourceDir = fs.mkdtempSync(
Expand Down Expand Up @@ -361,6 +418,7 @@ async function createProject(
packageName: options.packageName,
installCocoaPods: options.installPods,
version,
replaceDirectory: options.replaceDirectory,
});
}

Expand Down Expand Up @@ -406,12 +464,6 @@ export default (async function initialize(
return;
}

if (doesDirectoryExist(projectFolder)) {
throw new DirectoryAlreadyExistsError(directoryName);
} else {
fs.mkdirSync(projectFolder, {recursive: true});
}

let shouldBumpYarnVersion = true;
let shouldCreateGitRepository = false;

Expand Down