UNPKG

@rushstack/heft

Version:

Build all your JavaScript projects the same way: A way that works.

99 lines 5.04 kB
"use strict"; // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. Object.defineProperty(exports, "__esModule", { value: true }); exports.deleteFilesAsync = deleteFilesAsync; const node_core_library_1 = require("@rushstack/node-core-library"); const Constants_1 = require("../utilities/Constants"); const FileGlobSpecifier_1 = require("./FileGlobSpecifier"); async function _getPathsToDeleteAsync(rootFolderPath, deleteOperations) { const result = { filesToDelete: new Set(), foldersToDelete: new Set() }; await node_core_library_1.Async.forEachAsync(deleteOperations, async (deleteOperation) => { const absoluteSpecifier = (0, FileGlobSpecifier_1.asAbsoluteFileSelectionSpecifier)(rootFolderPath, deleteOperation); // Glob the files under the source path and add them to the set of files to delete const sourcePaths = await (0, FileGlobSpecifier_1.getFileSelectionSpecifierPathsAsync)({ fileGlobSpecifier: absoluteSpecifier, includeFolders: true }); for (const [sourcePath, dirent] of sourcePaths) { // If the sourcePath is a folder, add it to the foldersToDelete set. Otherwise, add it to // the filesToDelete set. Symlinks and junctions are treated as files, and thus will fall // into the filesToDelete set. if (dirent.isDirectory()) { result.foldersToDelete.add(sourcePath); } else { result.filesToDelete.add(sourcePath); } } }, { concurrency: Constants_1.Constants.maxParallelism }); return result; } async function deleteFilesAsync(rootFolderPath, deleteOperations, terminal) { const pathsToDelete = await _getPathsToDeleteAsync(rootFolderPath, deleteOperations); await _deleteFilesInnerAsync(pathsToDelete, terminal); } async function _deleteFilesInnerAsync(pathsToDelete, terminal) { let deletedFiles = 0; let deletedFolders = 0; const { filesToDelete, foldersToDelete } = pathsToDelete; await node_core_library_1.Async.forEachAsync(filesToDelete, async (pathToDelete) => { try { await node_core_library_1.FileSystem.deleteFileAsync(pathToDelete, { throwIfNotExists: true }); terminal.writeVerboseLine(`Deleted "${pathToDelete}".`); deletedFiles++; } catch (error) { // If it doesn't exist, we can ignore the error. if (!node_core_library_1.FileSystem.isNotExistError(error)) { throw error; } } }, { concurrency: Constants_1.Constants.maxParallelism }); // Reverse the list of matching folders. Assuming that the list of folders came from // the globber, the folders will be specified in tree-walk order, so by reversing the // list we delete the deepest folders first and avoid not-exist errors for subfolders // of an already-deleted parent folder. const reversedFoldersToDelete = Array.from(foldersToDelete).reverse(); // Clear out any folders that were encountered during the file deletion process. This // will recursively delete the folder and it's contents. There are two scenarios that // this handles: // - Deletions of empty folder structures (ex. when the delete glob is '**/*') // - Deletions of folders that still contain files (ex. when the delete glob is 'lib') // In the latter scenario, the count of deleted files will not be tracked. However, // this is a fair trade-off for the performance benefit of not having to glob the // folder structure again. await node_core_library_1.Async.forEachAsync(reversedFoldersToDelete, async (folderToDelete) => { try { await node_core_library_1.FileSystem.deleteFolderAsync(folderToDelete); terminal.writeVerboseLine(`Deleted folder "${folderToDelete}".`); deletedFolders++; } catch (error) { // If it doesn't exist, we can ignore the error. if (!node_core_library_1.FileSystem.isNotExistError(error)) { throw error; } } }, { concurrency: Constants_1.Constants.maxParallelism }); if (deletedFiles > 0 || deletedFolders > 0) { terminal.writeLine(`Deleted ${deletedFiles} file${deletedFiles !== 1 ? 's' : ''} ` + `and ${deletedFolders} folder${deletedFolders !== 1 ? 's' : ''}`); } } const PLUGIN_NAME = 'delete-files-plugin'; class DeleteFilesPlugin { apply(taskSession, heftConfiguration, pluginOptions) { taskSession.hooks.registerFileOperations.tap(PLUGIN_NAME, (fileOperations) => { for (const deleteOperation of pluginOptions.deleteOperations) { fileOperations.deleteOperations.add(deleteOperation); } return fileOperations; }); } } exports.default = DeleteFilesPlugin; //# sourceMappingURL=DeleteFilesPlugin.js.map