Created
August 30, 2024 10:10
-
-
Save zecka/30cf2a468978576d776f3dab965c11d4 to your computer and use it in GitHub Desktop.
Typescript : Sync two folder with watch option
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import chalk from 'chalk'; | |
import * as fs from 'fs'; | |
import * as path from 'path'; | |
import * as util from 'util'; | |
const copyFile = util.promisify(fs.copyFile); | |
interface SyncOptions { | |
sourceDir: string; | |
targetDir: string; | |
watch: boolean; | |
extensions?: string[]; // Optional array of extensions to filter files | |
} | |
/** | |
* Synchronizes the content from the source folder to the destination folder. | |
* It performs an initial sync of files and can optionally watch for changes in the source folder to keep the target folder updated. | |
* | |
* @param options - Object containing the following properties: | |
* - sourceDir: The source directory path. | |
* - targetDir: The target directory path. | |
* - watch: A boolean flag to enable watching the source directory for changes. | |
* - extensions: Optional array of file extensions to filter files to be synchronized. | |
* | |
* Example usage: | |
* | |
* syncFolderContent({ | |
* sourceDir: './source-folder', | |
* targetDir: './target-folder', | |
* watch: true, | |
* extensions: ['.txt', '.md'], // Optional: Only sync .txt and .md files | |
* }); | |
*/ | |
export async function syncFolderContent(options: SyncOptions) { | |
const { sourceDir, targetDir, watch, extensions } = options; | |
// Ensure the target directory exists | |
if (!fs.existsSync(targetDir)) { | |
fs.mkdirSync(targetDir, { recursive: true }); | |
} | |
// Sync initial content from source to target | |
await syncInitialContent(sourceDir, targetDir, extensions); | |
if (watch) { | |
// Watch for changes in the source directory | |
fs.watch(sourceDir, async (eventType, filename) => { | |
if (filename) { | |
const sourceFilePath = path.join(sourceDir, filename); | |
const targetFilePath = path.join(targetDir, filename); | |
// Check if the file matches the extensions filter if provided | |
if (shouldCopyFile(filename, extensions)) { | |
// Check if the file exists and handle the file accordingly | |
if (fs.existsSync(sourceFilePath)) { | |
try { | |
await copyFile(sourceFilePath, targetFilePath); | |
console.log(chalk.green(`File ${filename} was copied to ${targetDir}`)); | |
} catch (err) { | |
console.error(chalk.red(`Error copying file ${filename}:`), err); | |
} | |
} | |
} | |
} | |
}); | |
console.log(chalk.green(`Watching for changes in ${sourceDir}...`)); | |
} | |
} | |
/** | |
* Synchronizes the initial content from the source directory to the target directory. | |
* Deletes files in the target directory that do not exist in the source directory. | |
* | |
* @param sourceDir - The source directory path | |
* @param targetDir - The target directory path | |
* @param extensions - Optional array of file extensions to filter files | |
*/ | |
async function syncInitialContent(sourceDir: string, targetDir: string, extensions?: string[]) { | |
const sourceFiles = fs.readdirSync(sourceDir); | |
const targetFiles = fs.readdirSync(targetDir); | |
// Delete files in target that do not exist in source | |
for (const file of targetFiles) { | |
if (!sourceFiles.includes(file)) { | |
const targetFilePath = path.join(targetDir, file); | |
try { | |
fs.unlinkSync(targetFilePath); | |
console.log(chalk.red(`File ${file} was deleted from ${targetDir}`)); | |
} catch (err) { | |
console.error(chalk.red(`Error deleting file ${file} from ${targetDir}:`), err); | |
} | |
} | |
} | |
let count = 0; | |
for (const file of sourceFiles) { | |
if (shouldCopyFile(file, extensions)) { | |
const sourceFilePath = path.join(sourceDir, file); | |
const targetFilePath = path.join(targetDir, file); | |
if (fs.lstatSync(sourceFilePath).isFile()) { | |
try { | |
await copyFile(sourceFilePath, targetFilePath); | |
count++; | |
} catch (err) { | |
console.error(chalk.red(`Error copying file ${file}:`), err); | |
} | |
} | |
} | |
} | |
console.log(chalk.green(`Copied ${count} files from ${sourceDir} to ${targetDir}`)); | |
} | |
/** | |
* Checks if a file should be copied based on its extension. | |
* | |
* @param filename - The name of the file to check | |
* @param extensions - Optional array of file extensions to filter files | |
* @returns true if the file should be copied, false otherwise | |
*/ | |
function shouldCopyFile(filename: string, extensions?: string[]): boolean { | |
if (!extensions || extensions.length === 0) { | |
return true; // No filtering, copy all files | |
} | |
const fileExtension = path.extname(filename).toLowerCase(); | |
const extensionsSet = new Set(extensions.map(ext => ext.toLowerCase())); | |
return extensionsSet.has(fileExtension); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment