Skip to content

Instantly share code, notes, and snippets.

@zecka
Created August 30, 2024 10:10
Show Gist options
  • Save zecka/30cf2a468978576d776f3dab965c11d4 to your computer and use it in GitHub Desktop.
Save zecka/30cf2a468978576d776f3dab965c11d4 to your computer and use it in GitHub Desktop.
Typescript : Sync two folder with watch option
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