UNPKG

fipy

Version:

A simple and easy package to copy files

201 lines (161 loc) 6.73 kB
import fs from 'fs'; import path from 'path'; import { EventEmitter } from 'node:events'; import { FileCopySettings } from './fipy.settings'; export class FileCopy extends EventEmitter { constructor(private settings: FileCopySettings) { super(); } /** * Copies files from the source folder to the destination folder. Emits a fileCopied event for each file copied. * @param renameFunction A function that takes a file name as an argument and returns a new file name. If not provided, the file name is not changed. * @emits fileCopied An event that is emitted for each file copied. The event emits the source file and the destination file. * @emits error An event that is emitted if an error occurs. The event emits the error message. */ public copyFiles(renameFunction?: (file: string) => string): void { // Check if source folder exists if (!this.checkSourceFolder()) { return; } // Check if destination folder exists if (!this.checkDestinationFolder()) { return; } // Get files to copy const files = this.getFilesToCopy(); // Copy files for (const file of files) { this.copyFile(file, renameFunction); } } /** * Moves files from the source folder to the destination folder. Emits a fileCopied and a fileDeleted event for each file moved. * @emits fileCopied An event that is emitted for each file copied. The event emits the source file and the destination file. * @emits fileDeleted An event that is emitted for each file deleted. The event emits the source file. * @emits error An event that is emitted if an error occurs. The event emits the error message. */ public moveFiles(renameFunction?: (file: string) => string): void { // Check if source folder exists if (!this.checkSourceFolder()) { return; } // Check if destination folder exists if (!this.checkDestinationFolder()) { return; } // Get files to copy const files = this.getFilesToCopy(); // Copy files for (const file of files) { this.copyFile(file, renameFunction); // Delete file fs.unlinkSync(file); // Emit event this.emit('fileDeleted', file); } } private checkSourceFolder(): boolean { // Check if source folder exists if (!fs.existsSync(this.settings.source)) { this.emit('error', `Source folder does not exist: ${this.settings.source}`); return false; } return true; } private checkDestinationFolder(): boolean { // Check if destination folder exists if (!fs.existsSync(this.settings.destination)) { // Create destination folder if createDestinationFolder is true if (this.settings.createDestinationFolder) { fs.mkdirSync(this.settings.destination, { recursive: true }); } else { this.emit('error', `Destination folder does not exist: ${this.settings.destination}`); return false; } } return true; } private copyFile(file: string, renameFunction?: (file: string) => string): void { // If createDestinationFolder, create subfolders, else just use the destination folder const basename = path.basename(file); const destinationFolder = this.settings.createDestinationFolder ? path.join(this.settings.destination, path.dirname(path.relative(this.settings.source, file))) : this.settings.destination; const destinationFile = path.join(destinationFolder, renameFunction ? renameFunction(basename) : basename); // Get folder of destination file const destinationFileFolder = path.dirname(destinationFile); // Create destination folder if createDestinationFolder is true and if it doesn't exist if (this.settings.createDestinationFolder && !fs.existsSync(destinationFileFolder)) { fs.mkdirSync(destinationFileFolder, { recursive: true }); } // Copy file fs.copyFileSync(file, destinationFile, this.settings.overwrite ? undefined : fs.constants.COPYFILE_EXCL); // Emit event this.emit('fileCopied', file, destinationFile); } private getFilesToCopy(): string[] { // Create array to store files const files: string[] = []; // If recursive, get files recursively, else get top level files if (this.settings.recursive) { this.getFilesRecursive(this.settings.source, files); } else { this.getFiles(this.settings.source, files); } return files; } private getFilesRecursive(folder: string, files: string[]): void { const folderContents = fs.readdirSync(folder); folderContents.forEach((item) => { const itemPath = path.join(folder, item); const itemStat = fs.statSync(itemPath); if (itemStat.isDirectory()) { if (this.settings.folderGlob && !item.match(this.settings.folderGlob)) { return; } this.getFilesRecursive(itemPath, files); } else { if (this.settings.fileGlob && !item.match(this.settings.fileGlob)) { return; } files.push(itemPath); } }); } private getFiles(folder: string, files: string[]): void { const folderContents = fs.readdirSync(folder); folderContents.forEach((item) => { const itemPath = path.join(folder, item); const itemStat = fs.statSync(itemPath); if (itemStat.isDirectory()) { return; } if (this.settings.fileGlob && !item.match(this.settings.fileGlob)) { return; } files.push(itemPath); }); } /** * Adds a listener for the fileCopied event. * @param listener The callback function to call when the event is emitted. The callback function takes two arguments: file and destinationFile. */ public on(event: 'fileCopied', listener: (file: string, destinationFile: string) => void): this; /** * Adds a listener for the fileDeleted event. * @param listener The callback function to call when the event is emitted. The callback function takes one argument: file. */ public on(event: 'fileDeleted', listener: (file: string) => void): this; /** * Adds a listener for the error event. * @param listener The callback function to call when the event is emitted. The callback function takes one argument: error. */ public on(event: 'error', listener: (error: string) => void): this; /** * Adds a listener for the fileCopied or error event. * @param event The event to listen for. Supported events are 'fileCopied', 'fileDeleted', and 'error'. * @param listener The callback function to call when the event is emitted. The callback function takes two arguments: file and destinationFile. * @returns This instance of FileCopy. */ public override on(event: 'fileCopied' | 'fileDeleted' | 'error', listener: (...args: any[]) => void): this { return super.on(event, listener); } }