From 822778d43b026777c6defd6f3cd970ec951a877b Mon Sep 17 00:00:00 2001 From: Benjamin Toby Date: Mon, 21 Jul 2025 13:51:59 +0100 Subject: [PATCH] Updates --- dist/index.d.ts | 2 +- dist/index.js | 5 - dist/lib/sync.js | 70 ++++++-- dist/lib/watch/files.js | 104 +++-------- dist/lib/watch/folders.js | 120 +++---------- dist/types/index.d.ts | 8 +- dist/types/index.js | 2 + dist/utils/folders.d.ts | 2 + dist/utils/folders.js | 167 ++++++++++++++++++ dist/utils/get-last-edited-src.d.ts | 6 + dist/utils/get-last-edited-src.js | 57 ++++++ dist/utils/get-sync-config copy.d.ts | 2 + dist/utils/get-sync-config copy.js | 22 +++ dist/utils/get-sync-config.d.ts | 2 + dist/utils/get-sync-config.js | 22 +++ dist/utils/grab-dir-names copy.d.ts | 5 + dist/utils/grab-dir-names copy.js | 13 ++ dist/utils/grab-dir-names.d.ts | 5 + dist/utils/grab-dir-names.js | 13 ++ .../grab-folders-files-string-paths.d.ts | 3 + dist/utils/grab-folders-files-string-paths.js | 28 +++ dist/utils/grab0dir-names.d.ts | 5 + dist/utils/grab0dir-names.js | 13 ++ dist/utils/sync.d.ts | 2 + dist/utils/sync.js | 80 +++++++++ dist/utils/write-sync-config.d.ts | 2 + dist/utils/write-sync-config.js | 18 ++ index.ts | 8 +- lib/sync.ts | 49 +++-- lib/watch/files.ts | 130 ++++---------- lib/watch/folders.ts | 155 ++++------------ package.json | 2 +- types/index.ts | 10 +- utils/get-last-edited-src.ts | 67 +++++++ utils/get-sync-config.ts | 18 ++ utils/grab-dir-names.ts | 9 + utils/grab-folders-files-string-paths.ts | 31 ++++ utils/sync.ts | 95 ++++++++++ utils/write-sync-config.ts | 13 ++ 39 files changed, 944 insertions(+), 421 deletions(-) create mode 100644 dist/utils/folders.d.ts create mode 100644 dist/utils/folders.js create mode 100644 dist/utils/get-last-edited-src.d.ts create mode 100644 dist/utils/get-last-edited-src.js create mode 100644 dist/utils/get-sync-config copy.d.ts create mode 100644 dist/utils/get-sync-config copy.js create mode 100644 dist/utils/get-sync-config.d.ts create mode 100644 dist/utils/get-sync-config.js create mode 100644 dist/utils/grab-dir-names copy.d.ts create mode 100644 dist/utils/grab-dir-names copy.js create mode 100644 dist/utils/grab-dir-names.d.ts create mode 100644 dist/utils/grab-dir-names.js create mode 100644 dist/utils/grab-folders-files-string-paths.d.ts create mode 100644 dist/utils/grab-folders-files-string-paths.js create mode 100644 dist/utils/grab0dir-names.d.ts create mode 100644 dist/utils/grab0dir-names.js create mode 100644 dist/utils/sync.d.ts create mode 100644 dist/utils/sync.js create mode 100644 dist/utils/write-sync-config.d.ts create mode 100644 dist/utils/write-sync-config.js create mode 100644 utils/get-last-edited-src.ts create mode 100644 utils/get-sync-config.ts create mode 100644 utils/grab-dir-names.ts create mode 100644 utils/grab-folders-files-string-paths.ts create mode 100644 utils/sync.ts create mode 100644 utils/write-sync-config.ts diff --git a/dist/index.d.ts b/dist/index.d.ts index 16668e3..4965492 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -2,6 +2,6 @@ declare global { var SYNC_SUCCESS_EXIT_CODE: number; var CONFIG_DIR: string; - var SYNCING_FILE: string; + var SYNCING: boolean; } export {}; diff --git a/dist/index.js b/dist/index.js index ffeed3e..cff5972 100644 --- a/dist/index.js +++ b/dist/index.js @@ -10,10 +10,6 @@ const child_process_1 = require("child_process"); const env_1 = __importDefault(require("./utils/env")); const confFileProvidedPath = process.argv[process.argv.length - 1]; global.CONFIG_DIR = process.cwd(); -global.SYNCING_FILE = path_1.default.join(global.CONFIG_DIR, "syncing.txt"); -if (fs_1.default.existsSync(global.SYNCING_FILE)) { - fs_1.default.unlinkSync(global.SYNCING_FILE); -} if (confFileProvidedPath === "--version" || confFileProvidedPath === "-v") { try { const packageJson = fs_1.default.readFileSync(path_1.default.resolve(__dirname, "../package.json"), "utf-8"); @@ -55,7 +51,6 @@ try { const configArray = JSON.parse(parsedConfigJSON); for (let i = 0; i < configArray.length; i++) { const config = configArray[i]; - console.log(`Syncing \`${config.title} ...\``); const childProcess = (0, child_process_1.spawn)("node", [ path_1.default.resolve(__dirname, "./lib/sync.js"), `${JSON.stringify(config)}`, diff --git a/dist/lib/sync.js b/dist/lib/sync.js index 4f6f744..31261b7 100644 --- a/dist/lib/sync.js +++ b/dist/lib/sync.js @@ -1,23 +1,59 @@ "use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -const fs_1 = __importDefault(require("fs")); const child_process_1 = require("child_process"); const files_1 = __importDefault(require("./watch/files")); const folders_1 = __importDefault(require("./watch/folders")); -const path_1 = __importDefault(require("path")); +const get_last_edited_src_1 = __importDefault(require("../utils/get-last-edited-src")); +const grab_folders_files_string_paths_1 = __importStar(require("../utils/grab-folders-files-string-paths")); const confFileProvidedJSON = process.argv[process.argv.length - 1]; global.SYNC_SUCCESS_EXIT_CODE = 32; global.CONFIG_DIR = process.cwd(); -global.SYNCING_FILE = path_1.default.join(global.CONFIG_DIR, "syncing.txt"); +global.SYNCING = false; +const REWATCH_TIMEOUT = 1000; try { - if (fs_1.default.existsSync(global.SYNCING_FILE)) { - process.exit(0); - } const configFileObject = JSON.parse(confFileProvidedJSON); + const lastUpdated = (0, get_last_edited_src_1.default)({ + dirs: (0, grab_folders_files_string_paths_1.default)(configFileObject.folders), + files: (0, grab_folders_files_string_paths_1.default)(configFileObject.files), + }); console.log(`Running '${configFileObject.title}' ...`); + console.log(`Last Updated Path => '${lastUpdated || "N/A"}' ...`); if (Array.isArray(configFileObject.files) && Array.isArray(configFileObject.folders)) { throw new Error("Choose wither `files` or `folders`. Not both"); @@ -27,11 +63,24 @@ try { const folders = configFileObject === null || configFileObject === void 0 ? void 0 : configFileObject.folders; const firstFolder = folders === null || folders === void 0 ? void 0 : folders[0]; const options = configFileObject.options; - if (firstFile && (files === null || files === void 0 ? void 0 : files[0])) { - (0, files_1.default)({ files, options }); + const sortedFoldersByLastUpdated = (folders === null || folders === void 0 ? void 0 : folders[0]) && lastUpdated + ? [ + lastUpdated, + ...((folders === null || folders === void 0 ? void 0 : folders.filter((fl) => (0, grab_folders_files_string_paths_1.fldFileToStr)(fl) !== lastUpdated)) || []), + ] + : folders; + const sortedFilesByLastUpdated = (files === null || files === void 0 ? void 0 : files[0]) && lastUpdated + ? [ + lastUpdated, + ...((files === null || files === void 0 ? void 0 : files.filter((fl) => (0, grab_folders_files_string_paths_1.fldFileToStr)(fl) !== lastUpdated)) || + []), + ] + : files; + if (firstFile && (sortedFilesByLastUpdated === null || sortedFilesByLastUpdated === void 0 ? void 0 : sortedFilesByLastUpdated[0])) { + (0, files_1.default)({ files: sortedFilesByLastUpdated, options }); } - else if (firstFolder && (folders === null || folders === void 0 ? void 0 : folders[0])) { - (0, folders_1.default)({ folders, options }); + else if (firstFolder && (sortedFoldersByLastUpdated === null || sortedFoldersByLastUpdated === void 0 ? void 0 : sortedFoldersByLastUpdated[0])) { + (0, folders_1.default)({ folders: sortedFoldersByLastUpdated, options }); } } catch (error) { @@ -40,7 +89,6 @@ catch (error) { } process.on("exit", (code) => { if (code == global.SYNC_SUCCESS_EXIT_CODE) { - fs_1.default.unlinkSync(global.SYNCING_FILE); const args = process.argv; const cmd = args.shift(); if (cmd) { diff --git a/dist/lib/watch/files.js b/dist/lib/watch/files.js index 48b11f5..04fc850 100644 --- a/dist/lib/watch/files.js +++ b/dist/lib/watch/files.js @@ -13,13 +13,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) { }; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = watchFiles; -const child_process_1 = require("child_process"); const fs_1 = __importDefault(require("fs")); const delay_1 = __importDefault(require("../../utils/delay")); +const sync_1 = __importDefault(require("../../utils/sync")); function watchFiles(_a) { return __awaiter(this, arguments, void 0, function* ({ files, options, }) { let timeout; - const UPDATE_TIMEOUT = 2000; + const UPDATE_TIMEOUT = 1000; try { for (let i = 0; i < files.length; i++) { const file = files[i]; @@ -45,16 +45,6 @@ function watchFiles(_a) { fs_1.default.mkdirSync(fileDirPath, { recursive: true }); } fs_1.default.writeFileSync(filePath, ""); - if (typeof existingFilePath == "string") { - sync({ filePath: existingFilePath, files, options }); - } - else { - sync({ - filePath: existingFilePath.path, - files, - options, - }); - } } catch (error) { throw new Error(`File Doesn't exist and couldn't be created. Please check if Directory exists.\nERROR => ${error.message}`); @@ -67,25 +57,45 @@ function watchFiles(_a) { // TODO Handle SSH } else if (typeof file == "string") { - sync({ options, filePath, files }); yield (0, delay_1.default)(); fs_1.default.watchFile(filePath, { interval: interval || 200, }, (curr, prev) => { - if (fs_1.default.existsSync(global.SYNCING_FILE)) + if (global.SYNCING) return; const INTERVAL = (options === null || options === void 0 ? void 0 : options.interval) ? options.interval : UPDATE_TIMEOUT; clearTimeout(timeout); timeout = setTimeout(() => { - fs_1.default.writeFileSync(global.SYNCING_FILE, `SYNCING FILE: curr:${curr} :: prev:${prev}`); - sync({ options, filePath, files }); - process.exit(global.SYNC_SUCCESS_EXIT_CODE); + global.SYNCING = true; + (0, sync_1.default)({ + options, + dirPath: filePath, + dirs: files, + isFiles: true, + }).finally(() => { + process.exit(global.SYNC_SUCCESS_EXIT_CODE); + }); }, INTERVAL); }); } } + const lastUpdatedFile = files[0]; + const lastUpdatedFilePath = typeof lastUpdatedFile == "string" + ? lastUpdatedFile + : lastUpdatedFile.path; + global.SYNCING = true; + yield (0, sync_1.default)({ + dirPath: lastUpdatedFilePath, + dirs: files, + options, + isFiles: true, + firstRun: true, + }); + setTimeout(() => { + global.SYNCING = false; + }, UPDATE_TIMEOUT); } catch (error) { console.log("ERROR:", error.message); @@ -93,63 +103,3 @@ function watchFiles(_a) { } }); } -function sync({ options, filePath, files }) { - var _a; - const destFiles = files.filter((fl) => { - if (typeof fl == "string") - return fl !== filePath; - if (fl === null || fl === void 0 ? void 0 : fl.path) - return fl.path !== filePath; - return false; - }); - for (let j = 0; j < destFiles.length; j++) { - let cmdArray = ["rsync", "-avh"]; - if (options === null || options === void 0 ? void 0 : options.delete) { - cmdArray.push("--delete"); - } - if ((_a = options === null || options === void 0 ? void 0 : options.exclude) === null || _a === void 0 ? void 0 : _a[0]) { - options.exclude.forEach((excl) => { - cmdArray.push(`--exclude '${excl}'`); - }); - } - const dstFl = destFiles[j]; - if (typeof dstFl == "string") { - if (!fs_1.default.existsSync(dstFl)) - continue; - if (filePath === dstFl) { - console.log(`You can't sync the same paths. Please check your configuration and resolve duplicate paths`); - process.exit(6); - } - cmdArray.push(filePath, dstFl); - const cmd = cmdArray.join(" "); - console.log(`Running cmd 1 => ${cmd}`); - (0, child_process_1.execSync)(cmd, { - stdio: "inherit", - }); - } - else if (dstFl.path) { - if (!dstFl.host && !fs_1.default.existsSync(dstFl.path)) - continue; - if (filePath === dstFl.path) { - console.log(`You can't sync the same paths. Please check your configuration and resolve duplicate paths`); - process.exit(6); - } - if (dstFl.host && dstFl.ssh_key && dstFl.user) { - cmdArray.push("-e", `'ssh -i ${dstFl.ssh_key}'`); - cmdArray.push(filePath, `${dstFl.user}@${dstFl.host}:${dstFl.path}`); - const cmd = cmdArray.join(" "); - (0, child_process_1.execSync)(cmd, { - stdio: "inherit", - }); - } - else { - cmdArray.push(filePath, dstFl.path); - const cmd = cmdArray.join(" "); - console.log(`Running cmd 2 => ${cmd}`); - (0, child_process_1.execSync)(cmd, { - stdio: "inherit", - }); - } - } - } -} diff --git a/dist/lib/watch/folders.js b/dist/lib/watch/folders.js index 772d68b..f228506 100644 --- a/dist/lib/watch/folders.js +++ b/dist/lib/watch/folders.js @@ -14,17 +14,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) { Object.defineProperty(exports, "__esModule", { value: true }); exports.default = watchFolders; const fs_1 = __importDefault(require("fs")); -const path_1 = __importDefault(require("path")); -const child_process_1 = require("child_process"); const delay_1 = __importDefault(require("../../utils/delay")); +const sync_1 = __importDefault(require("../../utils/sync")); function watchFolders(_a) { return __awaiter(this, arguments, void 0, function* ({ folders, options, }) { let timeout; - let isSyncing = false; - const UPDATE_TIMEOUT = 500; + const UPDATE_TIMEOUT = 1000; try { const dirs = folders; + console.log("global.SYNCING", global.SYNCING); console.log(`Now handling ${dirs.length} Directories`); + /** + * # Watch Directories + */ const INTERVAL = (options === null || options === void 0 ? void 0 : options.interval) ? options.interval : UPDATE_TIMEOUT; for (let i = 0; i < dirs.length; i++) { const dir = dirs[i]; @@ -33,6 +35,7 @@ function watchFolders(_a) { continue; } const dirPath = typeof dir == "string" ? dir : dir.path; + console.log("global.SYNCING", global.SYNCING); if ((typeof dir == "string" && !fs_1.default.existsSync(dirPath)) || (typeof dir == "object" && dir.path && @@ -54,22 +57,6 @@ function watchFolders(_a) { fs_1.default.mkdirSync(dirPath, { recursive: true, }); - if (typeof existingDirPath == "string") { - sync({ - dirPath: existingDirPath, - dirs, - options, - init: true, - }); - } - else { - sync({ - dirPath: existingDirPath.path, - dirs, - options, - init: true, - }); - } } catch (error) { console.log("Error:", error.message); @@ -77,28 +64,41 @@ function watchFolders(_a) { } } if (typeof dir == "string") { - sync({ dirPath, dirs, options }); yield (0, delay_1.default)(); fs_1.default.watch(dirPath, { recursive: true }, (evt, fileName) => { console.log("Folder Changed", evt, fileName); - if (fs_1.default.existsSync(global.SYNCING_FILE) || isSyncing) { + if (global.SYNCING) { console.log("Existing Sync found. Returning ..."); return; } clearTimeout(timeout); timeout = setTimeout(() => { console.log("Folder Syncing in progress ..."); - console.log(`Writing Sync File =>${global.SYNCING_FILE}`); - fs_1.default.writeFileSync(global.SYNCING_FILE, `SYNCING Folder: evt:${evt} :: fileName:${fileName}`); - isSyncing = true; - sync({ dirPath, dirs, options }); - setTimeout(() => { + global.SYNCING = true; + (0, sync_1.default)({ dirPath, dirs, options }).finally(() => { process.exit(global.SYNC_SUCCESS_EXIT_CODE); - }, INTERVAL); + }); }, INTERVAL); }); } } + /** + * # Sync Last Updated + */ + const lastUpdatedDir = dirs[0]; + const lastUpdatedDirPath = typeof lastUpdatedDir == "string" + ? lastUpdatedDir + : lastUpdatedDir.path; + global.SYNCING = true; + yield (0, sync_1.default)({ + dirPath: lastUpdatedDirPath, + dirs, + options, + firstRun: true, + }); + setTimeout(() => { + global.SYNCING = false; + }, UPDATE_TIMEOUT); } catch (error) { console.log("ERROR:", error.message); @@ -106,67 +106,3 @@ function watchFolders(_a) { } }); } -function sync({ options, dirs, dirPath, init }) { - var _a, _b; - const dstDirs = dirs.filter((dr) => { - if (typeof dr == "string") - return dr !== dirPath; - if (dr === null || dr === void 0 ? void 0 : dr.path) - return dr.path !== dirPath; - return false; - }); - for (let j = 0; j < dstDirs.length; j++) { - let cmdArray = ["rsync", "-avh"]; - if (options === null || options === void 0 ? void 0 : options.delete) { - cmdArray.push("--delete"); - } - if ((_a = options === null || options === void 0 ? void 0 : options.include) === null || _a === void 0 ? void 0 : _a[0]) { - options.include.forEach((incl) => { - cmdArray.push(`--include='${incl}'`); - }); - } - if ((_b = options === null || options === void 0 ? void 0 : options.exclude) === null || _b === void 0 ? void 0 : _b[0]) { - options.exclude.forEach((excl) => { - cmdArray.push(`--exclude='${excl}'`); - }); - } - const dstDr = dstDirs[j]; - if (typeof dstDr == "string") { - if (!fs_1.default.existsSync(dstDr)) - continue; - if (dirPath === dstDr) { - console.log(`You can't sync the same paths. Please check your configuration and resolve duplicate paths`); - process.exit(6); - } - cmdArray.push(path_1.default.normalize(dirPath) + "/", path_1.default.normalize(dstDr) + "/"); - const cmd = cmdArray.join(" "); - (0, child_process_1.execSync)(cmd, { - stdio: "inherit", - }); - } - else if (dstDr.path) { - if (!dstDr.host && !fs_1.default.existsSync(dstDr.path)) - continue; - if (dirPath === dstDr.path) { - console.log(`You can't sync the same paths. Please check your configuration and resolve duplicate paths`); - process.exit(6); - } - if (dstDr.host && dstDr.ssh_key && dstDr.user) { - cmdArray.push("-e", `'ssh -i ${dstDr.ssh_key}'`); - cmdArray.push(path_1.default.normalize(dirPath) + "/", `${dstDr.user}@${dstDr.host}:${dstDr.path}/`); - const cmd = cmdArray.join(" "); - (0, child_process_1.execSync)(cmd, { - stdio: "inherit", - }); - } - else { - cmdArray.push(path_1.default.normalize(dirPath), path_1.default.normalize(dstDr.path)); - const cmd = cmdArray.join(" "); - (0, child_process_1.execSync)(cmd, { - stdio: "inherit", - }); - } - } - } - console.log("Folder Sync Complete. Exiting ..."); -} diff --git a/dist/types/index.d.ts b/dist/types/index.d.ts index 0db37f8..8432e4a 100644 --- a/dist/types/index.d.ts +++ b/dist/types/index.d.ts @@ -35,8 +35,14 @@ export interface SyncFoldersSyncFnParams { dirs: (string | TurboSyncFileObject)[]; options: TurboSyncOptions | undefined; dirPath: string; - init?: boolean; + firstRun?: boolean; + isFiles?: boolean; } export interface HandleEnvVarsFnParams { json: string; } +export declare const TurboSyncStatuses: readonly ["syncing", "error", "done"]; +export type SyncFileConfig = { + status?: (typeof TurboSyncStatuses)[number]; + lastSyncedPath?: string; +}; diff --git a/dist/types/index.js b/dist/types/index.js index c8ad2e5..3c87a7e 100644 --- a/dist/types/index.js +++ b/dist/types/index.js @@ -1,2 +1,4 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); +exports.TurboSyncStatuses = void 0; +exports.TurboSyncStatuses = ["syncing", "error", "done"]; diff --git a/dist/utils/folders.d.ts b/dist/utils/folders.d.ts new file mode 100644 index 0000000..53ff457 --- /dev/null +++ b/dist/utils/folders.d.ts @@ -0,0 +1,2 @@ +import { SyncFoldersFnParams } from "../../types"; +export default function watchFolders({ folders, options, }: SyncFoldersFnParams): Promise; diff --git a/dist/utils/folders.js b/dist/utils/folders.js new file mode 100644 index 0000000..3b3da07 --- /dev/null +++ b/dist/utils/folders.js @@ -0,0 +1,167 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = watchFolders; +const fs_1 = __importDefault(require("fs")); +const path_1 = __importDefault(require("path")); +const util_1 = __importDefault(require("util")); +const child_process_1 = require("child_process"); +const delay_1 = __importDefault(require("../../utils/delay")); +const write_sync_config_1 = __importDefault(require("../../utils/write-sync-config")); +const execPromise = util_1.default.promisify(child_process_1.exec); +function watchFolders(_a) { + return __awaiter(this, arguments, void 0, function* ({ folders, options, }) { + let timeout; + const UPDATE_TIMEOUT = 1000; + try { + const dirs = folders; + console.log(`Now handling ${dirs.length} Directories`); + /** + * # Sync Last Updated + */ + const lastUpdatedDir = dirs[0]; + const lastUpdatedDirPath = typeof lastUpdatedDir == "string" + ? lastUpdatedDir + : lastUpdatedDir.path; + yield sync({ dirPath: lastUpdatedDirPath, dirs, options }); + /** + * # Watch Directories + */ + const INTERVAL = (options === null || options === void 0 ? void 0 : options.interval) ? options.interval : UPDATE_TIMEOUT; + for (let i = 0; i < dirs.length; i++) { + const dir = dirs[i]; + if (!dir) { + console.log(`Dir: ${dir} doesn't exist`); + continue; + } + const dirPath = typeof dir == "string" ? dir : dir.path; + console.log("global.SYNCING", global.SYNCING); + if ((typeof dir == "string" && !fs_1.default.existsSync(dirPath)) || + (typeof dir == "object" && + dir.path && + !dir.host && + !fs_1.default.existsSync(dir.path))) { + console.log(`Dir ${dirPath} does not exist. Creating ...`); + try { + const existingDirPath = dirs.find((dr) => { + if (typeof dr == "string") + return fs_1.default.existsSync(dr); + if (!dr.host) + return fs_1.default.existsSync(dr.path); // TODO handle remote + return false; + }); + console.log(`Existing Dir to clone: ${existingDirPath}`); + if (!existingDirPath) { + throw new Error("No existing Directories for reference"); + } + fs_1.default.mkdirSync(dirPath, { + recursive: true, + }); + } + catch (error) { + console.log("Error:", error.message); + throw new Error(`Folder Doesn't exist and couldn't be created. Please check if Directory exists.\nERROR => ${error.message}`); + } + } + if (typeof dir == "string") { + yield (0, delay_1.default)(); + fs_1.default.watch(dirPath, { recursive: true }, (evt, fileName) => { + console.log("Folder Changed", evt, fileName); + if (global.SYNCING) { + console.log("Existing Sync found. Returning ..."); + return; + } + clearTimeout(timeout); + timeout = setTimeout(() => { + console.log("Folder Syncing in progress ..."); + global.SYNCING = true; + (0, write_sync_config_1.default)({ + status: "syncing", + lastSyncedPath: dirPath, + }); + sync({ dirPath, dirs, options }).finally(() => { + setTimeout(() => { + process.exit(global.SYNC_SUCCESS_EXIT_CODE); + }, INTERVAL); + }); + }, INTERVAL); + }); + } + } + } + catch (error) { + console.log("ERROR:", error.message); + process.exit(0); + } + }); +} +function sync(_a) { + return __awaiter(this, arguments, void 0, function* ({ options, dirs, dirPath, init }) { + var _b, _c; + const dstDirs = dirs.filter((dr) => { + if (typeof dr == "string") + return dr !== dirPath; + if (dr === null || dr === void 0 ? void 0 : dr.path) + return dr.path !== dirPath; + return false; + }); + const allCommandsArr = []; + for (let j = 0; j < dstDirs.length; j++) { + const dstDr = dstDirs[j]; + let cmdArray = ["rsync", "-azu", "--inplace"]; + if (options === null || options === void 0 ? void 0 : options.delete) { + cmdArray.push("--delete"); + } + if ((_b = options === null || options === void 0 ? void 0 : options.include) === null || _b === void 0 ? void 0 : _b[0]) { + options.include.forEach((incl) => { + cmdArray.push(`--include='${incl}'`); + }); + } + if ((_c = options === null || options === void 0 ? void 0 : options.exclude) === null || _c === void 0 ? void 0 : _c[0]) { + options.exclude.forEach((excl) => { + cmdArray.push(`--exclude='${excl}'`); + }); + } + if (typeof dstDr == "string") { + if (!fs_1.default.existsSync(dstDr)) + continue; + if (dirPath === dstDr) { + console.log(`You can't sync the same paths. Please check your configuration and resolve duplicate paths`); + process.exit(6); + } + cmdArray.push(path_1.default.normalize(dirPath) + "/", path_1.default.normalize(dstDr) + "/"); + } + else if (dstDr.path) { + if (!dstDr.host && !fs_1.default.existsSync(dstDr.path)) + continue; + if (dirPath === dstDr.path) { + console.log(`You can't sync the same paths. Please check your configuration and resolve duplicate paths`); + process.exit(6); + } + if (dstDr.host && dstDr.ssh_key && dstDr.user) { + cmdArray.push("-e", `'ssh -i ${dstDr.ssh_key}'`); + cmdArray.push(path_1.default.normalize(dirPath) + "/", `${dstDr.user}@${dstDr.host}:${dstDr.path}/`); + } + else { + cmdArray.push(path_1.default.normalize(dirPath), path_1.default.normalize(dstDr.path)); + } + } + allCommandsArr.push(cmdArray); + } + yield Promise.all(allCommandsArr.map((cmdArr) => { + return execPromise(cmdArr.join(" ")); + })); + console.log(`${dirPath} Folder Sync Complete. Exiting ...`); + }); +} diff --git a/dist/utils/get-last-edited-src.d.ts b/dist/utils/get-last-edited-src.d.ts new file mode 100644 index 0000000..03421cf --- /dev/null +++ b/dist/utils/get-last-edited-src.d.ts @@ -0,0 +1,6 @@ +type Params = { + dirs?: string[]; + files?: string[]; +}; +export default function getLatestSource({ dirs, files, }: Params): string | undefined; +export {}; diff --git a/dist/utils/get-last-edited-src.js b/dist/utils/get-last-edited-src.js new file mode 100644 index 0000000..bbb0c14 --- /dev/null +++ b/dist/utils/get-last-edited-src.js @@ -0,0 +1,57 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = getLatestSource; +const fs_1 = __importDefault(require("fs")); +const path_1 = __importDefault(require("path")); +function getLatestSource({ dirs, files, }) { + let latestDir = undefined; + let latestMtime = 0; + const isFiles = files === null || files === void 0 ? void 0 : files[0]; + const finalPaths = isFiles ? files : dirs; + if (!finalPaths) + return undefined; + for (const pth of finalPaths) { + try { + const stats = fs_1.default.statSync(pth); + const pathMtime = stats.isDirectory() + ? getLatestDirMtime(pth) + : stats.mtimeMs; + if (pathMtime > latestMtime) { + latestMtime = pathMtime; + latestDir = pth; + } + } + catch (error) { + console.error(`Error accessing ${pth}: ${error.message}`); + } + } + return latestDir; +} +function getLatestDirMtime(dir) { + let latestMtime = 0; + try { + const stats = fs_1.default.statSync(dir); + if (stats.isDirectory()) { + latestMtime = stats.mtimeMs; + const entries = fs_1.default.readdirSync(dir, { withFileTypes: true }); + for (const entry of entries) { + const entryPath = path_1.default.join(dir, entry.name); + if (entry.isDirectory()) { + const subMtime = getLatestDirMtime(entryPath); + latestMtime = Math.max(latestMtime, subMtime); + } + else if (entry.isFile()) { + const fileStats = fs_1.default.statSync(entryPath); + latestMtime = Math.max(latestMtime, fileStats.mtimeMs); + } + } + } + } + catch (error) { + console.error(`Error accessing ${dir}: ${error.message}`); + } + return latestMtime; +} diff --git a/dist/utils/get-sync-config copy.d.ts b/dist/utils/get-sync-config copy.d.ts new file mode 100644 index 0000000..1b10e95 --- /dev/null +++ b/dist/utils/get-sync-config copy.d.ts @@ -0,0 +1,2 @@ +import { SyncFileConfig } from "../types"; +export default function getSyncConfig(): SyncFileConfig; diff --git a/dist/utils/get-sync-config copy.js b/dist/utils/get-sync-config copy.js new file mode 100644 index 0000000..8f332e4 --- /dev/null +++ b/dist/utils/get-sync-config copy.js @@ -0,0 +1,22 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = getSyncConfig; +const fs_1 = __importDefault(require("fs")); +const grab_dir_names_1 = __importDefault(require("./grab-dir-names")); +function getSyncConfig() { + try { + const { syncConfigFilePath } = (0, grab_dir_names_1.default)(); + if (!fs_1.default.existsSync(syncConfigFilePath)) { + fs_1.default.writeFileSync(syncConfigFilePath, JSON.stringify({}), "utf-8"); + return {}; + } + const syncConfigJSON = fs_1.default.readFileSync(syncConfigFilePath, "utf-8"); + return JSON.parse(syncConfigJSON); + } + catch (error) { + return { status: "error" }; + } +} diff --git a/dist/utils/get-sync-config.d.ts b/dist/utils/get-sync-config.d.ts new file mode 100644 index 0000000..1b10e95 --- /dev/null +++ b/dist/utils/get-sync-config.d.ts @@ -0,0 +1,2 @@ +import { SyncFileConfig } from "../types"; +export default function getSyncConfig(): SyncFileConfig; diff --git a/dist/utils/get-sync-config.js b/dist/utils/get-sync-config.js new file mode 100644 index 0000000..8f332e4 --- /dev/null +++ b/dist/utils/get-sync-config.js @@ -0,0 +1,22 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = getSyncConfig; +const fs_1 = __importDefault(require("fs")); +const grab_dir_names_1 = __importDefault(require("./grab-dir-names")); +function getSyncConfig() { + try { + const { syncConfigFilePath } = (0, grab_dir_names_1.default)(); + if (!fs_1.default.existsSync(syncConfigFilePath)) { + fs_1.default.writeFileSync(syncConfigFilePath, JSON.stringify({}), "utf-8"); + return {}; + } + const syncConfigJSON = fs_1.default.readFileSync(syncConfigFilePath, "utf-8"); + return JSON.parse(syncConfigJSON); + } + catch (error) { + return { status: "error" }; + } +} diff --git a/dist/utils/grab-dir-names copy.d.ts b/dist/utils/grab-dir-names copy.d.ts new file mode 100644 index 0000000..268304c --- /dev/null +++ b/dist/utils/grab-dir-names copy.d.ts @@ -0,0 +1,5 @@ +export default function grabDirNames(): { + rootDir: string; + configFileName: string; + configFilePath: string; +}; diff --git a/dist/utils/grab-dir-names copy.js b/dist/utils/grab-dir-names copy.js new file mode 100644 index 0000000..104e969 --- /dev/null +++ b/dist/utils/grab-dir-names copy.js @@ -0,0 +1,13 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = grabDirNames; +const path_1 = __importDefault(require("path")); +function grabDirNames() { + const rootDir = process.cwd(); + const configFileName = "turbosync.config.json"; + const configFilePath = path_1.default.join(rootDir, configFileName); + return { rootDir, configFileName, configFilePath }; +} diff --git a/dist/utils/grab-dir-names.d.ts b/dist/utils/grab-dir-names.d.ts new file mode 100644 index 0000000..c0a605d --- /dev/null +++ b/dist/utils/grab-dir-names.d.ts @@ -0,0 +1,5 @@ +export default function grabDirNames(): { + rootDir: string; + syncConfigFileName: string; + syncConfigFilePath: string; +}; diff --git a/dist/utils/grab-dir-names.js b/dist/utils/grab-dir-names.js new file mode 100644 index 0000000..726b50b --- /dev/null +++ b/dist/utils/grab-dir-names.js @@ -0,0 +1,13 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = grabDirNames; +const path_1 = __importDefault(require("path")); +function grabDirNames() { + const rootDir = process.cwd(); + const syncConfigFileName = "__trsyc.json"; + const syncConfigFilePath = path_1.default.join(rootDir, syncConfigFileName); + return { rootDir, syncConfigFileName, syncConfigFilePath }; +} diff --git a/dist/utils/grab-folders-files-string-paths.d.ts b/dist/utils/grab-folders-files-string-paths.d.ts new file mode 100644 index 0000000..75a1632 --- /dev/null +++ b/dist/utils/grab-folders-files-string-paths.d.ts @@ -0,0 +1,3 @@ +import { TurboSyncFileObject } from "../types"; +export default function fldFileToStrArr(srces?: (string | TurboSyncFileObject)[]): string[] | undefined; +export declare function fldFileToStr(src?: string | TurboSyncFileObject): string | undefined; diff --git a/dist/utils/grab-folders-files-string-paths.js b/dist/utils/grab-folders-files-string-paths.js new file mode 100644 index 0000000..0b31791 --- /dev/null +++ b/dist/utils/grab-folders-files-string-paths.js @@ -0,0 +1,28 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = fldFileToStrArr; +exports.fldFileToStr = fldFileToStr; +function fldFileToStrArr(srces) { + if (!srces) + return undefined; + let arr = []; + for (let i = 0; i < srces.length; i++) { + const src = srces[i]; + const srcStr = fldFileToStr(src); + if (srcStr) { + arr.push(srcStr); + } + } + return arr; +} +function fldFileToStr(src) { + if (!src) + return undefined; + if (typeof src == "string") { + return src; + } + else if (typeof src == "object" && src.path) { + return src.path; + } + return undefined; +} diff --git a/dist/utils/grab0dir-names.d.ts b/dist/utils/grab0dir-names.d.ts new file mode 100644 index 0000000..268304c --- /dev/null +++ b/dist/utils/grab0dir-names.d.ts @@ -0,0 +1,5 @@ +export default function grabDirNames(): { + rootDir: string; + configFileName: string; + configFilePath: string; +}; diff --git a/dist/utils/grab0dir-names.js b/dist/utils/grab0dir-names.js new file mode 100644 index 0000000..104e969 --- /dev/null +++ b/dist/utils/grab0dir-names.js @@ -0,0 +1,13 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = grabDirNames; +const path_1 = __importDefault(require("path")); +function grabDirNames() { + const rootDir = process.cwd(); + const configFileName = "turbosync.config.json"; + const configFilePath = path_1.default.join(rootDir, configFileName); + return { rootDir, configFileName, configFilePath }; +} diff --git a/dist/utils/sync.d.ts b/dist/utils/sync.d.ts new file mode 100644 index 0000000..ba0aef0 --- /dev/null +++ b/dist/utils/sync.d.ts @@ -0,0 +1,2 @@ +import { SyncFoldersSyncFnParams } from "../types"; +export default function sync({ options, dirs, dirPath, isFiles, firstRun, }: SyncFoldersSyncFnParams): Promise; diff --git a/dist/utils/sync.js b/dist/utils/sync.js new file mode 100644 index 0000000..330c0e9 --- /dev/null +++ b/dist/utils/sync.js @@ -0,0 +1,80 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = sync; +const fs_1 = __importDefault(require("fs")); +const path_1 = __importDefault(require("path")); +const util_1 = __importDefault(require("util")); +const child_process_1 = require("child_process"); +const execPromise = util_1.default.promisify(child_process_1.exec); +function sync(_a) { + return __awaiter(this, arguments, void 0, function* ({ options, dirs, dirPath, isFiles, firstRun, }) { + var _b, _c; + const dstDirs = dirs.filter((dr) => { + if (typeof dr == "string") + return dr !== dirPath; + if (dr === null || dr === void 0 ? void 0 : dr.path) + return dr.path !== dirPath; + return false; + }); + const rsyncTrailingSlash = isFiles ? "" : "/"; + const allCommandsArr = []; + for (let j = 0; j < dstDirs.length; j++) { + const dstDr = dstDirs[j]; + let cmdArray = ["rsync", firstRun ? "-az" : "-azu", "--inplace"]; + if (options === null || options === void 0 ? void 0 : options.delete) { + cmdArray.push("--delete"); + } + if ((_b = options === null || options === void 0 ? void 0 : options.include) === null || _b === void 0 ? void 0 : _b[0]) { + options.include.forEach((incl) => { + cmdArray.push(`--include='${incl}'`); + }); + } + if ((_c = options === null || options === void 0 ? void 0 : options.exclude) === null || _c === void 0 ? void 0 : _c[0]) { + options.exclude.forEach((excl) => { + cmdArray.push(`--exclude='${excl}'`); + }); + } + if (typeof dstDr == "string") { + if (!fs_1.default.existsSync(dstDr)) + continue; + if (dirPath === dstDr) { + console.log(`You can't sync the same paths. Please check your configuration and resolve duplicate paths`); + process.exit(6); + } + cmdArray.push(path_1.default.normalize(dirPath) + rsyncTrailingSlash, path_1.default.normalize(dstDr) + rsyncTrailingSlash); + } + else if (dstDr.path) { + if (!dstDr.host && !fs_1.default.existsSync(dstDr.path)) + continue; + if (dirPath === dstDr.path) { + console.log(`You can't sync the same paths. Please check your configuration and resolve duplicate paths`); + process.exit(6); + } + if (dstDr.host && dstDr.ssh_key && dstDr.user) { + cmdArray.push("-e", `'ssh -i ${dstDr.ssh_key}'`); + cmdArray.push(path_1.default.normalize(dirPath) + rsyncTrailingSlash, `${dstDr.user}@${dstDr.host}:${dstDr.path}${rsyncTrailingSlash}`); + } + else { + cmdArray.push(path_1.default.normalize(dirPath), path_1.default.normalize(dstDr.path)); + } + } + allCommandsArr.push(cmdArray); + } + yield Promise.all(allCommandsArr.map((cmdArr) => { + return execPromise(cmdArr.join(" ")); + })); + console.log(`${dirPath} Folder Sync Complete. Exiting ...`); + }); +} diff --git a/dist/utils/write-sync-config.d.ts b/dist/utils/write-sync-config.d.ts new file mode 100644 index 0000000..e819994 --- /dev/null +++ b/dist/utils/write-sync-config.d.ts @@ -0,0 +1,2 @@ +import { SyncFileConfig } from "../types"; +export default function writeSyncConfig(config: SyncFileConfig): boolean; diff --git a/dist/utils/write-sync-config.js b/dist/utils/write-sync-config.js new file mode 100644 index 0000000..039fa39 --- /dev/null +++ b/dist/utils/write-sync-config.js @@ -0,0 +1,18 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = writeSyncConfig; +const fs_1 = __importDefault(require("fs")); +const grab_dir_names_1 = __importDefault(require("./grab-dir-names")); +function writeSyncConfig(config) { + try { + const { syncConfigFilePath } = (0, grab_dir_names_1.default)(); + fs_1.default.writeFileSync(syncConfigFilePath, JSON.stringify(config), "utf-8"); + return true; + } + catch (error) { + return false; + } +} diff --git a/index.ts b/index.ts index 1636595..1a7a37f 100644 --- a/index.ts +++ b/index.ts @@ -9,17 +9,12 @@ import { TurboSyncConfigArray } from "./types"; declare global { var SYNC_SUCCESS_EXIT_CODE: number; var CONFIG_DIR: string; - var SYNCING_FILE: string; + var SYNCING: boolean; } const confFileProvidedPath = process.argv[process.argv.length - 1]; global.CONFIG_DIR = process.cwd(); -global.SYNCING_FILE = path.join(global.CONFIG_DIR, "syncing.txt"); - -if (fs.existsSync(global.SYNCING_FILE)) { - fs.unlinkSync(global.SYNCING_FILE); -} if (confFileProvidedPath === "--version" || confFileProvidedPath === "-v") { try { @@ -89,7 +84,6 @@ try { for (let i = 0; i < configArray.length; i++) { const config = configArray[i]; - console.log(`Syncing \`${config.title} ...\``); const childProcess = spawn( "node", diff --git a/lib/sync.ts b/lib/sync.ts index 3f8acf4..4ecb3ca 100644 --- a/lib/sync.ts +++ b/lib/sync.ts @@ -1,25 +1,31 @@ -import fs from "fs"; import { spawn } from "child_process"; import watchFiles from "./watch/files"; import watchFolders from "./watch/folders"; import { TurboSyncConfigObject } from "../types"; -import path from "path"; +import getLatestSource from "../utils/get-last-edited-src"; +import fldFileToStrArr, { + fldFileToStr, +} from "../utils/grab-folders-files-string-paths"; const confFileProvidedJSON = process.argv[process.argv.length - 1]; global.SYNC_SUCCESS_EXIT_CODE = 32; global.CONFIG_DIR = process.cwd(); -global.SYNCING_FILE = path.join(global.CONFIG_DIR, "syncing.txt"); +global.SYNCING = false; + +const REWATCH_TIMEOUT = 1000; try { - if (fs.existsSync(global.SYNCING_FILE)) { - process.exit(0); - } - const configFileObject: TurboSyncConfigObject = JSON.parse(confFileProvidedJSON); + const lastUpdated = getLatestSource({ + dirs: fldFileToStrArr(configFileObject.folders), + files: fldFileToStrArr(configFileObject.files), + }); + console.log(`Running '${configFileObject.title}' ...`); + console.log(`Last Updated Path => '${lastUpdated || "N/A"}' ...`); if ( Array.isArray(configFileObject.files) && @@ -35,10 +41,29 @@ try { const options = configFileObject.options; - if (firstFile && files?.[0]) { - watchFiles({ files, options }); - } else if (firstFolder && folders?.[0]) { - watchFolders({ folders, options }); + const sortedFoldersByLastUpdated = + folders?.[0] && lastUpdated + ? [ + lastUpdated, + ...(folders?.filter( + (fl) => fldFileToStr(fl) !== lastUpdated + ) || []), + ] + : folders; + + const sortedFilesByLastUpdated = + files?.[0] && lastUpdated + ? [ + lastUpdated, + ...(files?.filter((fl) => fldFileToStr(fl) !== lastUpdated) || + []), + ] + : files; + + if (firstFile && sortedFilesByLastUpdated?.[0]) { + watchFiles({ files: sortedFilesByLastUpdated, options }); + } else if (firstFolder && sortedFoldersByLastUpdated?.[0]) { + watchFolders({ folders: sortedFoldersByLastUpdated, options }); } } catch (error) { console.log(error); @@ -47,8 +72,6 @@ try { process.on("exit", (code) => { if (code == global.SYNC_SUCCESS_EXIT_CODE) { - fs.unlinkSync(global.SYNCING_FILE); - const args = process.argv; const cmd = args.shift(); if (cmd) { diff --git a/lib/watch/files.ts b/lib/watch/files.ts index f5e2125..eeb4109 100644 --- a/lib/watch/files.ts +++ b/lib/watch/files.ts @@ -1,14 +1,15 @@ -import { execSync } from "child_process"; import fs from "fs"; import delay from "../../utils/delay"; -import { SyncFilesFnParams, SyncFilesSyncFnParams } from "../../types"; +import { SyncFilesFnParams } from "../../types"; + +import sync from "../../utils/sync"; export default async function watchFiles({ files, options, }: SyncFilesFnParams) { let timeout: any; - const UPDATE_TIMEOUT = 2000; + const UPDATE_TIMEOUT = 1000; try { for (let i = 0; i < files.length; i++) { @@ -39,16 +40,6 @@ export default async function watchFiles({ } fs.writeFileSync(filePath, ""); - - if (typeof existingFilePath == "string") { - sync({ filePath: existingFilePath, files, options }); - } else { - sync({ - filePath: existingFilePath.path, - files, - options, - }); - } } catch (error: any) { throw new Error( `File Doesn't exist and couldn't be created. Please check if Directory exists.\nERROR => ${error.message}` @@ -62,8 +53,6 @@ export default async function watchFiles({ if (typeof file == "object" && file.host) { // TODO Handle SSH } else if (typeof file == "string") { - sync({ options, filePath, files }); - await delay(); fs.watchFile( @@ -72,7 +61,7 @@ export default async function watchFiles({ interval: interval || 200, }, (curr, prev) => { - if (fs.existsSync(global.SYNCING_FILE)) return; + if (global.SYNCING) return; const INTERVAL = options?.interval ? options.interval @@ -81,92 +70,43 @@ export default async function watchFiles({ clearTimeout(timeout); timeout = setTimeout(() => { - fs.writeFileSync( - global.SYNCING_FILE, - `SYNCING FILE: curr:${curr} :: prev:${prev}` - ); - sync({ options, filePath, files }); - process.exit(global.SYNC_SUCCESS_EXIT_CODE); + global.SYNCING = true; + + sync({ + options, + dirPath: filePath, + dirs: files, + isFiles: true, + }).finally(() => { + process.exit(global.SYNC_SUCCESS_EXIT_CODE); + }); }, INTERVAL); } ); } } + + const lastUpdatedFile = files[0]; + const lastUpdatedFilePath = + typeof lastUpdatedFile == "string" + ? lastUpdatedFile + : lastUpdatedFile.path; + + global.SYNCING = true; + + await sync({ + dirPath: lastUpdatedFilePath, + dirs: files, + options, + isFiles: true, + firstRun: true, + }); + + setTimeout(() => { + global.SYNCING = false; + }, UPDATE_TIMEOUT); } catch (error: any) { console.log("ERROR:", error.message); process.exit(0); } } - -function sync({ options, filePath, files }: SyncFilesSyncFnParams) { - const destFiles = files.filter((fl) => { - if (typeof fl == "string") return fl !== filePath; - if (fl?.path) return fl.path !== filePath; - return false; - }); - - for (let j = 0; j < destFiles.length; j++) { - let cmdArray = ["rsync", "-avh"]; - - if (options?.delete) { - cmdArray.push("--delete"); - } - - if (options?.exclude?.[0]) { - options.exclude.forEach((excl) => { - cmdArray.push(`--exclude '${excl}'`); - }); - } - - const dstFl = destFiles[j]; - if (typeof dstFl == "string") { - if (!fs.existsSync(dstFl)) continue; - - if (filePath === dstFl) { - console.log( - `You can't sync the same paths. Please check your configuration and resolve duplicate paths` - ); - process.exit(6); - } - - cmdArray.push(filePath, dstFl); - const cmd = cmdArray.join(" "); - console.log(`Running cmd 1 => ${cmd}`); - - execSync(cmd, { - stdio: "inherit", - }); - } else if (dstFl.path) { - if (!dstFl.host && !fs.existsSync(dstFl.path)) continue; - - if (filePath === dstFl.path) { - console.log( - `You can't sync the same paths. Please check your configuration and resolve duplicate paths` - ); - process.exit(6); - } - - if (dstFl.host && dstFl.ssh_key && dstFl.user) { - cmdArray.push("-e", `'ssh -i ${dstFl.ssh_key}'`); - cmdArray.push( - filePath, - `${dstFl.user}@${dstFl.host}:${dstFl.path}` - ); - const cmd = cmdArray.join(" "); - execSync(cmd, { - stdio: "inherit", - }); - } else { - cmdArray.push(filePath, dstFl.path); - - const cmd = cmdArray.join(" "); - - console.log(`Running cmd 2 => ${cmd}`); - - execSync(cmd, { - stdio: "inherit", - }); - } - } - } -} diff --git a/lib/watch/folders.ts b/lib/watch/folders.ts index 0965149..4d9fa63 100644 --- a/lib/watch/folders.ts +++ b/lib/watch/folders.ts @@ -1,22 +1,24 @@ import fs from "fs"; -import path from "path"; -import { execSync } from "child_process"; import delay from "../../utils/delay"; -import { SyncFoldersFnParams, SyncFoldersSyncFnParams } from "../../types"; +import { SyncFoldersFnParams } from "../../types"; +import sync from "../../utils/sync"; export default async function watchFolders({ folders, options, }: SyncFoldersFnParams) { let timeout: any; - let isSyncing = false; - const UPDATE_TIMEOUT = 500; + const UPDATE_TIMEOUT = 1000; try { const dirs = folders; + console.log("global.SYNCING", global.SYNCING); console.log(`Now handling ${dirs.length} Directories`); + /** + * # Watch Directories + */ const INTERVAL = options?.interval ? options.interval : UPDATE_TIMEOUT; for (let i = 0; i < dirs.length; i++) { @@ -28,6 +30,9 @@ export default async function watchFolders({ } const dirPath = typeof dir == "string" ? dir : dir.path; + + console.log("global.SYNCING", global.SYNCING); + if ( (typeof dir == "string" && !fs.existsSync(dirPath)) || (typeof dir == "object" && @@ -55,22 +60,6 @@ export default async function watchFolders({ fs.mkdirSync(dirPath, { recursive: true, }); - - if (typeof existingDirPath == "string") { - sync({ - dirPath: existingDirPath, - dirs, - options, - init: true, - }); - } else { - sync({ - dirPath: existingDirPath.path, - dirs, - options, - init: true, - }); - } } catch (error: any) { console.log("Error:", error.message); @@ -81,14 +70,12 @@ export default async function watchFolders({ } if (typeof dir == "string") { - sync({ dirPath, dirs, options }); - await delay(); fs.watch(dirPath, { recursive: true }, (evt, fileName) => { console.log("Folder Changed", evt, fileName); - if (fs.existsSync(global.SYNCING_FILE) || isSyncing) { + if (global.SYNCING) { console.log("Existing Sync found. Returning ..."); return; } @@ -97,106 +84,40 @@ export default async function watchFolders({ timeout = setTimeout(() => { console.log("Folder Syncing in progress ..."); - console.log( - `Writing Sync File =>${global.SYNCING_FILE}` - ); - fs.writeFileSync( - global.SYNCING_FILE, - `SYNCING Folder: evt:${evt} :: fileName:${fileName}` - ); - isSyncing = true; - sync({ dirPath, dirs, options }); - setTimeout(() => { + + global.SYNCING = true; + + sync({ dirPath, dirs, options }).finally(() => { process.exit(global.SYNC_SUCCESS_EXIT_CODE); - }, INTERVAL); + }); }, INTERVAL); }); } } + + /** + * # Sync Last Updated + */ + const lastUpdatedDir = dirs[0]; + const lastUpdatedDirPath = + typeof lastUpdatedDir == "string" + ? lastUpdatedDir + : lastUpdatedDir.path; + + global.SYNCING = true; + + await sync({ + dirPath: lastUpdatedDirPath, + dirs, + options, + firstRun: true, + }); + + setTimeout(() => { + global.SYNCING = false; + }, UPDATE_TIMEOUT); } catch (error: any) { console.log("ERROR:", error.message); process.exit(0); } } - -function sync({ options, dirs, dirPath, init }: SyncFoldersSyncFnParams) { - const dstDirs = dirs.filter((dr) => { - if (typeof dr == "string") return dr !== dirPath; - if (dr?.path) return dr.path !== dirPath; - return false; - }); - - for (let j = 0; j < dstDirs.length; j++) { - let cmdArray = ["rsync", "-avh"]; - - if (options?.delete) { - cmdArray.push("--delete"); - } - - if (options?.include?.[0]) { - options.include.forEach((incl) => { - cmdArray.push(`--include='${incl}'`); - }); - } - - if (options?.exclude?.[0]) { - options.exclude.forEach((excl) => { - cmdArray.push(`--exclude='${excl}'`); - }); - } - - const dstDr = dstDirs[j]; - if (typeof dstDr == "string") { - if (!fs.existsSync(dstDr)) continue; - - if (dirPath === dstDr) { - console.log( - `You can't sync the same paths. Please check your configuration and resolve duplicate paths` - ); - process.exit(6); - } - - cmdArray.push( - path.normalize(dirPath) + "/", - path.normalize(dstDr) + "/" - ); - const cmd = cmdArray.join(" "); - - execSync(cmd, { - stdio: "inherit", - }); - } else if (dstDr.path) { - if (!dstDr.host && !fs.existsSync(dstDr.path)) continue; - - if (dirPath === dstDr.path) { - console.log( - `You can't sync the same paths. Please check your configuration and resolve duplicate paths` - ); - process.exit(6); - } - - if (dstDr.host && dstDr.ssh_key && dstDr.user) { - cmdArray.push("-e", `'ssh -i ${dstDr.ssh_key}'`); - cmdArray.push( - path.normalize(dirPath) + "/", - `${dstDr.user}@${dstDr.host}:${dstDr.path}/` - ); - const cmd = cmdArray.join(" "); - execSync(cmd, { - stdio: "inherit", - }); - } else { - cmdArray.push( - path.normalize(dirPath), - path.normalize(dstDr.path) - ); - const cmd = cmdArray.join(" "); - execSync(cmd, { - stdio: "inherit", - }); - } - } - } - - console.log("Folder Sync Complete. Exiting ..."); -} diff --git a/package.json b/package.json index 2d1552a..b98b4ac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@moduletrace/turbosync", - "version": "1.2.2", + "version": "1.2.3", "module": "dist/index.js", "scripts": { "start": "node dist/index.js", diff --git a/types/index.ts b/types/index.ts index f02c29e..46d30bd 100644 --- a/types/index.ts +++ b/types/index.ts @@ -42,9 +42,17 @@ export interface SyncFoldersSyncFnParams { dirs: (string | TurboSyncFileObject)[]; options: TurboSyncOptions | undefined; dirPath: string; - init?: boolean; + firstRun?: boolean; + isFiles?: boolean; } export interface HandleEnvVarsFnParams { json: string; } + +export const TurboSyncStatuses = ["syncing", "error", "done"] as const; + +export type SyncFileConfig = { + status?: (typeof TurboSyncStatuses)[number]; + lastSyncedPath?: string; +}; diff --git a/utils/get-last-edited-src.ts b/utils/get-last-edited-src.ts new file mode 100644 index 0000000..9b3974c --- /dev/null +++ b/utils/get-last-edited-src.ts @@ -0,0 +1,67 @@ +import fs from "fs"; +import path from "path"; + +type Params = { + dirs?: string[]; + files?: string[]; +}; + +export default function getLatestSource({ + dirs, + files, +}: Params): string | undefined { + let latestDir = undefined; + let latestMtime = 0; + + const isFiles = files?.[0]; + + const finalPaths = isFiles ? files : dirs; + + if (!finalPaths) return undefined; + + for (const pth of finalPaths) { + try { + const stats = fs.statSync(pth); + const pathMtime = stats.isDirectory() + ? getLatestDirMtime(pth) + : stats.mtimeMs; + + if (pathMtime > latestMtime) { + latestMtime = pathMtime; + latestDir = pth; + } + } catch (error: any) { + console.error(`Error accessing ${pth}: ${error.message}`); + } + } + + return latestDir; +} + +function getLatestDirMtime(dir: string) { + let latestMtime = 0; + + try { + const stats = fs.statSync(dir); + if (stats.isDirectory()) { + latestMtime = stats.mtimeMs; + + const entries = fs.readdirSync(dir, { withFileTypes: true }); + + for (const entry of entries) { + const entryPath = path.join(dir, entry.name); + if (entry.isDirectory()) { + const subMtime = getLatestDirMtime(entryPath); + latestMtime = Math.max(latestMtime, subMtime); + } else if (entry.isFile()) { + const fileStats = fs.statSync(entryPath); + latestMtime = Math.max(latestMtime, fileStats.mtimeMs); + } + } + } + } catch (error: any) { + console.error(`Error accessing ${dir}: ${error.message}`); + } + + return latestMtime; +} diff --git a/utils/get-sync-config.ts b/utils/get-sync-config.ts new file mode 100644 index 0000000..5a677f1 --- /dev/null +++ b/utils/get-sync-config.ts @@ -0,0 +1,18 @@ +import fs from "fs"; +import grabDirNames from "./grab-dir-names"; +import { SyncFileConfig } from "../types"; + +export default function getSyncConfig(): SyncFileConfig { + try { + const { syncConfigFilePath } = grabDirNames(); + if (!fs.existsSync(syncConfigFilePath)) { + fs.writeFileSync(syncConfigFilePath, JSON.stringify({}), "utf-8"); + return {}; + } + + const syncConfigJSON = fs.readFileSync(syncConfigFilePath, "utf-8"); + return JSON.parse(syncConfigJSON); + } catch (error) { + return { status: "error" }; + } +} diff --git a/utils/grab-dir-names.ts b/utils/grab-dir-names.ts new file mode 100644 index 0000000..90a0c92 --- /dev/null +++ b/utils/grab-dir-names.ts @@ -0,0 +1,9 @@ +import path from "path"; + +export default function grabDirNames() { + const rootDir = process.cwd(); + const syncConfigFileName = "__trsyc.json"; + const syncConfigFilePath = path.join(rootDir, syncConfigFileName); + + return { rootDir, syncConfigFileName, syncConfigFilePath }; +} diff --git a/utils/grab-folders-files-string-paths.ts b/utils/grab-folders-files-string-paths.ts new file mode 100644 index 0000000..133c6c5 --- /dev/null +++ b/utils/grab-folders-files-string-paths.ts @@ -0,0 +1,31 @@ +import { TurboSyncFileObject } from "../types"; + +export default function fldFileToStrArr( + srces?: (string | TurboSyncFileObject)[] +) { + if (!srces) return undefined; + + let arr: string[] = []; + + for (let i = 0; i < srces.length; i++) { + const src = srces[i]; + const srcStr = fldFileToStr(src); + if (srcStr) { + arr.push(srcStr); + } + } + + return arr; +} + +export function fldFileToStr(src?: string | TurboSyncFileObject) { + if (!src) return undefined; + + if (typeof src == "string") { + return src; + } else if (typeof src == "object" && src.path) { + return src.path; + } + + return undefined; +} diff --git a/utils/sync.ts b/utils/sync.ts new file mode 100644 index 0000000..fe08336 --- /dev/null +++ b/utils/sync.ts @@ -0,0 +1,95 @@ +import fs from "fs"; +import path from "path"; +import util from "util"; +import { exec } from "child_process"; +import { SyncFoldersSyncFnParams } from "../types"; + +const execPromise = util.promisify(exec); + +export default async function sync({ + options, + dirs, + dirPath, + isFiles, + firstRun, +}: SyncFoldersSyncFnParams) { + const dstDirs = dirs.filter((dr) => { + if (typeof dr == "string") return dr !== dirPath; + if (dr?.path) return dr.path !== dirPath; + return false; + }); + + const rsyncTrailingSlash = isFiles ? "" : "/"; + + const allCommandsArr: string[][] = []; + + for (let j = 0; j < dstDirs.length; j++) { + const dstDr = dstDirs[j]; + + let cmdArray = ["rsync", firstRun ? "-az" : "-azu", "--inplace"]; + + if (options?.delete) { + cmdArray.push("--delete"); + } + + if (options?.include?.[0]) { + options.include.forEach((incl) => { + cmdArray.push(`--include='${incl}'`); + }); + } + + if (options?.exclude?.[0]) { + options.exclude.forEach((excl) => { + cmdArray.push(`--exclude='${excl}'`); + }); + } + + if (typeof dstDr == "string") { + if (!fs.existsSync(dstDr)) continue; + + if (dirPath === dstDr) { + console.log( + `You can't sync the same paths. Please check your configuration and resolve duplicate paths` + ); + process.exit(6); + } + + cmdArray.push( + path.normalize(dirPath) + rsyncTrailingSlash, + path.normalize(dstDr) + rsyncTrailingSlash + ); + } else if (dstDr.path) { + if (!dstDr.host && !fs.existsSync(dstDr.path)) continue; + + if (dirPath === dstDr.path) { + console.log( + `You can't sync the same paths. Please check your configuration and resolve duplicate paths` + ); + process.exit(6); + } + + if (dstDr.host && dstDr.ssh_key && dstDr.user) { + cmdArray.push("-e", `'ssh -i ${dstDr.ssh_key}'`); + cmdArray.push( + path.normalize(dirPath) + rsyncTrailingSlash, + `${dstDr.user}@${dstDr.host}:${dstDr.path}${rsyncTrailingSlash}` + ); + } else { + cmdArray.push( + path.normalize(dirPath), + path.normalize(dstDr.path) + ); + } + } + + allCommandsArr.push(cmdArray); + } + + await Promise.all( + allCommandsArr.map((cmdArr) => { + return execPromise(cmdArr.join(" ")); + }) + ); + + console.log(`${dirPath} Folder Sync Complete. Exiting ...`); +} diff --git a/utils/write-sync-config.ts b/utils/write-sync-config.ts new file mode 100644 index 0000000..9fe86ce --- /dev/null +++ b/utils/write-sync-config.ts @@ -0,0 +1,13 @@ +import fs from "fs"; +import grabDirNames from "./grab-dir-names"; +import { SyncFileConfig } from "../types"; + +export default function writeSyncConfig(config: SyncFileConfig): boolean { + try { + const { syncConfigFilePath } = grabDirNames(); + fs.writeFileSync(syncConfigFilePath, JSON.stringify(config), "utf-8"); + return true; + } catch (error) { + return false; + } +}