From b3c93a95844a3133cfbd22aa3ada041c046b5dbf Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Fri, 5 Nov 2021 04:35:52 -0700 Subject: [PATCH 1/6] Better Handle EPERM on Windows Use async `fs-extra` function for rename, which hooks into a `graceful-fs` implementation that can handle AV locked files on Windows. See https://github.com/isaacs/node-graceful-fs/blob/89dc1330dcd8fa218c5dff92a97d8792b7da6b12/polyfills.js#L96-L118 for details. --- .../init/__tests__/editTemplate.test.ts | 12 ++-- .../cli/src/commands/init/editTemplate.ts | 56 +++++++++---------- packages/cli/src/commands/init/init.ts | 2 +- 3 files changed, 34 insertions(+), 36 deletions(-) diff --git a/packages/cli/src/commands/init/__tests__/editTemplate.test.ts b/packages/cli/src/commands/init/__tests__/editTemplate.test.ts index 96ebe5868..fe3a3047b 100644 --- a/packages/cli/src/commands/init/__tests__/editTemplate.test.ts +++ b/packages/cli/src/commands/init/__tests__/editTemplate.test.ts @@ -38,10 +38,10 @@ afterEach(() => { fs.removeSync(testPath); }); -test('should edit template', () => { +test('should edit template', async () => { jest.spyOn(process, 'cwd').mockImplementation(() => testPath); - changePlaceholderInTemplate({ + await changePlaceholderInTemplate({ projectName: PROJECT_NAME, placeholderName: PLACEHOLDER_NAME, }); @@ -90,10 +90,10 @@ test('should edit template', () => { ).toMatchSnapshot(); }); -test('should edit template with custom title', () => { +test('should edit template with custom title', async () => { jest.spyOn(process, 'cwd').mockImplementation(() => testPath); - changePlaceholderInTemplate({ + await changePlaceholderInTemplate({ projectName: PROJECT_NAME, placeholderName: PLACEHOLDER_NAME, projectTitle: PROJECT_TITLE, @@ -134,8 +134,8 @@ describe('changePlaceholderInTemplate', () => { jest.resetAllMocks(); }); - test(`should produce a lowercased version of "${PROJECT_NAME}" in package.json "name" field`, () => { - changePlaceholderInTemplate({ + test(`should produce a lowercased version of "${PROJECT_NAME}" in package.json "name" field`, async () => { + await changePlaceholderInTemplate({ projectName: PROJECT_NAME, placeholderName: PLACEHOLDER_NAME, }); diff --git a/packages/cli/src/commands/init/editTemplate.ts b/packages/cli/src/commands/init/editTemplate.ts index 5df68fa4c..a9a44adf9 100644 --- a/packages/cli/src/commands/init/editTemplate.ts +++ b/packages/cli/src/commands/init/editTemplate.ts @@ -1,4 +1,4 @@ -import fs from 'fs'; +import fs from 'fs-extra'; import path from 'path'; import {logger} from '@react-native-community/cli-tools'; import walk from '../../tools/walk'; @@ -44,7 +44,7 @@ function replaceNameInUTF8File( } } -function renameFile(filePath: string, oldName: string, newName: string) { +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), @@ -52,7 +52,7 @@ function renameFile(filePath: string, oldName: string, newName: string) { logger.debug(`Renaming ${filePath} -> file:${newFileName}`); - fs.renameSync(filePath, newFileName); + await fs.rename(filePath, newFileName); } function shouldRenameFile(filePath: string, nameToReplace: string) { @@ -74,17 +74,17 @@ const UNDERSCORED_DOTFILES = [ 'editorconfig', ]; -function processDotfiles(filePath: string) { +async function processDotfiles(filePath: string) { const dotfile = UNDERSCORED_DOTFILES.find((e) => filePath.includes(`_${e}`)); if (dotfile === undefined) { return; } - renameFile(filePath, `_${dotfile}`, `.${dotfile}`); + await renameFile(filePath, `_${dotfile}`, `.${dotfile}`); } -export function changePlaceholderInTemplate({ +export async function changePlaceholderInTemplate({ projectName, placeholderName, placeholderTitle = DEFAULT_TITLE_PLACEHOLDER, @@ -92,27 +92,25 @@ export function changePlaceholderInTemplate({ }: PlaceholderConfig) { logger.debug(`Changing ${placeholderName} for ${projectName} in template`); - walk(process.cwd()) - .reverse() - .forEach((filePath: string) => { - if (shouldIgnoreFile(filePath)) { - return; - } - if (!fs.statSync(filePath).isDirectory()) { - replaceNameInUTF8File(filePath, projectName, placeholderName); - replaceNameInUTF8File(filePath, projectTitle, placeholderTitle); - } - if (shouldRenameFile(filePath, placeholderName)) { - renameFile(filePath, placeholderName, projectName); - } - if (shouldRenameFile(filePath, placeholderName.toLowerCase())) { - renameFile( - filePath, - placeholderName.toLowerCase(), - projectName.toLowerCase(), - ); - } - - processDotfiles(filePath); - }); + for (const filePath of walk(process.cwd()).reverse()) { + if (shouldIgnoreFile(filePath)) { + return; + } + if (!fs.statSync(filePath).isDirectory()) { + replaceNameInUTF8File(filePath, projectName, placeholderName); + replaceNameInUTF8File(filePath, projectTitle, placeholderTitle); + } + if (shouldRenameFile(filePath, placeholderName)) { + await renameFile(filePath, placeholderName, projectName); + } + if (shouldRenameFile(filePath, placeholderName.toLowerCase())) { + await renameFile( + filePath, + placeholderName.toLowerCase(), + projectName.toLowerCase(), + ); + } + + processDotfiles(filePath); + } } diff --git a/packages/cli/src/commands/init/init.ts b/packages/cli/src/commands/init/init.ts index 6f58f1157..79a75a057 100644 --- a/packages/cli/src/commands/init/init.ts +++ b/packages/cli/src/commands/init/init.ts @@ -111,7 +111,7 @@ async function createFromTemplate({ loader.succeed(); loader.start('Processing template'); - changePlaceholderInTemplate({ + await changePlaceholderInTemplate({ projectName, projectTitle, placeholderName: templateConfig.placeholderName, From 1c9ed794a397f10ea9b07dc5f095121df321e0d4 Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Fri, 5 Nov 2021 04:43:56 -0700 Subject: [PATCH 2/6] Missing await --- packages/cli/src/commands/init/editTemplate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/commands/init/editTemplate.ts b/packages/cli/src/commands/init/editTemplate.ts index a9a44adf9..d11ad8683 100644 --- a/packages/cli/src/commands/init/editTemplate.ts +++ b/packages/cli/src/commands/init/editTemplate.ts @@ -111,6 +111,6 @@ export async function changePlaceholderInTemplate({ ); } - processDotfiles(filePath); + await processDotfiles(filePath); } } From f4779a919282aeee956cfa56a51bd5a94e073158 Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Fri, 5 Nov 2021 04:53:36 -0700 Subject: [PATCH 3/6] fix --- packages/cli/src/commands/init/editTemplate.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/cli/src/commands/init/editTemplate.ts b/packages/cli/src/commands/init/editTemplate.ts index d11ad8683..d099dc62d 100644 --- a/packages/cli/src/commands/init/editTemplate.ts +++ b/packages/cli/src/commands/init/editTemplate.ts @@ -16,14 +16,14 @@ interface PlaceholderConfig { */ const DEFAULT_TITLE_PLACEHOLDER = 'Hello App Display Name'; -function replaceNameInUTF8File( +async function replaceNameInUTF8File( filePath: string, projectName: string, templateName: string, ) { logger.debug(`Replacing in ${filePath}`); const isPackageJson = path.basename(filePath) === 'package.json'; - const fileContent = fs.readFileSync(filePath, 'utf8'); + const fileContent = await fs.readFile(filePath, 'utf8'); const replacedFileContent = fileContent .replace(new RegExp(templateName, 'g'), projectName) .replace( @@ -32,11 +32,11 @@ function replaceNameInUTF8File( ); if (fileContent !== replacedFileContent) { - fs.writeFileSync(filePath, replacedFileContent, 'utf8'); + await fs.writeFile(filePath, replacedFileContent, 'utf8'); } if (isPackageJson) { - fs.writeFileSync( + await fs.writeFile( filePath, fileContent.replace(templateName, projectName.toLowerCase()), 'utf8', @@ -94,11 +94,11 @@ export async function changePlaceholderInTemplate({ for (const filePath of walk(process.cwd()).reverse()) { if (shouldIgnoreFile(filePath)) { - return; + continue; } if (!fs.statSync(filePath).isDirectory()) { - replaceNameInUTF8File(filePath, projectName, placeholderName); - replaceNameInUTF8File(filePath, projectTitle, placeholderTitle); + await replaceNameInUTF8File(filePath, projectName, placeholderName); + await replaceNameInUTF8File(filePath, projectTitle, placeholderTitle); } if (shouldRenameFile(filePath, placeholderName)) { await renameFile(filePath, placeholderName, projectName); From 9c41f1732d97485588f2aef2ab976d034831d5a9 Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Fri, 5 Nov 2021 08:19:34 -0700 Subject: [PATCH 4/6] Use raw fs --- packages/cli/src/commands/init/editTemplate.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/commands/init/editTemplate.ts b/packages/cli/src/commands/init/editTemplate.ts index d099dc62d..688f92b48 100644 --- a/packages/cli/src/commands/init/editTemplate.ts +++ b/packages/cli/src/commands/init/editTemplate.ts @@ -1,4 +1,4 @@ -import fs from 'fs-extra'; +import {promises as fs} from 'fs'; import path from 'path'; import {logger} from '@react-native-community/cli-tools'; import walk from '../../tools/walk'; @@ -96,7 +96,7 @@ export async function changePlaceholderInTemplate({ if (shouldIgnoreFile(filePath)) { continue; } - if (!fs.statSync(filePath).isDirectory()) { + if (!(await fs.stat(filePath)).isDirectory()) { await replaceNameInUTF8File(filePath, projectName, placeholderName); await replaceNameInUTF8File(filePath, projectTitle, placeholderTitle); } From 4d38911ef480d222679ea3fb3f852a9ff8cc5331 Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Fri, 5 Nov 2021 08:48:29 -0700 Subject: [PATCH 5/6] Revert to `fs-extra` --- packages/cli/src/commands/init/editTemplate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/commands/init/editTemplate.ts b/packages/cli/src/commands/init/editTemplate.ts index 688f92b48..e4f4e7e5f 100644 --- a/packages/cli/src/commands/init/editTemplate.ts +++ b/packages/cli/src/commands/init/editTemplate.ts @@ -1,4 +1,4 @@ -import {promises as fs} from 'fs'; +import fs from 'fs-extra'; import path from 'path'; import {logger} from '@react-native-community/cli-tools'; import walk from '../../tools/walk'; From 7d3fa95d314ae07fc602b01bee322585d08788a1 Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Fri, 5 Nov 2021 09:38:14 -0700 Subject: [PATCH 6/6] add comment --- packages/cli/src/commands/init/editTemplate.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/commands/init/editTemplate.ts b/packages/cli/src/commands/init/editTemplate.ts index e4f4e7e5f..dc43820ff 100644 --- a/packages/cli/src/commands/init/editTemplate.ts +++ b/packages/cli/src/commands/init/editTemplate.ts @@ -1,8 +1,12 @@ -import fs from 'fs-extra'; import path from 'path'; import {logger} from '@react-native-community/cli-tools'; import walk from '../../tools/walk'; +// We need `graceful-fs` behavior around async file renames on Win32. +// `gracefulify` does not support patching `fs.promises`. Use `fs-extra`, which +// exposes its own promise-based interface over `graceful-fs`. +import fs from 'fs-extra'; + interface PlaceholderConfig { projectName: string; placeholderName: string;