fipy
Version:
A simple and easy package to copy files
201 lines (161 loc) • 6.73 kB
text/typescript
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);
}
}