Skip to content
Open
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
68 changes: 28 additions & 40 deletions packages/cli/src/commands/init/editTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,15 @@ export function validatePackageName(packageName: string) {
/^([a-zA-Z]([a-zA-Z0-9_])*\.)+[a-zA-Z]([a-zA-Z0-9_])*$/u;

if (packageNameParts.length < 2) {
throw `The package name ${packageName} is invalid. It should contain at least two segments, e.g. com.app`;
throw new CLIError(
`The package name ${packageName} is invalid. It should contain at least two segments, e.g. com.app`,
);
}

if (!packageNameRegex.test(packageName)) {
throw `The ${packageName} package name is not valid. It can contain only alphanumeric characters and dots.`;
throw new CLIError(
`The ${packageName} package name is not valid. It can contain only alphanumeric characters and dots.`,
);
}
}

Expand All @@ -43,9 +47,8 @@ export async function replaceNameInUTF8File(
logger.debug(`Replacing in ${filePath}`);
const fileContent = await fs.readFile(filePath, 'utf8');
const replacedFileContent = fileContent
.replace(new RegExp(templateName, 'g'), projectName)
.replace(
new RegExp(templateName.toLowerCase(), 'g'),
.replace(new RegExp(templateName, 'g'), () => projectName)
.replace(new RegExp(templateName.toLowerCase(), 'g'), () =>
projectName.toLowerCase(),
);

Expand All @@ -57,7 +60,7 @@ export async function replaceNameInUTF8File(
async function renameFile(filePath: string, oldName: string, newName: string) {
const newFileName = path.join(
path.dirname(filePath),
path.basename(filePath).replace(new RegExp(oldName, 'g'), newName),
path.basename(filePath).replace(new RegExp(oldName, 'g'), () => newName),
);

logger.debug(`Renaming ${filePath} -> file:${newFileName}`);
Expand All @@ -70,11 +73,16 @@ function shouldRenameFile(filePath: string, nameToReplace: string) {
}

function shouldIgnoreFile(filePath: string) {
return filePath.match(/node_modules|yarn.lock|package-lock.json/g);
return path
.relative(process.cwd(), filePath)
.split(path.sep)
.some((part) =>
['node_modules', 'yarn.lock', 'package-lock.json'].includes(part),
);
}

function isIosFile(filePath: string) {
return filePath.includes('ios');
return path.relative(process.cwd(), filePath).split(path.sep)[0] === 'ios';
}

const UNDERSCORED_DOTFILES = [
Expand All @@ -93,7 +101,9 @@ const UNDERSCORED_DOTFILES = [
];

async function processDotfiles(filePath: string) {
const dotfile = UNDERSCORED_DOTFILES.find((e) => filePath.includes(`_${e}`));
const dotfile = UNDERSCORED_DOTFILES.find(
(e) => path.basename(filePath) === `_${e}`,
);

if (dotfile === undefined) {
return;
Expand All @@ -106,36 +116,14 @@ async function createAndroidPackagePaths(
filePath: string,
packageName: string,
) {
const pathParts = filePath.split('/').slice(-2);

if (pathParts[0] === 'java' && pathParts[1] === 'com') {
const pathToFolders = filePath.split('/').slice(0, -2).join('/');
const segmentsList = packageName.split('.');

if (segmentsList.length > 1) {
const initialDir = process.cwd();
process.chdir(filePath.split('/').slice(0, -1).join('/'));

try {
await fs.rename(
`${filePath}/${segmentsList.join('.')}`,
`${pathToFolders}/${segmentsList[segmentsList.length - 1]}`,
);
await fs.rmdir(filePath);

for (const segment of segmentsList) {
fs.mkdirSync(segment);
process.chdir(segment);
}
await fs.rename(
`${pathToFolders}/${segmentsList[segmentsList.length - 1]}`,
process.cwd(),
);
} catch {
throw 'Failed to create correct paths for Android.';
}

process.chdir(initialDir);
const javaPath = path.dirname(filePath);
if (path.basename(javaPath) === 'java' && path.basename(filePath) === 'com') {
const segments = packageName.split('.');
const destination = path.join(javaPath, ...segments);
await fs.ensureDir(path.dirname(destination));
await fs.rename(path.join(filePath, packageName), destination);
if (segments[0] !== 'com') {
await fs.rmdir(filePath);
}
}
}
Expand Down Expand Up @@ -166,7 +154,7 @@ export async function replacePlaceholderWithPackageName({
'PRODUCT_BUNDLE_IDENTIFIER = "(.*)"',
);

if (filePath.includes('app.json')) {
if (path.basename(filePath) === 'app.json') {
await replaceNameInUTF8File(filePath, projectName, placeholderName);
} else {
const fileExtension = path.extname(filePath);
Expand Down
7 changes: 4 additions & 3 deletions packages/cli/src/commands/init/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import path from 'path';
import {logger, CLIError} from '@react-native-community/cli-tools';
import * as PackageManager from '../../tools/packageManager';
import copyFiles from '../../tools/copyFiles';
import replacePathSepForRegex from '../../tools/replacePathSepForRegex';
import fs from 'fs';
import pico from 'picocolors';
import {getYarnVersionIfAvailable} from '../../tools/yarn';
Expand Down Expand Up @@ -104,9 +103,11 @@ export async function copyTemplate(
);

logger.debug(`Copying template from ${templatePath}`);
let regexStr = path.resolve(templatePath, 'node_modules');
const nodeModulesPath = path
.resolve(templatePath, 'node_modules')
.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
await copyFiles(templatePath, process.cwd(), {
exclude: [new RegExp(replacePathSepForRegex(regexStr))],
exclude: [new RegExp(`^${nodeModulesPath}(?:[/\\\\]|$)`)],
});
}

Expand Down
56 changes: 12 additions & 44 deletions packages/cli/src/tools/copyFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@

import fs from 'fs';
import path from 'path';
import {promisify} from 'util';
import walk from './walk';

const copyBinaryFile = promisify(fs.copyFile);

type Options = {
exclude?: Array<RegExp>;
};
Expand All @@ -21,12 +24,13 @@ async function copyFiles(
destPath: string,
options: Options = {},
) {
const files = walk(
srcPath,
(filePath) =>
options.exclude?.some((p) => filePath.search(p) !== -1) ?? false,
);
return Promise.all(
walk(srcPath).map(async (absoluteSrcFilePath: string) => {
const exclude = options.exclude;
if (exclude && exclude.some((p) => p.test(absoluteSrcFilePath))) {
return;
}
files.map(async (absoluteSrcFilePath: string) => {
const relativeFilePath = path.relative(srcPath, absoluteSrcFilePath);
await copyFile(
absoluteSrcFilePath,
Expand All @@ -39,7 +43,7 @@ async function copyFiles(
/**
* Copy a file to given destination.
*/
function copyFile(srcPath: string, destPath: string) {
async function copyFile(srcPath: string, destPath: string) {
if (fs.lstatSync(srcPath).isDirectory()) {
if (!fs.existsSync(destPath)) {
fs.mkdirSync(destPath);
Expand All @@ -48,44 +52,8 @@ function copyFile(srcPath: string, destPath: string) {
return;
}

return new Promise((resolve, reject) => {
copyBinaryFile(srcPath, destPath, (err) => {
if (err) {
reject(err);
}
resolve(destPath);
});
});
}

/**
* Same as 'cp' on Unix. Don't do any replacements.
*/
function copyBinaryFile(
srcPath: string,
destPath: string,
cb: (err?: Error) => void,
) {
let cbCalled = false;
const {mode} = fs.statSync(srcPath);
const readStream = fs.createReadStream(srcPath);
const writeStream = fs.createWriteStream(destPath, {mode});
readStream.on('error', (err) => {
done(err);
});
writeStream.on('error', (err) => {
done(err);
});
readStream.on('close', () => {
done();
});
readStream.pipe(writeStream);
function done(err?: Error) {
if (!cbCalled) {
cb(err);
cbCalled = true;
}
}
await copyBinaryFile(srcPath, destPath);
return destPath;
}

export default copyFiles;
10 changes: 8 additions & 2 deletions packages/cli/src/tools/walk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,20 @@
import fs from 'fs';
import path from 'path';

function walk(current: string): string[] {
function walk(
current: string,
exclude?: (filePath: string) => boolean,
): string[] {
if (exclude?.(current)) {
return [];
}
if (!fs.lstatSync(current).isDirectory()) {
return [current];
}

const files = fs
.readdirSync(current)
.map((child) => walk(path.join(current, child)));
.map((child) => walk(path.join(current, child), exclude));
const result: string[] = [];
return result.concat.apply([current], files);
}
Expand Down
Loading