From f4a8932a498bdc5cea605838e126b9bd00929ebc Mon Sep 17 00:00:00 2001 From: Andre Bandarra Date: Tue, 8 Sep 2020 09:50:52 +0100 Subject: [PATCH 1/4] Added the `rmdir` method This function is used to delete a file or directory hierarchy and will be used to clean an existing project before regenerating it. --- packages/core/src/lib/util.ts | 21 +++++++++++++++++ packages/core/src/spec/lib/utilSpec.ts | 32 ++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/packages/core/src/lib/util.ts b/packages/core/src/lib/util.ts index c0853b5b..ec5df8c1 100644 --- a/packages/core/src/lib/util.ts +++ b/packages/core/src/lib/util.ts @@ -17,6 +17,7 @@ import * as extractZip from 'extract-zip'; import fetch from 'node-fetch'; import * as fs from 'fs'; +import {join} from 'path'; import {promisify} from 'util'; import {exec, execFile, spawn} from 'child_process'; import {x as extractTar} from 'tar'; @@ -224,3 +225,23 @@ export function validatePackageId(input: string): string | null{ } return null; } + +/** + * Removes a file or directory. If the path is a directory, recursively deletes files and + * directories inside it. + */ +export async function rmdirs(path: string): Promise { + const stat = await fs.promises.stat(path); + + // This is a regular file. Just delete it. + if (stat.isFile()) { + await fs.promises.unlink(path); + return; + } + + // This is a directory. We delete files and sub directories inside it, then delete the + // directory itself. + const entries = fs.readdirSync(path); + await Promise.all(entries.map((entry) => rmdirs(join(path, entry)))); + await fs.promises.rmdir(path); +}; diff --git a/packages/core/src/spec/lib/utilSpec.ts b/packages/core/src/spec/lib/utilSpec.ts index 95751572..e2406a5d 100644 --- a/packages/core/src/spec/lib/utilSpec.ts +++ b/packages/core/src/spec/lib/utilSpec.ts @@ -15,6 +15,8 @@ */ import * as util from '../../lib/util'; +import * as mockFs from 'mock-fs'; +import {existsSync} from 'fs'; describe('util', () => { describe('#findSuitableIcon', () => { @@ -224,4 +226,34 @@ describe('util', () => { expect(util.validatePackageId('_com.char.1twa')).not.toBeNull(); }); }); + + describe('rmdirs', () => { + it('Deletes a single file', async () => { + mockFs({'/app.txt': 'Test Content'}); + await util.rmdirs('/app.txt'); + expect(existsSync('/app.txt')).toBeFalse(); + mockFs.restore(); + }); + + it('Deletes a directory', async () => { + mockFs({ + '/test': { + 'app.txt': 'Test Content', + 'subdirectory': { + 'file2.txt': 'Test Content 2', + }, + }, + '/other-file.txt': 'This should not be deleted', + }); + await util.rmdirs('/test'); + expect(existsSync('/test')).toBeFalse(); + expect(existsSync('/other-file.txt')).toBeTrue(); + }); + + it('Skips empty directory', () => { + mockFs({}); + expectAsync(util.rmdirs('/app.txt')).toBeResolved(); + mockFs.restore(); + }); + }); }); From 443aa0183fc4766c082a2f5ebb53d1f724f1c9b4 Mon Sep 17 00:00:00 2001 From: Andre Bandarra Date: Tue, 8 Sep 2020 10:00:23 +0100 Subject: [PATCH 2/4] Added missing mockFs.restore call --- packages/core/src/spec/lib/utilSpec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core/src/spec/lib/utilSpec.ts b/packages/core/src/spec/lib/utilSpec.ts index e2406a5d..9b9e672f 100644 --- a/packages/core/src/spec/lib/utilSpec.ts +++ b/packages/core/src/spec/lib/utilSpec.ts @@ -248,6 +248,7 @@ describe('util', () => { await util.rmdirs('/test'); expect(existsSync('/test')).toBeFalse(); expect(existsSync('/other-file.txt')).toBeTrue(); + mockFs.restore(); }); it('Skips empty directory', () => { From 3b001ec07ebf3a101a687ee27887b28852bc02e5 Mon Sep 17 00:00:00 2001 From: Andre Bandarra Date: Tue, 8 Sep 2020 10:13:54 +0100 Subject: [PATCH 3/4] Renamed rmdirs to rmdir --- packages/core/src/lib/util.ts | 4 ++-- packages/core/src/spec/lib/utilSpec.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/src/lib/util.ts b/packages/core/src/lib/util.ts index ec5df8c1..4ba7a13a 100644 --- a/packages/core/src/lib/util.ts +++ b/packages/core/src/lib/util.ts @@ -230,7 +230,7 @@ export function validatePackageId(input: string): string | null{ * Removes a file or directory. If the path is a directory, recursively deletes files and * directories inside it. */ -export async function rmdirs(path: string): Promise { +export async function rmdir(path: string): Promise { const stat = await fs.promises.stat(path); // This is a regular file. Just delete it. @@ -242,6 +242,6 @@ export async function rmdirs(path: string): Promise { // This is a directory. We delete files and sub directories inside it, then delete the // directory itself. const entries = fs.readdirSync(path); - await Promise.all(entries.map((entry) => rmdirs(join(path, entry)))); + await Promise.all(entries.map((entry) => rmdir(join(path, entry)))); await fs.promises.rmdir(path); }; diff --git a/packages/core/src/spec/lib/utilSpec.ts b/packages/core/src/spec/lib/utilSpec.ts index 9b9e672f..b3d62f1b 100644 --- a/packages/core/src/spec/lib/utilSpec.ts +++ b/packages/core/src/spec/lib/utilSpec.ts @@ -230,7 +230,7 @@ describe('util', () => { describe('rmdirs', () => { it('Deletes a single file', async () => { mockFs({'/app.txt': 'Test Content'}); - await util.rmdirs('/app.txt'); + await util.rmdir('/app.txt'); expect(existsSync('/app.txt')).toBeFalse(); mockFs.restore(); }); @@ -245,7 +245,7 @@ describe('util', () => { }, '/other-file.txt': 'This should not be deleted', }); - await util.rmdirs('/test'); + await util.rmdir('/test'); expect(existsSync('/test')).toBeFalse(); expect(existsSync('/other-file.txt')).toBeTrue(); mockFs.restore(); @@ -253,7 +253,7 @@ describe('util', () => { it('Skips empty directory', () => { mockFs({}); - expectAsync(util.rmdirs('/app.txt')).toBeResolved(); + expectAsync(util.rmdir('/app.txt')).toBeResolved(); mockFs.restore(); }); }); From dc7423115133af9b2e8fa455e9b0c9ad4847f53c Mon Sep 17 00:00:00 2001 From: Andre Bandarra Date: Tue, 8 Sep 2020 10:53:35 +0100 Subject: [PATCH 4/4] Checks if file exists before calling stat - stat fails on MacOs when the file doesn't exist. --- packages/core/src/lib/util.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/core/src/lib/util.ts b/packages/core/src/lib/util.ts index 4ba7a13a..0afb88e9 100644 --- a/packages/core/src/lib/util.ts +++ b/packages/core/src/lib/util.ts @@ -231,6 +231,9 @@ export function validatePackageId(input: string): string | null{ * directories inside it. */ export async function rmdir(path: string): Promise { + if (!fs.existsSync(path)) { + return; + } const stat = await fs.promises.stat(path); // This is a regular file. Just delete it.